1 ---------------------------------------------------------------------------
2 -- @author Julien Danjou <julien@danjou.info>
3 -- @copyright 2008 Julien Danjou
4 -- @release @AWESOME_VERSION@
5 ---------------------------------------------------------------------------
7 -- Grab environment we need
14 keygrabber
= keygrabber
16 local util
= require("awful.util")
17 local beautiful
= require("beautiful")
19 --- Prompt module for awful
20 module("awful.prompt")
26 --- Load history file in history table
27 -- @param id The data.history identifier which is the path to the filename
28 -- @param max Optional parameter, the maximum number of entries in file
29 local function history_check_load(id
, max)
31 and not data
.history
[id
] then
32 data
.history
[id
] = { max = 50, table = {} }
35 data
.history
[id
].max = max
38 local f
= io
.open(id
, "r")
42 for line
in f
:lines() do
43 table.insert(data
.history
[id
].table, line
)
44 if #data
.history
[id
].table >= data
.history
[id
].max then
52 --- Save history table in history file
53 -- @param id The data.history identifier
54 local function history_save(id
)
55 if data
.history
[id
] then
56 local f
= io
.open(id
, "w")
59 for d
in id
:gmatch(".-/") do
62 util
.mkdir(id
:sub(1, i
- 1))
63 f
= assert(io
.open(id
, "w"))
65 for i
= 1, math
.min(#data
.history
[id
].table, data
.history
[id
].max) do
66 f
:write(data
.history
[id
].table[i
] .. "\n")
72 --- Return the number of items in history table regarding the id
73 -- @param id The data.history identifier
74 -- @return the number of items in history table, -1 if history is disabled
75 local function history_items(id
)
76 if data
.history
[id
] then
77 return #data
.history
[id
].table
83 --- Add an entry to the history file
84 -- @param id The data.history identifier
85 -- @param command The command to add
86 local function history_add(id
, command
)
87 if data
.history
[id
] then
89 and command
~= data
.history
[id
].table[#data
.history
[id
].table] then
90 table.insert(data
.history
[id
].table, command
)
92 -- Do not exceed our max_cmd
93 if #data
.history
[id
].table > data
.history
[id
].max then
94 table.remove(data
.history
[id
].table, 1)
103 --- Draw the prompt text with a cursor.
104 -- @param text The text.
105 -- @param text_color The text color.
106 -- @param cursor_color The cursor color.
107 -- @param cursor_pos The cursor position.
108 -- @param cursor_pos The cursor underline style.
109 local function prompt_text_with_cursor(text
, text_color
, cursor_color
, cursor_pos
, cursor_ul
)
111 if not text
then text
= "" end
112 if #text
< cursor_pos
then
115 char
= util
.escape(text
:sub(cursor_pos
, cursor_pos
))
117 local underline
= cursor_ul
or "none"
118 local text_start
= util
.escape(text
:sub(1, cursor_pos
- 1))
119 local text_end
= util
.escape(text
:sub(cursor_pos
+ 1))
120 return text_start
.. "<span background=\"" .. util
.color_strip_alpha(cursor_color
) .. "\" foreground=\"" .. util
.color_strip_alpha(text_color
) .. "\" underline=\"" .. underline
.. "\">" .. char
.. "</span>" .. text_end
123 --- Run a prompt in a box.
124 -- @param args A table with optional arguments: fg_cursor, bg_cursor, ul_cursor, prompt.
125 -- @param textbox The textbox to use for the prompt.
126 -- @param exe_callback The callback function to call with command as argument when finished.
127 -- @param completion_callback The callback function to call to get completion.
128 -- @param history_path Optional parameter: file path where the history should be saved, set nil to disable history
129 -- @param history_max Optional parameter: set the maximum entries in history file, 50 by default
130 -- @param done_callback Optional parameter: the callback function to always call without arguments, regardless of whether the prompt was cancelled.
131 function run(args
, textbox
, exe_callback
, completion_callback
, history_path
, history_max
, done_callback
)
132 local theme
= beautiful
.get()
133 if not args
then args
= {} end
135 local command_before_comp
136 local cur_pos_before_comp
137 local prettyprompt
= args
.prompt
or ""
138 local inv_col
= args
.fg_cursor
or theme
.fg_focus
or "black"
139 local cur_col
= args
.bg_cursor
or theme
.bg_focus
or "white"
140 local cur_ul
= args
.ul_cursor
142 history_check_load(history_path
, history_max
)
143 local history_index
= history_items(history_path
) + 1
144 -- The cursor position
146 -- The completion element to use on completion request.
148 if not textbox
or not exe_callback
then
151 textbox
.text
= prettyprompt
.. prompt_text_with_cursor(text
, inv_col
, cur_col
, cur_pos
, cur_ul
)
156 if key
== "c" or key
== "g" then
158 if done_callback
then done_callback() end
160 elseif key
== "j" or key
== "m" then
162 history_add(history_path
, command
)
163 exe_callback(command
)
164 if done_callback
then done_callback() end
168 if key
== "Return" then
170 history_add(history_path
, command
)
171 exe_callback(command
)
172 if done_callback
then done_callback() end
174 elseif key
== "Escape" then
176 if done_callback
then done_callback() end
185 elseif key
== "b" then
187 cur_pos
= cur_pos
- 1
189 elseif key
== "d" then
190 if cur_pos
<= #command
then
191 command
= command
:sub(1, cur_pos
- 1) .. command
:sub(cur_pos
+ 1)
193 elseif key
== "e" then
194 cur_pos
= #command
+ 1
195 elseif key
== "f" then
196 if cur_pos
<= #command
then
197 cur_pos
= cur_pos
+ 1
199 elseif key
== "h" then
201 command
= command
:sub(1, cur_pos
- 2) .. command
:sub(cur_pos
)
202 cur_pos
= cur_pos
- 1
204 elseif key
== "k" then
205 command
= command
:sub(1, cur_pos
- 1)
206 elseif key
== "u" then
207 command
= command
:sub(cur_pos
, #command
)
209 elseif key
== "w" then
212 local cword_start
= 1
214 while wend
< cur_pos
do
215 wend
= command
:find(" ", wstart
)
216 if not wend
then wend
= #command
+ 1 end
217 if cur_pos
>= wstart
and cur_pos
<= wend
+ 1 then
219 cword_end
= cur_pos
- 1
224 command
= command
:sub(1, cword_start
- 1) .. command
:sub(cword_end
+ 1)
225 cur_pos
= cword_start
228 if completion_callback
then
230 if key
:byte() == 9 or key
== "ISO_Left_Tab" then
231 if key
== "ISO_Left_Tab" then
232 if ncomp
== 1 then return true end
234 command
= command_before_comp
235 textbox
.text
= prettyprompt
.. prompt_text_with_cursor(command_before_comp
, inv_col
, cur_col
, cur_pos
)
240 elseif ncomp
== 1 then
241 command_before_comp
= command
242 cur_pos_before_comp
= cur_pos
244 command
, cur_pos
= completion_callback(command_before_comp
, cur_pos_before_comp
, ncomp
)
253 if key
== "Home" then
255 elseif key
== "End" then
256 cur_pos
= #command
+ 1
257 elseif key
== "BackSpace" then
259 command
= command
:sub(1, cur_pos
- 2) .. command
:sub(cur_pos
)
260 cur_pos
= cur_pos
- 1
263 elseif key
:byte() == 127 then
264 command
= command
:sub(1, cur_pos
- 1) .. command
:sub(cur_pos
+ 1)
265 elseif key
== "Left" then
266 cur_pos
= cur_pos
- 1
267 elseif key
== "Right" then
268 cur_pos
= cur_pos
+ 1
269 elseif key
== "Up" then
270 if history_index
> 1 then
271 history_index
= history_index
- 1
273 command
= data
.history
[history_path
].table[history_index
]
274 cur_pos
= #command
+ 2
276 elseif key
== "Down" then
277 if history_index
< history_items(history_path
) then
278 history_index
= history_index
+ 1
280 command
= data
.history
[history_path
].table[history_index
]
281 cur_pos
= #command
+ 2
282 elseif history_index
== history_items(history_path
) then
283 history_index
= history_index
+ 1
289 -- len() is UTF-8 aware but #key is not,
290 -- so check that we have one UTF-8 char but advance the cursor of # position
291 if key
:len() == 1 then
292 command
= command
:sub(1, cur_pos
- 1) .. key
.. command
:sub(cur_pos
)
293 cur_pos
= cur_pos
+ #key
298 elseif cur_pos
> #command
+ 1 then
299 cur_pos
= #command
+ 1
304 textbox
.text
= prettyprompt
.. prompt_text_with_cursor(command
, inv_col
, cur_col
, cur_pos
, cur_ul
)
310 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80