Skip to content

Commit 94a20e7

Browse files
committed
fix(cmdline): trigger autocorrection only if appending single character
Details: - Although appending multiple characters with a single key stroke is possible (like with `:cnoremap <C-x> www` or `:nnoremap <C-y> :ehco`), making it work with autocorrection is problematic. Currently it just treats new text as a single "separator character" and tries to autocorrect the text that was prior to it. This is problematic for navigating through history: if previous command is `:Pick files`, typing `:P<Up>` will treat `ick files` as a separator character and autocorrect `P`->`p` (as it is a valid autocorrection). Ideally it would be nice to have autocorrect all words that were created after appending new text, but that is problematic. Mostly because there is (currently) no way to actually compute these words (`vim.fn.getcomplpat()` works only for current command line text and cursor; moving cursor for this case is not quite clean). So instead make it more explicit that autocorrection is triggered only if *a single character* is appended to the command line text. This was more or less the initial intention, so do not mark as a breaking change.
1 parent b8b0c3b commit 94a20e7

File tree

3 files changed

+38
-40
lines changed

3 files changed

+38
-40
lines changed

doc/mini-cmdline.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,9 @@ Use `return vim.fn.getcmdtype() == ':'` as a predicate output.
188188
Autocorrect ~
189189

190190
`config.autocorrect` is used to configure autocorrection: automatic adjustment
191-
of bad words as you type them. This works only when appending text at the end
192-
of the command line. Editing already typed words does not trigger autocorrect
193-
(allows correcting the autocorrection).
191+
of bad words as you type them. This works only when appending single character
192+
at the end of the command line. Editing already typed words does not trigger
193+
autocorrect (allows correcting the autocorrection).
194194

195195
When to autocorrect is computed automatically based on |getcmdcomplpat()| after
196196
every key press: if it doesn't add the character to completion pattern, then

lua/mini/cmdline.lua

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,9 @@ end
159159
---# Autocorrect ~
160160
---
161161
--- `config.autocorrect` is used to configure autocorrection: automatic adjustment
162-
--- of bad words as you type them. This works only when appending text at the end
163-
--- of the command line. Editing already typed words does not trigger autocorrect
164-
--- (allows correcting the autocorrection).
162+
--- of bad words as you type them. This works only when appending single character
163+
--- at the end of the command line. Editing already typed words does not trigger
164+
--- autocorrect (allows correcting the autocorrection).
165165
---
166166
--- When to autocorrect is computed automatically based on |getcmdcomplpat()| after
167167
--- every key press: if it doesn't add the character to completion pattern, then
@@ -759,13 +759,14 @@ H.autocorrect = function(is_final)
759759
local line, line_prev = state.line, state_prev.line
760760
local pos, pos_prev = state.pos, state_prev.pos
761761

762-
-- Act only when text is appended. It makes it natural to tweak autocorrected
763-
-- text by going back and editing it. This is also easier to implement.
762+
-- Act only when a char is appended. It makes tweaking autocorrected text
763+
-- easier by going back and editing it. This is also easier to implement.
764764
local is_at_end, was_at_end = (pos - 1) == line:len(), (pos_prev - 1) == line_prev:len()
765-
local is_text_append = vim.startswith(line, line_prev) and is_at_end and was_at_end
765+
local new = line:sub(pos_prev)
766+
local is_char_append = is_at_end and was_at_end and line == (line_prev .. new) and vim.fn.strchars(new) <= 1
766767
local is_word_finished = not vim.startswith(state.complpat, state_prev.complpat)
767768

768-
if not (is_text_append and (is_word_finished or is_final)) then return end
769+
if not (is_char_append and (is_word_finished or is_final)) then return end
769770

770771
-- Compute autocorrection
771772
local state_to_use = is_final and state or state_prev

tests/test_cmdline.lua

Lines changed: 27 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -894,10 +894,19 @@ T['Autocorrect']['respects mappings'] = function()
894894
eq(ok, false)
895895
eq(err, 'E492: Not an editor command: ehco')
896896

897-
-- Should work if Command-line mode is only entered
898-
child.cmd('nnoremap <C-y> :ehco')
899-
type_keys('<C-y>', '<CR>')
900-
eq(child.fn.histget('cmd', -1), 'echo')
897+
-- Should act if text increases latest word by a single character
898+
child.cmd('cnoremap <C-x> w')
899+
type_keys(':', 'XX', '<C-x>')
900+
validate_cmdline('XXw')
901+
type_keys(' ')
902+
validate_cmdline('bw ')
903+
type_keys('<Esc>')
904+
905+
-- Should act if text finishes the last word
906+
child.cmd('cnoremap <C-y> <Space>')
907+
type_keys(':', 'XX', '<C-y>')
908+
validate_cmdline('ex ')
909+
type_keys('<Esc>')
901910
end
902911

903912
T['Autocorrect']['respects abbreviations'] = function()
@@ -924,26 +933,6 @@ T['Autocorrect']['respects abbreviations'] = function()
924933
eq(child.lua_get('_G.log'), {})
925934
end
926935

927-
T['Autocorrect']['can act after mappings appended text'] = function()
928-
-- Should act if text increases latest word
929-
child.cmd('cnoremap <C-x> www')
930-
type_keys(':', 'XX', '<C-x>')
931-
validate_cmdline('XXwww')
932-
type_keys(' ')
933-
validate_cmdline('below ')
934-
type_keys('<Esc>')
935-
936-
-- Should act if text finishes the last word.
937-
child.cmd('cnoremap <C-y> www<Space>')
938-
type_keys(':', 'XX', '<C-y>')
939-
-- Whole new text from the mapping is treated as "separator text" and is not
940-
-- included into autocorrected word. It might be good to have the new text
941-
-- also be part of the word to correct (`XXwww` here instead of `XX`), but it
942-
-- seems too complex to implement (if reasonably possible even).
943-
validate_cmdline('exwww ')
944-
type_keys('<Esc>')
945-
end
946-
947936
T['Autocorrect']['ignores editing previous text'] = function()
948937
type_keys(':', 'set ', '<Left>')
949938
type_keys('<BS>')
@@ -960,6 +949,14 @@ T['Autocorrect']['ignores editing previous text'] = function()
960949
validate_cmdline('sort ')
961950
end
962951

952+
T['Autocorrect']['ignores navigating through history'] = function()
953+
child.cmd('command Icommand echo "Hello"')
954+
type_keys(':', 'Icommand ', '<Esc>')
955+
-- Should not autocorrect `I` to `i` (as it happens in case of `:I<Space>`)
956+
type_keys(':', 'I', '<Up>')
957+
validate_cmdline('Icommand ')
958+
end
959+
963960
T['Autocorrect']['correctly computes word to autocorrect'] = function()
964961
if child.fn.has('nvim-0.11') == 0 then MiniTest.skip('Requires `getcmdcomplpat()`, present in Neovim>=0.11') end
965962

@@ -1065,7 +1062,7 @@ end
10651062

10661063
T['Autocorrect']['works just before final <CR>'] = function()
10671064
local validate = function(bad_word, ref_word)
1068-
type_keys(':', bad_word, '<CR>')
1065+
type_every_key(':', bad_word, '<CR>')
10691066
eq(child.fn.histget('cmd', -1), ref_word)
10701067
eq(child.fn.mode(), 'n')
10711068
end
@@ -1101,19 +1098,19 @@ T['Autocorrect']['uses correct state before final <CR>'] = function()
11011098
]])
11021099

11031100
-- Changing `type` just before `<CR>`
1104-
type_keys(':', 'cnoremap <C-x> www', '<CR>')
1105-
type_keys(':', 'cn', '<Up>')
1106-
validate_cmdline('cnoremap \24 www')
1101+
type_every_key(':', 'cnoremap <LT>C-x> www', '<CR>')
1102+
type_every_key(':', 'cnoremap <LT>C-x> ww', '<Up>')
1103+
validate_cmdline('cnoremap <C-x> www')
11071104
child.lua('_G.log = {}')
11081105
type_keys('<CR>')
1109-
local ref_word = child.fn.has('nvim-0.11') == 1 and '\24 www' or 'www'
1106+
local ref_word = child.fn.has('nvim-0.11') == 1 and '<C-x> www' or 'www'
11101107
eq(child.lua_get('_G.log'), { { word = ref_word, type = 'mapping' } })
11111108

11121109
-- Should include `!` in the word (when using `vim.fn.getcmdcomplpat()`)
11131110
if child.fn.has('nvim-0.11') == 0 then return end
11141111
child.cmd('split')
11151112
child.lua('_G.log = {}')
1116-
type_keys(':', 'q!', '<CR>')
1113+
type_every_key(':', 'q!', '<CR>')
11171114
eq(child.lua_get('_G.log'), { { word = 'q!', type = '' } })
11181115
end
11191116

0 commit comments

Comments
 (0)