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 -- @param selectall If true cursor is rendered on the entire text.
110 local function prompt_text_with_cursor(text
, text_color
, cursor_color
, cursor_pos
, cursor_ul
, selectall
)
111 local char
, spacer
, text_start
, text_end
112 if not text
then text
= "" end
113 if #text
< cursor_pos
then
117 char
= util
.escape(text
:sub(cursor_pos
, cursor_pos
))
120 text_start
= util
.escape(text
:sub(1, cursor_pos
- 1))
121 text_end
= util
.escape(text
:sub(cursor_pos
+ 1))
123 char
= text_start
.. char
.. text_end
127 local underline
= cursor_ul
or "none"
128 return text_start
.. "<span background=\"" .. util
.color_strip_alpha(cursor_color
) .. "\" foreground=\"" .. util
.color_strip_alpha(text_color
) .. "\" underline=\"" .. underline
.. "\">" .. char
.. "</span>" .. text_end
.. spacer
131 --- Run a prompt in a box.
132 -- @param args A table with optional arguments: fg_cursor, bg_cursor, ul_cursor, prompt, text, selectall .
133 -- @param textbox The textbox to use for the prompt.
134 -- @param exe_callback The callback function to call with command as argument when finished.
135 -- @param completion_callback The callback function to call to get completion.
136 -- @param history_path Optional parameter: file path where the history should be saved, set nil to disable history
137 -- @param history_max Optional parameter: set the maximum entries in history file, 50 by default
138 -- @param done_callback Optional parameter: the callback function to always call without arguments, regardless of whether the prompt was cancelled.
139 function run(args
, textbox
, exe_callback
, completion_callback
, history_path
, history_max
, done_callback
)
140 local theme
= beautiful
.get()
141 if not args
then args
= {} end
142 local command
= args
.text
or ""
143 local command_before_comp
144 local cur_pos_before_comp
145 local prettyprompt
= args
.prompt
or ""
146 local inv_col
= args
.fg_cursor
or theme
.fg_focus
or "black"
147 local cur_col
= args
.bg_cursor
or theme
.bg_focus
or "white"
148 local cur_ul
= args
.ul_cursor
149 local text
= args
.text
or ""
151 history_check_load(history_path
, history_max
)
152 local history_index
= history_items(history_path
) + 1
153 -- The cursor position
154 local cur_pos
= (args
.selectall
and 1) or text
:wlen() + 1
155 -- The completion element to use on completion request.
157 if not textbox
or not exe_callback
then
160 textbox
.text
= prettyprompt
.. prompt_text_with_cursor(text
, inv_col
, cur_col
, cur_pos
, cur_ul
, args
.selectall
)
164 if (mod.Control
and (key
== "c" or key
== "g"))
165 or (not mod.Control
and key
== "Escape") then
167 if done_callback
then done_callback() end
169 elseif (mod.Control
and (key
== "j" or key
== "m"))
170 or (not mod.Control
and key
== "Return")
171 or (not mod.Control
and key
== "KP_Enter") then
173 history_add(history_path
, command
)
174 capi
.keygrabber
.stop()
175 exe_callback(command
)
176 if done_callback
then done_callback() end
177 -- We already unregistered ourselves so we don't want to return
178 -- true, otherwise we may unregister someone else.
187 elseif key
== "b" then
189 cur_pos
= cur_pos
- 1
191 elseif key
== "d" then
192 if cur_pos
<= #command
then
193 command
= command
:sub(1, cur_pos
- 1) .. command
:sub(cur_pos
+ 1)
195 elseif key
== "e" then
196 cur_pos
= #command
+ 1
197 elseif key
== "f" then
198 if cur_pos
<= #command
then
199 cur_pos
= cur_pos
+ 1
201 elseif key
== "h" then
203 command
= command
:sub(1, cur_pos
- 2) .. command
:sub(cur_pos
)
204 cur_pos
= cur_pos
- 1
206 elseif key
== "k" then
207 command
= command
:sub(1, cur_pos
- 1)
208 elseif key
== "u" then
209 command
= command
:sub(cur_pos
, #command
)
211 elseif key
== "w" then
214 local cword_start
= 1
216 while wend
< cur_pos
do
217 wend
= command
:find(" ", wstart
)
218 if not wend
then wend
= #command
+ 1 end
219 if cur_pos
>= wstart
and cur_pos
<= wend
+ 1 then
221 cword_end
= cur_pos
- 1
226 command
= command
:sub(1, cword_start
- 1) .. command
:sub(cword_end
+ 1)
227 cur_pos
= cword_start
230 if completion_callback
then
232 if key
:byte() == 9 or key
== "ISO_Left_Tab" then
233 if key
== "ISO_Left_Tab" then
234 if ncomp
== 1 then return true end
236 command
= command_before_comp
237 textbox
.text
= prettyprompt
.. prompt_text_with_cursor(command_before_comp
, inv_col
, cur_col
, cur_pos
, args
.selectall
)
242 elseif ncomp
== 1 then
243 command_before_comp
= command
244 cur_pos_before_comp
= cur_pos
246 command
, cur_pos
= completion_callback(command_before_comp
, cur_pos_before_comp
, ncomp
)
255 if key
== "Home" then
257 elseif key
== "End" then
258 cur_pos
= #command
+ 1
259 elseif key
== "BackSpace" then
261 command
= command
:sub(1, cur_pos
- 2) .. command
:sub(cur_pos
)
262 cur_pos
= cur_pos
- 1
265 elseif key
:byte() == 127 then
266 command
= command
:sub(1, cur_pos
- 1) .. command
:sub(cur_pos
+ 1)
267 elseif key
== "Left" then
268 cur_pos
= cur_pos
- 1
269 elseif key
== "Right" then
270 cur_pos
= cur_pos
+ 1
271 elseif key
== "Up" then
272 if history_index
> 1 then
273 history_index
= history_index
- 1
275 command
= data
.history
[history_path
].table[history_index
]
276 cur_pos
= #command
+ 2
278 elseif key
== "Down" then
279 if history_index
< history_items(history_path
) then
280 history_index
= history_index
+ 1
282 command
= data
.history
[history_path
].table[history_index
]
283 cur_pos
= #command
+ 2
284 elseif history_index
== history_items(history_path
) then
285 history_index
= history_index
+ 1
291 -- wlen() is UTF-8 aware but #key is not,
292 -- so check that we have one UTF-8 char but advance the cursor of # position
293 if key
:wlen() == 1 then
294 if args
.selectall
then command
= "" end
295 command
= command
:sub(1, cur_pos
- 1) .. key
.. command
:sub(cur_pos
)
296 cur_pos
= cur_pos
+ #key
301 elseif cur_pos
> #command
+ 1 then
302 cur_pos
= #command
+ 1
308 textbox
.text
= prettyprompt
.. prompt_text_with_cursor(command
, inv_col
, cur_col
, cur_pos
, cur_ul
, args
.selectall
)
314 -- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80