console.lua: cycle through completions

This commit is contained in:
Guido Cella 2023-11-25 18:30:49 +01:00 committed by Dudemanguy
parent c16f868dd7
commit f886eb5678
2 changed files with 60 additions and 19 deletions

View File

@ -86,7 +86,11 @@ Shift+INSERT
Paste text (uses the primary selection on X11 and Wayland). Paste text (uses the primary selection on X11 and Wayland).
TAB and Ctrl+i TAB and Ctrl+i
Complete the command or property name at the cursor. Complete the text at the cursor. The first press inserts the longest common
prefix of the completions, and subsequent presses cycle through them.
Shift+TAB
Cycle through the completions backwards.
Ctrl+l Ctrl+l
Clear all log messages from the console. Clear all log messages from the console.

View File

@ -72,6 +72,7 @@ local styles = {
error = '{\\1c&H7a77f2&}', error = '{\\1c&H7a77f2&}',
fatal = '{\\1c&H5791f9&\\b1}', fatal = '{\\1c&H5791f9&\\b1}',
suggestion = '{\\1c&Hcc99cc&}', suggestion = '{\\1c&Hcc99cc&}',
selected_suggestion = '{\\1c&H2fbdfa&\\b1}',
} }
local repl_active = false local repl_active = false
@ -82,10 +83,13 @@ local cursor = 1
local history = {} local history = {}
local history_pos = 1 local history_pos = 1
local log_buffer = {} local log_buffer = {}
local suggestion_buffer = {}
local key_bindings = {} local key_bindings = {}
local global_margins = { t = 0, b = 0 } local global_margins = { t = 0, b = 0 }
local suggestion_buffer = {}
local selected_suggestion_index
local completion_start_position
local completion_append
local file_commands = {} local file_commands = {}
local path_separator = platform == 'windows' and '\\' or '/' local path_separator = platform == 'windows' and '\\' or '/'
@ -281,7 +285,9 @@ function format_table(list, width_max, rows_max)
local spaces = math.floor((width_max - width_total) / (column_count - 1)) local spaces = math.floor((width_max - width_total) / (column_count - 1))
spaces = math.max(spaces_min, math.min(spaces_max, spaces)) spaces = math.max(spaces_min, math.min(spaces_max, spaces))
local spacing = column_count > 1 and string.format('%' .. spaces .. 's', ' ') or '' local spacing = column_count > 1
and ass_escape(string.format('%' .. spaces .. 's', ' '))
or ''
local rows = {} local rows = {}
for row = 1, row_count do for row = 1, row_count do
@ -292,12 +298,17 @@ function format_table(list, width_max, rows_max)
-- more then 99 leads to 'invalid format (width or precision too long)' -- more then 99 leads to 'invalid format (width or precision too long)'
local format_string = column == column_count and '%s' local format_string = column == column_count and '%s'
or '%-' .. math.min(column_widths[column], 99) .. 's' or '%-' .. math.min(column_widths[column], 99) .. 's'
columns[column] = string.format(format_string, list[i]) columns[column] = ass_escape(string.format(format_string, list[i]))
if i == selected_suggestion_index then
columns[column] = styles.selected_suggestion .. columns[column]
.. '{\\b0}'.. styles.suggestion
end
end end
-- first row is at the bottom -- first row is at the bottom
rows[row_count - row + 1] = table.concat(columns, spacing) rows[row_count - row + 1] = table.concat(columns, spacing)
end end
return table.concat(rows, '\n'), row_count return table.concat(rows, ass_escape('\n')), row_count
end end
local function print_to_terminal() local function print_to_terminal()
@ -390,7 +401,7 @@ function update()
local width_max = math.ceil(screenx / opts.font_size * get_font_hw_ratio()) local width_max = math.ceil(screenx / opts.font_size * get_font_hw_ratio())
local suggestions, rows = format_table(suggestion_buffer, width_max, lines_max) local suggestions, rows = format_table(suggestion_buffer, width_max, lines_max)
local suggestion_ass = style .. styles.suggestion .. ass_escape(suggestions) local suggestion_ass = style .. styles.suggestion .. suggestions
local log_ass = '' local log_ass = ''
local log_messages = #log_buffer local log_messages = #log_buffer
@ -943,8 +954,29 @@ function max_overlap_length(s1, s2)
return 0 return 0
end end
local function cycle_through_suggestions(backwards)
selected_suggestion_index = selected_suggestion_index + (backwards and -1 or 1)
if selected_suggestion_index > #suggestion_buffer then
selected_suggestion_index = 1
elseif selected_suggestion_index < 1 then
selected_suggestion_index = #suggestion_buffer
end
local before_cur = line:sub(1, completion_start_position - 1) ..
suggestion_buffer[selected_suggestion_index] .. completion_append
line = before_cur .. line:sub(cursor)
cursor = before_cur:len() + 1
update()
end
-- Complete the option or property at the cursor (TAB) -- Complete the option or property at the cursor (TAB)
function complete() function complete(backwards)
if #suggestion_buffer > 0 then
cycle_through_suggestions(backwards)
return
end
local before_cur = line:sub(1, cursor - 1) local before_cur = line:sub(1, cursor - 1)
local after_cur = line:sub(cursor) local after_cur = line:sub(cursor)
@ -952,45 +984,49 @@ function complete()
for _, completer in ipairs(build_completers()) do for _, completer in ipairs(build_completers()) do
-- Completer patterns should return the start of the word to be -- Completer patterns should return the start of the word to be
-- completed as the first capture. -- completed as the first capture.
local s, s2 = before_cur:match(completer.pattern) local s2
if not s then completion_start_position, s2 = before_cur:match(completer.pattern)
if not completion_start_position then
-- Multiple input commands can be separated by semicolons, so all -- Multiple input commands can be separated by semicolons, so all
-- completions that are anchored at the start of the string with -- completions that are anchored at the start of the string with
-- '^' can start from a semicolon as well. Replace ^ with ; and try -- '^' can start from a semicolon as well. Replace ^ with ; and try
-- to match again. -- to match again.
s, s2 = before_cur:match(completer.pattern:gsub('^^', ';')) completion_start_position, s2 =
before_cur:match(completer.pattern:gsub('^^', ';'))
end end
if s then if completion_start_position then
local hint local hint
if s2 then if s2 then
hint = s hint = completion_start_position
s = s2 completion_start_position = s2
end end
-- If the completer's pattern found a word, check the completer's -- If the completer's pattern found a word, check the completer's
-- list for possible completions -- list for possible completions
local part = before_cur:sub(s) local part = before_cur:sub(completion_start_position)
local completions, prefix = complete_match(part, completer.list(hint)) local completions, prefix = complete_match(part, completer.list(hint))
if #completions > 0 then if #completions > 0 then
-- If there was only one full match from the list, add -- If there was only one full match from the list, add
-- completer.append to the final string. This is normally a -- completer.append to the final string. This is normally a
-- space or a quotation mark followed by a space. -- space or a quotation mark followed by a space.
local after_cur_index = 1 local after_cur_index = 1
completion_append = completer.append or ''
if #completions == 1 then if #completions == 1 then
local append = completer.append or '' prefix = prefix .. completion_append
prefix = prefix .. append
-- calculate offset into after_cur -- calculate offset into after_cur
local prefix_len = common_prefix_length(append, after_cur) local prefix_len = common_prefix_length(completion_append, after_cur)
local overlap_size = max_overlap_length(append, after_cur) local overlap_size = max_overlap_length(completion_append, after_cur)
after_cur_index = math.max(prefix_len, overlap_size) + 1 after_cur_index = math.max(prefix_len, overlap_size) + 1
else else
table.sort(completions) table.sort(completions)
suggestion_buffer = completions suggestion_buffer = completions
selected_suggestion_index = 0
end end
-- Insert the completion and update -- Insert the completion and update
before_cur = before_cur:sub(1, s - 1) .. prefix before_cur = before_cur:sub(1, completion_start_position - 1) ..
prefix
cursor = before_cur:len() + 1 cursor = before_cur:len() + 1
line = before_cur .. after_cur:sub(after_cur_index) line = before_cur .. after_cur:sub(after_cur_index)
update() update()
@ -1157,6 +1193,7 @@ function get_bindings()
{ 'alt+f', next_word }, { 'alt+f', next_word },
{ 'tab', complete }, { 'tab', complete },
{ 'ctrl+i', complete }, { 'ctrl+i', complete },
{ 'shift+tab', function() complete(true) end },
{ 'ctrl+a', go_home }, { 'ctrl+a', go_home },
{ 'home', go_home }, { 'home', go_home },
{ 'ctrl+e', go_end }, { 'ctrl+e', go_end },