2008-09-29 16:49:18 +02:00
|
|
|
---------------------------------------------------------------------------
|
2014-05-20 13:02:39 +02:00
|
|
|
--- Completion module.
|
|
|
|
--
|
|
|
|
-- This module store a set of function using shell to complete commands name.
|
|
|
|
--
|
2008-09-29 16:49:18 +02:00
|
|
|
-- @author Julien Danjou <julien@danjou.info>
|
2008-11-25 21:03:58 +01:00
|
|
|
-- @author Sébastien Gross <seb-awesome@chezwam.org>
|
|
|
|
-- @copyright 2008 Julien Danjou, Sébastien Gross
|
2008-09-29 16:49:18 +02:00
|
|
|
-- @release @AWESOME_VERSION@
|
2014-05-20 13:02:39 +02:00
|
|
|
-- @module awful.completion
|
2008-09-29 16:49:18 +02:00
|
|
|
---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- Grab environment we need
|
|
|
|
local io = io
|
2009-01-23 19:02:48 +01:00
|
|
|
local os = os
|
2008-09-29 16:49:18 +02:00
|
|
|
local table = table
|
2008-11-25 21:03:58 +01:00
|
|
|
local math = math
|
2008-12-11 22:14:07 +01:00
|
|
|
local print = print
|
2012-03-06 00:07:41 +01:00
|
|
|
local pairs = pairs
|
2012-05-06 23:35:07 +02:00
|
|
|
local string = string
|
2009-02-26 12:20:13 +01:00
|
|
|
local util = require("awful.util")
|
2008-09-29 16:49:18 +02:00
|
|
|
|
2012-06-12 20:13:09 +02:00
|
|
|
local completion = {}
|
2008-09-29 16:49:18 +02:00
|
|
|
|
|
|
|
-- mapping of command/completion function
|
|
|
|
local bashcomp_funcs = {}
|
2009-01-05 18:07:06 +01:00
|
|
|
local bashcomp_src = "@SYSCONFDIR@/bash_completion"
|
2008-09-29 16:49:18 +02:00
|
|
|
|
|
|
|
--- Enable programmable bash completion in awful.completion.bash at the price of
|
2009-08-28 15:32:38 +02:00
|
|
|
-- a slight overhead.
|
2008-11-07 14:27:54 +01:00
|
|
|
-- @param src The bash completion source file, /etc/bash_completion by default.
|
2012-06-12 20:13:09 +02:00
|
|
|
function completion.bashcomp_load(src)
|
2008-09-29 16:49:18 +02:00
|
|
|
if src then bashcomp_src = src end
|
2008-12-11 22:14:07 +01:00
|
|
|
local c, err = io.popen("/usr/bin/env bash -c 'source " .. bashcomp_src .. "; complete -p'")
|
|
|
|
if c then
|
|
|
|
while true do
|
|
|
|
local line = c:read("*line")
|
|
|
|
if not line then break end
|
|
|
|
-- if a bash function is used for completion, register it
|
|
|
|
if line:match(".* -F .*") then
|
|
|
|
bashcomp_funcs[line:gsub(".* (%S+)$","%1")] = line:gsub(".*-F +(%S+) .*$", "%1")
|
|
|
|
end
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
2008-12-11 22:14:07 +01:00
|
|
|
c:close()
|
|
|
|
else
|
|
|
|
print(err)
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2008-12-25 18:57:39 +01:00
|
|
|
local function bash_escape(str)
|
2009-01-10 14:13:07 +01:00
|
|
|
str = str:gsub(" ", "\\ ")
|
|
|
|
str = str:gsub("%[", "\\[")
|
|
|
|
str = str:gsub("%]", "\\]")
|
|
|
|
str = str:gsub("%(", "\\(")
|
|
|
|
str = str:gsub("%)", "\\)")
|
|
|
|
return str
|
2008-12-25 18:57:39 +01:00
|
|
|
end
|
|
|
|
|
2009-02-26 12:20:13 +01:00
|
|
|
--- Use shell completion system to complete command and filename.
|
2008-09-29 16:49:18 +02:00
|
|
|
-- @param command The command line.
|
|
|
|
-- @param cur_pos The cursor position.
|
|
|
|
-- @param ncomp The element number to complete.
|
2009-02-26 12:20:13 +01:00
|
|
|
-- @param shell The shell to use for completion (bash (default) or zsh).
|
2010-10-11 12:34:58 +02:00
|
|
|
-- @return The new command, the new cursor position, the table of all matches.
|
2012-06-12 20:13:09 +02:00
|
|
|
function completion.shell(command, cur_pos, ncomp, shell)
|
2008-09-29 16:49:18 +02:00
|
|
|
local wstart = 1
|
|
|
|
local wend = 1
|
|
|
|
local words = {}
|
|
|
|
local cword_index = 0
|
|
|
|
local cword_start = 0
|
|
|
|
local cword_end = 0
|
|
|
|
local i = 1
|
|
|
|
local comptype = "file"
|
|
|
|
|
|
|
|
-- do nothing if we are on a letter, i.e. not at len + 1 or on a space
|
|
|
|
if cur_pos ~= #command + 1 and command:sub(cur_pos, cur_pos) ~= " " then
|
|
|
|
return command, cur_pos
|
|
|
|
elseif #command == 0 then
|
|
|
|
return command, cur_pos
|
|
|
|
end
|
|
|
|
|
|
|
|
while wend <= #command do
|
|
|
|
wend = command:find(" ", wstart)
|
|
|
|
if not wend then wend = #command + 1 end
|
|
|
|
table.insert(words, command:sub(wstart, wend - 1))
|
|
|
|
if cur_pos >= wstart and cur_pos <= wend + 1 then
|
|
|
|
cword_start = wstart
|
|
|
|
cword_end = wend
|
|
|
|
cword_index = i
|
|
|
|
end
|
|
|
|
wstart = wend + 1
|
|
|
|
i = i + 1
|
|
|
|
end
|
|
|
|
|
2012-05-06 23:35:07 +02:00
|
|
|
if cword_index == 1 and not string.find(words[cword_index], "/") then
|
2008-09-29 16:49:18 +02:00
|
|
|
comptype = "command"
|
|
|
|
end
|
|
|
|
|
2009-02-26 12:20:13 +01:00
|
|
|
local shell_cmd
|
|
|
|
if shell == "zsh" or (not shell and os.getenv("SHELL"):match("zsh$")) then
|
|
|
|
if comptype == "file" then
|
2015-03-31 00:11:20 +02:00
|
|
|
-- NOTE: ${~:-"..."} turns on GLOB_SUBST, useful for expansion of
|
|
|
|
-- "~/" ($HOME). ${:-"foo"} is the string "foo" as var.
|
|
|
|
shell_cmd = "/usr/bin/env zsh -c 'local -a res; res=( ${~:-"
|
|
|
|
.. string.format('%q', words[cword_index]) .. "}* ); "
|
|
|
|
.. "print -ln -- ${res[@]}'"
|
2009-02-26 12:20:13 +01:00
|
|
|
else
|
|
|
|
-- check commands, aliases, builtins, functions and reswords
|
|
|
|
shell_cmd = "/usr/bin/env zsh -c 'local -a res; "..
|
|
|
|
"res=( "..
|
|
|
|
"\"${(k)commands[@]}\" \"${(k)aliases[@]}\" \"${(k)builtins[@]}\" \"${(k)functions[@]}\" \"${(k)reswords[@]}\" "..
|
2012-05-06 23:35:07 +02:00
|
|
|
"${PWD}/*(:t)"..
|
2009-02-26 12:20:13 +01:00
|
|
|
"); "..
|
2015-01-21 09:23:47 +01:00
|
|
|
"print -ln -- ${(M)res[@]:#" .. string.format('%q', words[cword_index]) .. "*}'"
|
2009-02-26 12:20:13 +01:00
|
|
|
end
|
2008-09-29 16:49:18 +02:00
|
|
|
else
|
2009-02-26 12:20:13 +01:00
|
|
|
if bashcomp_funcs[words[1]] then
|
|
|
|
-- fairly complex command with inline bash script to get the possible completions
|
|
|
|
shell_cmd = "/usr/bin/env bash -c 'source " .. bashcomp_src .. "; " ..
|
|
|
|
"__print_completions() { for ((i=0;i<${#COMPREPLY[*]};i++)); do echo ${COMPREPLY[i]}; done }; " ..
|
|
|
|
"COMP_WORDS=(" .. command .."); COMP_LINE=\"" .. command .. "\"; " ..
|
|
|
|
"COMP_COUNT=" .. cur_pos .. "; COMP_CWORD=" .. cword_index-1 .. "; " ..
|
2009-09-22 17:15:49 +02:00
|
|
|
bashcomp_funcs[words[1]] .. "; __print_completions'"
|
2009-02-26 12:20:13 +01:00
|
|
|
else
|
2015-01-21 09:23:47 +01:00
|
|
|
shell_cmd = "/usr/bin/env bash -c 'compgen -A " .. comptype .. " "
|
|
|
|
.. string.format('%q', words[cword_index]) .. "'"
|
2009-02-26 12:20:13 +01:00
|
|
|
end
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
2009-09-22 17:15:49 +02:00
|
|
|
local c, err = io.popen(shell_cmd .. " | sort -u")
|
2008-09-29 16:49:18 +02:00
|
|
|
local output = {}
|
|
|
|
i = 0
|
2008-12-11 22:14:07 +01:00
|
|
|
if c then
|
|
|
|
while true do
|
|
|
|
local line = c:read("*line")
|
|
|
|
if not line then break end
|
2015-01-21 09:23:47 +01:00
|
|
|
if os.execute("test -d " .. string.format('%q', line)) == 0 then
|
2009-01-23 19:02:48 +01:00
|
|
|
line = line .. "/"
|
|
|
|
end
|
2008-12-25 18:57:39 +01:00
|
|
|
table.insert(output, bash_escape(line))
|
2008-12-11 22:14:07 +01:00
|
|
|
end
|
2008-09-29 16:49:18 +02:00
|
|
|
|
2008-12-11 22:14:07 +01:00
|
|
|
c:close()
|
|
|
|
else
|
|
|
|
print(err)
|
|
|
|
end
|
2008-09-29 16:49:18 +02:00
|
|
|
|
|
|
|
-- no completion, return
|
|
|
|
if #output == 0 then
|
|
|
|
return command, cur_pos
|
|
|
|
end
|
|
|
|
|
2009-09-22 17:15:49 +02:00
|
|
|
-- cycle
|
2008-09-29 16:49:18 +02:00
|
|
|
while ncomp > #output do
|
|
|
|
ncomp = ncomp - #output
|
|
|
|
end
|
|
|
|
|
|
|
|
local str = command:sub(1, cword_start - 1) .. output[ncomp] .. command:sub(cword_end)
|
|
|
|
cur_pos = cword_end + #output[ncomp] + 1
|
|
|
|
|
2010-10-11 12:34:58 +02:00
|
|
|
return str, cur_pos, output
|
2008-09-29 16:49:18 +02:00
|
|
|
end
|
|
|
|
|
2008-11-25 21:03:58 +01:00
|
|
|
--- Run a generic completion.
|
|
|
|
-- For this function to run properly the awful.completion.keyword table should
|
|
|
|
-- be fed up with all keywords. The completion is run against these keywords.
|
|
|
|
-- @param text The current text the user had typed yet.
|
|
|
|
-- @param cur_pos The current cursor position.
|
|
|
|
-- @param ncomp The number of yet requested completion using current text.
|
awful.completion: remove keywords global variable
* move keywords global variable to generic() last parameter.
This prevents from having table clash.
Please udate you configuration according this feature in your
awful.prompt.run() calls.
If keywords parameter is missing then no completion would be
done.
Before:
awful.completion.keywords = kw
awful.prompt.run( [ ... ],
function(t, p, n) return awful.completion.generic(t, p, n) end,
[ ... ] )
Now:
awful.prompt.run( [ ... ],
function(t, p, n) return awful.completion.generic(t, p, n, kw) end,
[ ... ] )
Signed-off-by: Sébastien Gross <seb-awesome@chezwam.org>
Signed-off-by: Julien Danjou <julien@danjou.info>
2008-12-18 13:18:07 +01:00
|
|
|
-- @param keywords The keywords table uised for completion.
|
2010-10-11 12:34:58 +02:00
|
|
|
-- @return The new match, the new cursor position, the table of all matches.
|
2012-06-12 20:13:09 +02:00
|
|
|
function completion.generic(text, cur_pos, ncomp, keywords)
|
2008-11-25 21:03:58 +01:00
|
|
|
-- The keywords table may be empty
|
|
|
|
if #keywords == 0 then
|
|
|
|
return text, #text + 1
|
|
|
|
end
|
|
|
|
|
|
|
|
-- if no text had been typed yet, then we could start cycling around all
|
|
|
|
-- keywords with out filtering and move the cursor at the end of keyword
|
|
|
|
if text == nil or #text == 0 then
|
2012-06-14 22:48:18 +02:00
|
|
|
ncomp = math.fmod(ncomp - 1, #keywords) + 1
|
2008-11-25 21:03:58 +01:00
|
|
|
return keywords[ncomp], #keywords[ncomp] + 2
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Filter out only keywords starting with text
|
|
|
|
local matches = {}
|
2012-03-06 00:07:41 +01:00
|
|
|
for _, x in pairs(keywords) do
|
|
|
|
if x:sub(1, #text) == text then
|
2008-11-25 21:03:58 +01:00
|
|
|
table.insert(matches, x)
|
|
|
|
end
|
2012-03-06 00:07:41 +01:00
|
|
|
end
|
2008-11-25 21:03:58 +01:00
|
|
|
|
|
|
|
-- if there are no matches just leave out with the current text and position
|
|
|
|
if #matches == 0 then
|
2010-10-11 12:34:58 +02:00
|
|
|
return text, #text + 1, matches
|
2008-11-25 21:03:58 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
-- cycle around all matches
|
2012-06-14 22:48:18 +02:00
|
|
|
ncomp = math.fmod(ncomp - 1, #matches) + 1
|
2010-10-11 12:34:58 +02:00
|
|
|
return matches[ncomp], #matches[ncomp] + 1, matches
|
2008-11-25 21:03:58 +01:00
|
|
|
end
|
|
|
|
|
2012-06-12 20:13:09 +02:00
|
|
|
return completion
|
|
|
|
|
2011-09-11 16:50:01 +02:00
|
|
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|