From b09deda37a068fb5b88f8dfaaf579b87079eae82 Mon Sep 17 00:00:00 2001 From: Guido Cella Date: Wed, 29 Nov 2023 19:14:47 +0100 Subject: console.lua: implement case-insensitive completion This is useful for completing files and more rarely for profiles. It will also be useful to third-party scripts interacting with the console once the API to do it is merged. --- DOCS/man/console.rst | 5 ++++ player/lua/console.lua | 74 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/DOCS/man/console.rst b/DOCS/man/console.rst index b57e7e2d4a..b9f169f1cc 100644 --- a/DOCS/man/console.rst +++ b/DOCS/man/console.rst @@ -158,6 +158,11 @@ Configurable Options Set the font border size used for the REPL and the console. +``case_sensitive`` + Default: no on Windows, yes on other platforms. + + Whether Tab completion is case sensitive. Only works with ASCII characters. + ``history_dedup`` Default: true diff --git a/player/lua/console.lua b/player/lua/console.lua index f7b2da9693..ef1a43a710 100644 --- a/player/lua/console.lua +++ b/player/lua/console.lua @@ -27,6 +27,7 @@ local opts = { -- multiplied by "scale". font_size = 16, border_size = 1, + case_sensitive = true, -- Remove duplicate entries in history as to only keep the latest one. history_dedup = true, -- The ratio of font height to font width. @@ -49,6 +50,7 @@ end local platform = detect_platform() if platform == 'windows' then opts.font = 'Consolas' + opts.case_sensitive = false elseif platform == 'darwin' then opts.font = 'Menlo' else @@ -901,30 +903,70 @@ function build_completers() return completers end --- Use 'list' to find possible tab-completions for 'part.' --- Returns a list of all potential completions and the longest --- common prefix of all the matching list items. -function complete_match(part, list) - local completions = {} +-- Find the longest common case-sensitive prefix of the entries in "list". +local function find_common_prefix(part, list) local prefix = nil for _, candidate in ipairs(list) do - if candidate:sub(1, part:len()) == part then - if prefix and prefix ~= candidate then - local prefix_len = part:len() - while prefix:sub(1, prefix_len + 1) - == candidate:sub(1, prefix_len + 1) do - prefix_len = prefix_len + 1 - end - prefix = candidate:sub(1, prefix_len) - else - prefix = candidate + if prefix and prefix ~= candidate then + local prefix_len = part:len() + while prefix:sub(1, prefix_len + 1) == candidate:sub(1, prefix_len + 1) do + prefix_len = prefix_len + 1 end + prefix = candidate:sub(1, prefix_len) + else + prefix = candidate + end + end + + return prefix +end + +-- Return the entries of "list" beginning with "part" and the longest common +-- prefix of the matches. +local function complete_match(part, list) + local completions = {} + + for _, candidate in pairs(list) do + if candidate:sub(1, part:len()) == part then completions[#completions + 1] = candidate end end - return completions, prefix + local prefix = find_common_prefix(part, completions) + + if opts.case_sensitive then + return completions, prefix + end + + completions = {} + local lower_case_completions = {} + local lower_case_part = part:lower() + + for _, candidate in pairs(list) do + if candidate:sub(1, part:len()):lower() == lower_case_part then + completions[#completions + 1] = candidate + lower_case_completions[#lower_case_completions + 1] = candidate:lower() + end + end + + local lower_case_prefix = find_common_prefix(lower_case_part, + lower_case_completions) + + -- Behave like GNU readline with completion-ignore-case On. + -- part = 'fooBA', completions = {'foobarbaz', 'fooBARqux'} => + -- prefix = 'fooBARqux', lower_case_prefix = 'foobar', return 'fooBAR' + if prefix then + return completions, prefix:sub(1, lower_case_prefix:len()) + end + + -- part = 'fooba', completions = {'fooBARbaz', 'fooBarqux'} => + -- prefix = nil, lower_case_prefix ='foobar', return 'fooBAR' + if lower_case_prefix then + return completions, completions[1]:sub(1, lower_case_prefix:len()) + end + + return {}, part end function common_prefix_length(s1, s2) -- cgit v1.2.3