cmake: Remove useless program checks
[awesome.git] / lib / awful / completion.lua.in
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 ---------------------------------------------------------------------------
7
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")
15
16 --- Completion module.
17 -- This module store a set of function using shell to complete commands name.
18 module("awful.completion")
19
20 -- mapping of command/completion function
21 local bashcomp_funcs = {}
22 local bashcomp_src = "@SYSCONFDIR@/bash_completion"
23
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
44
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
53
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"
69
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
76
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
89
90     if cword_index == 1 then
91         comptype = "command"
92     end
93
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].."*}'"
105         end
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] .. "'"
116         end
117     end
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 .. "/"
127             end
128             table.insert(output, bash_escape(line))
129         end
130
131         c:close()
132     else
133         print(err)
134     end
135
136     -- no completion, return
137     if #output == 0 then
138         return command, cur_pos
139     end
140
141     -- cycle
142     while ncomp > #output do
143         ncomp = ncomp - #output
144     end
145
146     local str = command:sub(1, cword_start - 1) .. output[ncomp] .. command:sub(cword_end)
147     cur_pos = cword_end + #output[ncomp] + 1
148
149     return str, cur_pos
150 end
151
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
164     end
165
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
171     end
172
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)
178         end
179     end)
180
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
184     end
185
186     --  cycle around all matches
187     ncomp = math.mod(ncomp - 1, #matches) + 1
188     return matches[ncomp], #matches[ncomp] + 1
189 end
190
191 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80