diff src/testdir/test_options.vim @ 33399:95db67c7b754 v9.0.1958

patch 9.0.1958: cannot complete option values Commit: https://github.com/vim/vim/commit/900894b09a95398dfc75599e9f0aa2ea25723384 Author: Yee Cheng Chin <ychin.git@gmail.com> Date: Fri Sep 29 20:42:32 2023 +0200 patch 9.0.1958: cannot complete option values Problem: cannot complete option values Solution: Add completion functions for several options Add cmdline tab-completion for setting string options Add tab-completion for setting string options on the cmdline using `:set=` (along with `:set+=` and `:set-=`). The existing tab completion for setting options currently only works when nothing is typed yet, and it only fills in with the existing value, e.g. when the user does `:set diffopt=<Tab>` it will be completed to `set diffopt=internal,filler,closeoff` and nothing else. This isn't too useful as a user usually wants auto-complete to suggest all the possible values, such as 'iblank', or 'algorithm:patience'. For set= and set+=, this adds a new optional callback function for each option that can be invoked when doing completion. This allows for each option to have control over how completion works. For example, in 'diffopt', it will suggest the default enumeration, but if `algorithm:` is selected, it will further suggest different algorithm types like 'meyers' and 'patience'. When using set=, the existing option value will be filled in as the first choice to preserve the existing behavior. When using set+= this won't happen as it doesn't make sense. For flag list options (e.g. 'mouse' and 'guioptions'), completion will take into account existing typed values (and in the case of set+=, the existing option value) to make sure it doesn't suggest duplicates. For set-=, there is a new `ExpandSettingSubtract` function which will handle flag list and comma-separated options smartly, by only suggesting values that currently exist in the option. Note that Vim has some existing code that adds special handling for 'filetype', 'syntax', and misc dir options like 'backupdir'. This change preserves them as they already work, instead of converting to the new callback API for each option. closes: #13182 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yee Cheng Chin <ychin.git@gmail.com>
author Christian Brabandt <cb@256bit.org>
date Fri, 29 Sep 2023 20:45:04 +0200
parents 23a824d80de3
children 0909ef6aac3d
line wrap: on
line diff
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -295,11 +295,11 @@ func Test_set_completion()
   call assert_equal('"set tabstop thesaurus thesaurusfunc ttyscroll', @:)
 
   " Expand current value
-  call feedkeys(":set fileencodings=\<C-A>\<C-B>\"\<CR>", 'tx')
-  call assert_equal('"set fileencodings=ucs-bom,utf-8,default,latin1', @:)
+  call feedkeys(":set suffixes=\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"set suffixes=.bak,~,.o,.h,.info,.swp,.obj', @:)
 
-  call feedkeys(":set fileencodings:\<C-A>\<C-B>\"\<CR>", 'tx')
-  call assert_equal('"set fileencodings:ucs-bom,utf-8,default,latin1', @:)
+  call feedkeys(":set suffixes:\<C-A>\<C-B>\"\<CR>", 'tx')
+  call assert_equal('"set suffixes:.bak,~,.o,.h,.info,.swp,.obj', @:)
 
   " Expand key codes.
   call feedkeys(":set <H\<C-A>\<C-B>\"\<CR>", 'tx')
@@ -360,13 +360,17 @@ func Test_set_completion()
   call assert_equal("\"set invtabstop=", @:)
 
   " Expand options for 'spellsuggest'
-  call feedkeys(":set spellsuggest=best,file:xyz\<Tab>\<C-B>\"\<CR>", 'xt')
-  call assert_equal("\"set spellsuggest=best,file:xyz", @:)
+  call feedkeys(":set spellsuggest=file:test_options.v\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal("\"set spellsuggest=file:test_options.vim", @:)
+  call feedkeys(":set spellsuggest=best,file:test_options.v\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal("\"set spellsuggest=best,file:test_options.vim", @:)
 
   " Expand value for 'key'
   set key=abcd
   call feedkeys(":set key=\<Tab>\<C-B>\"\<CR>", 'xt')
   call assert_equal('"set key=*****', @:)
+  call feedkeys(":set key-=\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set key-=*****', @:)
   set key=
 
   " Expand values for 'filetype'
@@ -382,6 +386,278 @@ func Test_set_completion()
   call assert_equal('"set syntax=' .. getcompletion('a*', 'syntax')->join(), @:)
 endfunc
 
+" Test handling of expanding individual string option values
+func Test_set_completion_string_values()
+  "
+  " Test basic enum string options that have well-defined enum names
+  "
+
+  call assert_equal(getcompletion('set display=', 'cmdline'), ['lastline', 'truncate', 'uhex'])
+  call assert_equal(getcompletion('set display=t', 'cmdline'), ['truncate'])
+  call assert_equal(getcompletion('set display=*ex*', 'cmdline'), ['uhex'])
+
+  " Test that if a value is set, it will populate the results, but only if
+  " typed value is empty.
+  set display=uhex,lastline
+  call assert_equal(getcompletion('set display=', 'cmdline'), ['uhex,lastline', 'lastline', 'truncate', 'uhex'])
+  call assert_equal(getcompletion('set display=u', 'cmdline'), ['uhex'])
+  " If the set value is part of the enum list, it will show as the first
+  " result with no duplicate.
+  set display=uhex
+  call assert_equal(getcompletion('set display=', 'cmdline'), ['uhex', 'lastline', 'truncate'])
+  " If empty value, will just show the normal list without an empty item
+  set display=
+  call assert_equal(getcompletion('set display=', 'cmdline'), ['lastline', 'truncate', 'uhex'])
+  " Test escaping of the values
+  call assert_equal(getcompletion('set fillchars=', 'cmdline')[0], 'vert:\|,fold:-,eob:~,lastline:@')
+
+  " Test comma-separated lists will expand after a comma.
+  call assert_equal(getcompletion('set display=truncate,*ex*', 'cmdline'), ['uhex'])
+  " Also test the positioning of the expansion is correct
+  call feedkeys(":set display=truncate,l\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set display=truncate,lastline', @:)
+  set display&
+
+  " Test single-value options will not expand after a comma
+  call assert_equal(getcompletion('set ambw=single,', 'cmdline'), [])
+
+  " Test the other simple options to make sure they have basic auto-complete,
+  " but don't exhaustively validate their results.
+  call assert_equal(getcompletion('set ambw=', 'cmdline')[0], 'single')
+  call assert_match('light\|dark', getcompletion('set bg=', 'cmdline')[1])
+  call assert_equal(getcompletion('set backspace=', 'cmdline')[0], 'indent')
+  call assert_equal(getcompletion('set backupcopy=', 'cmdline')[1], 'yes')
+  call assert_equal(getcompletion('set belloff=', 'cmdline')[1], 'backspace')
+  call assert_equal(getcompletion('set briopt=', 'cmdline')[1], 'min:')
+  if exists('+browsedir')
+    call assert_equal(getcompletion('set browsedir=', 'cmdline')[1], 'current')
+  endif
+  call assert_equal(getcompletion('set bufhidden=', 'cmdline')[1], 'unload')
+  call assert_equal(getcompletion('set buftype=', 'cmdline')[1], 'nowrite')
+  call assert_equal(getcompletion('set casemap=', 'cmdline')[1], 'internal')
+  if exists('+clipboard')
+    call assert_match('unnamed', getcompletion('set clipboard=', 'cmdline')[1])
+  endif
+  call assert_equal(getcompletion('set complete=', 'cmdline')[1], '.')
+  call assert_equal(getcompletion('set completeopt=', 'cmdline')[1], 'menu')
+  if exists('+completeslash')
+    call assert_equal(getcompletion('set completeslash=', 'cmdline')[1], 'backslash')
+  endif
+  if exists('+cryptmethod')
+    call assert_equal(getcompletion('set cryptmethod=', 'cmdline')[1], 'zip')
+  endif
+  if exists('+cursorlineopt')
+    call assert_equal(getcompletion('set cursorlineopt=', 'cmdline')[1], 'line')
+  endif
+  call assert_equal(getcompletion('set debug=', 'cmdline')[1], 'throw')
+  call assert_equal(getcompletion('set eadirection=', 'cmdline')[1], 'ver')
+  call assert_equal(getcompletion('set fileformat=', 'cmdline')[2], 'mac')
+  if exists('+foldclose')
+    call assert_equal(getcompletion('set foldclose=', 'cmdline')[0], 'all')
+  endif
+  if exists('+foldmethod')
+    call assert_equal(getcompletion('set foldmethod=', 'cmdline')[1], 'expr')
+  endif
+  if exists('+foldopen')
+    call assert_equal(getcompletion('set foldopen=', 'cmdline')[1], 'all')
+  endif
+  call assert_equal(getcompletion('set jumpoptions=', 'cmdline')[0], 'stack')
+  call assert_equal(getcompletion('set keymodel=', 'cmdline')[1], 'stopsel')
+  call assert_equal(getcompletion('set lispoptions=', 'cmdline')[1], 'expr:1')
+  call assert_match('popup', getcompletion('set mousemodel=', 'cmdline')[2])
+  call assert_equal(getcompletion('set nrformats=', 'cmdline')[1], 'bin')
+  if exists('+rightleftcmd')
+    call assert_equal(getcompletion('set rightleftcmd=', 'cmdline')[0], 'search')
+  endif
+  call assert_equal(getcompletion('set scrollopt=', 'cmdline')[1], 'ver')
+  call assert_equal(getcompletion('set selection=', 'cmdline')[1], 'exclusive')
+  call assert_equal(getcompletion('set selectmode=', 'cmdline')[1], 'key')
+  if exists('+ssop')
+    call assert_equal(getcompletion('set ssop=', 'cmdline')[1], 'buffers')
+  endif
+  call assert_equal(getcompletion('set showcmdloc=', 'cmdline')[1], 'statusline')
+  if exists('+signcolumn')
+    call assert_equal(getcompletion('set signcolumn=', 'cmdline')[1], 'yes')
+  endif
+  if exists('+spelloptions')
+    call assert_equal(getcompletion('set spelloptions=', 'cmdline')[0], 'camel')
+  endif
+  if exists('+spellsuggest')
+    call assert_equal(getcompletion('set spellsuggest+=', 'cmdline')[0], 'best')
+  endif
+  call assert_equal(getcompletion('set splitkeep=', 'cmdline')[1], 'screen')
+  call assert_equal(getcompletion('set swapsync=', 'cmdline')[1], 'sync')
+  call assert_equal(getcompletion('set switchbuf=', 'cmdline')[1], 'usetab')
+  call assert_equal(getcompletion('set tagcase=', 'cmdline')[1], 'ignore')
+  if exists('+termwintype')
+    call assert_equal(getcompletion('set termwintype=', 'cmdline')[1], 'conpty')
+  endif
+  if exists('+toolbar')
+    call assert_equal(getcompletion('set toolbar=', 'cmdline')[1], 'text')
+  endif
+  if exists('+tbis')
+    call assert_equal(getcompletion('set tbis=', 'cmdline')[2], 'medium')
+  endif
+  if exists('+ttymouse')
+    set ttymouse=
+    call assert_equal(getcompletion('set ttymouse=', 'cmdline')[1], 'xterm2')
+    set ttymouse&
+  endif
+  call assert_equal(getcompletion('set virtualedit=', 'cmdline')[1], 'insert')
+  call assert_equal(getcompletion('set wildmode=', 'cmdline')[1], 'longest')
+  call assert_equal(getcompletion('set wildmode=list,longest:', 'cmdline')[0], 'full')
+  call assert_equal(getcompletion('set wildoptions=', 'cmdline')[1], 'tagfile')
+  if exists('+winaltkeys')
+    call assert_equal(getcompletion('set winaltkeys=', 'cmdline')[1], 'yes')
+  endif
+
+  " Other string options that queries the system rather than fixed enum names
+  call assert_equal(getcompletion('set eventignore=', 'cmdline')[0:1], ['all', 'BufAdd'])
+  call assert_equal(getcompletion('set fileencodings=', 'cmdline')[1], 'latin1')
+  call assert_equal(getcompletion('set printoptions=', 'cmdline')[0], 'top')
+  call assert_equal(getcompletion('set wincolor=', 'cmdline')[0], 'SpecialKey')
+
+  call assert_equal(getcompletion('set listchars+=', 'cmdline')[0], 'eol')
+  call assert_equal(getcompletion('setl listchars+=', 'cmdline')[0], 'eol')
+  call assert_equal(getcompletion('set fillchars+=', 'cmdline')[0], 'stl')
+  call assert_equal(getcompletion('setl fillchars+=', 'cmdline')[0], 'stl')
+
+  "
+  " Unique string options below
+  "
+
+  " keyprotocol: only auto-complete when after ':' with known protocol types
+  call assert_equal(getcompletion('set keyprotocol=', 'cmdline'), [&keyprotocol])
+  call feedkeys(":set keyprotocol+=someterm:m\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set keyprotocol+=someterm:mok2', @:)
+  set keyprotocol&
+
+  " previewpopup / completepopup
+  call assert_equal(getcompletion('set previewpopup=', 'cmdline')[0], 'height:')
+  call assert_equal(getcompletion('set previewpopup=highlight:End*Buffer', 'cmdline')[0], 'EndOfBuffer')
+  call feedkeys(":set previewpopup+=border:\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set previewpopup+=border:on', @:)
+  call feedkeys(":set completepopup=height:10,align:\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set completepopup=height:10,align:item', @:)
+  call assert_equal(getcompletion('set completepopup=bogusname:', 'cmdline'), [])
+  set previewpopup& completepopup&
+
+  " diffopt: special handling of algorithm:<alg_list>
+  call assert_equal(getcompletion('set diffopt+=', 'cmdline')[0], 'filler')
+  call assert_equal(getcompletion('set diffopt+=iblank,foldcolumn:', 'cmdline'), [])
+  call assert_equal(getcompletion('set diffopt+=iblank,algorithm:pat*', 'cmdline')[0], 'patience')
+
+  " highlight: special parsing, including auto-completing highlight groups
+  " after ':'
+  call assert_equal(getcompletion('set hl=', 'cmdline')[0:1], [&hl, '8'])
+  call assert_equal(getcompletion('set hl+=', 'cmdline')[0], '8')
+  call assert_equal(getcompletion('set hl+=8', 'cmdline')[0:2], ['8:', '8b', '8i'])
+  call assert_equal(getcompletion('set hl+=8b', 'cmdline')[0], '8bi')
+  call assert_equal(getcompletion('set hl+=8:No*ext', 'cmdline')[0], 'NonText')
+  " If all the display modes are used up we should be suggesting nothing. Make
+  " a hl typed option with all the modes which will look like '8bi-nrsuc2d=t',
+  " and make sure nothing is suggested from that.
+  let hl_display_modes = join(
+        \ filter(map(getcompletion('set hl+=8', 'cmdline'),
+        \            {idx, val -> val[1]}),
+        \        {idx, val -> val != ':'}),
+        \ '')
+  call assert_equal(getcompletion('set hl+=8'..hl_display_modes, 'cmdline'), [])
+
+  "
+  " Test flag lists
+  "
+
+  " Test set=. Show the original value if nothing is typed after '='.
+  " Otherwise, the list should avoid showing what's already typed.
+  set mouse=v
+  call assert_equal(getcompletion('set mouse=', 'cmdline'), ['v','a','n','i','c','h','r'])
+  set mouse=nvi
+  call assert_equal(getcompletion('set mouse=', 'cmdline'), ['nvi','a','n','v','i','c','h','r'])
+  call assert_equal(getcompletion('set mouse=hn', 'cmdline'), ['a','v','i','c','r'])
+
+  " Test set+=. Never show original value, and it also tries to avoid listing
+  " flags that's already in the option value.
+  call assert_equal(getcompletion('set mouse+=', 'cmdline'), ['a','c','h','r'])
+  call assert_equal(getcompletion('set mouse+=hn', 'cmdline'), ['a','c','r'])
+  call assert_equal(getcompletion('set mouse+=acrhn', 'cmdline'), [])
+
+  " Test that the position of the expansion is correct (even if there are
+  " additional values after the current cursor)
+  call feedkeys(":set mouse=hn\<Left>\<Tab>\<C-B>\"\<CR>", 'xt')
+  call assert_equal('"set mouse=han', @:)
+  set mouse&
+
+  " Test that other flag list options have auto-complete, but don't
+  " exhaustively validate their results.
+  if exists('+concealcursor')
+    call assert_equal(getcompletion('set cocu=', 'cmdline')[0], 'n')
+  endif
+  call assert_equal(getcompletion('set cpo=', 'cmdline')[1], 'a')
+  call assert_equal(getcompletion('set fo=', 'cmdline')[1], 't')
+  if exists('+guioptions')
+    call assert_equal(getcompletion('set go=', 'cmdline')[1], '!')
+  endif
+  call assert_equal(getcompletion('set shortmess=', 'cmdline')[1], 'r')
+  call assert_equal(getcompletion('set whichwrap=', 'cmdline')[1], 'b')
+
+  "
+  "Test set-=
+  "
+
+  " Normal single-value option just shows the existing value
+  set ambiwidth=double
+  call assert_equal(getcompletion('set ambw-=', 'cmdline'), ['double'])
+  set ambiwidth&
+
+  " Works on numbers and term options as well
+  call assert_equal(getcompletion('set laststatus-=', 'cmdline'), [string(&laststatus)])
+  set t_Ce=testCe
+  call assert_equal(getcompletion('set t_Ce-=', 'cmdline'), ['testCe'])
+  set t_Ce&
+
+  " Comma-separated lists should present each option
+  set diffopt=context:123,,,,,iblank,iwhiteall
+  call assert_equal(getcompletion('set diffopt-=', 'cmdline'), ['context:123', 'iblank', 'iwhiteall'])
+  call assert_equal(getcompletion('set diffopt-=*n*', 'cmdline'), ['context:123', 'iblank'])
+  call assert_equal(getcompletion('set diffopt-=i', 'cmdline'), ['iblank', 'iwhiteall'])
+  " Don't present more than one option as it doesn't make sense in set-=
+  call assert_equal(getcompletion('set diffopt-=iblank,', 'cmdline'), [])
+  " Test empty option
+  set diffopt=
+  call assert_equal(getcompletion('set diffopt-=', 'cmdline'), [])
+  set diffopt&
+
+  " Test escaping output
+  call assert_equal(getcompletion('set fillchars-=', 'cmdline')[0], 'vert:\|')
+
+  " Test files with commas in name are being parsed and escaped properly
+  set path=has\\\ space,file\\,with\\,comma,normal_file
+  if exists('+completeslash')
+    call assert_equal(getcompletion('set path-=', 'cmdline'), ['has\\\ space', 'file\,with\,comma', 'normal_file'])
+  else
+    call assert_equal(getcompletion('set path-=', 'cmdline'), ['has\\\ space', 'file\\,with\\,comma', 'normal_file'])
+  endif
+  set path&
+
+  " Flag list should present orig value, then individual flags
+  set mouse=v
+  call assert_equal(getcompletion('set mouse-=', 'cmdline'), ['v'])
+  set mouse=avn
+  call assert_equal(getcompletion('set mouse-=', 'cmdline'), ['avn','a','v','n'])
+  " Don't auto-complete when we have at least one flags already
+  call assert_equal(getcompletion('set mouse-=n', 'cmdline'), [])
+  " Test empty option
+  set mouse=
+  call assert_equal(getcompletion('set mouse-=', 'cmdline'), [])
+  set mouse&
+
+  " 'whichwrap' is an odd case where it's both flag list and comma-separated
+  set ww=b,h
+  call assert_equal(getcompletion('set ww-=', 'cmdline'), ['b','h'])
+  set ww&
+endfunc
+
 func Test_set_option_errors()
   call assert_fails('set scroll=-1', 'E49:')
   call assert_fails('set backupcopy=', 'E474:')
@@ -1447,7 +1723,7 @@ func Test_opt_cdhome()
   set cdhome&
 endfunc
 
-func Test_set_completion_2()
+func Test_set_completion_fuzzy()
   CheckOption termguicolors
 
   " Test default option completion