fix(prompt): handle multibyte character in Backspace, ^h, ^b and ^f

This commit is contained in:
actionless 2021-06-11 00:36:37 +02:00
parent b63399f656
commit bbaccb05bc
1 changed files with 35 additions and 27 deletions

View File

@ -114,7 +114,6 @@ local io = io
local table = table local table = table
local math = math local math = math
local ipairs = ipairs local ipairs = ipairs
local pcall = pcall
local capi = local capi =
{ {
selection = selection selection = selection
@ -258,6 +257,11 @@ local function history_add(id, command)
end end
local function have_multibyte_char_at(text, position)
return text:sub(position, position):wlen() == -1
end
--- Draw the prompt text with a cursor. --- Draw the prompt text with a cursor.
-- @tparam table args The table of arguments. -- @tparam table args The table of arguments.
-- @field text The text. -- @field text The text.
@ -285,10 +289,14 @@ local function prompt_text_with_cursor(args)
text_start = gstring.xml_escape(text) text_start = gstring.xml_escape(text)
text_end = "" text_end = ""
else else
char = gstring.xml_escape(text:sub(args.cursor_pos, args.cursor_pos)) local offset = 0
if have_multibyte_char_at(text, args.cursor_pos) then
offset = 1
end
char = gstring.xml_escape(text:sub(args.cursor_pos, args.cursor_pos + offset))
spacer = " " spacer = " "
text_start = gstring.xml_escape(text:sub(1, args.cursor_pos - 1)) text_start = gstring.xml_escape(text:sub(1, args.cursor_pos - 1))
text_end = gstring.xml_escape(text:sub(args.cursor_pos + 1)) text_end = gstring.xml_escape(text:sub(args.cursor_pos + 1 + offset))
end end
local cursor_color = gcolor.ensure_pango_color(args.cursor_color) local cursor_color = gcolor.ensure_pango_color(args.cursor_color)
@ -663,6 +671,9 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
elseif key == "b" then elseif key == "b" then
if cur_pos > 1 then if cur_pos > 1 then
cur_pos = cur_pos - 1 cur_pos = cur_pos - 1
if have_multibyte_char_at(command, cur_pos) then
cur_pos = cur_pos - 1
end
end end
elseif key == "d" then elseif key == "d" then
if cur_pos <= #command then if cur_pos <= #command then
@ -711,12 +722,20 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
end end
elseif key == "f" then elseif key == "f" then
if cur_pos <= #command then if cur_pos <= #command then
if have_multibyte_char_at(command, cur_pos) then
cur_pos = cur_pos + 2
else
cur_pos = cur_pos + 1 cur_pos = cur_pos + 1
end end
end
elseif key == "h" then elseif key == "h" then
if cur_pos > 1 then if cur_pos > 1 then
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos) local offset = 0
cur_pos = cur_pos - 1 if have_multibyte_char_at(command, cur_pos - 1) then
offset = 1
end
command = command:sub(1, cur_pos - 2 - offset) .. command:sub(cur_pos)
cur_pos = cur_pos - 1 - offset
end end
elseif key == "k" then elseif key == "k" then
command = command:sub(1, cur_pos - 1) command = command:sub(1, cur_pos - 1)
@ -844,8 +863,12 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
cur_pos = #command + 1 cur_pos = #command + 1
elseif key == "BackSpace" then elseif key == "BackSpace" then
if cur_pos > 1 then if cur_pos > 1 then
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos) local offset = 0
cur_pos = cur_pos - 1 if have_multibyte_char_at(command, cur_pos - 1) then
offset = 1
end
command = command:sub(1, cur_pos - 2 - offset) .. command:sub(cur_pos)
cur_pos = cur_pos - 1 - offset
end end
elseif key == "Delete" then elseif key == "Delete" then
command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1) command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
@ -889,22 +912,7 @@ function prompt.run(args, textbox, exe_callback, completion_callback,
selectall = nil selectall = nil
end end
local success = pcall(update) update()
while not success do
-- TODO UGLY HACK TODO
-- Setting the text failed. Most likely reason is that the user
-- entered a multibyte character and pressed backspace which only
-- removed the last byte. Let's remove another byte.
if cur_pos <= 1 then
-- No text left?!
break
end
command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
cur_pos = cur_pos - 1
success = pcall(update)
end
if changed_callback then if changed_callback then
changed_callback(command) changed_callback(command)
end end