1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @author Sébastien Gross <seb-awesome@chezwam.org>
4 -- @copyright 2008 Julien Danjou, Sébastien Gross
5 -- @release @AWESOME_VERSION@
6 ---------------------------------------------------------------------------
8 -- Grab environment we need
15 local util
= require("awful.util")
17 --- Completion module.
18 -- This module store a set of function using shell to complete commands name.
19 module("awful.completion")
21 -- mapping of command/completion function
22 local bashcomp_funcs
= {}
23 local bashcomp_src
= "@SYSCONFDIR@/bash_completion"
25 --- Enable programmable bash completion in awful.completion.bash at the price of
27 -- @param src The bash completion source file, /etc/bash_completion by default.
28 function bashcomp_load(src
)
29 if src
then bashcomp_src
= src
end
30 local c
, err
= io
.popen("/usr/bin/env bash -c 'source " .. bashcomp_src
.. "; complete -p'")
33 local line
= c
:read("*line")
34 if not line
then break end
35 -- if a bash function is used for completion, register it
36 if line
:match(".* -F .*") then
37 bashcomp_funcs
[line
:gsub(".* (%S+)$","%1")] = line
:gsub(".*-F +(%S+) .*$", "%1")
46 local function bash_escape(str
)
47 str
= str
:gsub(" ", "\\ ")
48 str
= str
:gsub("%[", "\\[")
49 str
= str
:gsub("%]", "\\]")
50 str
= str
:gsub("%(", "\\(")
51 str
= str
:gsub("%)", "\\)")
55 --- Use shell completion system to complete command and filename.
56 -- @param command The command line.
57 -- @param cur_pos The cursor position.
58 -- @param ncomp The element number to complete.
59 -- @param shell The shell to use for completion (bash (default) or zsh).
60 -- @return The new command, the new cursor position, the table of all matches.
61 function shell(command
, cur_pos
, ncomp
, shell
)
69 local comptype
= "file"
71 -- do nothing if we are on a letter, i.e. not at len + 1 or on a space
72 if cur_pos
~= #command
+ 1 and command
:sub(cur_pos
, cur_pos
) ~= " " then
73 return command
, cur_pos
74 elseif #command
== 0 then
75 return command
, cur_pos
78 while wend
<= #command
do
79 wend
= command
:find(" ", wstart
)
80 if not wend
then wend
= #command
+ 1 end
81 table.insert(words
, command
:sub(wstart
, wend
- 1))
82 if cur_pos
>= wstart
and cur_pos
<= wend
+ 1 then
91 if cword_index
== 1 then
96 if shell
== "zsh" or (not shell
and os
.getenv("SHELL"):match("zsh$")) then
97 if comptype
== "file" then
98 shell_cmd
= "/usr/bin/env zsh -c 'local -a res; res=( " .. words
[cword_index
] .. "* ); print -l -- ${res[@]}'"
100 -- check commands, aliases, builtins, functions and reswords
101 shell_cmd
= "/usr/bin/env zsh -c 'local -a res; "..
103 "\"${(k)commands[@]}\" \"${(k)aliases[@]}\" \"${(k)builtins[@]}\" \"${(k)functions[@]}\" \"${(k)reswords[@]}\" "..
105 "print -l -- ${(M)res[@]:#"..words
[cword_index
].."*}'"
108 if bashcomp_funcs
[words
[1]]
then
109 -- fairly complex command with inline bash script to get the possible completions
110 shell_cmd
= "/usr/bin/env bash -c 'source " .. bashcomp_src
.. "; " ..
111 "__print_completions() { for ((i=0;i<${#COMPREPLY[*]};i++)); do echo ${COMPREPLY[i]}; done }; " ..
112 "COMP_WORDS=(" .. command
.."); COMP_LINE=\"" .. command
.. "\"; " ..
113 "COMP_COUNT=" .. cur_pos
.. "; COMP_CWORD=" .. cword_index
-1 .. "; " ..
114 bashcomp_funcs
[words
[1]]
.. "; __print_completions'"
116 shell_cmd
= "/usr/bin/env bash -c 'compgen -A " .. comptype
.. " " .. words
[cword_index
] .. "'"
119 local c
, err
= io
.popen(shell_cmd
.. " | sort -u")
124 local line
= c
:read("*line")
125 if not line
then break end
126 if os
.execute("test -d " .. line
) == 0 then
129 table.insert(output
, bash_escape(line
))
137 -- no completion, return
139 return command
, cur_pos
143 while ncomp
> #output
do
144 ncomp
= ncomp
- #output
147 local str
= command
:sub(1, cword_start
- 1) .. output
[ncomp
] .. command
:sub(cword_end
)
148 cur_pos
= cword_end
+ #output
[ncomp
] + 1
150 return str
, cur_pos
, output
153 --- Run a generic completion.
154 -- For this function to run properly the awful.completion.keyword table should
155 -- be fed up with all keywords. The completion is run against these keywords.
156 -- @param text The current text the user had typed yet.
157 -- @param cur_pos The current cursor position.
158 -- @param ncomp The number of yet requested completion using current text.
159 -- @param keywords The keywords table uised for completion.
160 -- @return The new match, the new cursor position, the table of all matches.
161 function generic(text
, cur_pos
, ncomp
, keywords
)
162 -- The keywords table may be empty
163 if #keywords
== 0 then
164 return text
, #text
+ 1
167 -- if no text had been typed yet, then we could start cycling around all
168 -- keywords with out filtering and move the cursor at the end of keyword
169 if text
== nil or #text
== 0 then
170 ncomp
= math
.mod(ncomp
- 1, #keywords
) + 1
171 return keywords
[ncomp
], #keywords
[ncomp
] + 2
174 -- Filter out only keywords starting with text
176 for _
, x
in pairs(keywords
) do
177 if x
:sub(1, #text
) == text
then
178 table.insert(matches
, x
)
182 -- if there are no matches just leave out with the current text and position
183 if #matches
== 0 then
184 return text
, #text
+ 1, matches
187 -- cycle around all matches
188 ncomp
= math
.mod(ncomp
- 1, #matches
) + 1
189 return matches
[ncomp
], #matches
[ncomp
] + 1, matches
192 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80