From bd913cea8308270def2aab1c028ecf73cfc6be1b Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sat, 25 Jan 2025 17:04:34 -0800 Subject: [PATCH 001/228] Support extra side mouse buttons in MacVim (X1 and X2) --- runtime/doc/term.txt | 2 +- src/MacVim/MMBackend.m | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index f56dbd4b6d..48ea6f93f7 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -1166,7 +1166,7 @@ Mouse clicks can be mapped. The codes for mouse clicks are: The X1 and X2 buttons refer to the extra buttons found on some mice. The 'Microsoft Explorer' mouse has these buttons available to the right thumb. -Currently X1 and X2 only work on Win32 and X11 environments. +Currently X1 and X2 only work on MacVim, Win32, and X11 environments. Examples: > :noremap diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 569d4c7c98..879ac2a355 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -3793,9 +3793,9 @@ static unsigned eventModifierFlagsToVimMouseModMask(unsigned modifierFlags) static int eventButtonNumberToVimMouseButton(int buttonNumber) { - static int mouseButton[] = { MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE }; + static int mouseButton[] = { MOUSE_LEFT, MOUSE_RIGHT, MOUSE_MIDDLE, MOUSE_X1, MOUSE_X2 }; - return (buttonNumber >= 0 && buttonNumber < 3) + return (buttonNumber >= 0 && buttonNumber < 5) ? mouseButton[buttonNumber] : -1; } From fbe2dd7b4c9c41107fa355f58ebc6ed7d19a0f50 Mon Sep 17 00:00:00 2001 From: John Marriott Date: Thu, 20 Feb 2025 23:17:09 +0100 Subject: [PATCH 002/228] patch 9.1.1129: missing out-of-memory test in buf_write() Problem: missing out-of-memory test in buf_write() Solution: Check that the returned allocated buffer is not NULL (John Marriott) closes: #16678 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/bufwrite.c | 2 ++ src/version.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/bufwrite.c b/src/bufwrite.c index 23cd884bfa..e88c7b8384 100644 --- a/src/bufwrite.c +++ b/src/bufwrite.c @@ -2316,6 +2316,8 @@ buf_write( { errmsg_allocated = TRUE; errmsg = alloc(300); + if (errmsg == NULL) + goto fail; vim_snprintf((char *)errmsg, 300, _(e_write_error_conversion_failed_in_line_nr_make_fenc_empty_to_override), (long)write_info.bw_conv_error_lnum); } diff --git a/src/version.c b/src/version.c index fc31646662..646905303f 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1129, /**/ 1128, /**/ From 3cb41489dc8856959c1d586217f141ce057dc373 Mon Sep 17 00:00:00 2001 From: David Mandelberg Date: Thu, 20 Feb 2025 23:22:17 +0100 Subject: [PATCH 003/228] runtime(sieve): set fileformat=dos in filetype plugin References: https://datatracker.ietf.org/doc/html/rfc5228#section-2.2 closes: #16685 Signed-off-by: David Mandelberg Signed-off-by: Christian Brabandt --- runtime/ftplugin/sieve.vim | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/runtime/ftplugin/sieve.vim b/runtime/ftplugin/sieve.vim index 3092b5d2d3..8161fe99ac 100644 --- a/runtime/ftplugin/sieve.vim +++ b/runtime/ftplugin/sieve.vim @@ -1,20 +1,19 @@ " Vim filetype plugin file " Language: Sieve filtering language input file +" Maintainer: This runtime file is looking for a new maintainer. " Previous Maintainer: Nikolai Weibull -" Latest Revision: 2008-07-09 +" Latest Revision: 2025 Feb 20 if exists("b:did_ftplugin") finish endif let b:did_ftplugin = 1 -let s:cpo_save = &cpo -set cpo&vim - -let b:undo_ftplugin = "setl com< cms< fo<" +let b:undo_ftplugin = "setl com< cms< fo< ff<" setlocal comments=s1:/*,mb:*,ex:*/,:# commentstring=#\ %s setlocal formatoptions-=t formatoptions+=croql -let &cpo = s:cpo_save -unlet s:cpo_save +" https://datatracker.ietf.org/doc/html/rfc5228#section-2.2 says +" "newlines (CRLF, never just CR or LF)" +setlocal fileformat=dos From 13f100e9328b1344fec79806791eb3f5234d4ccc Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Feb 2025 19:49:44 +0100 Subject: [PATCH 004/228] patch 9.1.1130: 'listchars' "precedes" is not drawn on Tabs. Problem: 'listchars' "precedes" is not drawn on Tabs. Solution: Only draw 'listchars' "precedes" when not skipping over cells. (zeertzjq) fixes: #5927 closes: #16691 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/drawline.c | 1 + src/testdir/dumps/Test_wincolor_lcs.dump | 2 +- src/testdir/test_listchars.vim | 25 ++++++++++++++++++++++-- src/version.c | 2 ++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/drawline.c b/src/drawline.c index aabf461110..f24e7e036d 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -3727,6 +3727,7 @@ win_line( && wlv.filler_todo <= 0 #endif && wlv.draw_state > WL_NR + && skip_cells <= 0 && c != NUL) { c = wp->w_lcs_chars.prec; diff --git a/src/testdir/dumps/Test_wincolor_lcs.dump b/src/testdir/dumps/Test_wincolor_lcs.dump index 1e146b2318..3cbfc3fb48 100644 --- a/src/testdir/dumps/Test_wincolor_lcs.dump +++ b/src/testdir/dumps/Test_wincolor_lcs.dump @@ -1,5 +1,5 @@ |<+0#4040ff13#ffff4012| +0#0000001&@73 -|-+0#0000e05&@2|>|-@6>s+0#0000001&|o|m|e|.+0#0000e05&|r+0#0000001&|a|n|d|o|m|.+0#0000e05&|*+0#e0e0e08#6c6c6c255|.+0#0000e05#ffff4012|e+0#0000001&|n|o|u|g|h|.+0#0000e05&|l+0#0000001&|o|n|g|.+0#0000e05&|t+0#0000001&|o|.+0#0000e05&|s+0#0000001&|h|o|w|.+0#0000e05&|'+0#0000001&|e|x|t|e|n|d|s|'|.+0#0000e05&|a+0#0000001&|n|d|.+0#0000e05&|'+0#0000001&|p|r|e|c|e|d|e|s|'|.+0#0000e05&|i+0#0000001&|n|c|l|>+0#4040ff13& +|<+0#4040ff13&|-+0#0000e05&@1|>|-@6>s+0#0000001&|o|m|e|.+0#0000e05&|r+0#0000001&|a|n|d|o|m|.+0#0000e05&|*+0#e0e0e08#6c6c6c255|.+0#0000e05#ffff4012|e+0#0000001&|n|o|u|g|h|.+0#0000e05&|l+0#0000001&|o|n|g|.+0#0000e05&|t+0#0000001&|o|.+0#0000e05&|s+0#0000001&|h|o|w|.+0#0000e05&|'+0#0000001&|e|x|t|e|n|d|s|'|.+0#0000e05&|a+0#0000001&|n|d|.+0#0000e05&|'+0#0000001&|p|r|e|c|e|d|e|s|'|.+0#0000e05&|i+0#0000001&|n|c|l|>+0#4040ff13& |<| +0#0000001&@73 |~+0#4040ff13&| @73 |~| @73 diff --git a/src/testdir/test_listchars.vim b/src/testdir/test_listchars.vim index 481540d7ee..8bed38a589 100644 --- a/src/testdir/test_listchars.vim +++ b/src/testdir/test_listchars.vim @@ -677,6 +677,7 @@ func Test_listchars_precedes_with_wide_char() call setline(1, '123口456') call assert_equal(['123口456$ '], ScreenLines(1, 10)) let attr = screenattr(1, 9) + normal! zl call assert_equal(['!3口456$ '], ScreenLines(1, 10)) call assert_equal(attr, screenattr(1, 1)) @@ -688,8 +689,7 @@ func Test_listchars_precedes_with_wide_char() call assert_equal(attr, screenattr(1, 1)) call assert_equal(attr, screenattr(1, 2)) normal! zl - " TODO: should it be '!' instead of '<' here? - call assert_equal(['<456$ '], ScreenLines(1, 10)) + call assert_equal(['!456$ '], ScreenLines(1, 10)) call assert_equal(attr, screenattr(1, 1)) normal! zl call assert_equal(['!56$ '], ScreenLines(1, 10)) @@ -701,4 +701,25 @@ func Test_listchars_precedes_with_wide_char() bw! endfunc +func Test_listchars_precedes_with_tab() + new + setlocal nowrap list listchars=eol:$,precedes:!,tab:<-> + call setline(1, "1234\t56") + let expected_line = '1234<-->56$ ' + call assert_equal([expected_line], ScreenLines(1, 12)) + let expected_attrs = mapnew(range(1, 12), 'screenattr(1, v:val)') + let attr = expected_attrs[-2] + + for i in range(8) + normal! zl + let expected_line = '!' .. expected_line[2:] .. ' ' + let expected_attrs = [attr] + expected_attrs[2:] + expected_attrs[-1:] + call assert_equal([expected_line], ScreenLines(1, 12)) + let attrs = mapnew(range(1, 12), 'screenattr(1, v:val)') + call assert_equal(expected_attrs, attrs) + endfor + + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 646905303f..6961a2719a 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1130, /**/ 1129, /**/ From a48693c6c2a1e85639bc9f14a92c1d9c9dbd6231 Mon Sep 17 00:00:00 2001 From: glepnir Date: Fri, 21 Feb 2025 19:52:13 +0100 Subject: [PATCH 005/228] runtime(doc): remove resolved complete item from todo list closes: #16690 Signed-off-by: Christian Brabandt --- runtime/doc/todo.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index 5338b8b0a1..9bdcfe0e3e 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 9.1. Last change: 2025 Feb 02 +*todo.txt* For Vim version 9.1. Last change: 2025 Feb 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -4736,8 +4736,6 @@ Omni completion: Insert mode completion/expansion: - Is it possible to keep the complete menu open when calling complete()? (Prabir Shrestha, 2017 May 19, #1713) -- When 'completeopt' has "noselect" does not insert a newline. - (Lifepillar, 2017 Apr 23, #1653) - When complete() first argument is before where insert started and 'backspace' is Vi compatible, the completion fails. (Hirohito Higashi, 2015 Feb 19) From b79fa3d9c8a08f15267797511d779e33bd33e68e Mon Sep 17 00:00:00 2001 From: John Marriott Date: Fri, 21 Feb 2025 19:59:56 +0100 Subject: [PATCH 006/228] patch 9.1.1131: potential out-of-memory issue in search.c Problem: potential out-of-memory issue in search.c Solution: improve situation and refactor search.c slightly (John Marriott) - In function update_search_stat(): add a check for a theoretical null pointer reference, set and remember the length of lastpat, remove the three calls to STRLEN() and use the various string's associated lengths instead, add a check for an out-of-memory condition. - In function search_for_fuzz_match(): remove a call to strnsave() and thus avoid having to add a check for an out-of-memory condition, also replace the call to STRLEN() by ml_get_buf_len(). closes: #16689 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/search.c | 23 +++++++++++++---------- src/version.c | 2 ++ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/search.c b/src/search.c index 46fa7b9d99..3519c32cb6 100644 --- a/src/search.c +++ b/src/search.c @@ -3269,6 +3269,7 @@ update_search_stat( static int last_maxcount = SEARCH_STAT_DEF_MAX_COUNT; static int chgtick = 0; static char_u *lastpat = NULL; + static size_t lastpatlen = 0; static buf_T *lbuf = NULL; #ifdef FEAT_RELTIME proftime_T start; @@ -3295,8 +3296,10 @@ update_search_stat( // Unfortunately, there is no MB_STRNICMP function. // XXX: above comment should be "no MB_STRCMP function" ? if (!(chgtick == CHANGEDTICK(curbuf) - && MB_STRNICMP(lastpat, spats[last_idx].pat, STRLEN(lastpat)) == 0 - && STRLEN(lastpat) == STRLEN(spats[last_idx].pat) + && (lastpat != NULL + && MB_STRNICMP(lastpat, spats[last_idx].pat, lastpatlen) == 0 + && lastpatlen == spats[last_idx].patlen + ) && EQUAL_POS(lastpos, *cursor_pos) && lbuf == curbuf) || wraparound || cur < 0 || (maxcount > 0 && cur > maxcount) || recompute) @@ -3355,7 +3358,11 @@ update_search_stat( if (done_search) { vim_free(lastpat); - lastpat = vim_strsave(spats[last_idx].pat); + lastpat = vim_strnsave(spats[last_idx].pat, spats[last_idx].patlen); + if (lastpat == NULL) + lastpatlen = 0; + else + lastpatlen = spats[last_idx].patlen; chgtick = CHANGEDTICK(curbuf); lbuf = curbuf; lastpos = p; @@ -5291,8 +5298,6 @@ search_for_fuzzy_match( pos_T circly_end; int found_new_match = FALSE; int looped_around = FALSE; - char_u *next_word_end = NULL; - char_u *match_word = NULL; if (whole_line) current_pos.lnum += dir; @@ -5330,10 +5335,9 @@ search_for_fuzzy_match( { if (ctrl_x_mode_normal()) { - match_word = vim_strnsave(*ptr, *len); - if (STRCMP(match_word, pattern) == 0) + if (STRNCMP(*ptr, pattern, *len) == 0 && pattern[*len] == NUL) { - next_word_end = find_word_start(*ptr + *len); + char_u *next_word_end = find_word_start(*ptr + *len); if (*next_word_end != NUL && *next_word_end != NL) { // Find end of the word. @@ -5355,7 +5359,6 @@ search_for_fuzzy_match( *len = next_word_end - *ptr; current_pos.col = *len; } - vim_free(match_word); } *pos = current_pos; break; @@ -5369,7 +5372,7 @@ search_for_fuzzy_match( { found_new_match = TRUE; *pos = current_pos; - *len = (int)STRLEN(*ptr); + *len = (int)ml_get_buf_len(buf, current_pos.lnum); break; } } diff --git a/src/version.c b/src/version.c index 6961a2719a..8e338f75f5 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1131, /**/ 1130, /**/ From 060e6556e2cd97512cee1f46bc7915768c0f9e21 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 21 Feb 2025 20:06:26 +0100 Subject: [PATCH 007/228] patch 9.1.1132: Mark positions wrong after triggering multiline completion Problem: Mark positions wrong after triggering multiline completion. Solution: Call deleted_lines_mark() after deleting lines. (zeertzjq) closes: #16687 Co-authored-by: Sean Dewar <6256228+seandewar@users.noreply.github.com> Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/insexpand.c | 1 + src/testdir/test_ins_complete.vim | 44 +++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 47 insertions(+) diff --git a/src/insexpand.c b/src/insexpand.c index 624165a9bf..dc8c76beec 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -4429,6 +4429,7 @@ ins_compl_delete(void) VIM_CLEAR(remaining); return; } + deleted_lines_mark(curwin->w_cursor.lnum, 1L); curwin->w_cursor.lnum--; } // move cursor to end of line diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index 0b943712bb..c34f6426e0 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -3182,4 +3182,48 @@ function Test_completeopt_preinsert() delfunc Omni_test endfunc +" Check that mark positions are correct after triggering multiline completion. +func Test_complete_multiline_marks() + func Omni_test(findstart, base) + if a:findstart + return col(".") + endif + return [ + \ #{word: "func ()\n\t\nend"}, + \ #{word: "foobar"}, + \ #{word: "你好\n\t\n我好"} + \ ] + endfunc + set omnifunc=Omni_test + + new + let lines = mapnew(range(10), 'string(v:val)') + call setline(1, lines) + call setpos("'a", [0, 3, 1, 0]) + + call feedkeys("A \\\\", 'tx') + call assert_equal(lines, getline(1, '$')) + call assert_equal([0, 3, 1, 0], getpos("'a")) + + call feedkeys("A \\\\\", 'tx') + call assert_equal(lines, getline(1, '$')) + call assert_equal([0, 3, 1, 0], getpos("'a")) + + call feedkeys("A \\\\\\", 'tx') + call assert_equal(lines, getline(1, '$')) + call assert_equal([0, 3, 1, 0], getpos("'a")) + + call feedkeys("A \\\\\\\", 'tx') + call assert_equal(lines, getline(1, '$')) + call assert_equal([0, 3, 1, 0], getpos("'a")) + + call feedkeys("A \\\", 'tx') + call assert_equal(['0 func ()', "\t", 'end'] + lines[1:], getline(1, '$')) + call assert_equal([0, 5, 1, 0], getpos("'a")) + + bw! + set omnifunc& + delfunc Omni_test +endfunc + " vim: shiftwidth=2 sts=2 expandtab nofoldenable diff --git a/src/version.c b/src/version.c index 8e338f75f5..d09ba285cc 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1132, /**/ 1131, /**/ From b62bf814886185cb8607ce15051aa7017b8c88ba Mon Sep 17 00:00:00 2001 From: David Mandelberg Date: Fri, 21 Feb 2025 20:09:35 +0100 Subject: [PATCH 008/228] patch 9.1.1133: filetype: xkb files not recognized everywhere Problem: filetype: xkb files not recognized everywhere Solution: detect xkb files in more places (David Mandelberg) References: https://xkbcommon.org/doc/current/user-configuration.html#user-config-locations closes: #16684 Signed-off-by: David Mandelberg Signed-off-by: Christian Brabandt --- runtime/filetype.vim | 2 +- src/testdir/test_filetype.vim | 3 ++- src/version.c | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 46650e9c42..b4207942ea 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -3273,7 +3273,7 @@ au BufNewFile,BufRead XF86Config* \|call s:StarSetf('xf86conf') " XKB -au BufNewFile,BufRead */usr/share/X11/xkb/{compat,geometry,keycodes,symbols,types}/* call s:StarSetf('xkb') +au BufNewFile,BufRead */{,.}xkb/{compat,geometry,keycodes,symbols,types}/* call s:StarSetf('xkb') " X11 xmodmap au BufNewFile,BufRead *xmodmap* call s:StarSetf('xmodmap') diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 3125ffc99a..6fe383d871 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -883,7 +883,8 @@ def s:GetFilenameChecks(): dict> xf86conf: ['xorg.conf', 'xorg.conf-4'], xhtml: ['file.xhtml', 'file.xht'], xinetd: ['/etc/xinetd.conf', '/etc/xinetd.d/file', 'any/etc/xinetd.conf', 'any/etc/xinetd.d/file'], - xkb: ['/usr/share/X11/xkb/compat/pc', '/usr/share/X11/xkb/geometry/pc', '/usr/share/X11/xkb/keycodes/evdev', '/usr/share/X11/xkb/symbols/pc', '/usr/share/X11/xkb/types/pc'], + xkb: ['any/xkb/compat/pc', 'any/xkb/geometry/pc', 'any/xkb/keycodes/evdev', 'any/xkb/symbols/pc', 'any/xkb/types/pc', + 'any/.xkb/compat/pc', 'any/.xkb/geometry/pc', 'any/.xkb/keycodes/evdev', 'any/.xkb/symbols/pc', 'any/.xkb/types/pc'], xmath: ['file.msc', 'file.msf'], xml: ['/etc/blkid.tab', '/etc/blkid.tab.old', 'file.xmi', 'file.csproj', 'file.csproj.user', 'file.fsproj', 'file.fsproj.user', 'file.vbproj', 'file.vbproj.user', 'file.ui', 'file.tpm', '/etc/xdg/menus/file.menu', 'fglrxrc', 'file.xlf', 'file.xliff', 'file.xul', 'file.wsdl', 'file.wpl', 'any/etc/blkid.tab', 'any/etc/blkid.tab.old', diff --git a/src/version.c b/src/version.c index d09ba285cc..f552a271d7 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1133, /**/ 1132, /**/ From 41a6026f007facb1ada3ff2a63a054913432860c Mon Sep 17 00:00:00 2001 From: David Mandelberg Date: Fri, 21 Feb 2025 20:13:54 +0100 Subject: [PATCH 009/228] patch 9.1.1134: filetype: Guile init file not recognized Problem: filetype: Guile init file not recognized Solution: detect '.guile' file as scheme filetype (David Mandelberg) References: https://www.gnu.org/software/guile/manual/html_node/Init-File.html > When run interactively, Guile will load a local initialization file > from ~/.guile. This file should contain Scheme expressions for > evaluation. closes: #16683 Signed-off-by: David Mandelberg Signed-off-by: Christian Brabandt --- runtime/filetype.vim | 4 ++-- src/testdir/test_filetype.vim | 2 +- src/version.c | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/runtime/filetype.vim b/runtime/filetype.vim index b4207942ea..0acd187181 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -2347,8 +2347,8 @@ au BufNewFile,BufRead *.zsh,*.zsh-theme,*.zunit setf zsh " Salt state files au BufNewFile,BufRead *.sls setf salt -" Scheme, Supertux configuration, Lips.js history ("racket" patterns are now separate, see above) -au BufNewFile,BufRead *.scm,*.ss,*.sld,*.stsg,*/supertux2/config,.lips_repl_history setf scheme +" Scheme, Supertux configuration, Lips.js history, Guile init file ("racket" patterns are now separate, see above) +au BufNewFile,BufRead *.scm,*.ss,*.sld,*.stsg,*/supertux2/config,.lips_repl_history,.guile setf scheme " Screen RC au BufNewFile,BufRead .screenrc,screenrc setf screen diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 6fe383d871..3196856c03 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -675,7 +675,7 @@ def s:GetFilenameChecks(): dict> sass: ['file.sass'], sbt: ['file.sbt'], scala: ['file.scala', 'file.mill'], - scheme: ['file.scm', 'file.ss', 'file.sld', 'file.stsg', 'any/local/share/supertux2/config', '.lips_repl_history'], + scheme: ['file.scm', 'file.ss', 'file.sld', 'file.stsg', 'any/local/share/supertux2/config', '.lips_repl_history', '.guile'], scilab: ['file.sci', 'file.sce'], screen: ['.screenrc', 'screenrc'], scss: ['file.scss'], diff --git a/src/version.c b/src/version.c index f552a271d7..620e7b26f5 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1134, /**/ 1133, /**/ From 6a15942bc275dc59814f44064e2984b831f7a03d Mon Sep 17 00:00:00 2001 From: Christ van Willegen Date: Fri, 21 Feb 2025 20:23:26 +0100 Subject: [PATCH 010/228] CI: add Makefile target to verify default highlighting groups are present When adding new highlight groups, one needs to make sure to also add a "default link NewHlGroup ExistingHlGroup" in highlight.c code, so that when resetting a color scheme the old color won't be left behind. So add a Makefile in the 'ci' directory that verifies that all documented '*hl-' from the documentation are either reflected in the source code, or belong to a list of 'known to be ignored' highlight groups and let that check run as part of the CI test suite. related: #16676 closes: #16678 Signed-off-by: Christ van Willegen Signed-off-by: Christian Brabandt --- .github/workflows/ci.yml | 7 +++++ Filelist | 2 ++ ci/hlgroups.ignore | 66 ++++++++++++++++++++++++++++++++++++++++ ci/hlgroups.make | 18 +++++++++++ ci/unlisted.make | 2 +- 5 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 ci/hlgroups.ignore create mode 100644 ci/hlgroups.make diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a4eccf07f9..2dbf922c51 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -96,6 +96,13 @@ jobs: # exit with an error code and list the missing entries. make -f ci/unlisted.make + - name: Check hlgroups (are any new hlgroups added, but not handled in highlight.c) + run: | + # If any highlight groups have been documented, but not handled in + # highlight.c, nor listed as 'intentionally left out' in hlgroups.ignore, + # exit with an error code and list the missing entries. + make -C ci -f hlgroups.make + - run: sudo dpkg --add-architecture i386 if: matrix.architecture == 'i386' diff --git a/Filelist b/Filelist index 22a987c4db..07560e5db5 100644 --- a/Filelist +++ b/Filelist @@ -1148,6 +1148,8 @@ IGNORE = \ .github/workflows/label.yml \ SECURITY.md \ ci/unlisted.make \ + ci/hlgroups.make \ + ci/hlgroups.ignore \ src/libvterm/CODE-MAP \ runtime/syntax/testdir/input/html_html \ diff --git a/ci/hlgroups.ignore b/ci/hlgroups.ignore new file mode 100644 index 0000000000..41e06f1bde --- /dev/null +++ b/ci/hlgroups.ignore @@ -0,0 +1,66 @@ +ColorColumn +ComplMatchIns +Conceal +Cursor +CursorColumn +CursorIM +CursorLine +CursorLineNr +debugBreakpoint +debugPC +DiffAdd +DiffChange +DiffDelete +DiffText +Directory +ErrorMsg +FoldColumn +Folded +Ignore +IncSearch +lCursor +LineNr +LineNrAbove +LineNrBelow +MatchParen +Menu +ModeMsg +MoreMsg +MsgArea +NonText +Normal +Pmenu +PmenuSbar +PmenuSel +PmenuThumb +Question +Scrollbar +Search +SignColumn +SpecialKey +SpellBad +SpellCap +SpellLocal +SpellRare +StatusLine +StatusLineNC +StatusLineTerm +StatusLineTermNC +TabLine +TabLineFill +TabLineSel +Terminal +Title +TOhtmlProgress +TOhtml-progress-color +ToolbarButton +ToolbarLine +Tooltip +User1 +User1..9 +User9 +VertSplit +Visual +VisualNOS +WarningMsg +WildMenu diff --git a/ci/hlgroups.make b/ci/hlgroups.make new file mode 100644 index 0000000000..65b39a8c1d --- /dev/null +++ b/ci/hlgroups.make @@ -0,0 +1,18 @@ +# vim: ft=make +SHELL = /bin/bash + +# Default target to actually run the comparison: +.PHONY: check +.INTERMEDIATE: hlgroups deflinks hlgroups.stripped + +check: hlgroups.stripped deflinks + diff hlgroups.stripped deflinks + +hlgroups: + grep '\*hl-' ../runtime/doc/*txt | sed -E -e 's/.*: hlgroups + +deflinks: ../src/highlight.c + grep '"default link' $< | sed 's/.*default link\s*\(.*\)\s.*/\1/' | sort > deflinks + +hlgroups.stripped: hlgroups.ignore hlgroups + grep -v -x -F -f hlgroups.ignore hlgroups > hlgroups.stripped diff --git a/ci/unlisted.make b/ci/unlisted.make index 04dfcb53f3..6d506eb79c 100644 --- a/ci/unlisted.make +++ b/ci/unlisted.make @@ -21,7 +21,7 @@ $(eval all_patterns := $(shell \ p; \ }')) -# In Makefile's `prepeare` target, all the IN_README_DIR files are moved from +# In Makefile's `prepare` target, all the IN_README_DIR files are moved from # READMEdir to the root, so add those files in their Git-tracked location: all_patterns := $(all_patterns) \ $(foreach readme, $(IN_README_DIR), READMEdir/$(readme)) From 51eefba1d614f00009fd93034471c3bfd3c0afbe Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 22 Feb 2025 08:48:06 +0100 Subject: [PATCH 011/228] runtime(filetype): move filetype detection into filetypedetect augroup closes: #16701 Signed-off-by: Christian Brabandt --- runtime/filetype.vim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 0acd187181..0e669047e6 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -1,7 +1,7 @@ " Vim support file to detect file types " " Maintainer: The Vim Project -" Last Change: 2025 Feb 08 +" Last Change: 2025 Feb 22 " Former Maintainer: Bram Moolenaar " Listen very carefully, I will say this only once @@ -3284,6 +3284,15 @@ au BufNewFile,BufRead */etc/xinetd.d/* call s:StarSetf('xinetd') " yum conf (close enough to dosini) au BufNewFile,BufRead */etc/yum.repos.d/* call s:StarSetf('dosini') +" Yarn lock +au BufNewFile,BufRead yarn.lock setf yaml + +" Zathurarc +au BufNewFile,BufRead zathurarc setf zathurarc + +" Rofi stylesheet +au BufNewFile,BufRead *.rasi setf rasi + " Z-Shell script ending in a star au BufNewFile,BufRead .zsh*,.zlog*,.zcompdump* call s:StarSetf('zsh') au BufNewFile,BufRead zsh*,zlog* call s:StarSetf('zsh') @@ -3329,15 +3338,6 @@ au filetypedetect BufNewFile,BufRead,StdinReadPost * \ setf FALLBACK conf | \ endif -" Yarn lock -au BufNewFile,BufRead yarn.lock setf yaml - -" Zathurarc -au BufNewFile,BufRead zathurarc setf zathurarc - -" Rofi stylesheet -au BufNewFile,BufRead *.rasi setf rasi - " If the GUI is already running, may still need to install the Syntax menu. " Don't do it when the 'M' flag is included in 'guioptions'. if has("menu") && has("gui_running") From bf595ae4ac9ecc1e0620664177072926ed3679ff Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 22 Feb 2025 09:13:17 +0100 Subject: [PATCH 012/228] patch 9.1.1135: 'suffixesadd' doesn't work with multiple items Problem: 'suffixesadd' doesn't work with multiple items (after 9.1.1122). Solution: Don't concat multiple suffixes together. (zeertzjq) fixes: #16694 closes: #16699 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/findfile.c | 12 ++++++++---- src/testdir/test_findfile.vim | 30 ++++++++++++++++++++++++++++++ src/testdir/test_gf.vim | 32 ++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/findfile.c b/src/findfile.c index ccb3ef8853..2bd1e7eb99 100644 --- a/src/findfile.c +++ b/src/findfile.c @@ -1082,6 +1082,7 @@ vim_findfile(void *search_ctx_arg) * Try without extra suffix and then with suffixes * from 'suffixesadd'. */ + len = file_path.length; if (search_ctx->ffsc_tagfile) suf = (char_u *)""; else @@ -1164,8 +1165,8 @@ vim_findfile(void *search_ctx_arg) // Not found or found already, try next suffix. if (*suf == NUL) break; - file_path.length += copy_option_part(&suf, file_path.string + file_path.length, - MAXPATHL - file_path.length, ","); + file_path.length = len + copy_option_part(&suf, + file_path.string + len, MAXPATHL - len, ","); } } } @@ -1872,6 +1873,7 @@ find_file_in_path_option( if (first == TRUE) { int l; + int NameBufflen; int run; size_t rel_fnamelen = 0; char_u *suffix; @@ -1912,6 +1914,7 @@ find_file_in_path_option( // When the file doesn't exist, try adding parts of // 'suffixesadd'. + NameBufflen = l; suffix = suffixes; for (;;) { @@ -1920,12 +1923,13 @@ find_file_in_path_option( || ((find_what == FINDFILE_DIR) == mch_isdir(NameBuff)))) { - file_name = vim_strnsave(NameBuff, l); + file_name = vim_strnsave(NameBuff, NameBufflen); goto theend; } if (*suffix == NUL) break; - l += copy_option_part(&suffix, NameBuff + l, MAXPATHL - l, ","); + NameBufflen = l + copy_option_part(&suffix, NameBuff + l, + MAXPATHL - l, ","); } } } diff --git a/src/testdir/test_findfile.vim b/src/testdir/test_findfile.vim index c974c40147..a7c3dc26de 100644 --- a/src/testdir/test_findfile.vim +++ b/src/testdir/test_findfile.vim @@ -222,6 +222,36 @@ func Test_finddir_error() call assert_fails('call finddir("x", repeat("x", 5000))', 'E854:') endfunc +func Test_findfile_with_suffixesadd() + let save_path = &path + let save_dir = getcwd() + set path=,, + call mkdir('Xfinddir1', 'pR') + cd Xfinddir1 + + call writefile([], 'foo.c', 'D') + call writefile([], 'bar.cpp', 'D') + call writefile([], 'baz.cc', 'D') + call writefile([], 'foo.o', 'D') + call writefile([], 'bar.o', 'D') + call writefile([], 'baz.o', 'D') + + set suffixesadd=.c,.cpp + call assert_equal('foo.c', findfile('foo')) + call assert_equal('./foo.c', findfile('./foo')) + call assert_equal('bar.cpp', findfile('bar')) + call assert_equal('./bar.cpp', findfile('./bar')) + call assert_equal('', findfile('baz')) + call assert_equal('', findfile('./baz')) + set suffixesadd+=.cc + call assert_equal('baz.cc', findfile('baz')) + call assert_equal('./baz.cc', findfile('./baz')) + + set suffixesadd& + call chdir(save_dir) + let &path = save_path +endfunc + " Test for the :find, :sfind and :tabfind commands func Test_find_cmd() new diff --git a/src/testdir/test_gf.vim b/src/testdir/test_gf.vim index cc12b3618f..4dc01c925b 100644 --- a/src/testdir/test_gf.vim +++ b/src/testdir/test_gf.vim @@ -353,4 +353,36 @@ func Test_gf_switchbuf() %bw! endfunc +func Test_gf_with_suffixesadd() + let cwd = getcwd() + let dir = 'Xtestgf_sua_dir' + call mkdir(dir, 'R') + call chdir(dir) + + call writefile([], 'foo.c', 'D') + call writefile([], 'bar.cpp', 'D') + call writefile([], 'baz.cc', 'D') + call writefile([], 'foo.o', 'D') + call writefile([], 'bar.o', 'D') + call writefile([], 'baz.o', 'D') + + new + setlocal path=,, suffixesadd=.c,.cpp + call setline(1, ['./foo', './bar', './baz']) + exe "normal! gg\f" + call assert_equal('foo.c', expand('%:t')) + close + exe "normal! 2gg\f" + call assert_equal('bar.cpp', expand('%:t')) + close + call assert_fails('exe "normal! 3gg\f"', 'E447:') + setlocal suffixesadd+=.cc + exe "normal! 3gg\f" + call assert_equal('baz.cc', expand('%:t')) + close + + %bwipe! + call chdir(cwd) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 620e7b26f5..4a9af920b2 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1135, /**/ 1134, /**/ From 7bbb0f357e9f9d3a737dac75e4b5ba7dfbf3ecc1 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Sat, 22 Feb 2025 09:19:04 +0100 Subject: [PATCH 013/228] patch 9.1.1136: Match highlighting marks a buffer region as changed Problem: Match highlighting marks a buffer region to be redrawn as if its buffer text was changed, unnecessarily invoking syntax code. Solution: Set the `w_redraw_top/bot` variables instead of the b_mod_* ones (Luuk van Baal) closes: #16697 Signed-off-by: Luuk van Baal Signed-off-by: Christian Brabandt --- src/drawscreen.c | 22 +++++++++++++++++----- src/fold.c | 7 +------ src/match.c | 30 ++---------------------------- src/proto/drawscreen.pro | 1 + src/version.c | 2 ++ 5 files changed, 23 insertions(+), 39 deletions(-) diff --git a/src/drawscreen.c b/src/drawscreen.c index a08cea3d61..4736bf1120 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -3365,9 +3365,21 @@ redrawWinline( win_T *wp, linenr_T lnum) { - if (wp->w_redraw_top == 0 || wp->w_redraw_top > lnum) - wp->w_redraw_top = lnum; - if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < lnum) - wp->w_redraw_bot = lnum; - redraw_win_later(wp, UPD_VALID); + redraw_win_range_later(wp, lnum, lnum); +} + + void +redraw_win_range_later( + win_T *wp, + linenr_T first, + linenr_T last) +{ + if (last >= wp->w_topline && first < wp->w_botline) + { + if (wp->w_redraw_top == 0 || wp->w_redraw_top > first) + wp->w_redraw_top = first; + if (wp->w_redraw_bot == 0 || wp->w_redraw_bot < last) + wp->w_redraw_bot = last; + redraw_win_later(wp, UPD_VALID); + } } diff --git a/src/fold.c b/src/fold.c index 64f9447b72..b165dc9b0e 100644 --- a/src/fold.c +++ b/src/fold.c @@ -2384,12 +2384,7 @@ foldUpdateIEMS(win_T *wp, linenr_T top, linenr_T bot) // this in other situations, the changed lines will be redrawn anyway and // this method can cause the whole window to be updated. if (end != bot) - { - if (wp->w_redraw_top == 0 || wp->w_redraw_top > top) - wp->w_redraw_top = top; - if (wp->w_redraw_bot < end) - wp->w_redraw_bot = end; - } + redraw_win_range_later(wp, top, end); invalid_top = (linenr_T)0; } diff --git a/src/match.c b/src/match.c index bc50757b37..ef2587947d 100644 --- a/src/match.c +++ b/src/match.c @@ -187,20 +187,7 @@ match_add( // Calculate top and bottom lines for redrawing area if (toplnum != 0) { - if (wp->w_buffer->b_mod_set) - { - if (wp->w_buffer->b_mod_top > toplnum) - wp->w_buffer->b_mod_top = toplnum; - if (wp->w_buffer->b_mod_bot < botlnum) - wp->w_buffer->b_mod_bot = botlnum; - } - else - { - wp->w_buffer->b_mod_set = TRUE; - wp->w_buffer->b_mod_top = toplnum; - wp->w_buffer->b_mod_bot = botlnum; - wp->w_buffer->b_mod_xlines = 0; - } + redraw_win_range_later(wp, toplnum, botlnum); m->mit_toplnum = toplnum; m->mit_botlnum = botlnum; rtype = UPD_VALID; @@ -269,20 +256,7 @@ match_delete(win_T *wp, int id, int perr) vim_free(cur->mit_pattern); if (cur->mit_toplnum != 0) { - if (wp->w_buffer->b_mod_set) - { - if (wp->w_buffer->b_mod_top > cur->mit_toplnum) - wp->w_buffer->b_mod_top = cur->mit_toplnum; - if (wp->w_buffer->b_mod_bot < cur->mit_botlnum) - wp->w_buffer->b_mod_bot = cur->mit_botlnum; - } - else - { - wp->w_buffer->b_mod_set = TRUE; - wp->w_buffer->b_mod_top = cur->mit_toplnum; - wp->w_buffer->b_mod_bot = cur->mit_botlnum; - wp->w_buffer->b_mod_xlines = 0; - } + redraw_win_range_later(wp, cur->mit_toplnum, cur->mit_botlnum); rtype = UPD_VALID; } vim_free(cur->mit_pos_array); diff --git a/src/proto/drawscreen.pro b/src/proto/drawscreen.pro index 6fa5e2c451..6f1d3e37a9 100644 --- a/src/proto/drawscreen.pro +++ b/src/proto/drawscreen.pro @@ -24,4 +24,5 @@ void status_redraw_curbuf(void); void redraw_statuslines(void); void win_redraw_last_status(frame_T *frp); void redrawWinline(win_T *wp, linenr_T lnum); +void redraw_win_range_later(win_T *wp, linenr_T first, linenr_T last); /* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c index 4a9af920b2..792747e55d 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1136, /**/ 1135, /**/ From 14e8208d8432df86828830b3798553f99bedb333 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 22 Feb 2025 13:35:15 +0100 Subject: [PATCH 014/228] runtime(doc): get rid of the titlestring hack for terminal-api fixes: #16656 Signed-off-by: Christian Brabandt --- runtime/doc/terminal.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 53e2dcd081..1d502abc97 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -1,4 +1,4 @@ -*terminal.txt* For Vim version 9.1. Last change: 2025 Feb 13 +*terminal.txt* For Vim version 9.1. Last change: 2025 Feb 22 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1092,11 +1092,11 @@ Currently supported commands: Example in JSON: > ["drop", "path/file.txt", {"ff": "dos"}] -A trick to have Vim send this escape sequence: > - exe "set t_ts=\]51; t_fs=\x07" - let &titlestring = '["call","Tapi_TryThis",["hello",123]]' - redraw - set t_ts& t_fs& +You can use |echoraw()| to make Vim send this escape sequence: > + call echoraw("\]51;[\"call\", \"Tapi_TryThis\", [\"hello\", 123]]\x07") + call echoraw("\]51;[\"drop\", \"README.md\"]\x07") +Note: JSON requires double quotes around string values, hence those have to be +escaped. Rationale: Why not allow for any command or expression? Because that might create a security problem. From d15114c148e615b0c244e94bf91548299f6af047 Mon Sep 17 00:00:00 2001 From: Konfekt Date: Sat, 22 Feb 2025 15:07:09 +0100 Subject: [PATCH 015/228] runtime(compiler): include svelte-check compiler closes: #16704 Signed-off-by: Konfekt Signed-off-by: Christian Brabandt --- .github/MAINTAINERS | 1 + runtime/compiler/svelte-check.vim | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 runtime/compiler/svelte-check.vim diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS index a17e53281b..7a080f936d 100644 --- a/.github/MAINTAINERS +++ b/.github/MAINTAINERS @@ -98,6 +98,7 @@ runtime/compiler/se.vim @dkearns runtime/compiler/shellcheck.vim @dkearns runtime/compiler/sml.vim @dkearns runtime/compiler/spectral.vim @romainl +runtime/compiler/svelte-check.vim @Konfekt runtime/compiler/stylelint.vim @dkearns runtime/compiler/tcl.vim @dkearns runtime/compiler/tidy.vim @dkearns diff --git a/runtime/compiler/svelte-check.vim b/runtime/compiler/svelte-check.vim new file mode 100644 index 0000000000..883aefdbc4 --- /dev/null +++ b/runtime/compiler/svelte-check.vim @@ -0,0 +1,19 @@ +" Vim compiler file +" Compiler: svelte-check +" Maintainer: @Konfekt +" Last Change: 2025 Feb 22 + +if exists("current_compiler") | finish | endif +let current_compiler = "svelte-check" + +CompilerSet makeprg=npx\ svelte-check\ --output\ machine +CompilerSet errorformat=%*\\d\ %t%*\\a\ \"%f\"\ %l:%c\ %m +CompilerSet errorformat+=%-G%.%# + +" " Fall-back for versions of svelte-check that don't support --output machine +" " before May 2020 https://github.com/sveltejs/language-tools/commit/9f7a90379d287a41621a5e78af5b010a8ab810c3 +" " which is before the first production release 1.1.31 of Svelte-Check +" CompilerSet makeprg=npx\ svelte-check +" CompilerSet errorformat=%E%f:%l:%c, +" CompilerSet errorformat+=%+ZError\:\ %m, +" CompilerSet errorformat+=%-G%.%# From 61af587f26f56be7d6b55f77e42cc89504942cc0 Mon Sep 17 00:00:00 2001 From: David Mandelberg Date: Sat, 22 Feb 2025 15:09:03 +0100 Subject: [PATCH 016/228] runtime(dockerfile): set comments in filetype plugin closes: #16698 Signed-off-by: David Mandelberg Signed-off-by: Honza Pokorny Signed-off-by: Christian Brabandt --- runtime/ftplugin/dockerfile.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/ftplugin/dockerfile.vim b/runtime/ftplugin/dockerfile.vim index e45bf4c1d8..f9268fe89b 100644 --- a/runtime/ftplugin/dockerfile.vim +++ b/runtime/ftplugin/dockerfile.vim @@ -1,7 +1,7 @@ " Vim filetype plugin " Language: Dockerfile " Maintainer: Honza Pokorny -" Last Change: 2024 Dec 20 +" Last Change: 2025 Feb 21 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -11,6 +11,7 @@ endif " Don't load another plugin for this buffer let b:did_ftplugin = 1 +setlocal comments=:# setlocal commentstring=#\ %s -let b:undo_ftplugin = "setl commentstring<" +let b:undo_ftplugin = "setl comments< commentstring<" From 95a3120b40e1fc7fd322ac0a326eefbc8297bd7b Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sat, 22 Feb 2025 22:28:53 -0800 Subject: [PATCH 017/228] Add better scroll wheel support and option to disable acceleration macOS applies a acceleration to the mouse scroll wheel which is often frustrating to use. In particular, a single click on the scroll wheel generates a fractional scroll, which doesn't do anything in MacVim as it scrolls by line. Fix this by forcing each scroll event to scroll at least one line (this is configurable to be higher, or 0 which means use the old behavior). Also add an option to simply turn off acceleration and scroll a fixed number of lines. Also, fix horizontal scrolling using mouse wheel. In macOS, the way to scroll horizontally using a normal wheel is to hold Shift key and scroll, and the OS will convert that to a horizontal scroll event. However, we were sending the shift modifier to Vim as well, which interprets it as which is not what we want (this scrolls a whole page). We manually remove the shift modifier when we detect this. It does mean there's functionally no way to send shift-scroll wheel events to Vim, but it is ok, as macOS generally works this way and it's consistent with how native GUI apps work. --- runtime/doc/gui_mac.txt | 15 ++++++--- runtime/doc/tags | 3 ++ src/MacVim/Base.lproj/Preferences.xib | 47 ++++++++++++++++++++++++--- src/MacVim/MMAppController.m | 3 ++ src/MacVim/MMTextViewHelper.m | 44 ++++++++++++++++++++----- src/MacVim/Miscellaneous.h | 5 ++- src/MacVim/Miscellaneous.m | 3 ++ 7 files changed, 102 insertions(+), 18 deletions(-) diff --git a/runtime/doc/gui_mac.txt b/runtime/doc/gui_mac.txt index 619c88a314..283d22dd29 100644 --- a/runtime/doc/gui_mac.txt +++ b/runtime/doc/gui_mac.txt @@ -283,8 +283,6 @@ as general information regarding macOS user defaults. Here is a list of relevant dictionary entries: KEY VALUE ~ -*MMAllowForceClickLookUp* use Force click for data lookup instead of - [bool] *MMCellWidthMultiplier* width of a normal glyph in em units [float] *MMCmdLineAlignBottom* Pin command-line to bottom of MacVim [bool] *MMDialogsTrackPwd* open/save dialogs track the Vim pwd [bool] @@ -307,7 +305,6 @@ KEY VALUE ~ *MMTitlebarAppearsTransparent* enable a transparent titlebar [bool] *MMAppearanceModeSelection* dark mode selection (|macvim-dark-mode|)[bool] *MMRendererClipToRow* clip tall characters to the row they are on [bool] -*MMScrollOneDirectionOnly* scroll along one axis only when using trackpad [bool] *MMSmoothResize* allow smooth resizing of MacVim window [bool] *MMShareFindPboard* share search text to Find Pasteboard [bool] *MMTextInsetBottom* text area offset in pixels [int] @@ -316,12 +313,22 @@ KEY VALUE ~ *MMTextInsetTop* text area offset in pixels [int] *MMTexturedWindow* use brushed metal window (Tiger only) [bool] *MMTranslateCtrlClick* interpret ctrl-click as right-click [bool] -*MMUseMouseTime* use mousetime to detect multiple clicks [bool] *MMVerticalSplit* files open in vertical splits [bool] *MMZoomBoth* zoom button maximizes both directions [bool] *MMUpdaterPrereleaseChannel* opt-in to pre-release software update [bool] *MMShowWhatsNewOnStartup* show "What's New" after updating to new version [bool] +Mouse / Trackpad ~ +*MMScrollOneDirectionOnly* scroll along one axis only when using trackpad [bool] +*MMAllowForceClickLookUp* use Force click for data lookup instead of + [bool] +*MMMouseWheelDisableAcceleration* disable OS scroll wheel acceleration [bool] +*MMMouseWheelNumLines* how many lines to scroll when scroll wheel + acceleration is turned off [int] +*MMMouseWheelMinLines* min number of lines to scroll per scroll wheel + click when acceleration is on [int] +*MMUseMouseTime* use mousetime to detect multiple clicks [bool] + Tabs ~ *MMTabColorsMode* use default/auto/colorscheme for tab colors [int] *MMWindowUseTabBackgroundColor* use tabs background fill color as window color [bool] diff --git a/runtime/doc/tags b/runtime/doc/tags index 6009d63700..a651266968 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -5654,6 +5654,9 @@ MMFullScreenFadeTime gui_mac.txt /*MMFullScreenFadeTime* MMLoginShell gui_mac.txt /*MMLoginShell* MMLoginShellArgument gui_mac.txt /*MMLoginShellArgument* MMLoginShellCommand gui_mac.txt /*MMLoginShellCommand* +MMMouseWheelDisableAcceleration gui_mac.txt /*MMMouseWheelDisableAcceleration* +MMMouseWheelMinLines gui_mac.txt /*MMMouseWheelMinLines* +MMMouseWheelNumLines gui_mac.txt /*MMMouseWheelNumLines* MMNativeFullScreen gui_mac.txt /*MMNativeFullScreen* MMNoFontSubstitution gui_mac.txt /*MMNoFontSubstitution* MMNoTitleBarWindow gui_mac.txt /*MMNoTitleBarWindow* diff --git a/src/MacVim/Base.lproj/Preferences.xib b/src/MacVim/Base.lproj/Preferences.xib index 6d70bccb9f..c5ad95715a 100644 --- a/src/MacVim/Base.lproj/Preferences.xib +++ b/src/MacVim/Base.lproj/Preferences.xib @@ -573,11 +573,11 @@ - + - + @@ -604,11 +604,11 @@ - + - + @@ -617,7 +617,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/MacVim/MMAppController.m b/src/MacVim/MMAppController.m index 393ddd502e..596dd4da4c 100644 --- a/src/MacVim/MMAppController.m +++ b/src/MacVim/MMAppController.m @@ -217,6 +217,9 @@ + (void)registerDefaults [NSNumber numberWithBool:YES], MMUseInlineImKey, #endif // INCLUDE_OLD_IM_CODE [NSNumber numberWithBool:NO], MMSuppressTerminationAlertKey, + [NSNumber numberWithBool:NO], MMMouseWheelDisableAccelerationKey, + [NSNumber numberWithInt:1], MMMouseWheelMinLinesKey, + [NSNumber numberWithInt:3], MMMouseWheelNumLinesKey, [NSNumber numberWithBool:YES], MMNativeFullScreenKey, [NSNumber numberWithDouble:0.0], MMFullScreenFadeTimeKey, [NSNumber numberWithBool:NO], MMNonNativeFullScreenShowMenuKey, diff --git a/src/MacVim/MMTextViewHelper.m b/src/MacVim/MMTextViewHelper.m index c854e95b87..533a1b40c6 100644 --- a/src/MacVim/MMTextViewHelper.m +++ b/src/MacVim/MMTextViewHelper.m @@ -312,15 +312,15 @@ - (void)doCommandBySelector:(SEL)sel else interpretKeyEventsSwallowedKey = NO; } -- (void)scrollWheel:(NSEvent *)event +- (void)calcScrollWheelDeltas:(NSEvent *)event x:(float *)x y:(float *)y eventFlags:(NSEventModifierFlags*)eventFlags { float dx = 0; float dy = 0; -#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_7 + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + if ([event hasPreciseScrollingDeltas]) { const NSEventPhase phase = event.phase; - NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; CGFloat eventScrollingDeltaX = event.scrollingDeltaX; CGFloat eventScrollingDeltaY = event.scrollingDeltaY; @@ -366,12 +366,40 @@ - (void)scrollWheel:(NSEvent *)event scrollingDeltaY = 0; dx = [event scrollingDeltaX]; dy = [event scrollingDeltaY]; + + if ([ud boolForKey:MMMouseWheelDisableAccelerationKey]) { + // Use scroll wheel as discrete steps as macOS acceleration often + // leads to unintuitive results. + NSInteger numLines = [ud integerForKey:MMMouseWheelNumLinesKey]; + dx = dx == 0 ? 0 : (dx > 0 ? numLines : -numLines); + dy = dy == 0 ? 0 : (dy > 0 ? numLines : -numLines); + } else { + // Use the default OS acceleration, but we still want to round up + // the delta to at least 1 (configurable) as otherwise it's + // frustrating to use as MacVim fails to scroll even one line. + NSInteger minScrollLines = [ud integerForKey:MMMouseWheelMinLinesKey]; + dx = dx == 0 ? 0 : (fabs(dx) > minScrollLines ? dx : (dx > 0 ? minScrollLines : -minScrollLines)); + dy = dy == 0 ? 0 : (fabs(dy) > minScrollLines ? dy : (dy > 0 ? minScrollLines : -minScrollLines)); + } + + if ((*eventFlags & NSEventModifierFlagShift) && dx != 0 && dy == 0) { + // A shift-scroll in macOS converts a normal Y to X-axis scroll. + // There is no proper API to query this so this is just done by + // heuristics. We need to remove the shift flag, or Vim would see + // this as a which is not what we want. + *eventFlags = *eventFlags & (~NSEventModifierFlagShift); + } } -#else - dx = [event deltaX]; - dy = [event deltaY]; -#endif + *x = dx; + *y = dy; +} + +- (void)scrollWheel:(NSEvent *)event +{ + NSEventModifierFlags eventFlags = event.modifierFlags; + float dx = 0, dy = 0; + [self calcScrollWheelDeltas:event x:&dx y:&dy eventFlags:&eventFlags]; if (dx == 0 && dy == 0) return; @@ -386,7 +414,7 @@ - (void)scrollWheel:(NSEvent *)event int row, col; NSPoint pt = [textView convertPoint:[event locationInWindow] fromView:nil]; if ([textView convertPoint:pt toRow:&row column:&col]) { - unsigned flags = (unsigned)[event modifierFlags]; + unsigned flags = (unsigned)eventFlags; NSMutableData *data = [NSMutableData data]; [data appendBytes:&row length:sizeof(int)]; diff --git a/src/MacVim/Miscellaneous.h b/src/MacVim/Miscellaneous.h index d36d2f1f4a..0216de6f21 100644 --- a/src/MacVim/Miscellaneous.h +++ b/src/MacVim/Miscellaneous.h @@ -61,6 +61,9 @@ extern NSString *MMUseInlineImKey; extern NSString *MMSuppressTerminationAlertKey; extern NSString *MMNativeFullScreenKey; extern NSString *MMUseMouseTimeKey; +extern NSString *MMMouseWheelDisableAccelerationKey; ///< Disable native macOS mouse scroll wheel acceleration +extern NSString *MMMouseWheelMinLinesKey; ///< Minimum scroll delta for mouse scroll wheel, only when acceleration is on +extern NSString *MMMouseWheelNumLinesKey; ///< Number of lines to scroll using mouse scroll wheel, only when acceleration is off extern NSString *MMFullScreenFadeTimeKey; extern NSString *MMNonNativeFullScreenShowMenuKey; extern NSString *MMNonNativeFullScreenSafeAreaBehaviorKey; @@ -69,7 +72,7 @@ extern NSString *MMCmdLineAlignBottomKey; extern NSString *MMRendererClipToRowKey; extern NSString *MMAllowForceClickLookUpKey; extern NSString *MMUpdaterPrereleaseChannelKey; -extern NSString *MMLastUsedBundleVersionKey; // The last used version of MacVim before this launch +extern NSString *MMLastUsedBundleVersionKey; ///< The last used version of MacVim before this launch extern NSString *MMShowWhatsNewOnStartupKey; extern NSString *MMScrollOneDirectionOnlyKey; diff --git a/src/MacVim/Miscellaneous.m b/src/MacVim/Miscellaneous.m index 17e9304238..3618db398e 100644 --- a/src/MacVim/Miscellaneous.m +++ b/src/MacVim/Miscellaneous.m @@ -57,6 +57,9 @@ NSString *MMSuppressTerminationAlertKey = @"MMSuppressTerminationAlert"; NSString *MMNativeFullScreenKey = @"MMNativeFullScreen"; NSString *MMUseMouseTimeKey = @"MMUseMouseTime"; +NSString *MMMouseWheelDisableAccelerationKey = @"MMMouseWheelDisbleAcceleration"; +NSString *MMMouseWheelMinLinesKey = @"MMMouseWheelMinLines"; +NSString *MMMouseWheelNumLinesKey = @"MMMouseWheelNumLines"; NSString *MMFullScreenFadeTimeKey = @"MMFullScreenFadeTime"; NSString *MMNonNativeFullScreenShowMenuKey = @"MMNonNativeFullScreenShowMenu"; NSString *MMNonNativeFullScreenSafeAreaBehaviorKey = @"MMNonNativeFullScreenSafeAreaBehavior"; From 066a5340e3d7ccc1fd9d1ee3ddf02cdc5ccf2813 Mon Sep 17 00:00:00 2001 From: "Philip H." <47042125+pheiduck@users.noreply.github.com> Date: Sun, 23 Feb 2025 08:50:54 +0100 Subject: [PATCH 018/228] CI: Install netbeans on windows to make sure to run test_netbeans.vim otherwise the test src/testdir/test_netbeans.vim wouldn't be executed by the CI. closes: #16710 Signed-off-by: Philip H. <47042125+pheiduck@users.noreply.github.com> Signed-off-by: Christian Brabandt --- .github/workflows/ci.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dbf922c51..4ffd759765 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -678,6 +678,17 @@ jobs: # name: vim${{ matrix.bits }}-${{ matrix.toolchain }} # path: ./artifacts + - name: Install packages for testing + shell: bash + run: | + if ${{ matrix.features != 'TINY' }}; then + if ${{ matrix.arch == 'x64' }}; then + choco install netbeans --no-progress + else + exit 0 + fi + fi + - name: Test and show the result of testing gVim if: matrix.GUI == 'yes' || matrix.VIMDLL == 'yes' shell: cmd From f4b36417e893ff40296f1a5a264a4ecc6965f1d5 Mon Sep 17 00:00:00 2001 From: John Marriott Date: Sun, 23 Feb 2025 09:09:59 +0100 Subject: [PATCH 019/228] patch 9.1.1137: ins_str() is inefficient by calling STRLEN() Problem: ins_str() is inefficient by calling STRLLEN() Solution: refactor ins_str() to take a length argument and let all callers provide the correct length when calling ins_str() (John Marriott) closes: #16711 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/change.c | 13 ++++++------- src/edit.c | 8 ++++---- src/gui.c | 2 +- src/indent.c | 4 +++- src/insexpand.c | 24 ++++++++++++------------ src/ops.c | 28 ++++++++++++++++------------ src/proto/change.pro | 2 +- src/textformat.c | 2 +- src/version.c | 2 ++ 9 files changed, 46 insertions(+), 39 deletions(-) diff --git a/src/change.c b/src/change.c index fb33971e1b..294adf7dfc 100644 --- a/src/change.c +++ b/src/change.c @@ -1178,10 +1178,9 @@ ins_char_bytes(char_u *buf, int charlen) * Caller must have prepared for undo. */ void -ins_str(char_u *s) +ins_str(char_u *s, size_t slen) { char_u *oldp, *newp; - int newlen = (int)STRLEN(s); int oldlen; colnr_T col; linenr_T lnum = curwin->w_cursor.lnum; @@ -1193,16 +1192,16 @@ ins_str(char_u *s) oldp = ml_get(lnum); oldlen = (int)ml_get_len(lnum); - newp = alloc(oldlen + newlen + 1); + newp = alloc(oldlen + slen + 1); if (newp == NULL) return; if (col > 0) mch_memmove(newp, oldp, (size_t)col); - mch_memmove(newp + col, s, (size_t)newlen); - mch_memmove(newp + col + newlen, oldp + col, (size_t)(oldlen - col + 1)); + mch_memmove(newp + col, s, slen); + mch_memmove(newp + col + slen, oldp + col, (size_t)(oldlen - col + 1)); ml_replace(lnum, newp, FALSE); - inserted_bytes(lnum, col, newlen); - curwin->w_cursor.col += newlen; + inserted_bytes(lnum, col, slen); + curwin->w_cursor.col += slen; } /* diff --git a/src/edit.c b/src/edit.c index 8ad5a66e24..a1224a2b17 100644 --- a/src/edit.c +++ b/src/edit.c @@ -2058,7 +2058,7 @@ insert_special( if (stop_arrow() == FAIL) return; p[len - 1] = NUL; - ins_str(p); + ins_str(p, len - 1); AppendToRedobuffLit(p, -1); ctrlv = FALSE; } @@ -2275,7 +2275,7 @@ insertchar( do_digraph(buf[i-1]); // may be the start of a digraph #endif buf[i] = NUL; - ins_str(buf); + ins_str(buf, i); if (flags & INSCHAR_CTRLV) { redo_literal(*buf); @@ -4300,7 +4300,7 @@ ins_bs( ins_char(' '); else { - ins_str((char_u *)" "); + ins_str((char_u *)" ", 1); if ((State & REPLACE_FLAG)) replace_push(NUL); } @@ -4976,7 +4976,7 @@ ins_tab(void) ins_char(' '); else { - ins_str((char_u *)" "); + ins_str((char_u *)" ", 1); if (State & REPLACE_FLAG) // no char replaced replace_push(NUL); } diff --git a/src/gui.c b/src/gui.c index bebd51af96..0b9e328999 100644 --- a/src/gui.c +++ b/src/gui.c @@ -5289,7 +5289,7 @@ gui_do_findrepl( del_bytes((long)(regmatch.endp[0] - regmatch.startp[0]), FALSE, FALSE); - ins_str(repl_text); + ins_str(repl_text, STRLEN(repl_text)); } } else diff --git a/src/indent.c b/src/indent.c index e7de005ae0..6951cfcdb1 100644 --- a/src/indent.c +++ b/src/indent.c @@ -1429,11 +1429,13 @@ change_indent( ptr = alloc(i + 1); if (ptr != NULL) { + size_t ptrlen; new_cursor_col += i; ptr[i] = NUL; + ptrlen = i; while (--i >= 0) ptr[i] = ' '; - ins_str(ptr); + ins_str(ptr, ptrlen); vim_free(ptr); } } diff --git a/src/insexpand.c b/src/insexpand.c index dc8c76beec..deae1ce346 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -4402,7 +4402,7 @@ ins_compl_delete(void) // In insert mode: Delete the typed part. // In replace mode: Put the old characters back, if any. int col = compl_col + (compl_status_adding() ? compl_length : 0); - char_u *remaining = NULL; + string_T remaining = {NULL, 0}; int orig_col; int has_preinsert = ins_compl_preinsert_effect(); if (has_preinsert) @@ -4415,18 +4415,18 @@ ins_compl_delete(void) { if (curwin->w_cursor.col < ml_get_curline_len()) { - char_u *line = ml_get_curline(); - remaining = vim_strnsave(line + curwin->w_cursor.col, - (size_t)STRLEN(line + curwin->w_cursor.col)); - if (remaining == NULL) + char_u *line = ml_get_cursor(); + remaining.length = ml_get_cursor_len(); + remaining.string = vim_strnsave(line, remaining.length); + if (remaining.string == NULL) return; } while (curwin->w_cursor.lnum > compl_lnum) { if (ml_delete(curwin->w_cursor.lnum) == FAIL) { - if (remaining) - VIM_CLEAR(remaining); + if (remaining.string) + vim_free(remaining.string); return; } deleted_lines_mark(curwin->w_cursor.lnum, 1L); @@ -4440,20 +4440,20 @@ ins_compl_delete(void) { if (stop_arrow() == FAIL) { - if (remaining) - VIM_CLEAR(remaining); + if (remaining.string) + vim_free(remaining.string); return; } backspace_until_column(col); compl_ins_end_col = curwin->w_cursor.col; } - if (remaining != NULL) + if (remaining.string != NULL) { orig_col = curwin->w_cursor.col; - ins_str(remaining); + ins_str(remaining.string, remaining.length); curwin->w_cursor.col = orig_col; - vim_free(remaining); + vim_free(remaining.string); } // TODO: is this sufficient for redrawing? Redrawing everything causes // flicker, thus we can't do that. diff --git a/src/ops.c b/src/ops.c index 9efef383d1..0e3711eb75 100644 --- a/src/ops.c +++ b/src/ops.c @@ -2796,8 +2796,6 @@ do_addsub( linenr_T Prenum1) { int col; - char_u *buf1; - char_u buf2[NUMBUFLEN]; int pre; // 'X'/'x': hex; '0': octal; 'B'/'b': bin static int hexupper = FALSE; // 0xABC uvarnumber_T n; @@ -3012,6 +3010,10 @@ do_addsub( } else { + char_u *buf1; + int buf1len; + char_u buf2[NUMBUFLEN]; + int buf2len; pos_T save_pos; int i; @@ -3174,20 +3176,20 @@ do_addsub( for (bit = bits; bit > 0; bit--) if ((n >> (bit - 1)) & 0x1) break; - for (i = 0; bit > 0 && i < (NUMBUFLEN - 1); bit--) - buf2[i++] = ((n >> (bit - 1)) & 0x1) ? '1' : '0'; + for (buf2len = 0; bit > 0 && buf2len < (NUMBUFLEN - 1); bit--) + buf2[buf2len++] = ((n >> (bit - 1)) & 0x1) ? '1' : '0'; - buf2[i] = '\0'; + buf2[buf2len] = NUL; } else if (pre == 0) - vim_snprintf((char *)buf2, NUMBUFLEN, "%llu", n); + buf2len = vim_snprintf((char *)buf2, NUMBUFLEN, "%llu", n); else if (pre == '0') - vim_snprintf((char *)buf2, NUMBUFLEN, "%llo", n); + buf2len = vim_snprintf((char *)buf2, NUMBUFLEN, "%llo", n); else if (pre && hexupper) - vim_snprintf((char *)buf2, NUMBUFLEN, "%llX", n); + buf2len = vim_snprintf((char *)buf2, NUMBUFLEN, "%llX", n); else - vim_snprintf((char *)buf2, NUMBUFLEN, "%llx", n); - length -= (int)STRLEN(buf2); + buf2len = vim_snprintf((char *)buf2, NUMBUFLEN, "%llx", n); + length -= buf2len; /* * Adjust number of zeros to the new number of digits, so the @@ -3199,8 +3201,10 @@ do_addsub( while (length-- > 0) *ptr++ = '0'; *ptr = NUL; + buf1len = (int)(ptr - buf1); - STRCAT(buf1, buf2); + STRCPY(buf1 + buf1len, buf2); + buf1len += buf2len; // Insert just after the first character to be removed, so that any // text properties will be adjusted. Then delete the old number @@ -3208,7 +3212,7 @@ do_addsub( save_pos = curwin->w_cursor; if (todel > 0) inc_cursor(); - ins_str(buf1); // insert the new number + ins_str(buf1, (size_t)buf1len); // insert the new number vim_free(buf1); // del_char() will also mark line needing displaying diff --git a/src/proto/change.pro b/src/proto/change.pro index 502b2855fd..f24d74cd59 100644 --- a/src/proto/change.pro +++ b/src/proto/change.pro @@ -23,7 +23,7 @@ void ins_bytes(char_u *p); void ins_bytes_len(char_u *p, int len); void ins_char(int c); void ins_char_bytes(char_u *buf, int charlen); -void ins_str(char_u *s); +void ins_str(char_u *s, size_t slen); int del_char(int fixpos); int del_chars(long count, int fixpos); int del_bytes(long count, int fixpos_arg, int use_delcombine); diff --git a/src/textformat.c b/src/textformat.c index 77e3eefafd..018565f724 100644 --- a/src/textformat.c +++ b/src/textformat.c @@ -434,7 +434,7 @@ internal_format( // add the additional whitespace needed after the // comment leader for the numbered list. for (i = 0; i < padding; i++) - ins_str((char_u *)" "); + ins_str((char_u *)" ", 1); } else { diff --git a/src/version.c b/src/version.c index 792747e55d..948abdf784 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1137, /**/ 1136, /**/ From a7b8120820dc5257c76dc6086c077fa86189f4ee Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sun, 23 Feb 2025 09:32:47 +0100 Subject: [PATCH 020/228] patch 9.1.1138: cmdline completion for :hi is too simplistic Problem: Existing cmdline completion for :highlight was barebone and only completed the highlight group names. Solution: Implement full completion for the highlight group arguments such as guifg and cterm. If the user tries to complete immediately after the '=' (e.g. `hi Normal guifg=`), the completion will fill in the existing value, similar to how cmdline completion for options work (Yee Cheng Chin). closes: #16712 Signed-off-by: Yee Cheng Chin Signed-off-by: Christian Brabandt --- runtime/doc/eval.txt | 4 +- runtime/doc/syntax.txt | 6 +- runtime/doc/version9.txt | 83 ++++--- src/cmdexpand.c | 37 ++- src/highlight.c | 420 +++++++++++++++++++++++++++++++++-- src/proto/cmdexpand.pro | 1 + src/proto/highlight.pro | 1 + src/syntax.c | 2 +- src/testdir/test_cmdline.vim | 131 ++++++++++- src/testdir/test_syntax.vim | 2 +- src/version.c | 2 + src/vim.h | 1 + 12 files changed, 614 insertions(+), 76 deletions(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 0851aa5c35..0ada1b2159 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 9.1. Last change: 2025 Jan 29 +*eval.txt* For Vim version 9.1. Last change: 2025 Feb 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2081,7 +2081,7 @@ v:colornames A dictionary that maps color names to hex color strings. These You can make changes to that file, but make sure to add new keys instead of updating existing ones, otherwise Vim will skip - loading the file (thinking is hasn't been changed). + loading the file (thinking it hasn't been changed). *v:completed_item* *completed_item-variable* v:completed_item diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index cb6704cd33..d8c4b08ea6 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.1. Last change: 2025 Feb 20 +*syntax.txt* For Vim version 9.1. Last change: 2025 Feb 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -5432,6 +5432,10 @@ in their own color. See |:highlight-default| for the optional [default] argument. +:hi[ghlight][!] [default] link {from-group} {to-group} +:hi[ghlight][!] [default] link {from-group} NONE + See |:hi-link|. + Normally a highlight group is added once when starting up. This sets the default values for the highlighting. After that, you can use additional highlight commands to change the arguments that you want to set to non-default diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index de38d08e2e..99a4002c12 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.1. Last change: 2025 Feb 11 +*version9.txt* For Vim version 9.1. Last change: 2025 Feb 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -41573,8 +41573,11 @@ Include the "linematch" algorithm for the 'diffopt' setting. This aligns changes between buffers on similar lines improving the diff highlighting in Vim -Adjusted default values ~ ------------------------ + *changed-9.2* +Changed~ +------- + +Default values: ~ - the default 'history' option value has been increased to 200 and removed from |defaults.vim| - the default 'backspace' option for Vim has been set to "indent,eol,start" @@ -41584,61 +41587,69 @@ Adjusted default values ~ - the default value of the 'keyprotocol' option has been updated and support for the ghostty terminal emulator (using kitty protocol) has been added - *changed-9.2* -Changed~ -------- -- use 'smoothscroll' logic for CTRL-F and CTRL-B for pagewise scrolling -- use 'smoothscroll' logic for CTRL-D and CTRL-U for half-pagewise scrolling + +Completion: ~ +- allow to complete directories from 'cdpath' for |:cd| and similar commands, + add the "cd_in_path" completion type for e.g. |:command-complete| and + |getcompletion()| +- allow to complete shell commands and files using the new shellcmdline + completion type using |:command-complete| and |getcmdcomplpat()| +- allow to specify additional attributes in the completion menu (allows to + mark deprecated attributes from LSP server) |complete-items| +- the completed word and completion type are provided when handling the + |CompleteDone| autocommand in the |v:event| dictionary +- |complete_info()| returns the list of matches shown in the poppu menu via + the "matches" key +- New option value for 'completeopt': + "nosort" - do not sort completion results + "preinsert" - highlight to be inserted values +- handle multi-line completion as expected +- improved commandline completion for the |:hi| command + +Options: ~ - the default for 'commentstring' contains whitespace padding to have automatic comments look nicer |comment-install| - 'completeopt' is now a |global-local| option. - 'nrformats' accepts the new "blank" suboption, to determine a signed or unsigned number based on whitespace in front of a minus sign. +- add 'cpoptions' flag "z" |cpo-z|, to disable some (traditional) vi + behaviour/inconsistency (see |d-special| and |cw|). +- 'rulerformat' now supports the |stl-%!| item +- use 'smoothscroll' logic for CTRL-F / CTRL-B for pagewise scrolling + and CTRL-D / CTRL-U for half-pagewise scrolling + +Ex commands: ~ - allow to specify a priority when defining a new sign |:sign-define| -- provide information about function arguments using the get(func, "arity") - function |get()-func| - |:bwipe| also wipes jumplist and tagstack data - moving in the buffer list using |:bnext| and similar commands, behaves as documented and skips help buffers (if not run from a help buffer, else moves to the next/previous help buffer). -- allow to complete directories from 'cdpath' for |:cd| and similar commands, - add the "cd_in_path" completion type for e.g. |:command-complete| and - |getcompletion()| -- allow to complete shell commands and files using the new shellcmdline - completion type using |:command-complete| and |getcmdcomplpat()| -- add 'cpoptions' flag "z" |cpo-z|, to disable some (traditional) vi - behaviour/inconsistency (see |d-special| and |cw|). -- allow to specify additional attributes in the completion menu (allows to - mark deprecated attributes from LSP server) |complete-items| -- the regex engines match correctly case-insensitive multi-byte characters - (and apply proper case folding) - |:keeppatterns| preserves the last substitute pattern when used with |:s| + +Functions: ~ +- provide information about function arguments using the get(func, "arity") + function |get()-func| - |setqflist()| and |setloclist()| can optionally try to preserve the current selection in the quickfix list with the "u" action. +- allow to pass local Vim script variables to python interpreter |py3eval()| +- |getwininfo()| now also returns the "leftcol" property for a window +- |v:stacktrace| The stack trace of the exception most recently caught and + not finished +- Add the optional {opts} |Dict| argument to |getchar()| to control: cursor + behaviour, return type and whether or not to simplify the returned key + +Others: ~ +- the regex engines match correctly case-insensitive multi-byte characters + (and apply proper case folding) - the putty terminal is detected using an |TermResponse| autocommand in |defaults.vim| and Vim switches to a dark background - the |help-TOC| package is included to ease navigating the documentation. - an interactive tutor plugin has been included |vim-tutor-mode|, can be started via |:Tutor| - improve the |vimtutor| and add a second chapter for more advanced tips -- allow to pass local Vim script variables to python interpreter |py3eval()| -- |getwininfo()| now also returns the "leftcol" property for a window -- 'rulerformat' now supports the |stl-%!| item -- the completed word and completion type are provided when handling the - |CompleteDone| autocommand in the |v:event| dictionary -- |complete_info()| returns the list of matches shown in the poppu menu via - the "matches" key -- |v:stacktrace| The stack trace of the exception most recently caught and - not finished -- New option value for 'completeopt': - "nosort" - do not sort completion results - "preinsert" - highlight to be inserted values - add |dist#vim9#Launch()| and |dist#vim9#Open()| to the |vim-script-library| and decouple it from |netrw| - new digraph "APPROACHES THE LIMIT" using ".=" -- Add the optional {opts} |Dict| argument to |getchar()| to control: cursor - behaviour, return type and whether or not to simplify the returned key -- handle multi-line completion as expected *added-9.2* Added ~ diff --git a/src/cmdexpand.c b/src/cmdexpand.c index f707b1d586..c14eee2c93 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -3221,6 +3221,8 @@ ExpandFromContext( ret = ExpandMappings(pat, ®match, numMatches, matches); else if (xp->xp_context == EXPAND_ARGOPT) ret = expand_argopt(pat, xp, ®match, matches, numMatches); + else if (xp->xp_context == EXPAND_HIGHLIGHT_GROUP) + ret = expand_highlight_group(pat, xp, ®match, matches, numMatches); #if defined(FEAT_TERMINAL) else if (xp->xp_context == EXPAND_TERMINALOPT) ret = expand_terminal_opt(pat, xp, ®match, matches, numMatches); @@ -3239,6 +3241,21 @@ ExpandFromContext( return ret; } + int +ExpandGeneric( + char_u *pat, + expand_T *xp, + regmatch_T *regmatch, + char_u ***matches, + int *numMatches, + char_u *((*func)(expand_T *, int)), + // returns a string from the list + int escaped) +{ + return ExpandGenericExt( + pat, xp, regmatch, matches, numMatches, func, escaped, 0); +} + /* * Expand a list of names. * @@ -3249,10 +3266,14 @@ ExpandFromContext( * If 'fuzzy' is TRUE, then fuzzy matching is used. Otherwise, regex matching * is used. * + * 'sortStartIdx' allows the caller to control sorting behavior. Items before + * the index will not be sorted. Pass 0 to sort all, and -1 to prevent any + * sorting. + * * Returns OK when no problems encountered, FAIL for error (out of memory). */ int -ExpandGeneric( +ExpandGenericExt( char_u *pat, expand_T *xp, regmatch_T *regmatch, @@ -3260,7 +3281,8 @@ ExpandGeneric( int *numMatches, char_u *((*func)(expand_T *, int)), // returns a string from the list - int escaped) + int escaped, + int sortStartIdx) { int i; garray_T ga; @@ -3271,6 +3293,7 @@ ExpandGeneric( int match; int sort_matches = FALSE; int funcsort = FALSE; + int sortStartMatchIdx = -1; fuzzy = cmdline_fuzzy_complete(pat); *matches = NULL; @@ -3346,6 +3369,12 @@ ExpandGeneric( } #endif + if (sortStartIdx >= 0 && i >= sortStartIdx && sortStartMatchIdx == -1) + { + // Found first item to start sorting from. This is usually 0. + sortStartMatchIdx = ga.ga_len; + } + ++ga.ga_len; } @@ -3371,14 +3400,14 @@ ExpandGeneric( funcsort = TRUE; // Sort the matches. - if (sort_matches) + if (sort_matches && sortStartMatchIdx != -1) { if (funcsort) // functions should be sorted to the end. qsort((void *)ga.ga_data, (size_t)ga.ga_len, sizeof(char_u *), sort_func_compare); else - sort_strings((char_u **)ga.ga_data, ga.ga_len); + sort_strings((char_u **)ga.ga_data + sortStartMatchIdx, ga.ga_len - sortStartMatchIdx); } if (!fuzzy) diff --git a/src/highlight.c b/src/highlight.c index 8c1ad8049e..2d965660e2 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -1691,6 +1691,8 @@ do_highlight( break; } + // Note: Keep this in sync with get_highlight_group_key. + // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" // or "guibg"). while (*linep && !VIM_ISWHITE(*linep) && *linep != '=') @@ -3058,6 +3060,7 @@ highlight_list_one(int id) if (message_filtered(sgp->sg_name)) return; + // Note: Keep this in sync with expand_highlight_group(). didh = highlight_list_arg(id, didh, LIST_ATTR, sgp->sg_term, NULL, "term"); didh = highlight_list_arg(id, didh, LIST_STRING, @@ -3108,37 +3111,24 @@ highlight_list_one(int id) #endif } - static int -highlight_list_arg( - int id, - int didh, + static char_u* +highlight_arg_to_string( int type, int iarg, char_u *sarg, - char *name) + char_u *buf) { - char_u buf[MAX_ATTR_LEN]; - char_u *ts; - int i; - - if (got_int) - return FALSE; - - if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0)) - return didh; - - ts = buf; if (type == LIST_INT) sprintf((char *)buf, "%d", iarg - 1); else if (type == LIST_STRING) - ts = sarg; + return sarg; else // type == LIST_ATTR { size_t buflen; buf[0] = NUL; buflen = 0; - for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i) + for (int i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i) { if (iarg & highlight_index_tab[i]->key) { @@ -3153,6 +3143,28 @@ highlight_list_arg( } } } + return buf; +} + + static int +highlight_list_arg( + int id, + int didh, + int type, + int iarg, + char_u *sarg, + char *name) +{ + char_u buf[MAX_ATTR_LEN]; + char_u *ts; + + if (got_int) + return FALSE; + + if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0)) + return didh; + + ts = highlight_arg_to_string(type, iarg, sarg, buf); (void)syn_list_header(didh, (int)(vim_strsize(ts) + STRLEN(name) + 1), id); @@ -4078,6 +4090,15 @@ highlight_changed(void) static void highlight_list(void); static void highlight_list_two(int cnt, int attr); +// context for :highlight expansion +static int expand_hi_synid = 0; // ID for highlight group being completed +static int expand_hi_equal_col = 0; // column where the '=' is +static int expand_hi_include_orig = 0; // whether to fill the existing current value or not +static char_u *expand_hi_curvalue = NULL; // the existing current value +#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) +static dict_iterator_T expand_colornames_iter; // iterator for looping through v:colornames +#endif + /* * Handle command line completion for :highlight command. */ @@ -4085,10 +4106,12 @@ static void highlight_list_two(int cnt, int attr); set_context_in_highlight_cmd(expand_T *xp, char_u *arg) { char_u *p; + int expand_group = TRUE; // Default: expand group names xp->xp_context = EXPAND_HIGHLIGHT; xp->xp_pattern = arg; + include_none = 0; include_link = 2; include_default = 1; @@ -4114,9 +4137,11 @@ set_context_in_highlight_cmd(expand_T *xp, char_u *arg) // past group name include_link = 0; if (arg[1] == 'i' && arg[0] == 'N') + { highlight_list(); - if (STRNCMP("link", arg, p - arg) == 0 - || STRNCMP("clear", arg, p - arg) == 0) + expand_group = FALSE; + } + if (STRNCMP("link", arg, p - arg) == 0) { xp->xp_pattern = skipwhite(p); p = skiptowhite(xp->xp_pattern); @@ -4124,10 +4149,67 @@ set_context_in_highlight_cmd(expand_T *xp, char_u *arg) { xp->xp_pattern = skipwhite(p); p = skiptowhite(xp->xp_pattern); + include_none = 1; } + expand_group = FALSE; + } + else if (STRNCMP("clear", arg, p - arg) == 0) + { + xp->xp_pattern = skipwhite(p); + p = skiptowhite(xp->xp_pattern); + expand_group = FALSE; } if (*p != NUL) // past group name(s) - xp->xp_context = EXPAND_NOTHING; + { + if (expand_group) + { + // expansion will be done in expand_highlight_group() + xp->xp_context = EXPAND_HIGHLIGHT_GROUP; + + expand_hi_synid = syn_namen2id(arg, (int)(p - arg)); + + while (*p != NUL) + { + arg = skipwhite(p); + p = skiptowhite(arg); + } + + p = vim_strchr(arg, '='); + if (p == NULL) + { + // Didn't find a key= pattern + xp->xp_pattern = arg; + expand_hi_equal_col = -1; + expand_hi_include_orig = FALSE; + } + else + { + // Found key= pattern, record the exact location + expand_hi_equal_col = (int)(p - xp->xp_line); + + // Only include the original value if the pattern is empty + if (*(p + 1) == NUL) + expand_hi_include_orig = TRUE; + else + expand_hi_include_orig = FALSE; + + // Account for comma-separated values + if (STRNCMP(arg, "term=", 5) == 0 || + STRNCMP(arg, "cterm=", 6) == 0 || + STRNCMP(arg, "gui=", 4) == 0) + { + char_u *comma = vim_strrchr(p + 1, ','); + if (comma != NULL) + p = comma; + } + xp->xp_pattern = p + 1; + } + } + else + { + xp->xp_context = EXPAND_NOTHING; + } + } } /* @@ -4178,7 +4260,7 @@ get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared) return (char_u *)""; if (idx == highlight_ga.ga_len && include_none != 0) - return (char_u *)"none"; + return (char_u *)"NONE"; if (idx == highlight_ga.ga_len + include_none && include_default != 0) return (char_u *)"default"; if (idx == highlight_ga.ga_len + include_none + include_default @@ -4192,6 +4274,300 @@ get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared) return HL_TABLE()[idx].sg_name; } + static char_u * +get_highlight_attr_name(expand_T *xp UNUSED, int idx) +{ + if (idx == 0) + { + // Fill with current value first + if (expand_hi_curvalue != NULL) + return expand_hi_curvalue; + else + return (char_u*)""; + } + if (idx < (int)ARRAY_LENGTH(highlight_index_tab) + 1) + { + char_u *value = highlight_index_tab[idx-1]->value.string; + if (expand_hi_curvalue != NULL && STRCMP(expand_hi_curvalue, value) == 0) + { + // Already returned the current value above, just skip. + return (char_u*)""; + } + return value; + } + return NULL; +} + + static char_u * +get_highlight_cterm_color(expand_T *xp UNUSED, int idx) +{ + if (idx == 0) + { + // Fill with current value first + if (expand_hi_curvalue != NULL) + return expand_hi_curvalue; + else + return (char_u*)""; + } + // See highlight_set_cterm_color() + else if (idx == 1) + return (char_u*)"fg"; + else if (idx == 2) + return (char_u*)"bg"; + if (idx < (int)ARRAY_LENGTH(color_name_tab) + 3) + { + char_u *value = color_name_tab[idx-3].value.string; + return value; + } + return NULL; +} + +#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) + static char_u * +get_highlight_gui_color(expand_T *xp UNUSED, int idx) +{ + if (idx == 0) + { + // Fill with current value first + if (expand_hi_curvalue != NULL) + return expand_hi_curvalue; + else + return (char_u*)""; + } + // See color_name2handle() + else if (idx == 1) + return (char_u*)"fg"; + else if (idx == 2) + return (char_u*)"bg"; + else if (idx == 3) + return (char_u*)"NONE"; + + // Complete from v:colornames. Don't do platform specific names for now. + typval_T *tv_result; + char_u *colorname = dict_iterate_next(&expand_colornames_iter, &tv_result); + if (colorname != NULL) + { + // :hi command doesn't allow space, so don't suggest any malformed items + if (vim_strchr(colorname, ' ') != NULL) + return (char_u*)""; + + if (expand_hi_curvalue != NULL && STRICMP(expand_hi_curvalue, colorname) == 0) + { + // Already returned the current value above, just skip. + return (char_u*)""; + } + } + return colorname; +} +#endif + + static char_u * +get_highlight_group_key(expand_T *xp UNUSED, int idx) +{ + // Note: Keep this in sync with do_highlight. + static char *(p_hi_group_key_values[]) = + { + "term=", + "start=", + "stop=", + "cterm=", + "ctermfg=", + "ctermbg=", + "ctermul=", + "ctermfont=", +#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + "gui=", + "guifg=", + "guibg=", + "guisp=", +#endif +#ifdef FEAT_GUI + "font=", +#endif + "NONE", + }; + + if (idx < (int)ARRAY_LENGTH(p_hi_group_key_values)) + return (char_u*)p_hi_group_key_values[idx]; + return NULL; +} + +/* + * Command-line expansion for :hi {group-name} ... + */ + int +expand_highlight_group( + char_u *pat, + expand_T *xp, + regmatch_T *rmp, + char_u ***matches, + int *numMatches) +{ + if (expand_hi_equal_col != -1) + { + // List the values. First fill in the current value, then if possible colors + // or attribute names. + char_u *(*expandfunc)(expand_T *, int) = NULL; + int type = 0; + hl_group_T *sgp = NULL; + int iarg = 0; + char_u *sarg = NULL; + + int unsortedItems = -1; // don't sort by default + + if (expand_hi_synid != 0) + sgp = &HL_TABLE()[expand_hi_synid - 1]; // index is ID minus one + + // Note: Keep this in sync with highlight_list_one(). + char_u *name_end = xp->xp_line + expand_hi_equal_col; + if (name_end - xp->xp_line >= 5 + && STRNCMP(name_end - 5, " term", 5) == 0) + { + expandfunc = get_highlight_attr_name; + if (sgp) + { + type = LIST_ATTR; + iarg = sgp->sg_term; + } + } + else if (name_end - xp->xp_line >= 6 + && STRNCMP(name_end - 6, " cterm", 6) == 0) + { + expandfunc = get_highlight_attr_name; + if (sgp) + { + type = LIST_ATTR; + iarg = sgp->sg_cterm; + } + } +#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + else if (name_end - xp->xp_line >= 4 + && STRNCMP(name_end - 4, " gui", 4) == 0) + { + expandfunc = get_highlight_attr_name; + if (sgp) + { + type = LIST_ATTR; + iarg = sgp->sg_gui; + } + } +#endif + else if (name_end - xp->xp_line >= 8 + && STRNCMP(name_end - 8, " ctermfg", 8) == 0) + { + expandfunc = get_highlight_cterm_color; + if (sgp) + { + type = LIST_INT; + iarg = sgp->sg_cterm_fg; + } + } + else if (name_end - xp->xp_line >= 8 + && STRNCMP(name_end - 8, " ctermbg", 8) == 0) + { + expandfunc = get_highlight_cterm_color; + if (sgp) + { + type = LIST_INT; + iarg = sgp->sg_cterm_bg; + } + } + else if (name_end - xp->xp_line >= 8 + && STRNCMP(name_end - 8, " ctermul", 8) == 0) + { + expandfunc = get_highlight_cterm_color; + if (sgp) + { + type = LIST_INT; + iarg = sgp->sg_cterm_ul; + } + } +#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) + else if (name_end - xp->xp_line >= 6 + && STRNCMP(name_end - 6, " guifg", 6) == 0) + { + expandfunc = get_highlight_gui_color; + if (sgp) + { + type = LIST_STRING; + sarg = sgp->sg_gui_fg_name; + } + } + else if (name_end - xp->xp_line >= 6 + && STRNCMP(name_end - 6, " guibg", 6) == 0) + { + expandfunc = get_highlight_gui_color; + if (sgp) + { + type = LIST_STRING; + sarg = sgp->sg_gui_bg_name; + } + } + else if (name_end - xp->xp_line >= 6 + && STRNCMP(name_end - 6, " guisp", 6) == 0) + { + expandfunc = get_highlight_gui_color; + if (sgp) + { + type = LIST_STRING; + sarg = sgp->sg_gui_sp_name; + } + } +#endif + +#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) + if (expandfunc == get_highlight_gui_color) + { + // Top 4 items are special, after that sort all the color names + unsortedItems = 4; + + dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES); + typval_T colornames_val; + colornames_val.v_type = VAR_DICT; + colornames_val.vval.v_dict = colornames_table; + dict_iterate_start(&colornames_val, &expand_colornames_iter); + } +#endif + + char_u buf[MAX_ATTR_LEN]; + + if (expand_hi_synid != 0 && type != 0 && expand_hi_include_orig) + { + // Retrieve the current value to go first in completion + expand_hi_curvalue = highlight_arg_to_string( + type, iarg, sarg, buf); + } + else + expand_hi_curvalue = NULL; + + if (expandfunc != NULL) + { + return ExpandGenericExt( + pat, + xp, + rmp, + matches, + numMatches, + expandfunc, + FALSE, + unsortedItems); + } + + return FAIL; + } + + // List all the key names + return ExpandGenericExt( + pat, + xp, + rmp, + matches, + numMatches, + get_highlight_group_key, + FALSE, + -1); +} + #if defined(FEAT_GUI) || defined(PROTO) /* * Free all the highlight group fonts. diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro index dfa6d1b96b..bfcc5c8386 100644 --- a/src/proto/cmdexpand.pro +++ b/src/proto/cmdexpand.pro @@ -17,6 +17,7 @@ void set_expand_context(expand_T *xp); void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline); int expand_cmdline(expand_T *xp, char_u *str, int col, int *matchcount, char_u ***matches); int ExpandGeneric(char_u *pat, expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches, char_u *((*func)(expand_T *, int)), int escaped); +int ExpandGenericExt(char_u *pat, expand_T *xp, regmatch_T *regmatch, char_u ***matches, int *numMatches, char_u *((*func)(expand_T *, int)), int escaped, int sortStartIdx); void globpath(char_u *path, char_u *file, garray_T *ga, int expand_options, int dirs); int wildmenu_translate_key(cmdline_info_T *cclp, int key, expand_T *xp, int did_wild_list); int wildmenu_process_key(cmdline_info_T *cclp, int key, expand_T *xp); diff --git a/src/proto/highlight.pro b/src/proto/highlight.pro index 33f90c6525..531825836a 100644 --- a/src/proto/highlight.pro +++ b/src/proto/highlight.pro @@ -43,6 +43,7 @@ int highlight_changed(void); void set_context_in_highlight_cmd(expand_T *xp, char_u *arg); char_u *get_highlight_name(expand_T *xp, int idx); char_u *get_highlight_name_ext(expand_T *xp, int idx, int skip_cleared); +int expand_highlight_group( char_u *pat, expand_T *xp, regmatch_T *rmp, char_u ***matches, int *numMatches); void free_highlight_fonts(void); void f_hlget(typval_T *argvars, typval_T *rettv); void f_hlset(typval_T *argvars, typval_T *rettv); diff --git a/src/syntax.c b/src/syntax.c index 34563aad9d..bbcf02b052 100644 --- a/src/syntax.c +++ b/src/syntax.c @@ -6372,7 +6372,7 @@ reset_expand_highlight(void) } /* - * Handle command line completion for :match and :echohl command: Add "None" + * Handle command line completion for :match and :echohl command: Add "NONE" * as highlight group. */ void diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index dfebb400b5..042710c2aa 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -418,8 +418,8 @@ func Test_match_completion() hi Aardig ctermfg=green call feedkeys(":match \\\"\", 'xt') call assert_equal('"match Aardig', @:) - call feedkeys(":match \\\"\", 'xt') - call assert_equal('"match none', @:) + call feedkeys(":match NON\\\"\", 'xt') + call assert_equal('"match NONE', @:) call feedkeys(":match | chist\\\"\", 'xt') call assert_equal('"match | chistory', @:) endfunc @@ -428,20 +428,37 @@ func Test_highlight_completion() hi Aardig ctermfg=green call feedkeys(":hi \\\"\", 'xt') call assert_equal('"hi Aardig', getreg(':')) + + " hi default call feedkeys(":hi default \\\"\", 'xt') call assert_equal('"hi default Aardig', getreg(':')) - call feedkeys(":hi clear Aa\\\"\", 'xt') - call assert_equal('"hi clear Aardig', getreg(':')) - call feedkeys(":hi li\\\"\", 'xt') - call assert_equal('"hi link', getreg(':')) call feedkeys(":hi d\\\"\", 'xt') call assert_equal('"hi default', getreg(':')) + call feedkeys(":hi default link Aa\\\"\", 'xt') + call assert_equal('"hi default link Aardig', getreg(':')) + + " hi clear only accepts one parameter call feedkeys(":hi c\\\"\", 'xt') call assert_equal('"hi clear', getreg(':')) + call feedkeys(":hi clear Aa\\\"\", 'xt') + call assert_equal('"hi clear Aardig', getreg(':')) call feedkeys(":hi clear Aardig Aard\\\"\", 'xt') - call assert_equal('"hi clear Aardig Aardig', getreg(':')) - call feedkeys(":hi Aardig \\\"\", 'xt') - call assert_equal("\"hi Aardig \t", getreg(':')) + call assert_equal("\"hi clear Aardig Aard\", getreg(':')) + " hi link accepts up to two parameters + call feedkeys(":hi li\\\"\", 'xt') + call assert_equal('"hi link', getreg(':')) + call assert_equal('"hi link', getreg(':')) + call feedkeys(":hi link Aa\\\"\", 'xt') + call assert_equal('"hi link Aardig', getreg(':')) + call feedkeys(":hi link Aardig Aard\\\"\", 'xt') + call assert_equal("\"hi link Aardig Aardig", getreg(':')) + call feedkeys(":hi link Aardig Aardig Aard\\\"\", 'xt') + call assert_equal("\"hi link Aardig Aardig Aard\", getreg(':')) + " hi link will complete to "NONE" for second parameter + call feedkeys(":hi link NON\\\"\", 'xt') + call assert_equal("\"hi link NonText", getreg(':')) + call feedkeys(":hi link Aardig NON\\\"\", 'xt') + call assert_equal("\"hi link Aardig NONE", getreg(':')) " A cleared group does not show up in completions. hi Anders ctermfg=green @@ -460,6 +477,102 @@ func Test_highlight_easter_egg() call test_override('ALL', 0) endfunc +func Test_highlight_group_completion() + " Test completing keys + call assert_equal('term=', getcompletion('hi Foo ', 'cmdline')[0]) + call assert_equal('ctermfg=', getcompletion('hi Foo c*fg', 'cmdline')[0]) + call assert_equal('NONE', getcompletion('hi Foo NON', 'cmdline')[0]) + set wildoptions+=fuzzy + call assert_equal('ctermbg=', getcompletion('hi Foo cmbg', 'cmdline')[0]) + set wildoptions-=fuzzy + + " Test completing the current value + hi FooBar term=bold,underline cterm=undercurl ctermfg=lightgray ctermbg=12 ctermul=34 + call assert_equal('bold,underline', getcompletion('hi FooBar term=', 'cmdline')[0]) + call assert_equal('undercurl', getcompletion('hi FooBar cterm=', 'cmdline')[0]) + call assert_equal('7', getcompletion('hi FooBar ctermfg=', 'cmdline')[0]) + call assert_equal('12', getcompletion('hi FooBar ctermbg=', 'cmdline')[0]) + call assert_equal('34', getcompletion('hi FooBar ctermul=', 'cmdline')[0]) + + " "bold,underline" is unique and creates an extra item. "undercurl" and + " should be de-duplicated + call assert_equal(len(getcompletion('hi FooBar term=', 'cmdline')), + \ 1 + len(getcompletion('hi FooBar cterm=', 'cmdline'))) + + " don't complete original value if we have user input already, similar to + " behavior in :set