tag.lua: move() re-index tags
[awesome.git] / lib / awful / completion.lua.in
blobdb0391d73e9ebe5427971fe731dfbae44652dc45
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
9 local io = io
10 local os = os
11 local table = table
12 local math = math
13 local print = print
14 local util = require("awful.util")
16 --- Completion module.
17 -- This module store a set of function using shell to complete commands name.
18 module("awful.completion")
20 -- mapping of command/completion function
21 local bashcomp_funcs = {}
22 local bashcomp_src = "@SYSCONFDIR@/bash_completion"
24 --- Enable programmable bash completion in awful.completion.bash at the price of
25 -- a slight overhead.
26 -- @param src The bash completion source file, /etc/bash_completion by default.
27 function bashcomp_load(src)
28 if src then bashcomp_src = src end
29 local c, err = io.popen("/usr/bin/env bash -c 'source " .. bashcomp_src .. "; complete -p'")
30 if c then
31 while true do
32 local line = c:read("*line")
33 if not line then break end
34 -- if a bash function is used for completion, register it
35 if line:match(".* -F .*") then
36 bashcomp_funcs[line:gsub(".* (%S+)$","%1")] = line:gsub(".*-F +(%S+) .*$", "%1")
37 end
38 end
39 c:close()
40 else
41 print(err)
42 end
43 end
45 local function bash_escape(str)
46 str = str:gsub(" ", "\\ ")
47 str = str:gsub("%[", "\\[")
48 str = str:gsub("%]", "\\]")
49 str = str:gsub("%(", "\\(")
50 str = str:gsub("%)", "\\)")
51 return str
52 end
54 --- Use shell completion system to complete command and filename.
55 -- @param command The command line.
56 -- @param cur_pos The cursor position.
57 -- @param ncomp The element number to complete.
58 -- @param shell The shell to use for completion (bash (default) or zsh).
59 -- @return The new command and the new cursor position.
60 function shell(command, cur_pos, ncomp, shell)
61 local wstart = 1
62 local wend = 1
63 local words = {}
64 local cword_index = 0
65 local cword_start = 0
66 local cword_end = 0
67 local i = 1
68 local comptype = "file"
70 -- do nothing if we are on a letter, i.e. not at len + 1 or on a space
71 if cur_pos ~= #command + 1 and command:sub(cur_pos, cur_pos) ~= " " then
72 return command, cur_pos
73 elseif #command == 0 then
74 return command, cur_pos
75 end
77 while wend <= #command do
78 wend = command:find(" ", wstart)
79 if not wend then wend = #command + 1 end
80 table.insert(words, command:sub(wstart, wend - 1))
81 if cur_pos >= wstart and cur_pos <= wend + 1 then
82 cword_start = wstart
83 cword_end = wend
84 cword_index = i
85 end
86 wstart = wend + 1
87 i = i + 1
88 end
90 if cword_index == 1 then
91 comptype = "command"
92 end
94 local shell_cmd
95 if shell == "zsh" or (not shell and os.getenv("SHELL"):match("zsh$")) then
96 if comptype == "file" then
97 shell_cmd = "/usr/bin/env zsh -c 'local -a res; res=( " .. words[cword_index] .. "* ); print -l -- ${res[@]}'"
98 else
99 -- check commands, aliases, builtins, functions and reswords
100 shell_cmd = "/usr/bin/env zsh -c 'local -a res; "..
101 "res=( "..
102 "\"${(k)commands[@]}\" \"${(k)aliases[@]}\" \"${(k)builtins[@]}\" \"${(k)functions[@]}\" \"${(k)reswords[@]}\" "..
103 "); "..
104 "print -l -- ${(M)res[@]:#"..words[cword_index].."*}'"
106 else
107 if bashcomp_funcs[words[1]] then
108 -- fairly complex command with inline bash script to get the possible completions
109 shell_cmd = "/usr/bin/env bash -c 'source " .. bashcomp_src .. "; " ..
110 "__print_completions() { for ((i=0;i<${#COMPREPLY[*]};i++)); do echo ${COMPREPLY[i]}; done }; " ..
111 "COMP_WORDS=(" .. command .."); COMP_LINE=\"" .. command .. "\"; " ..
112 "COMP_COUNT=" .. cur_pos .. "; COMP_CWORD=" .. cword_index-1 .. "; " ..
113 bashcomp_funcs[words[1]] .. "; __print_completions'"
114 else
115 shell_cmd = "/usr/bin/env bash -c 'compgen -A " .. comptype .. " " .. words[cword_index] .. "'"
118 local c, err = io.popen(shell_cmd .. " | sort -u")
119 local output = {}
120 i = 0
121 if c then
122 while true do
123 local line = c:read("*line")
124 if not line then break end
125 if os.execute("test -d " .. line) == 0 then
126 line = line .. "/"
128 table.insert(output, bash_escape(line))
131 c:close()
132 else
133 print(err)
136 -- no completion, return
137 if #output == 0 then
138 return command, cur_pos
141 -- cycle
142 while ncomp > #output do
143 ncomp = ncomp - #output
146 local str = command:sub(1, cword_start - 1) .. output[ncomp] .. command:sub(cword_end)
147 cur_pos = cword_end + #output[ncomp] + 1
149 return str, cur_pos
152 --- Run a generic completion.
153 -- For this function to run properly the awful.completion.keyword table should
154 -- be fed up with all keywords. The completion is run against these keywords.
155 -- @param text The current text the user had typed yet.
156 -- @param cur_pos The current cursor position.
157 -- @param ncomp The number of yet requested completion using current text.
158 -- @param keywords The keywords table uised for completion.
159 -- @return The new match and the new cursor position.
160 function generic(text, cur_pos, ncomp, keywords)
161 -- The keywords table may be empty
162 if #keywords == 0 then
163 return text, #text + 1
166 -- if no text had been typed yet, then we could start cycling around all
167 -- keywords with out filtering and move the cursor at the end of keyword
168 if text == nil or #text == 0 then
169 ncomp = math.mod(ncomp - 1, #keywords) + 1
170 return keywords[ncomp], #keywords[ncomp] + 2
173 -- Filter out only keywords starting with text
174 local matches = {}
175 table.foreach(keywords, function(_, x)
176 if x:sub(1 , #text) == text then
177 table.insert(matches, x)
179 end)
181 -- if there are no matches just leave out with the current text and position
182 if #matches == 0 then
183 return text, #text + 1
186 -- cycle around all matches
187 ncomp = math.mod(ncomp - 1, #matches) + 1
188 return matches[ncomp], #matches[ncomp] + 1
191 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80