From 3d224e42e71c3ee781ca999145aec27cd4259d30 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 8 Apr 2026 18:18:53 +0000 Subject: [PATCH 001/545] patch 9.2.0322: tests: test_popupwin fails Problem: tests: test_popupwin fails (after v9.2.0319) Solution: Regenerate the dump file (Yasuhiro Matsumoto). The reference dump for Test_popupwin_opacity_wide_2 was not updated when patch 9.2.0319 changed the blending behavior for double-width characters at popup padding boundaries. The covered half of a double-width character now correctly shows a space instead of the full character. related: #19881 closes: #19934 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/testdir/dumps/Test_popupwin_opacity_wide_2.dump | 6 +++--- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump b/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump index 50ca50354a..a6ffdb11de 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump @@ -6,9 +6,9 @@ |い*&| +&|╭+0#ffffff16#0000e05|─@13|╮| +0#0000000#ffffff0|ー*&@7|い|!+&| |6| @3 |い*&| +&|│+0#ffffff16#0000e05|あ*&|め|ん|ぼ|赤|い|な|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |7| @3 |い*&| +&|│+0#ffffff16#0000e05|あ*&|い|う|え|お| +&| +0&#e000002|ー*&|│+&| |ー*&@5|ー*0#0000000#ffffff0@1|い|!+&| |8| @3 -|い*&| +&|│+0#ffffff16#0000e05|ー*&@4| +&| +0&#e000002|カ*&|ラ|フ|ル|な|ー@2|ー*0#0000000#ffffff0@1|い|!+&| |9| @3 -|い*&| +&|╰+0#ffffff16#0000e05|─@10|─+0&#e000002|ポ*&|ッ|プ|ア|ッ|プ|で|ー|ー*0#0000000#ffffff0@1|い|!+&| |1|0| @2 -|い*&|え|ー@4| +&| +0#ffffff16#e000002|最*&|上|川|ー@4|ー*0#0000000#ffffff0@1|い|!+&| |1@1| @2 +|い*&| +&|│+0#ffffff16#0000e05|ー*&@4| +&| +0&#e000002|カ*&|ラ|フ|ル|な|ー@1| +&@1|ー*0#0000000#ffffff0@1|い|!+&| |9| @3 +|い*&| +&|╰+0#ffffff16#0000e05|─@10|─+0&#e000002|ポ*&|ッ|プ|ア|ッ|プ|で| +&@1|ー*0#0000000#ffffff0@1|い|!+&| |1|0| @2 +|い*&|え|ー@4| +&| +0#ffffff16#e000002|最*&|上|川|ー@3| +&@1|ー*0#0000000#ffffff0@1|い|!+&| |1@1| @2 |い*&|え|ー@4| +&| +0#ffffff16#e000002|ー*&@7|ー*0#0000000#ffffff0@1|い|!+&| |1|2| @2 |い*&|え|ー@15|い|!+&| |1|3| @2 |い*&|え|ー@15|い|!+&| |1|4| @2 diff --git a/src/version.c b/src/version.c index baacac399d..f31488e285 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 322, /**/ 321, /**/ From c2734dc03cd9ee437d25a6940e8c37ac7873b01d Mon Sep 17 00:00:00 2001 From: Stefan VanBuren Date: Wed, 8 Apr 2026 18:28:08 +0000 Subject: [PATCH 002/545] patch 9.2.0323: filetype: buf.lock files are not recognized Problem: filetype: buf.lock files are not recognized Solution: Detect buf.lock files as yaml filetype (Stefan VanBuren) Reference: https://buf.build/docs/configuration/v2/buf-lock/ closes: #19935 Signed-off-by: Stefan VanBuren 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 aa36477c1d..8bfd13da17 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: 2026 Mar 24 +" Last Change: 2026 Apr 08 " Former Maintainer: Bram Moolenaar " If the filetype can be detected from extension or file name(the final path component), @@ -1312,7 +1312,7 @@ au BufNewFile,BufRead *Xmodmap setf xmodmap au BufNewFile,BufRead *.y call dist#ft#FTy() " Yaml -au BufNewFile,BufRead */.kube/config,*/.kube/kuberc setf yaml +au BufNewFile,BufRead buf.lock,*/.kube/config,*/.kube/kuberc setf yaml " yum conf (close enough to dosini) au BufNewFile,BufRead */etc/yum.conf setf dosini diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 6bd96aed3a..7a6615531c 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -991,7 +991,7 @@ def s:GetFilenameChecks(): dict> xslt: ['file.xsl', 'file.xslt'], yacc: ['file.yy', 'file.yxx', 'file.y++'], yaml: ['file.yaml', 'file.yml', 'file.eyaml', 'file.kyaml', 'file.kyml', 'any/.bundle/config', '.clangd', '.clang-format', '.clang-tidy', 'file.mplstyle', 'matplotlibrc', 'yarn.lock', - '/home/user/.kube/config', '/home/user/.kube/kuberc', '.condarc', 'condarc', '.mambarc', 'mambarc', 'pixi.lock'], + '/home/user/.kube/config', '/home/user/.kube/kuberc', '.condarc', 'condarc', '.mambarc', 'mambarc', 'pixi.lock', 'buf.lock'], yang: ['file.yang'], yara: ['file.yara', 'file.yar'], yuck: ['file.yuck'], diff --git a/src/version.c b/src/version.c index f31488e285..fbe3e48eba 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 323, /**/ 322, /**/ From 3e194b10685a99a63a2bf4c97beac3541af0c4ac Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 9 Apr 2026 18:35:39 +0000 Subject: [PATCH 003/545] runtime(vimball): detect more path traversal attacks Signed-off-by: Christian Brabandt --- runtime/autoload/vimball.vim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/vimball.vim b/runtime/autoload/vimball.vim index fb4df5eb66..d661ded631 100644 --- a/runtime/autoload/vimball.vim +++ b/runtime/autoload/vimball.vim @@ -6,7 +6,8 @@ " GetLatestVimScripts: 1502 1 :AutoInstall: vimball.vim " Last Change: " 2025 Feb 28 by Vim Project: add support for bzip3 (#16755) -" 2026 Apr 05 by Vim Project: Detect Path Traversal Attacks +" 2026 Apr 05 by Vim Project: Detect path traversal attacks +" 2026 Apr 09 by Vim Project: Detect more path traversal attacks " Copyright: (c) 2004-2011 by Charles E. Campbell " The VIM LICENSE applies to Vimball.vim, and Vimball.txt " (see |copyright|) except use "Vimball" instead of "Vim". @@ -229,7 +230,8 @@ fun! vimball#Vimball(really,...) let fsize = substitute(getline(linenr+1),'^\(\d\+\).\{-}$','\1','')+0 let fenc = substitute(getline(linenr+1),'^\d\+\s*\(\S\{-}\)$','\1','') let filecnt = filecnt + 1 - if fname =~ '\.\.' + " Do not allow a leading / or .. anywhere in the file name + if fname =~ '\.\.' || fname =~ '^/' echomsg "(Vimball) Path Traversal Attack detected, aborting..." exe "tabn ".curtabnr bw! Vimball From 72cefe6b72c92b5f3686ee297277a2940cbb702f Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Thu, 9 Apr 2026 18:38:35 +0000 Subject: [PATCH 004/545] runtime(help): support highlighting groups in translated syntax doc closes: #19942 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/syntax/help.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/syntax/help.vim b/runtime/syntax/help.vim index effdc4b874..c851bdfec2 100644 --- a/runtime/syntax/help.vim +++ b/runtime/syntax/help.vim @@ -1,8 +1,9 @@ " Vim syntax file " Language: Vim help file " Maintainer: Doug Kearns -" Last Change: 2025 Nov 13 " Former Maintainer: Bram Moolenaar +" Last Change: 2025 Nov 13 +" 2026 Apr 09 by Vim project: improve pattern for translated syntaxt.txt #19942 " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") @@ -306,7 +307,7 @@ hi def link helpDiffAdded Added hi def link helpDiffChanged Changed hi def link helpDiffRemoved Removed -if has('textprop') && expand('%:p') =~ '[/\\]doc[/\\]syntax.txt' +if has('textprop') && expand('%:p') =~? '[/\\]doc[/\\]syntax.\(txt\|\a\ax\)$' " highlight groups with their respective color import 'dist/vimhelp.vim' call vimhelp.HighlightGroups() From c23aebdf99ae733841de881d79e0f80319da947b Mon Sep 17 00:00:00 2001 From: Maxim Kim Date: Thu, 9 Apr 2026 18:41:47 +0000 Subject: [PATCH 005/545] runtime(org): Link to generic bold/Italic highlighting groups fixes: #19933 Signed-off-by: Maxim Kim Signed-off-by: Christian Brabandt --- runtime/syntax/org.vim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/syntax/org.vim b/runtime/syntax/org.vim index f4e938f9ee..75264be5d4 100644 --- a/runtime/syntax/org.vim +++ b/runtime/syntax/org.vim @@ -3,6 +3,7 @@ " Previous Maintainer: Luca Saccarola " Maintainer: This runtime file is looking for a new maintainer. " Last Change: 2025 Aug 05 +" 2026 Apr 09: Link to generic Bold/Italic groups " " Reference Specification: Org mode manual " GNU Info: `$ info Org` @@ -18,17 +19,17 @@ syn case ignore " Bold syn region orgBold matchgroup=orgBoldDelimiter start="\(^\|[- '"({\]]\)\@<=\*\ze[^ ]" end="^\@!\*\([^\k\*]\|$\)\@=" keepend -hi def link orgBold markdownBold +hi def link orgBold Bold hi def link orgBoldDelimiter orgBold " Italic syn region orgItalic matchgroup=orgItalicDelimiter start="\(^\|[- '"({\]]\)\@<=\/\ze[^ ]" end="^\@!\/\([^\k\/]\|$\)\@=" keepend -hi def link orgItalic markdownItalic +hi def link orgItalic Italic hi def link orgItalicDelimiter orgItalic " Strikethrogh syn region orgStrikethrough matchgroup=orgStrikethroughDelimiter start="\(^\|[ '"({\]]\)\@<=+\ze[^ ]" end="^\@!+\([^\k+]\|$\)\@=" keepend -hi def link orgStrikethrough markdownStrike +hi def orgStrikethrough term=strikethrough cterm=strikethrough gui=strikethrough hi def link orgStrikethroughDelimiter orgStrikethrough " Underline From 9d9381fb2804c426f88cd9b30650b94fc500d6bb Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Thu, 9 Apr 2026 18:49:43 +0000 Subject: [PATCH 006/545] runtime(doc): Tweak documentation style a bit closes: #19939 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/filetype.txt | 3 +-- runtime/doc/netbeans.txt | 2 +- runtime/doc/options.txt | 14 +++++++------- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 3aa41db21a..790cfc2bd1 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1,4 +1,4 @@ -*filetype.txt* For Vim version 9.2. Last change: 2026 Apr 07 +*filetype.txt* For Vim version 9.2. Last change: 2026 Apr 09 VIM REFERENCE MANUAL by Bram Moolenaar @@ -652,7 +652,6 @@ One command, :DiffGitCached, is provided to show a diff of the current commit in the preview window. It is equivalent to calling "git diff --cached" plus any arguments given to the command. - The length of the first line of the commit message used for syntax highlighting can be configured via `g:gitcommit_summary_length`. The default is 50. Example: > diff --git a/runtime/doc/netbeans.txt b/runtime/doc/netbeans.txt index d20ae51907..4803e97497 100644 --- a/runtime/doc/netbeans.txt +++ b/runtime/doc/netbeans.txt @@ -1,4 +1,4 @@ -*netbeans.txt* For Vim version 9.2. Last change: 2026 Mar 07 +*netbeans.txt* For Vim version 9.2. Last change: 2026 Apr 09 VIM REFERENCE MANUAL by Gordon Prieur et al. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index ffe463e441..03f77c519c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6886,7 +6886,7 @@ A jump table for the options with a short description can be found at |Q_op|. global Defines a border and optional decorations for the popup menu in completion. The value is a comma-separated list of keywords. - See |'pumopt'| for a consolidated alternative. + See 'pumopt' for a consolidated alternative. Border styles (at most one): "single" use thin box-drawing characters @@ -6922,7 +6922,7 @@ A jump table for the options with a short description can be found at |Q_op|. Determines the maximum number of items to show in the popup menu for Insert mode completion. When zero as much space as available is used. |ins-completion-menu|. - See |'pumopt'| for a consolidated alternative. + See 'pumopt' for a consolidated alternative. *'pummaxwidth'* *'pmw'* 'pummaxwidth' 'pmw' number (default 0) @@ -6934,13 +6934,13 @@ A jump table for the options with a short description can be found at |Q_op|. This option takes precedence over 'pumwidth'. |ins-completion-menu|. - See |'pumopt'| for a consolidated alternative. + See 'pumopt' for a consolidated alternative. *'pumopt'* 'pumopt' string (default "") global Configures the popup menu used for Insert mode completion. - The value is a comma-separated list of key:value pairs and flags. + The value is a comma-separated list of "key:value" pairs and flags. Keys with values: border:{style} set a border style (at most one): @@ -6970,8 +6970,8 @@ A jump table for the options with a short description can be found at |Q_op|. shadow draws a shadow at the right and bottom edges. Border styles using box-drawing characters ("single", "double", - "round") are only available when 'encoding' is "utf-8" and - 'ambiwidth' is "single". + "round") are only available when 'encoding' is "utf-8" and 'ambiwidth' + is "single". Highlight groups: |hl-PmenuBorder| used for the border characters @@ -6994,7 +6994,7 @@ A jump table for the options with a short description can be found at |Q_op|. global Determines the minimum width to use for the popup menu for Insert mode completion. |ins-completion-menu|. - See |'pumopt'| for a consolidated alternative. + See 'pumopt' for a consolidated alternative. *'pythondll'* 'pythondll' string (default depends on the build) From 3e2012914e0905eebfcf5de27a977ab166f93494 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Apr 2026 18:55:02 +0000 Subject: [PATCH 007/545] patch 9.2.0324: 0x9b byte not unescaped in mapping Problem: 0x9b byte not unescaped in mapping (BenYip). Solution: Translate K_CSI to CSI like what is done in vgetc(). (zeertzjq). fixes: #19936 closes: #19937 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/getchar.c | 7 +++++++ src/testdir/test_mapping.vim | 9 ++++++++- src/version.c | 2 ++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/getchar.c b/src/getchar.c index b4ef9ce82e..57e407b899 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -4283,6 +4283,13 @@ getcmdkeycmd( // to a single Esc here. if (c1 == K_ESC) c1 = ESC; + +#ifdef FEAT_GUI + // Translate K_CSI to CSI. The special key is only used to + // avoid it being recognized as the start of a special key. + if (c1 == K_CSI) + c1 = CSI; +#endif } if (got_int) diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim index 01f2d1f944..29f42848d4 100644 --- a/src/testdir/test_mapping.vim +++ b/src/testdir/test_mapping.vim @@ -1124,7 +1124,11 @@ func Test_map_cmdkey() call setline(1, ['some short lines', 'of test text']) call feedkeys(":bar\x\\"\r", 'xt') call assert_equal('"barx', @:) - unmap! + + " test for chars with 0x80 or 0x9b bytes + map let x = '洛固四最倒倀' + call feedkeys("\", 'xt') + call assert_equal('洛固四最倒倀', x) " test for calling a function let lines =<< trim END @@ -1137,7 +1141,10 @@ func Test_map_cmdkey() source Xscript call feedkeys("\", 'xt') call assert_equal(32, g:x) + unlet g:x + unmap + unmap! unmap unmap! %bw! diff --git a/src/version.c b/src/version.c index fbe3e48eba..99a25eb7c1 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 324, /**/ 323, /**/ From 00285c035aa7d92971bc50b7b124e1408dda53ad Mon Sep 17 00:00:00 2001 From: Aaron Burrow Date: Thu, 9 Apr 2026 19:06:13 +0000 Subject: [PATCH 008/545] patch 9.2.0325: runtime(tar): bug in zstd handling Problem: patch 9.2.0325: runtime(tar): bug in zstd handling Solution: use correct --zstd argument, separated from other arguments, rework testing framework (Aaron Burrow). The tar.vim plugin allows vim to read and manipulate zstd archives, but it had a bug that caused extraction attempts to fail. Specifically, if the archive has a .tar.zst or .tzst extension, then the code was generating invalid extraction commands that looked like this: tar --zstdpxf foo.tar.zst foo When they should be like this: tar --zstd -pxf foo.tar.zst foo This patch changes the flag manipulation logic so that --zstd isn't glued to pxf. The labor for this change was divided between ChatGPT 5.4 and me. ChatGPT 5.4 identified the issue (from a code scan?), and I wrote the patch and tested vim. related: #19930 Signed-off-by: Aaron Burrow Signed-off-by: Christian Brabandt --- runtime/autoload/tar.vim | 5 +- src/testdir/test_plugin_tar.vim | 142 +++++++++++++++++++++++--------- src/version.c | 2 + 3 files changed, 107 insertions(+), 42 deletions(-) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index 110327e95b..b068bd181b 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -21,6 +21,7 @@ " 2026 Feb 06 by Vim Project: consider 'nowrapscan' (#19333) " 2026 Feb 07 by Vim Project: make the path traversal detection more robust (#19341) " 2026 Apr 06 by Vim Project: fix bugs with lz4 support (#19925) +" 2026 Apr 09 by Vim Project: fix bugs with zstd support (#19930) " " Contains many ideas from Michael Toren's " @@ -687,7 +688,7 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tzst") - let extractcmd= substitute(extractcmd,"-","--zstd","") + let extractcmd= substitute(extractcmd,"-","--zstd -","") call system(extractcmd." ".shellescape(tarbase).".tzst ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tzst {fname}: failed!") @@ -696,7 +697,7 @@ fun! tar#Extract() endif elseif filereadable(tarbase.".tar.zst") - let extractcmd= substitute(extractcmd,"-","--zstd","") + let extractcmd= substitute(extractcmd,"-","--zstd -","") call system(extractcmd." ".shellescape(tarbase).".tar.zst ".shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.zst {fname}: failed!") diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim index d6dbdd7613..28cf709f85 100644 --- a/src/testdir/test_plugin_tar.vim +++ b/src/testdir/test_plugin_tar.vim @@ -148,56 +148,118 @@ def g:Test_tar_path_traversal_with_nowrapscan() bw! enddef -def g:Test_tar_lz4_extract() - CheckExecutable lz4 - - delete('X.txt') - delete('Xarchive.tar') - delete('Xarchive.tar.lz4') - call writefile(['hello'], 'X.txt') - call system('tar -cf Xarchive.tar X.txt') +def CreateTar(archivename: string, content: string, outputdir: string) + var tempdir = tempname() + mkdir(tempdir, 'R') + call writefile([content], tempdir .. '/X.txt') + assert_true(filereadable(tempdir .. '/X.txt')) + call system('tar -C ' .. tempdir .. ' -cf ' .. outputdir .. '/' .. archivename .. ' X.txt') assert_equal(0, v:shell_error) +enddef - call system('lz4 -z Xarchive.tar Xarchive.tar.lz4') +def CreateTgz(archivename: string, content: string, outputdir: string) + var tempdir = tempname() + mkdir(tempdir, 'R') + call writefile([content], tempdir .. '/X.txt') + assert_true(filereadable(tempdir .. '/X.txt')) + call system('tar -C ' .. tempdir .. ' -czf ' .. outputdir .. '/' .. archivename .. ' X.txt') assert_equal(0, v:shell_error) +enddef - delete('X.txt') - delete('Xarchive.tar') - defer delete('Xarchive.tar.lz4') - - e Xarchive.tar.lz4 - assert_match('X.txt', getline(5)) - :5 - normal x - assert_true(filereadable('X.txt')) - assert_equal(['hello'], readfile('X.txt')) - delete('X.txt') - bw! +def CreateTbz(archivename: string, content: string, outputdir: string) + var tempdir = tempname() + mkdir(tempdir, 'R') + call writefile([content], tempdir .. '/X.txt') + assert_true(filereadable(tempdir .. '/X.txt')) + call system('tar -C ' .. tempdir .. ' -cjf ' .. outputdir .. '/' .. archivename .. ' X.txt') + assert_equal(0, v:shell_error) enddef -def g:Test_tlz4_extract() - CheckExecutable lz4 +def CreateTxz(archivename: string, content: string, outputdir: string) + var tempdir = tempname() + mkdir(tempdir, 'R') + call writefile([content], tempdir .. '/X.txt') + assert_true(filereadable(tempdir .. '/X.txt')) + call system('tar -C ' .. tempdir .. ' -cJf ' .. outputdir .. '/' .. archivename .. ' X.txt') + assert_equal(0, v:shell_error) +enddef - delete('X.txt') - delete('Xarchive.tar') - delete('Xarchive.tlz4') - call writefile(['goodbye'], 'X.txt') - call system('tar -cf Xarchive.tar X.txt') +def CreateTzst(archivename: string, content: string, outputdir: string) + var tempdir = tempname() + mkdir(tempdir, 'R') + call writefile([content], tempdir .. '/X.txt') + assert_true(filereadable(tempdir .. '/X.txt')) + call system('tar --zstd -C ' .. tempdir .. ' -cf ' .. outputdir .. '/' .. archivename .. ' X.txt') assert_equal(0, v:shell_error) +enddef - call system('lz4 -z Xarchive.tar Xarchive.tlz4') +def CreateTlz4(archivename: string, content: string, outputdir: string) + var tempdir = tempname() + mkdir(tempdir, 'R') + call writefile([content], tempdir .. '/X.txt') + assert_true(filereadable(tempdir .. '/X.txt')) + call system('tar -C ' .. tempdir .. ' -cf ' .. tempdir .. '/Xarchive.tar X.txt') + assert_equal(0, v:shell_error) + assert_true(filereadable(tempdir .. '/Xarchive.tar')) + call system('lz4 -z ' .. tempdir .. '/Xarchive.tar ' .. outputdir .. '/' .. archivename) assert_equal(0, v:shell_error) +enddef - delete('X.txt') - delete('Xarchive.tar') - defer delete('Xarchive.tlz4') +# XXX: Add test for .tar.bz3 +def g:Test_extraction() + var control = [ + {create: CreateTar, + archive: 'Xarchive.tar'}, + {create: CreateTgz, + archive: 'Xarchive.tgz'}, + {create: CreateTgz, + archive: 'Xarchive.tar.gz'}, + {create: CreateTbz, + archive: 'Xarchive.tbz'}, + {create: CreateTbz, + archive: 'Xarchive.tar.bz2'}, + {create: CreateTxz, + archive: 'Xarchive.txz'}, + {create: CreateTxz, + archive: 'Xarchive.tar.xz'}, + ] + + if executable('lz4') == 1 + control->add({ + create: CreateTlz4, + archive: 'Xarchive.tar.lz4' + }) + control->add({ + create: CreateTlz4, + archive: 'Xarchive.tlz4' + }) + endif + if executable('zstd') == 1 + control->add({ + create: CreateTzst, + archive: 'Xarchive.tar.zst' + }) + control->add({ + create: CreateTzst, + archive: 'Xarchive.tzst' + }) + endif - e Xarchive.tlz4 - assert_match('X.txt', getline(5)) - :5 - normal x - assert_true(filereadable('X.txt')) - assert_equal(['goodbye'], readfile('X.txt')) - delete('X.txt') - bw! + for c in control + var dir = tempname() + mkdir(dir, 'R') + call(c.create, [c.archive, 'hello', dir]) + + delete('X.txt') + execute 'edit ' .. dir .. '/' .. c.archive + assert_match('X.txt', getline(5), 'line 5 wrong in archive: ' .. c.archive) + :5 + normal x + assert_equal(0, v:shell_error, 'vshell error not 0') + assert_true(filereadable('X.txt'), 'X.txt not readable for archive: ' .. c.archive) + assert_equal(['hello'], readfile('X.txt'), 'X.txt wrong contents for archive: ' .. c.archive) + delete('X.txt') + delete(dir .. '/' .. c.archive) + bw! + endfor enddef diff --git a/src/version.c b/src/version.c index 99a25eb7c1..5fc6428c2c 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 325, /**/ 324, /**/ From 4a1bcc67b4b6fc2dfe564ab4faca490f8afac857 Mon Sep 17 00:00:00 2001 From: Aaron Burrow Date: Thu, 9 Apr 2026 19:11:16 +0000 Subject: [PATCH 009/545] patch 9.2.0326: runtime(tar): but with dotted path Problem: runtime(tar): but with dotted path Solution: Do not strip everything after the first dot (Aaron Burrow) tar#Extract was getting the extensionless basename by stripping away everything starting with the leftmost dot. So if a directory had a dot or the file had an 'extra' dot then the code did the wrong thing. For example, if it was given: /tmp/foo.bar/baz.tar.gz Then it would treat /tmp/foo as the extensionless basename, but it actually should have grabbed: /tmp/foo.bar/baz This patch fixes the issue by instead looking at the rightmost dot(s). This bug was discovered by ChatGPT 5.4. I wrote the patch and tested vim. closes: #19930 Signed-off-by: Aaron Burrow Signed-off-by: Christian Brabandt --- runtime/autoload/tar.vim | 80 +++++++++++++++++---------------- src/testdir/test_plugin_tar.vim | 50 +++++++++++++++++++++ src/version.c | 2 + 3 files changed, 94 insertions(+), 38 deletions(-) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index b068bd181b..722a0ab680 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -22,6 +22,7 @@ " 2026 Feb 07 by Vim Project: make the path traversal detection more robust (#19341) " 2026 Apr 06 by Vim Project: fix bugs with lz4 support (#19925) " 2026 Apr 09 by Vim Project: fix bugs with zstd support (#19930) +" 2026 Apr 09 by Vim Project: fix bug with dotted filename (#19930) " " Contains many ideas from Michael Toren's " @@ -612,117 +613,120 @@ fun! tar#Extract() return endif + let extractcmd= s:WinPath(g:tar_extractcmd) let tarball = expand("%") - let tarbase = substitute(tarball,'\..*$','','') + if !filereadable(tarball) + let &report= repkeep + return + endif - let extractcmd= s:WinPath(g:tar_extractcmd) - if filereadable(tarbase.".tar") - call system(extractcmd." ".shellescape(tarbase).".tar ".shellescape(fname)) + if tarball =~# "\.tar$" + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ". fname endif - elseif filereadable(tarbase.".tgz") + elseif tarball =~# "\.tgz$" let extractcmd= substitute(extractcmd,"-","-z","") - call system(extractcmd." ".shellescape(tarbase).".tgz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tgz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.gz") + elseif tarball =~# "\.tar\.gz$" let extractcmd= substitute(extractcmd,"-","-z","") - call system(extractcmd." ".shellescape(tarbase).".tar.gz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.gz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tbz") + elseif tarball =~# "\.tbz$" let extractcmd= substitute(extractcmd,"-","-j","") - call system(extractcmd." ".shellescape(tarbase).".tbz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tbz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.bz2") + elseif tarball =~# "\.tar\.bz2$" let extractcmd= substitute(extractcmd,"-","-j","") - call system(extractcmd." ".shellescape(tarbase).".tar.bz2 ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.bz2 {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.bz3") + elseif tarball =~# "\.tar\.bz3$" let extractcmd= substitute(extractcmd,"-","-j","") - call system(extractcmd." ".shellescape(tarbase).".tar.bz3 ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.bz3 {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".txz") + elseif tarball =~# "\.txz$" let extractcmd= substitute(extractcmd,"-","-J","") - call system(extractcmd." ".shellescape(tarbase).".txz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.txz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.xz") + elseif tarball =~# "\.tar\.xz$" let extractcmd= substitute(extractcmd,"-","-J","") - call system(extractcmd." ".shellescape(tarbase).".tar.xz ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.xz {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tzst") + elseif tarball =~# "\.tzst$" let extractcmd= substitute(extractcmd,"-","--zstd -","") - call system(extractcmd." ".shellescape(tarbase).".tzst ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tzst {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.zst") + elseif tarball =~# "\.tar\.zst$" let extractcmd= substitute(extractcmd,"-","--zstd -","") - call system(extractcmd." ".shellescape(tarbase).".tar.zst ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.zst {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tlz4") + elseif tarball =~# "\.tlz4$" if has("linux") let extractcmd= substitute(extractcmd,"-","-I lz4 -","") endif - call system(extractcmd." ".shellescape(tarbase).".tlz4 ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tlz4 {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif - elseif filereadable(tarbase.".tar.lz4") + elseif tarball =~# "\.tar\.lz4$" if has("linux") let extractcmd= substitute(extractcmd,"-","-I lz4 -","") endif - call system(extractcmd." ".shellescape(tarbase).".tar.lz4 ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) if v:shell_error != 0 - call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarbase}.tar.lz4 {fname}: failed!") + call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else echo "***note*** successfully extracted ".fname endif diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim index 28cf709f85..bde79052dd 100644 --- a/src/testdir/test_plugin_tar.vim +++ b/src/testdir/test_plugin_tar.vim @@ -263,3 +263,53 @@ def g:Test_extraction() bw! endfor enddef + +def g:Test_extract_with_dotted_dir() + delete('X.txt') + writefile(['when they kiss they spit white noise'], 'X.txt') + + var dirname = tempname() + mkdir(dirname, 'R') + dirname = dirname .. '/foo.bar' + mkdir(dirname, 'R') + var tarpath = dirname .. '/Xarchive.tar.gz' + system('tar -czf ' .. tarpath .. ' X.txt') + assert_true(filereadable(tarpath)) + assert_equal(0, v:shell_error) + + delete('X.txt') + defer delete(tarpath) + + execute 'e ' .. tarpath + assert_match('X.txt', getline(5)) + :5 + normal x + assert_true(filereadable('X.txt')) + assert_equal(['when they kiss they spit white noise'], readfile('X.txt')) + delete('X.txt') + bw! +enddef + +def g:Test_extract_with_dotted_filename() + delete('X.txt') + writefile(['holiday inn'], 'X.txt') + + var dirname = tempname() + mkdir(dirname, 'R') + var tarpath = dirname .. '/Xarchive.foo.tar.gz' + system('tar -czf ' .. tarpath .. ' X.txt') + assert_true(filereadable(tarpath)) + assert_equal(0, v:shell_error) + + delete('X.txt') + defer delete(tarpath) + + execute 'e ' .. tarpath + assert_match('X.txt', getline(5)) + :5 + normal x + assert_true(filereadable('X.txt')) + assert_equal(['holiday inn'], readfile('X.txt')) + delete('X.txt') + bw! +enddef diff --git a/src/version.c b/src/version.c index 5fc6428c2c..70bca36892 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 326, /**/ 325, /**/ From 959817472dd64bae17c8db47e82097533e088013 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Stemmer?= Date: Thu, 9 Apr 2026 19:30:53 +0000 Subject: [PATCH 010/545] runtime(jjdescription): Anchor status matches to start of line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The regex for status line highlighting was too broad, `jjComment` lines containing e.g. the letter 'A' followed by a space anywhere in the line were highlighted. related: #19879 Signed-off-by: Joël Stemmer Signed-off-by: Gregory Anders Signed-off-by: Christian Brabandt --- runtime/syntax/jjdescription.vim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/syntax/jjdescription.vim b/runtime/syntax/jjdescription.vim index bd1c6f22fd..28e396f9f4 100644 --- a/runtime/syntax/jjdescription.vim +++ b/runtime/syntax/jjdescription.vim @@ -3,14 +3,15 @@ " Maintainer: Gregory Anders " Last Change: 2024 May 8 " 2025 Apr 17 by Vim Project (don't require space to start comments, #17130) +" 2026 Apr 09 by Vim Project (anchor status regex to beginning of line, #19879) if exists('b:current_syntax') finish endif -syn match jjAdded "A .*" contained -syn match jjRemoved "D .*" contained -syn match jjChanged "M .*" contained +syn match jjAdded "^JJ:\s\+\zsA\s.*" contained +syn match jjRemoved "^JJ:\s\+\zsD\s.*" contained +syn match jjChanged "^JJ:\s\+\zsM\s.*" contained syn region jjComment start="^JJ:" end="$" contains=jjAdded,jjRemoved,jjChanged From f554a7c7090ea37a4437c7d27389ecb8a9069d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=ABl=20Stemmer?= Date: Thu, 9 Apr 2026 19:32:15 +0000 Subject: [PATCH 011/545] runtime(jjdescription): Add highlighting for 'Renamed' status lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `jj status` output uses the 'R' prefix for renamed files. closes: #19879 Signed-off-by: Joël Stemmer Signed-off-by: Gregory Anders Signed-off-by: Christian Brabandt --- runtime/syntax/jjdescription.vim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/syntax/jjdescription.vim b/runtime/syntax/jjdescription.vim index 28e396f9f4..f7b76ba260 100644 --- a/runtime/syntax/jjdescription.vim +++ b/runtime/syntax/jjdescription.vim @@ -4,6 +4,7 @@ " Last Change: 2024 May 8 " 2025 Apr 17 by Vim Project (don't require space to start comments, #17130) " 2026 Apr 09 by Vim Project (anchor status regex to beginning of line, #19879) +" 2026 Apr 09 by Vim Project (detect renames of files, #19879) if exists('b:current_syntax') finish @@ -12,8 +13,9 @@ endif syn match jjAdded "^JJ:\s\+\zsA\s.*" contained syn match jjRemoved "^JJ:\s\+\zsD\s.*" contained syn match jjChanged "^JJ:\s\+\zsM\s.*" contained +syn match jjRenamed "^JJ:\s\+\zsR\s.*" contained -syn region jjComment start="^JJ:" end="$" contains=jjAdded,jjRemoved,jjChanged +syn region jjComment start="^JJ:" end="$" contains=jjAdded,jjRemoved,jjChanged,jjRenamed syn include @jjCommitDiff syntax/diff.vim syn region jjCommitDiff start=/\%(^diff --\%(git\|cc\|combined\) \)\@=/ end=/^\%(diff --\|$\|@@\@!\|[^[:alnum:]\ +-]\S\@!\)\@=/ fold contains=@jjCommitDiff @@ -22,5 +24,6 @@ hi def link jjComment Comment hi def link jjAdded Added hi def link jjRemoved Removed hi def link jjChanged Changed +hi def link jjRenamed Changed let b:current_syntax = 'jjdescription' From 6c16ccaf6101a5a4fa28df9ca31e0bf871541aa4 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 9 Apr 2026 20:50:01 +0000 Subject: [PATCH 012/545] patch 9.2.0327: filetype: uv scripts are not detected Problem: filetype: uv scripts are not detected (Asger Hautop Drewsen) Solution: Detect uv scripts as python filetype if the shebang line contains "uv run" Reference: https://docs.astral.sh/uv/guides/scripts/#using-a-shebang-to-create-an-executable-file fixes: #19941 Signed-off-by: Christian Brabandt --- runtime/autoload/dist/script.vim | 4 ++-- src/testdir/test_filetype.vim | 3 ++- src/version.c | 2 ++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/runtime/autoload/dist/script.vim b/runtime/autoload/dist/script.vim index de168f0c09..0106900f8f 100644 --- a/runtime/autoload/dist/script.vim +++ b/runtime/autoload/dist/script.vim @@ -4,7 +4,7 @@ vim9script # Invoked from "scripts.vim" in 'runtimepath' # # Maintainer: The Vim Project -# Last Change: 2025 Dec 22 +# Last Change: 2026 Apr 09 # Former Maintainer: Bram Moolenaar export def DetectFiletype() @@ -115,7 +115,7 @@ export def Exe2filetype(name: string, line1: string): string return 'php' # Python - elseif name =~ 'python' + elseif name =~ 'python' || (name == 'uv' && line1 =~ '\') return 'python' # Groovy diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 7a6615531c..04036b0eb4 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -1108,7 +1108,8 @@ def s:GetScriptChecks(): dict>> php: [['#!/path/php']], python: [['#!/path/python'], ['#!/path/python2'], - ['#!/path/python3']], + ['#!/path/python3'], + ['#!/usr/bin/env -S uv run --script']], groovy: [['#!/path/groovy']], ruby: [['#!/path/ruby']], javascript: [['#!/path/node'], diff --git a/src/version.c b/src/version.c index 70bca36892..483f55d9f6 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 327, /**/ 326, /**/ From d42b047f78e743bcfab8e76c00c357c0471d3308 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 9 Apr 2026 21:15:30 +0000 Subject: [PATCH 013/545] patch 9.2.0328: Cannot handle mouseclicks in the statusline Problem: Cannot handle mouseclicks in the statusline Solution: Add the %[FuncName] statusline item to define clickable regions with a callback function. (Yasuhiro Matsumoto) closes: #19841 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 47 +++++++++- runtime/doc/tags | 2 + runtime/doc/various.txt | 3 +- runtime/doc/version9.txt | 4 +- src/buffer.c | 138 ++++++++++++++++++++++++++--- src/evalfunc.c | 7 ++ src/gui.c | 2 +- src/hardcopy.c | 3 +- src/mouse.c | 119 ++++++++++++++++++++++++- src/option.h | 3 +- src/optionstr.c | 48 ++++++++++ src/proto/buffer.pro | 6 +- src/screen.c | 81 ++++++++++++++++- src/structs.h | 21 +++++ src/tabpanel.c | 3 +- src/testdir/test_statusline.vim | 152 ++++++++++++++++++++++++++++++++ src/version.c | 2 + src/window.c | 8 ++ 18 files changed, 627 insertions(+), 22 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 03f77c519c..300eebbc22 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 07 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 09 VIM REFERENCE MANUAL by Bram Moolenaar @@ -8624,6 +8624,51 @@ A jump table for the options with a short description can be found at |Q_op|. @ - Inserts a newline. This only takes effect when the "maxheight" value of 'statuslineopt' is greater than 1, or for |tabpanel|. + *stl-%[FuncName]* + %[ defines clickable regions in the statusline. When the user clicks + on a region with the mouse, the specified function is called. + + %[FuncName] Start of a clickable region. "FuncName" is the name + of a Vim function to call when the region is clicked. + %[] End of the clickable region. If omitted, the region + extends to the end of the statusline or to the start + of the next clickable region. + + A {minwid} value can be used to pass an identifier to the callback: + %3[FuncName] Starts a clickable region with minwid 3. + + The function receives a single |Dictionary| argument with these + entries: + "minwid" The minwid value from %N[Func] (0 if not specified). + "nclicks" Number of clicks: 1, 2, or 3. + "button" Mouse button: "l" (left), "m" (middle), "r" (right). + "mods" Modifier keys: combination of "s" (shift), "c" (ctrl), + "a" (alt). Empty string if no modifiers. + "winid" |window-ID| of the window whose statusline was clicked. + + If the function returns non-zero, the statusline is redrawn. + Dragging the statusline to resize the window still works even when + click handlers are defined. + + Example: > + func! ClickFile(info) + if a:info.button ==# 'l' && a:info.nclicks == 2 + browse edit + endif + return 0 + endfunc + set statusline=%[ClickFile]%f%[]\ %l:%c +< This makes the filename in the statusline clickable. Double-clicking + it opens the file browser. + + Use `has('statusline_click')` to check if this feature is available. + This is useful for backward compatibility: > + if has('statusline_click') + set statusline=%[ClickFile]%f%[]\ %l:%c + else + set statusline=%f\ %l:%c + endif +< When displaying a flag, Vim removes the leading comma, if any, when that flag comes right after plaintext. This will make a nice display when flags are used like in the examples below. diff --git a/runtime/doc/tags b/runtime/doc/tags index 3da46d2fd4..6505d1b88e 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -1524,6 +1524,7 @@ $quote eval.txt /*$quote* +spell various.txt /*+spell* +startuptime various.txt /*+startuptime* +statusline various.txt /*+statusline* ++statusline_click various.txt /*+statusline_click* +sun_workshop various.txt /*+sun_workshop* +syntax various.txt /*+syntax* +system() various.txt /*+system()* @@ -10547,6 +10548,7 @@ status-line windows.txt /*status-line* statusmsg-variable eval.txt /*statusmsg-variable* stl-%! options.txt /*stl-%!* stl-%@ options.txt /*stl-%@* +stl-%[FuncName] options.txt /*stl-%[FuncName]* stl-%{ options.txt /*stl-%{* str2blob() builtin.txt /*str2blob()* str2float() builtin.txt /*str2float()* diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 518f6caabd..9de6614522 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*various.txt* For Vim version 9.2. Last change: 2026 Apr 06 VIM REFERENCE MANUAL by Bram Moolenaar @@ -496,6 +496,7 @@ N *+spell* spell checking support, see |spell| N *+startuptime* |--startuptime| argument N *+statusline* Options 'statusline', 'rulerformat' and special formats of 'titlestring' and 'iconstring' +N *+statusline_click* Click handlers in 'statusline' |stl-%[FuncName]| - *+sun_workshop* Removed: |workshop| N *+syntax* Syntax highlighting |syntax| *+system()* Unix only: opposite of |+fork| diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index acce37226a..e0976453d9 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 07 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 09 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52613,6 +52613,8 @@ Other ~ pairs individually (e.g. 'listchars', 'fillchars', 'diffopt'). - |system()| and |systemlist()| functions accept a list as first argument, bypassing the shell completely. +- Allow mouse clickable regions in the |status-line| using the + |stl-%[FuncName]| atom. Platform specific ~ ----------------- diff --git a/src/buffer.c b/src/buffer.c index 6c99acd0a1..cbee49e1aa 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -49,7 +49,8 @@ static int value_changed(char_u *str, char_u **last); static int build_stl_str_hl_local(stl_mode_T mode, win_T *wp, char_u *out, size_t outlen, char_u **fmt_arg, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, - stl_hlrec_T **hltab, stl_hlrec_T **tabtab, int *lbreaks); + stl_hlrec_T **hltab, stl_hlrec_T **tabtab, + stl_clickrec_T **clicktab, int *lbreaks); #endif static int append_arg_number(win_T *wp, char_u *buf, size_t buflen, int add_file); static void free_buffer(buf_T *); @@ -4080,7 +4081,7 @@ maketitle(void) if (stl_syntax & STL_IN_TITLE) build_stl_str_hl(curwin, title_str, sizeof(buf), p_titlestring, (char_u *)"titlestring", 0, - 0, maxlen, NULL, NULL); + 0, maxlen, NULL, NULL, NULL); else #endif title_str = p_titlestring; @@ -4251,7 +4252,8 @@ maketitle(void) #ifdef FEAT_STL_OPT if (stl_syntax & STL_IN_ICON) build_stl_str_hl(curwin, icon_str, sizeof(buf), p_iconstring, - (char_u *)"iconstring", 0, 0, 0, NULL, NULL); + (char_u *)"iconstring", 0, 0, 0, NULL, NULL, + NULL); else #endif icon_str = p_iconstring; @@ -4347,8 +4349,10 @@ typedef struct Separate, Highlight, TabPage, + ClickFunc, Trunc } stl_type; + char_u *stl_clickfunc; // function name for ClickFunc items } stl_item_T; static size_t stl_items_len = 20; // Initial value, grows as needed. @@ -4356,6 +4360,7 @@ static stl_item_T *stl_items = NULL; static int *stl_groupitem = NULL; static stl_hlrec_T *stl_hltab = NULL; static stl_hlrec_T *stl_tabtab = NULL; +static stl_clickrec_T *stl_clicktab = NULL; static int *stl_separator_locations = NULL; /* @@ -4383,10 +4388,12 @@ build_stl_str_hl( int fillchar, int maxwidth, stl_hlrec_T **hltab, // return: HL attributes (can be NULL) - stl_hlrec_T **tabtab) // return: tab page nrs (can be NULL) + stl_hlrec_T **tabtab, // return: tab page nrs (can be NULL) + stl_clickrec_T **clicktab) // return: click func regions (can be NULL) { return build_stl_str_hl_local(STL_MODE_SINGLE, wp, out, outlen, &fmt, - opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, NULL); + opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, clicktab, + NULL); } int @@ -4400,10 +4407,12 @@ build_stl_str_hl_mline( int fillchar, int maxwidth, stl_hlrec_T **hltab, // return: HL attributes (can be NULL) - stl_hlrec_T **tabtab) // return: tab page nrs (can be NULL) + stl_hlrec_T **tabtab, // return: tab page nrs (can be NULL) + stl_clickrec_T **clicktab) // return: click func regions (can be NULL) { return build_stl_str_hl_local(STL_MODE_MULTI, wp, out, outlen, fmt, - opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, NULL); + opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, clicktab, + NULL); } # ifdef ENABLE_STL_MODE_MULTI_NL @@ -4418,10 +4427,12 @@ build_stl_str_hl_mline_nl( int fillchar, int maxwidth, stl_hlrec_T **hltab, // return: HL attributes (can be NULL) - stl_hlrec_T **tabtab) // return: tab page nrs (can be NULL) + stl_hlrec_T **tabtab, // return: tab page nrs (can be NULL) + stl_clickrec_T **clicktab) // return: click func regions (can be NULL) { return build_stl_str_hl_local(STL_MODE_MULTI_NL, wp, out, outlen, fmt, - opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, NULL); + opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, clicktab, + NULL); } # endif @@ -4442,7 +4453,7 @@ get_stl_rendered_height( ++emsg_off; (void)build_stl_str_hl_local(STL_MODE_GET_RENDERED_HEIGHT, wp, buf, sizeof(buf), &fmt, - opt_name, opt_scope, 0, 0, NULL, NULL, &rendered_height); + opt_name, opt_scope, 0, 0, NULL, NULL, NULL, &rendered_height); --emsg_off; return rendered_height; } @@ -4477,6 +4488,7 @@ build_stl_str_hl_local( int maxwidth, stl_hlrec_T **hltab, // return: HL attributes (can be NULL) stl_hlrec_T **tabtab, // return: tab page nrs (can be NULL) + stl_clickrec_T **clicktab, // return: click func regions (can be NULL) int *rendered_height) // return: stl rendered height (can be NULL) { linenr_T lnum; @@ -4538,6 +4550,7 @@ build_stl_str_hl_local( // end of the list. stl_hltab = ALLOC_MULT(stl_hlrec_T, stl_items_len + 1); stl_tabtab = ALLOC_MULT(stl_hlrec_T, stl_items_len + 1); + stl_clicktab = ALLOC_MULT(stl_clickrec_T, stl_items_len + 1); stl_separator_locations = ALLOC_MULT(int, stl_items_len); } @@ -4632,6 +4645,12 @@ build_stl_str_hl_local( break; stl_tabtab = new_hlrec; + stl_clickrec_T *new_clickrec = vim_realloc(stl_clicktab, + sizeof(stl_clickrec_T) * (new_len + 1)); + if (new_clickrec == NULL) + break; + stl_clicktab = new_clickrec; + int *new_separator_locs = vim_realloc(stl_separator_locations, sizeof(int) * new_len); if (new_separator_locs == NULL) @@ -4641,6 +4660,8 @@ build_stl_str_hl_local( stl_items_len = new_len; } + stl_items[curitem].stl_clickfunc = NULL; + if (*s != '%') prevchar_isflag = prevchar_isitem = FALSE; @@ -4675,8 +4696,39 @@ build_stl_str_hl_local( if (*s == NUL) // ignore trailing % break; + if (*s == STL_CLICKFUNC) + { + // %[] - end click region + if (s[1] == ']') + { + stl_items[curitem].stl_type = ClickFunc; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_minwid = 0; + stl_items[curitem].stl_clickfunc = NULL; + s += 2; + curitem++; + continue; + } + // %[FuncName] - start click region + if (ASCII_ISALPHA(s[1]) || s[1] == '_') + { + char_u *rb = vim_strchr(s + 1, ']'); + if (rb != NULL) + { + stl_items[curitem].stl_type = ClickFunc; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_minwid = 0; + stl_items[curitem].stl_clickfunc = + vim_strnsave(s + 1, rb - s - 1); + s = rb + 1; + curitem++; + continue; + } + } + } if (*s == STL_LINEBREAK) { + // Plain %@ - line break if (mode == STL_MODE_MULTI # ifdef ENABLE_STL_MODE_MULTI_NL || mode == STL_MODE_MULTI_NL @@ -5233,6 +5285,40 @@ build_stl_str_hl_local( ++s; continue; } + + case STL_CLICKFUNC: + // %N[] - end click region (with minwid, minwid is ignored) + if (*s == ']') + { + stl_items[curitem].stl_type = ClickFunc; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_minwid = 0; + stl_items[curitem].stl_clickfunc = NULL; + s++; + curitem++; + continue; + } + // %N[FuncName] with minwid + if (ASCII_ISALPHA(*s) || *s == '_') + { + char_u *rb = vim_strchr(s, ']'); + if (rb != NULL) + { + stl_items[curitem].stl_type = ClickFunc; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_minwid = minwid; + stl_items[curitem].stl_clickfunc = + vim_strnsave(s, rb - s); + s = rb + 1; + curitem++; + continue; + } + } + continue; + + case STL_LINEBREAK: + // %N@ - line break (already handled above, fallback) + continue; } stl_items[curitem].stl_start = p; @@ -5388,6 +5474,10 @@ build_stl_str_hl_local( if (mode == STL_MODE_GET_RENDERED_HEIGHT) { + // Free click function names that were allocated during parsing. + for (l = 0; l < itemcnt; l++) + if (stl_items[l].stl_type == ClickFunc) + vim_free(stl_items[l].stl_clickfunc); if (rendered_height != NULL) *rendered_height = rheight; return 0; @@ -5561,6 +5651,34 @@ build_stl_str_hl_local( sp->userhl = 0; } + // Store the info about click function regions. + if (clicktab != NULL) + { + stl_clickrec_T *cp; + + *clicktab = stl_clicktab; + cp = stl_clicktab; + for (l = 0; l < itemcnt; l++) + { + if (stl_items[l].stl_type == ClickFunc) + { + cp->start = stl_items[l].stl_start; + cp->funcname = stl_items[l].stl_clickfunc; + cp->minwid = stl_items[l].stl_minwid; + cp++; + } + } + cp->start = NULL; + cp->funcname = NULL; + } + else + { + // Free click function names when caller doesn't need them. + for (l = 0; l < itemcnt; l++) + if (stl_items[l].stl_type == ClickFunc) + vim_free(stl_items[l].stl_clickfunc); + } + redraw_not_allowed = save_redraw_not_allowed; // A user function may reset KeyTyped, restore it. diff --git a/src/evalfunc.c b/src/evalfunc.c index b4ea12c3c3..f61135f77c 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -7509,6 +7509,13 @@ f_has(typval_T *argvars, typval_T *rettv) 1 #else 0 +#endif + }, + {"statusline_click", +#ifdef FEAT_STL_OPT + 1 +#else + 0 #endif }, {"netbeans_intg", diff --git a/src/gui.c b/src/gui.c index 4238b8b731..1d60cd7f34 100644 --- a/src/gui.c +++ b/src/gui.c @@ -3841,7 +3841,7 @@ get_tabline_label( // Can't use NameBuff directly, build_stl_str_hl() uses it. build_stl_str_hl(curwin, res, MAXPATHL, *opt, opt_name, 0, - 0, (int)Columns, NULL, NULL); + 0, (int)Columns, NULL, NULL, NULL); STRCPY(NameBuff, res); // Back to the original curtab. diff --git a/src/hardcopy.c b/src/hardcopy.c index 55d4384182..e87caf3020 100644 --- a/src/hardcopy.c +++ b/src/hardcopy.c @@ -489,7 +489,8 @@ prt_header( printer_page_num = pagenum; build_stl_str_hl(curwin, tbuf, (size_t)(width + IOSIZE), p_header, - (char_u *)"printheader", 0, ' ', width, NULL, NULL); + (char_u *)"printheader", 0, ' ', width, NULL, NULL, + NULL); // Reset line numbers curwin->w_cursor.lnum = tmp_lnum; diff --git a/src/mouse.c b/src/mouse.c index 3f2f86dc83..6bff3d7c28 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -19,6 +19,8 @@ */ static long mouse_hor_step = 6; static long mouse_vert_step = 3; +static win_T *dragwin = NULL; // window being dragged +static int stl_click_handler(win_T *wp, int mcol, int which_button, int mods); void mouse_set_vert_scroll_step(long step) @@ -760,6 +762,22 @@ do_mouse( in_status_line = (jump_flags & IN_STATUS_LINE); in_sep_line = (jump_flags & IN_SEP_LINE); + // Check for statusline click handler early, before visual mode or + // other button-specific handling can interfere. + if (in_status_line && is_click && !is_drag + && stl_click_handler(dragwin, mouse_col, + which_button, mod_mask)) + { +#ifdef FEAT_MOUSESHAPE + if (!drag_status_line) + { + drag_status_line = TRUE; + update_mouseshape(-1); + } +#endif + return FALSE; + } + #ifdef FEAT_NETBEANS_INTG if (isNetbeansBuffer(curbuf) && !(jump_flags & (IN_STATUS_LINE | IN_SEP_LINE))) @@ -1621,7 +1639,106 @@ mouse_model_popup(void) return (p_mousem[0] == 'p'); } -static win_T *dragwin = NULL; // window being dragged +/* + * Call a statusline click handler function. + * Returns TRUE if the function was called and handled the click. + */ + static int +stl_click_handler(win_T *wp, int mcol, int which_button, int mods) +{ +#ifdef FEAT_EVAL + int n; + int nclicks; + char_u button_str[2]; + char_u mods_str[4]; + int mi = 0; + dict_T *info; + typval_T argvars[2]; + typval_T rettv; + funcexe_T funcexe; + int col = mcol; + + if (wp == NULL || wp->w_stl_click == NULL || wp->w_stl_click_count == 0) + return FALSE; + + // Find the click region at the given column. + for (n = 0; n < wp->w_stl_click_count; n++) + { + if (col >= wp->w_stl_click[n].col_start + && col < wp->w_stl_click[n].col_end) + break; + } + if (n >= wp->w_stl_click_count || wp->w_stl_click[n].funcname == NULL) + return FALSE; + + // Build the info dictionary. + info = dict_alloc(); + if (info == NULL) + return FALSE; + + dict_add_number(info, "minwid", wp->w_stl_click[n].minwid); + + // Determine number of clicks. + // MOD_MASK_2CLICK=0x20, MOD_MASK_3CLICK=0x40, MOD_MASK_4CLICK=0x60 + nclicks = ((mods & MOD_MASK_MULTI_CLICK) >> 5) + 1; + if (nclicks > 3) + nclicks = 3; + dict_add_number(info, "nclicks", nclicks); + + // Button. + if (which_button == MOUSE_LEFT) + button_str[0] = 'l'; + else if (which_button == MOUSE_RIGHT) + button_str[0] = 'r'; + else + button_str[0] = 'm'; + button_str[1] = NUL; + dict_add_string(info, "button", button_str); + + // Modifiers. + if (mods & MOD_MASK_SHIFT) + mods_str[mi++] = 's'; + if (mods & MOD_MASK_CTRL) + mods_str[mi++] = 'c'; + if (mods & MOD_MASK_ALT) + mods_str[mi++] = 'a'; + mods_str[mi] = NUL; + dict_add_string(info, "mods", mods_str); + + dict_add_number(info, "winid", wp->w_id); + + // Call the function with the info dict as argument. + argvars[0].v_type = VAR_DICT; + argvars[0].vval.v_dict = info; + ++info->dv_refcount; + argvars[1].v_type = VAR_UNKNOWN; + + rettv.v_type = VAR_NUMBER; + rettv.vval.v_number = 0; + + CLEAR_FIELD(funcexe); + funcexe.fe_evaluate = TRUE; + (void)call_func(wp->w_stl_click[n].funcname, -1, + &rettv, 1, argvars, &funcexe); + + n = (int)rettv.vval.v_number; + clear_tv(&rettv); + dict_unref(info); + + if (n != 0) + redraw_statuslines(); + + return TRUE; +#else + (void)wp; + (void)mcol; + (void)which_button; + (void)mods; + return FALSE; +#endif +} + +// dragwin is declared near the top of the file /* * Reset the window being dragged. To be called when switching tab page. diff --git a/src/option.h b/src/option.h index adafbd8d36..7165ea80c6 100644 --- a/src/option.h +++ b/src/option.h @@ -364,9 +364,10 @@ typedef enum { #define STL_USER_HL '*' // highlight from (User)1..9 or 0 #define STL_HIGHLIGHT '#' // highlight name #define STL_LINEBREAK '@' // insert a line break +#define STL_CLICKFUNC '[' // click handler region #define STL_TABPAGENR 'T' // tab page label nr #define STL_TABCLOSENR 'X' // tab page close nr -#define STL_ALL ((char_u *) "fFtcvVlLknoObBrRhHmYyWwMqpPaNS{#@") +#define STL_ALL ((char_u *) "fFtcvVlLknoObBrRhHmYyWwMqpPaNS{#@[") // flags used for parsed 'wildmode' #define WIM_FULL 0x01 diff --git a/src/optionstr.c b/src/optionstr.c index 9647f7c015..e82c2b78cd 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -671,8 +671,30 @@ check_stl_option(char_u *s) if (!*s) break; s++; + if (*s == STL_CLICKFUNC) + { + if (s[1] == ']') + { + // %[] - end click region + s += 2; + continue; + } + if (ASCII_ISALPHA(s[1]) || s[1] == '_') + { + // %[FuncName] - start click region + char_u *rb = vim_strchr(s + 2, ']'); + if (rb != NULL) + { + s = rb + 1; + continue; + } + } + // Bare %[ is invalid + return illegal_char(errbuf, errbuflen, *s); + } if (*s == STL_LINEBREAK) { + // Plain %@ - line break s++; continue; } @@ -694,6 +716,32 @@ check_stl_option(char_u *s) s++; if (*s == STL_USER_HL) continue; + if (*s == STL_CLICKFUNC) + { + // %N[FuncName] or %N[] + if (s[1] == ']') + { + s += 2; + continue; + } + if (ASCII_ISALPHA(s[1]) || s[1] == '_') + { + char_u *rb = vim_strchr(s + 2, ']'); + if (rb != NULL) + { + s = rb + 1; + continue; + } + } + // Bare %N[ is invalid + return illegal_char(errbuf, errbuflen, *s); + } + if (*s == STL_LINEBREAK) + { + // %N@ - line break + s++; + continue; + } if (*s == '.') { s++; diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro index 438ddb1a86..13c273d6aa 100644 --- a/src/proto/buffer.pro +++ b/src/proto/buffer.pro @@ -49,9 +49,9 @@ int col_print(char_u *buf, size_t buflen, int col, int vcol); void maketitle(void); void resettitle(void); void free_titles(void); -int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab); -int build_stl_str_hl_mline(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab); -int build_stl_str_hl_mline_nl(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab); +int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab, stl_clickrec_T **clicktab); +int build_stl_str_hl_mline(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab, stl_clickrec_T **clicktab); +int build_stl_str_hl_mline_nl(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab, stl_clickrec_T **clicktab); int get_stl_rendered_height(win_T *wp, char_u *fmt, char_u *opt_name, int opt_scope); int get_rel_pos(win_T *wp, char_u *buf, int buflen); char_u *fix_fname(char_u *fname); diff --git a/src/screen.c b/src/screen.c index 04c1f237a2..311735d821 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1296,6 +1296,7 @@ win_redr_custom( int opt_scope = 0; stl_hlrec_T *hltab; stl_hlrec_T *tabtab; + stl_clickrec_T *clicktab; win_T *ewp; int p_crb_save; bool override_success = false; @@ -1396,7 +1397,8 @@ win_redr_custom( width = build_stl_str_hl_mline(ewp, buf, sizeof(buf), &stl_tmp, opt_name, opt_scope, - fillchar, maxwidth, &hltab, &tabtab); + fillchar, maxwidth, &hltab, &tabtab, + &clicktab); // Make all characters printable. p = transstr(buf); @@ -1475,6 +1477,83 @@ win_redr_custom( TabPageIdxs[col++] = fillchar; } + // Resolve click function regions for statusline. + if (wp != NULL && !draw_ruler) + { + int click_count = 0; + + // Count the click regions. + for (n = 0; clicktab[n].start != NULL; n++) + click_count++; + + // Free old click regions. + if (wp->w_stl_click != NULL) + { + for (n = 0; n < wp->w_stl_click_count; n++) + vim_free(wp->w_stl_click[n].funcname); + VIM_CLEAR(wp->w_stl_click); + } + wp->w_stl_click_count = 0; + + if (click_count > 0) + { + stl_click_region_T *regions; + int rcount = 0; + + regions = ALLOC_MULT(stl_click_region_T, click_count); + if (regions != NULL) + { + char_u *cur_funcname = NULL; + int cur_minwid = 0; + int region_start = wp->w_wincol; + + // Walk through click records converting buffer positions + // to screen columns. + len = 0; + p = buf; + for (n = 0; clicktab[n].start != NULL; n++) + { + len += vim_strnsize(p, + (int)(clicktab[n].start - p)); + p = clicktab[n].start; + + // Close previous region if there was one. + if (cur_funcname != NULL) + { + regions[rcount].col_start = region_start; + regions[rcount].col_end = wp->w_wincol + len; + regions[rcount].funcname = + vim_strsave(cur_funcname); + regions[rcount].minwid = cur_minwid; + rcount++; + } + + cur_funcname = clicktab[n].funcname; + cur_minwid = clicktab[n].minwid; + region_start = wp->w_wincol + len; + } + + // Close final region if it extends to the end. + if (cur_funcname != NULL) + { + regions[rcount].col_start = region_start; + regions[rcount].col_end = wp->w_wincol + maxwidth; + regions[rcount].funcname = + vim_strsave(cur_funcname); + regions[rcount].minwid = cur_minwid; + rcount++; + } + + wp->w_stl_click = regions; + wp->w_stl_click_count = rcount; + } + } + + // Free the funcname strings allocated by build_stl_str_hl_local(). + for (n = 0; clicktab[n].start != NULL; n++) + vim_free(clicktab[n].funcname); + } + theend: if (override_success) pop_highlight_overrides(); diff --git a/src/structs.h b/src/structs.h index 5bb51dd549..955b9f1001 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1436,6 +1436,25 @@ typedef struct int userhl; // 0: no HL, 1-9: User HL, < 0 for syn ID } stl_hlrec_T; +/* + * Used for statusline click function regions. + */ +typedef struct { + char_u *start; // position in output buffer where region starts + char_u *funcname; // function name (NULL = end/close marker) + int minwid; // minwid value from %N@Func@ +} stl_clickrec_T; + +/* + * Per-window resolved click regions (screen column based). + */ +typedef struct { + int col_start; // screen column where region starts + int col_end; // screen column where region ends + char_u *funcname; // function name (allocated copy) + int minwid; // minwid value +} stl_click_region_T; + /* * Syntax items - usually buffer-specific. @@ -4117,6 +4136,8 @@ struct window_S int w_prev_height; // previous height used for 'splitkeep' int w_stl_rendered_height; // rendered height of window-local 'stl' // (number of "%@" + 1) + stl_click_region_T *w_stl_click; // statusline click regions + int w_stl_click_count; // number of click regions int w_status_height; // number of status lines. // If 'statuslineopt' was changed, this // member holds the previous value until diff --git a/src/tabpanel.c b/src/tabpanel.c index 2a6dbe8318..51929a8b1c 100644 --- a/src/tabpanel.c +++ b/src/tabpanel.c @@ -507,7 +507,8 @@ do_by_tplmode( #endif (args.cwp, buf, sizeof(buf), &usefmt, opt_name, opt_scope, TPL_FILLCHAR, - args.col_end - args.col_start, &hltab, &tabtab); + args.col_end - args.col_start, &hltab, &tabtab, + NULL); args.prow = &row; args.pcol = &col; diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 174c1a4754..673a6ed125 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -721,4 +721,156 @@ func Test_statusline_singlebyte_negative() let [&columns, &ls, &stl, &enc] = [_columns, _ls, _stl, _enc] endfunc +func g:StlClickTestFunc(info) + let g:stl_click_info = a:info + return 0 +endfunc + +func g:StlClickReturn1(info) + let g:stl_click_info = a:info + return 1 +endfunc + +func Test_statusline_click_handler() + let save_mouse = &mouse + let save_stl = &statusline + let save_ls = &laststatus + set mouse=a + set laststatus=2 + + " Basic click handler + set statusline=%[StlClickTestFunc][Click]%[]\ %f + redraw! + + " Click on the [Click] region + let stl_row = win_screenpos(0)[0] + winheight(0) + call test_setmouse(stl_row, 2) + call feedkeys("\", 'xt') + call feedkeys("\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal('l', g:stl_click_info.button) + call assert_equal(1, g:stl_click_info.nclicks) + call assert_equal(0, g:stl_click_info.minwid) + call assert_equal(win_getid(), g:stl_click_info.winid) + unlet! g:stl_click_info + + " Click outside click region (on the filename part) + call test_setmouse(stl_row, 20) + call feedkeys("\", 'xt') + call feedkeys("\", 'xt') + call assert_false(exists('g:stl_click_info')) + + " Test with minwid + set statusline=%42[StlClickTestFunc][Click]%[]\ %f + redraw! + call test_setmouse(stl_row, 2) + call feedkeys("\", 'xt') + call feedkeys("\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal(42, g:stl_click_info.minwid) + unlet! g:stl_click_info + + " Test middle click + call test_setmouse(stl_row, 2) + call feedkeys("\", 'xt') + call feedkeys("\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal('m', g:stl_click_info.button) + unlet! g:stl_click_info + let &mouse = save_mouse + let &statusline = save_stl + let &laststatus = save_ls +endfunc + +func Test_statusline_click_multiple_regions() + let save_mouse = &mouse + let save_stl = &statusline + let save_ls = &laststatus + set mouse=a + set laststatus=2 + + " Two adjacent click regions with different minwid + set statusline=%1[StlClickTestFunc][AAA]%[]%2[StlClickTestFunc][BBB]%[] + redraw! + + let stl_row = win_screenpos(0)[0] + winheight(0) + + " Click on [AAA] region (col 2) + call test_setmouse(stl_row, 2) + call feedkeys("\", 'xt') + call feedkeys("\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal(1, g:stl_click_info.minwid) + unlet! g:stl_click_info + + " Click on [BBB] region (col 7) + call test_setmouse(stl_row, 7) + call feedkeys("\", 'xt') + call feedkeys("\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal(2, g:stl_click_info.minwid) + unlet! g:stl_click_info + + let &mouse = save_mouse + let &statusline = save_stl + let &laststatus = save_ls +endfunc + +func Test_statusline_click_region_extends_to_end() + let save_mouse = &mouse + let save_stl = &statusline + let save_ls = &laststatus + set mouse=a + set laststatus=2 + + " Click region without %[] extends to end of statusline + set statusline=xxx%[StlClickTestFunc]Clickable + redraw! + + let stl_row = win_screenpos(0)[0] + winheight(0) + + " Click near the end of the statusline + call test_setmouse(stl_row, 15) + call feedkeys("\", 'xt') + call feedkeys("\", 'xt') + call assert_true(exists('g:stl_click_info')) + unlet! g:stl_click_info + + " Click on "xxx" (before the click region) + call test_setmouse(stl_row, 1) + call feedkeys("\", 'xt') + call feedkeys("\", 'xt') + call assert_false(exists('g:stl_click_info')) + + let &mouse = save_mouse + let &statusline = save_stl + let &laststatus = save_ls +endfunc + +func Test_statusline_click_option_validation() + " Valid formats should not produce errors + let save_stl = &statusline + set statusline=%[Func]text%[] + set statusline=%3[Func]text%[] + set statusline=%[Func]text + set statusline=%[Func_Name]text%[] + " %@ alone is still valid (line break) + set statusline=%@ + let &statusline = save_stl +endfunc + +func Test_statusline_click_linebreak_still_works() + " Ensure %@ without FuncName still works as line break + let save_stl = &statusline + let save_ls = &laststatus + set laststatus=2 + + " This should not error - %@ is line break + set statusline=line1%@line2 + redraw! + + let &statusline = save_stl + let &laststatus = save_ls +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 483f55d9f6..14387f650e 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 328, /**/ 327, /**/ diff --git a/src/window.c b/src/window.c index 38bc4677e7..b42845ecf2 100644 --- a/src/window.c +++ b/src/window.c @@ -6045,6 +6045,14 @@ win_free( remove_highlight_overrides(wp->w_hl); vim_free(wp->w_hl); + // Free statusline click regions. + if (wp->w_stl_click != NULL) + { + for (i = 0; i < wp->w_stl_click_count; i++) + vim_free(wp->w_stl_click[i].funcname); + vim_free(wp->w_stl_click); + } + clear_winopt(&wp->w_onebuf_opt); clear_winopt(&wp->w_allbuf_opt); From 158947e294ee1cfe11f5725f4c28c78eb1175cab Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 9 Apr 2026 21:30:19 +0000 Subject: [PATCH 014/545] patch 9.2.0329: tests: test_indent.vim leaves swapfiles behind Problem: tests: test_indent.vim leaves swapfiles behind Solution: Close open buffer using :bw! instead of :close! Signed-off-by: Christian Brabandt --- src/testdir/test_indent.vim | 24 ++++++++++++------------ src/version.c | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/testdir/test_indent.vim b/src/testdir/test_indent.vim index 52d7be1390..c3f7953caa 100644 --- a/src/testdir/test_indent.vim +++ b/src/testdir/test_indent.vim @@ -29,7 +29,7 @@ func Test_preserveindent() call assert_equal("\t \t l", getline(1)) set sw& et& pi& - close! + bw! endfunc " Test for indent() @@ -42,7 +42,7 @@ func Test_indent_func() call assert_equal(4, indent(1)) call setline(1, " \t abc") call assert_equal(12, indent(1)) - close! + bw! endfunc " Test for reindenting a line using the '=' operator @@ -56,7 +56,7 @@ func Test_reindent() call setline(1, ['foo', 'bar']) call feedkeys('ggVG=', 'xt') call assert_equal(['foo', 'bar'], getline(1, 2)) - close! + bw! endfunc " Test indent operator creating one undo entry @@ -103,7 +103,7 @@ func Test_preproc_indent() call assert_equal('#define FOO 1', getline(1)) set cindent& - close! + bw! endfunc func Test_userlabel_indent() @@ -116,7 +116,7 @@ func Test_userlabel_indent() normal GV= call assert_equal('läbél:', getline(2)) - close! + bw! endfunc " Test that struct members are aligned @@ -141,7 +141,7 @@ func Test_struct_indent() call setline(1, 'return (struct a) {') normal gg=G call assert_equal(getline(2), getline(3)) - close! + bw! endfunc " Test for 'copyindent' @@ -156,7 +156,7 @@ func Test_copyindent() call feedkeys("ol", 'xt') call assert_equal(" \t l", getline(2)) set sw& ai& et& ci& - close! + bw! endfunc " Test for changing multiple lines with lisp indent @@ -166,7 +166,7 @@ func Test_lisp_indent_change_multiline() call setline(1, ['(if a', ' (if b', ' (return 5)))']) normal! jc2j(return 4)) call assert_equal(' (return 4))', getline(2)) - close! + bw! endfunc func Test_lisp_indent() @@ -179,7 +179,7 @@ func Test_lisp_indent() normal! jostr1" normal! jostr2" call assert_equal([' ;; comment', ' ;; comment', ' \ abc', ' \ abc', '', ' ;; ret', ' " str1\', ' str1"', ' " st\b', ' str2"'], getline(2, 11)) - close! + bw! endfunc func Test_lisp_indent_quoted() @@ -209,7 +209,7 @@ func Test_modeline_indent_expr() set modelineexpr& delfunc GetIndent let &modeline = modeline - close! + bw! endfunc func Test_indent_func_with_gq() @@ -321,7 +321,7 @@ func Test_indent_overflow_count() norm! V2147483647> " indents by INT_MAX call assert_equal(2147483647, indent(1)) - close! + bw! endfunc func Test_indent_overflow_count2() @@ -335,7 +335,7 @@ func Test_indent_overflow_count2() call setline(1, "\tabc") norm! << call assert_equal(0, indent(1)) - close! + bw! endfunc " Test that mouse shape is restored to Normal mode after using "gq" when diff --git a/src/version.c b/src/version.c index 14387f650e..c418648bbb 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 329, /**/ 328, /**/ From 2fbc69c9adbaac5cd33d30a3ccbcb9b3f40eba41 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Apr 2026 22:21:23 +0000 Subject: [PATCH 015/545] patch 9.2.0330: tests: some patterns in tar and zip plugin tests not strict enough Problem: Some patterns in tar and zip plugin tests not strict enough. Solution: Use assert_equal() for lines that should match exactly. Match a literal dot properly (zeertzjq). closes: #19946 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/testdir/test_plugin_tar.vim | 14 +++++++------- src/testdir/test_plugin_zip.vim | 18 +++++++++--------- src/version.c | 2 ++ 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim index bde79052dd..80b7a76d6d 100644 --- a/src/testdir/test_plugin_tar.vim +++ b/src/testdir/test_plugin_tar.vim @@ -22,8 +22,8 @@ def g:Test_tar_basic() assert_match('^" Browsing tarfile .*/X.tar', getline(2)) assert_match('^" Select a file with cursor and press ENTER, "x" to extract a file', getline(3)) assert_match('^$', getline(4)) - assert_match('testtar/', getline(5)) - assert_match('testtar/file1.txt', getline(6)) + assert_equal('testtar/', getline(5)) + assert_equal('testtar/file1.txt', getline(6)) ### Check ENTER on header :1 @@ -78,7 +78,7 @@ def g:Test_tar_evil() assert_match('^" Select a file with cursor and press ENTER, "x" to extract a file', getline(3)) assert_match('^" Note: Path Traversal Attack detected', getline(4)) assert_match('^$', getline(5)) - assert_match('/etc/ax-pwn', getline(6)) + assert_equal('/etc/ax-pwn', getline(6)) ### Check ENTER on header :1 @@ -141,7 +141,7 @@ def g:Test_tar_path_traversal_with_nowrapscan() assert_match('^" Select a file with cursor and press ENTER, "x" to extract a file', getline(3)) assert_match('^" Note: Path Traversal Attack detected', getline(4)) assert_match('^$', getline(5)) - assert_match('/etc/ax-pwn', getline(6)) + assert_equal('/etc/ax-pwn', getline(6)) assert_equal(1, b:leading_slash) @@ -252,7 +252,7 @@ def g:Test_extraction() delete('X.txt') execute 'edit ' .. dir .. '/' .. c.archive - assert_match('X.txt', getline(5), 'line 5 wrong in archive: ' .. c.archive) + assert_equal('X.txt', getline(5), 'line 5 wrong in archive: ' .. c.archive) :5 normal x assert_equal(0, v:shell_error, 'vshell error not 0') @@ -281,7 +281,7 @@ def g:Test_extract_with_dotted_dir() defer delete(tarpath) execute 'e ' .. tarpath - assert_match('X.txt', getline(5)) + assert_equal('X.txt', getline(5)) :5 normal x assert_true(filereadable('X.txt')) @@ -305,7 +305,7 @@ def g:Test_extract_with_dotted_filename() defer delete(tarpath) execute 'e ' .. tarpath - assert_match('X.txt', getline(5)) + assert_equal('X.txt', getline(5)) :5 normal x assert_true(filereadable('X.txt')) diff --git a/src/testdir/test_plugin_zip.vim b/src/testdir/test_plugin_zip.vim index 3d39e24ef7..4357131543 100644 --- a/src/testdir/test_plugin_zip.vim +++ b/src/testdir/test_plugin_zip.vim @@ -22,7 +22,7 @@ def g:Test_zip_basic() ### Check header assert_match('^" zip\.vim version v\d\+', getline(1)) - assert_match('^" Browsing zipfile .*/X.zip', getline(2)) + assert_match('^" Browsing zipfile .*/X\.zip', getline(2)) assert_match('^" Select a file with cursor and press ENTER', getline(3)) assert_match('^$', getline(4)) @@ -43,7 +43,7 @@ def g:Test_zip_basic() :1 search('file.txt') exe ":normal \" - assert_match('zipfile://.*/X.zip::Xzip/file.txt', @%) + assert_match('zipfile://.*/X\.zip::Xzip/file\.txt', @%) assert_equal('one', getline(1)) ### Check editing file @@ -68,7 +68,7 @@ def g:Test_zip_basic() assert_true(filereadable("Xzip/file.txt")) ## Check not overwriting existing file - assert_match(' .* not overwriting!', execute("normal x")) + assert_match(' .* not overwriting!', execute("normal x")) delete("Xzip", "rf") @@ -118,7 +118,7 @@ def g:Test_zip_basic() ### Check when "zip" report failure if executable("false") g:zip_zipcmd = "false" - assert_match('sorry, unable to update .*/X.zip with Xzip/file.txt', + assert_match('sorry, unable to update .*/X\.zip with Xzip/file\.txt', execute("write")) endif bw!|bw @@ -189,7 +189,7 @@ def g:Test_zip_glob_fname() fname = 'a[a].txt' search('\V' .. fname) exe ":normal \" - assert_match('zipfile://.*/X.zip::zipglob/a\[a\].txt', @%) + assert_match('zipfile://.*/X\.zip::zipglob/a\[a\]\.txt', @%) assert_equal('a test file with []', getline(1)) bw @@ -198,7 +198,7 @@ def g:Test_zip_glob_fname() fname = 'a*.txt' search('\V' .. fname) exe ":normal \" - assert_match('zipfile://.*/X.zip::zipglob/a\*.txt', @%) + assert_match('zipfile://.*/X\.zip::zipglob/a\*\.txt', @%) assert_equal('a test file with a*', getline(1)) bw @@ -207,7 +207,7 @@ def g:Test_zip_glob_fname() fname = 'a?.txt' search('\V' .. fname) exe ":normal \" - assert_match('zipfile://.*/X.zip::zipglob/a?.txt', @%) + assert_match('zipfile://.*/X\.zip::zipglob/a?\.txt', @%) assert_equal('a test file with a?', getline(1)) bw @@ -216,7 +216,7 @@ def g:Test_zip_glob_fname() fname = 'a\.txt' search('\V' .. escape(fname, '\\')) exe ":normal \" - assert_match('zipfile://.*/X.zip::zipglob/a\\.txt', @%) + assert_match('zipfile://.*/X\.zip::zipglob/a\\\.txt', @%) assert_equal('a test file with a\', getline(1)) bw @@ -225,7 +225,7 @@ def g:Test_zip_glob_fname() fname = 'a\\.txt' search('\V' .. escape(fname, '\\')) exe ":normal \" - assert_match('zipfile://.*/X.zip::zipglob/a\\\\.txt', @%) + assert_match('zipfile://.*/X\.zip::zipglob/a\\\\\.txt', @%) assert_equal('a test file with a double \', getline(1)) bw diff --git a/src/version.c b/src/version.c index c418648bbb..535e85ef85 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 330, /**/ 329, /**/ From 07faa961a05bc5ea007ab70ff483ea1b32c3371d Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 9 Apr 2026 22:27:36 +0000 Subject: [PATCH 016/545] patch 9.2.0331: spellfile: stack buffer overflows in spell file generation Problem: spell_read_aff() uses sprintf() into a fixed-size stack buffer without bounds checking. store_aff_word() uses STRCAT() to append attacker-controlled strings into newword[MAXWLEN] without checking remaining space. Both are reachable via :mkspell with crafted .aff/.dic files (xinyi234) Solution: Replace sprintf() with vim_snprintf() in spell_read_aff(). Replace STRCAT() with STRNCAT() with explicit remaining-space calculation in store_aff_word(). closes: #19944 Signed-off-by: Christian Brabandt --- src/spellfile.c | 14 ++++++++++---- src/testdir/test_spellfile.vim | 27 +++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/spellfile.c b/src/spellfile.c index c570920795..a9a347a89a 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -2736,10 +2736,12 @@ spell_read_aff(spellinfo_T *spin, char_u *fname) char_u buf[MAXLINELEN]; aff_entry->ae_cond = getroom_save(spin, items[4]); + // Note: this silently truncates the buffer, but this should + // not happen in practice if (*items[0] == 'P') - sprintf((char *)buf, "^%s", items[4]); + vim_snprintf((char *)buf, sizeof(buf), "^%s", items[4]); else - sprintf((char *)buf, "%s$", items[4]); + vim_snprintf((char *)buf, sizeof(buf), "%s$", items[4]); aff_entry->ae_prog = vim_regcomp(buf, RE_MAGIC + RE_STRING + RE_STRICT); if (aff_entry->ae_prog == NULL) @@ -3906,7 +3908,9 @@ store_aff_word( else p += STRLEN(ae->ae_chop); } - STRCAT(newword, p); + // Note: this silently truncates the buffer, but this should + // not happen in practice + STRNCAT(newword, p, MAXWLEN - STRLEN(newword) - 1); } else { @@ -3922,7 +3926,9 @@ store_aff_word( *p = NUL; } if (ae->ae_add != NULL) - STRCAT(newword, ae->ae_add); + // Note: this silently truncates the buffer, but this should + // not happen in practice + STRNCAT(newword, ae->ae_add, MAXWLEN - STRLEN(newword) - 1); } use_flags = flags; diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim index b72974ed07..f46a25d99e 100644 --- a/src/testdir/test_spellfile.vim +++ b/src/testdir/test_spellfile.vim @@ -1165,5 +1165,32 @@ func Test_mkspell_empty_dic() call delete('XtestEmpty.spl') endfunc +" This used to cause a buffer overflow +func Test_mkspell_no_buffer_overflow() + CheckNotMSWindows + + let aff_lines = ['SET ISO8859-1', 'SFX A Y 1', + \ 'SFX A 0 s ' .. repeat(nr2char(0xff), 491)] + call writefile(aff_lines, 'Xbof.aff', 'D') + call writefile(['1', 'word/A'], 'Xbof.dic', 'D') + " Must not crash; ignore any conversion/regex errors. + try + mkspell! Xbof.spl Xbof + catch + endtry + defer delete('Xbof.spl') + + let long = repeat(nr2char(0xff), 200) + let aff2_lines = ['SET ISO8859-1', 'SFX A Y 1', + \ 'SFX A 0 ' .. long .. ' .'] + call writefile(aff2_lines, 'Xbof2.aff', 'D') + call writefile(['1', long .. '/A'], 'Xbof2.dic', 'D') + try + mkspell! Xbof2.spl Xbof2 + catch + endtry + defer delete('Xbof2.spl') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 535e85ef85..ccffba42d0 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 331, /**/ 330, /**/ From 7e0eb5271e455beee52bd3f444bb68052900dfd5 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 10 Apr 2026 17:43:59 +0000 Subject: [PATCH 017/545] patch 9.2.0332: popup: still opacity rendering issues Problem: popup: still opacity rendering issues Solution: Fix remaining issues, see below (Yasuhiro Matsumoto). This PR fixes the following issues: - Padding blend hole at wide char boundary: when a padding cell overlaps the second half of a wide character, the right half's attr value is unreliable. Use the left half's saved attr for blending instead. - Wide char background split at popup boundary: when a wide character in an upper popup straddles the edge of a lower opacity popup, both halves got different background colors. Since terminals cannot render different left/right background colors for a wide character, detect the lower popup with popup_is_over_opacity() and use the non-popup side's underlying attr for both halves. - Wrong blend color with cterm-only highlights under 'termguicolors': when a popup highlight has ctermbg but no guibg, bg_rgb is set to CTERMCOLOR (not INVALCOLOR). hl_blend_attr() used this value as a real RGB color, producing gray instead of the intended color. Use COLOR_INVALID() to detect both INVALCOLOR and CTERMCOLOR, and fall back to converting the cterm color number to RGB. closes: #19943 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/highlight.c | 87 ++++++++++++++----- src/popupwin.c | 80 ++++++++++++++++- src/proto/popupwin.pro | 1 + src/screen.c | 54 +++++++++--- .../dumps/Test_popupwin_opacity_wide_1.dump | 20 ++--- .../dumps/Test_popupwin_opacity_wide_2.dump | 20 ++--- src/testdir/test_popupwin.vim | 11 +-- src/version.c | 2 + 8 files changed, 211 insertions(+), 64 deletions(-) diff --git a/src/highlight.c b/src/highlight.c index cf5921f4bc..73b39e98b2 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -3125,6 +3125,32 @@ hl_combine_attr(int char_attr, int prim_attr) } #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) + +# ifdef FEAT_TERMGUICOLORS +/* + * Convert a cterm color number (1-16) to an RGB value. + * Used as a fallback when 'termguicolors' is set but only cterm colors are + * specified (no guifg/guibg). + * Returns INVALCOLOR if the color number is out of range. + */ + static guicolor_T +cterm_color_to_rgb(int color_nr) +{ + // ANSI color order: Black, Red, Green, Yellow, Blue, Magenta, + // Cyan, White, then bright variants. + static const guicolor_T cterm_color_16[16] = { + 0x000000, 0xc00000, 0x008000, 0x808000, + 0x0000c0, 0xc000c0, 0x004080, 0xc0c0c0, + 0x808080, 0xff8080, 0x00ff00, 0xffff00, + 0x6060ff, 0xff40ff, 0x00ffff, 0xffffff + }; + + if (color_nr < 1 || color_nr > 16) + return INVALCOLOR; + return cterm_color_16[color_nr - 1]; +} +# endif + /* * Blend two RGB colors based on blend value (0-100). * blend: 0=use popup color, 100=use background color @@ -3273,32 +3299,47 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED) if (popup_aep->ae_u.cterm.bg_color > 0) new_en.ae_u.cterm.bg_color = popup_aep->ae_u.cterm.bg_color; #ifdef FEAT_TERMGUICOLORS - // Blend RGB colors for termguicolors mode - if (blend_fg) + // Blend RGB colors for termguicolors mode. + // Fall back to cterm color converted to RGB when + // gui color is not set. { - // blend_fg=TRUE: fade underlying text toward popup bg. - if (popup_aep->ae_u.cterm.bg_rgb != INVALCOLOR) + guicolor_T popup_bg = popup_aep->ae_u.cterm.bg_rgb; + guicolor_T popup_fg = popup_aep->ae_u.cterm.fg_rgb; + + if (COLOR_INVALID(popup_bg) + && popup_aep->ae_u.cterm.bg_color > 0) + popup_bg = cterm_color_to_rgb( + popup_aep->ae_u.cterm.bg_color); + if (COLOR_INVALID(popup_fg) + && popup_aep->ae_u.cterm.fg_color > 0) + popup_fg = cterm_color_to_rgb( + popup_aep->ae_u.cterm.fg_color); + + if (blend_fg) { - int base_fg = 0xFFFFFF; - if (char_aep != NULL - && char_aep->ae_u.cterm.fg_rgb != INVALCOLOR) - base_fg = char_aep->ae_u.cterm.fg_rgb; - new_en.ae_u.cterm.fg_rgb = blend_colors( - base_fg, popup_aep->ae_u.cterm.bg_rgb, blend); + // blend_fg=TRUE: fade underlying text toward popup bg. + if (popup_bg != INVALCOLOR) + { + int base_fg = 0xFFFFFF; + if (char_aep != NULL + && char_aep->ae_u.cterm.fg_rgb != INVALCOLOR) + base_fg = char_aep->ae_u.cterm.fg_rgb; + new_en.ae_u.cterm.fg_rgb = blend_colors( + base_fg, popup_bg, blend); + } + } + else if (popup_fg != INVALCOLOR) + // blend_fg=FALSE: use popup foreground + new_en.ae_u.cterm.fg_rgb = popup_fg; + if (popup_bg != INVALCOLOR) + { + // Blend popup bg toward underlying bg + guicolor_T underlying_bg = INVALCOLOR; + if (char_aep != NULL) + underlying_bg = char_aep->ae_u.cterm.bg_rgb; + new_en.ae_u.cterm.bg_rgb = blend_colors( + popup_bg, underlying_bg, blend); } - } - else if (popup_aep->ae_u.cterm.fg_rgb != INVALCOLOR) - // blend_fg=FALSE: use popup foreground - new_en.ae_u.cterm.fg_rgb = popup_aep->ae_u.cterm.fg_rgb; - if (popup_aep->ae_u.cterm.bg_rgb != INVALCOLOR) - { - // Blend popup bg toward underlying bg - guicolor_T underlying_bg = INVALCOLOR; - if (char_aep != NULL) - underlying_bg = char_aep->ae_u.cterm.bg_rgb; - new_en.ae_u.cterm.bg_rgb = blend_colors( - popup_aep->ae_u.cterm.bg_rgb, - underlying_bg, blend); } #endif } diff --git a/src/popupwin.c b/src/popupwin.c index d5fdbb8d26..bdf53cdc4f 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -4438,6 +4438,37 @@ popup_is_under_opacity(int row, int col) return opacity_zindex[row * opacity_zindex_cols + col] > screen_zindex; } +/* + * Return TRUE if cell (row, col) is covered by a lower-zindex opacity popup. + */ + int +popup_is_over_opacity(int row, int col) +{ + win_T *wp; + + FOR_ALL_POPUPWINS(wp) + if ((wp->w_popup_flags & POPF_OPACITY) + && wp->w_popup_blend > 0 + && !(wp->w_popup_flags & POPF_HIDDEN) + && wp->w_zindex < screen_zindex + && row >= wp->w_winrow + && row < wp->w_winrow + popup_height(wp) + && col >= wp->w_wincol + && col < wp->w_wincol + popup_width(wp)) + return TRUE; + FOR_ALL_POPUPWINS_IN_TAB(curtab, wp) + if ((wp->w_popup_flags & POPF_OPACITY) + && wp->w_popup_blend > 0 + && !(wp->w_popup_flags & POPF_HIDDEN) + && wp->w_zindex < screen_zindex + && row >= wp->w_winrow + && row < wp->w_winrow + popup_height(wp) + && col >= wp->w_wincol + && col < wp->w_wincol + popup_width(wp)) + return TRUE; + return FALSE; +} + /* * Return TRUE if any cell in row "row" from "start_col" to "end_col" * (exclusive) is covered by a higher-zindex opacity popup. @@ -4840,8 +4871,10 @@ draw_opacity_padding_cell( screen_char(base_off, row, base_col); // Draw padding in the right half. + // Use left half's attr since the right half of a + // wide char may have an unreliable attr value. ScreenLines[off] = ' '; - ScreenAttrs[off] = saved_screenattrs[save_off]; + ScreenAttrs[off] = saved_screenattrs[base_save_off]; if (enc_utf8) ScreenLinesUC[off] = 0; int popup_attr_val = @@ -4855,10 +4888,53 @@ draw_opacity_padding_cell( screen_char(off, row, col); return; } + // The content drawing cleared the left half to a + // space (wide char didn't fit at content edge), + // but the saved data has a wide char. Restore it + // spanning both the content cell and padding cell. + if (base_save_off >= 0 + && saved_screenlinesuc[base_save_off] != 0 + && utf_char2cells( + saved_screenlinesuc[base_save_off]) == 2 + && ScreenLines[base_off] == ' ' + && ScreenLinesUC[base_off] == 0) + { + int popup_attr_val = + get_win_attr(screen_opacity_popup); + int blend = + screen_opacity_popup->w_popup_blend; + + ScreenLines[base_off] = + saved_screenlines[base_save_off]; + ScreenLinesUC[base_off] = + saved_screenlinesuc[base_save_off]; + ScreenAttrs[base_off] = + saved_screenattrs[base_save_off]; + ScreenAttrs[base_off] = hl_blend_attr( + ScreenAttrs[base_off], + popup_attr_val, blend, TRUE); + + ScreenLines[off] = 0; + ScreenLinesUC[off] = 0; + ScreenAttrs[off] = ScreenAttrs[base_off]; + + popup_set_base_screen_cell(row, base_col, + ScreenLines[base_off], + ScreenAttrs[base_off], + ScreenLinesUC[base_off]); + popup_set_base_screen_cell(row, col, + ScreenLines[off], + ScreenAttrs[off], + ScreenLinesUC[off]); + screen_char(base_off, row, base_col); + return; + } // Draw padding in the right half. + // Use left half's attr since the right half of a + // wide char may have an unreliable attr value. ScreenLines[off] = ' '; - ScreenAttrs[off] = saved_screenattrs[save_off]; + ScreenAttrs[off] = saved_screenattrs[base_save_off]; if (enc_utf8 && ScreenLinesUC != NULL) ScreenLinesUC[off] = 0; int popup_attr_val = get_win_attr(screen_opacity_popup); diff --git a/src/proto/popupwin.pro b/src/proto/popupwin.pro index d6ac2b72a0..14e4a6fd29 100644 --- a/src/proto/popupwin.pro +++ b/src/proto/popupwin.pro @@ -53,6 +53,7 @@ int popup_do_filter(int c); int popup_no_mapping(void); void popup_check_cursor_pos(void); int popup_is_under_opacity(int row, int col); +int popup_is_over_opacity(int row, int col); int popup_is_under_opacity_range(int row, int start_col, int end_col); void may_update_popup_mask(int type); void may_update_popup_position(void); diff --git a/src/screen.c b/src/screen.c index 311735d821..f667e6eb24 100644 --- a/src/screen.c +++ b/src/screen.c @@ -945,27 +945,36 @@ screen_line( popup_get_base_screen_cell(row, col + coloff, NULL, &underlying_attr, NULL); - ScreenAttrs[off_to] = hl_blend_attr(underlying_attr, - combined, blend, FALSE); - // For double-wide characters, the second cell may have a - // different underlying attr (e.g. at popup boundary), - // so blend it independently. + // For double-wide characters, a terminal cannot render + // different background colors for the left and right + // halves. When one half is over a lower opacity popup + // and the other is not, use the non-popup side's + // underlying attr for both to avoid color leaking. if (char_cells == 2) { int underlying_attr2 = 0; + int scol1 = col + coloff; + int over1 = popup_is_over_opacity(row, scol1); + int over2 = popup_is_over_opacity(row, scol1 + 1); - popup_get_base_screen_cell(row, col + coloff + 1, + popup_get_base_screen_cell(row, scol1 + 1, NULL, &underlying_attr2, NULL); + if (over1 != over2) + { + // One half is over a lower popup, the other is + // not. Use the non-popup side for both. + if (over1) + underlying_attr = underlying_attr2; + else + underlying_attr2 = underlying_attr; + } ScreenAttrs[off_to + 1] = hl_blend_attr( underlying_attr2, combined, blend, FALSE); - if (blend == 100) - resolve_wide_char_opacity_attrs(row, - col + coloff, col + coloff + 1, - &ScreenAttrs[off_to], - &ScreenAttrs[off_to + 1]); } + ScreenAttrs[off_to] = hl_blend_attr(underlying_attr, + combined, blend, FALSE); } else #endif @@ -2330,11 +2339,28 @@ screen_char(unsigned off, int row, int col) // output the final blended result. // Also suppress if this is a wide character whose second cell // is under an opacity popup. - if (popup_is_under_opacity(row, col) - || (enc_utf8 && ScreenLinesUC[off] != 0 + if (popup_is_under_opacity(row, col)) + { + // If this is a wide character whose left half is under an opacity + // popup but right half is not, clear the right half so the old + // blended value doesn't remain as a ghost after popup_move(). + if (enc_utf8 && ScreenLinesUC[off] != 0 && utf_char2cells(ScreenLinesUC[off]) == 2 && col + 1 < screen_Columns - && popup_is_under_opacity(row, col + 1))) + && !popup_is_under_opacity(row, col + 1)) + { + int off2 = off + 1; + ScreenLines[off2] = ' '; + ScreenLinesUC[off2] = 0; + screen_char(off2, row, col + 1); + } + screen_cur_col = 9999; + return; + } + if (enc_utf8 && ScreenLinesUC[off] != 0 + && utf_char2cells(ScreenLinesUC[off]) == 2 + && col + 1 < screen_Columns + && popup_is_under_opacity(row, col + 1)) { screen_cur_col = 9999; return; diff --git a/src/testdir/dumps/Test_popupwin_opacity_wide_1.dump b/src/testdir/dumps/Test_popupwin_opacity_wide_1.dump index be464f7721..875430044b 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_wide_1.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_wide_1.dump @@ -1,13 +1,13 @@ ->い*0&#ffffff0|え|ー@15|い|!+&| |1| @3 -|い*&|え|ー@15|い|!+&| |2| @3 -|い*&|え|ー@15|い|!+&| |3| @3 -|い*&|え*0#ffffff16#e000002|ー@6| +&| +0#0000000#ffffff0|ー*&@7|い|!+&| |4| @3 -|い*&| +0#ffffff16#e000002|カ*&|ラ|フ|ル|な| +&|ー*&@1| +&| +0#0000000#ffffff0|ー*&@7|い|!+&| |5| @3 -|い*&| +0#ffffff16#e000002|ポ*&|ッ|プ|ア|ッ|プ|で|─+&|╮| +0#0000000#ffffff0|ー*&@7|い|!+&| |6| @3 -|い*&| +0#ffffff16#e000002|最*&|上|川| +&|ぼ*&|赤|い|な|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |7| @3 -|い*&| +0#ffffff16#e000002|│|あ*&|い|う|え|お|ー@1|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |8| @3 -|い*&| +&|│+0#ffffff16#0000e05|ー*&@6|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |9| @3 -|い*&| +&|╰+0#ffffff16#0000e05|─@13|╯| +0#0000000#ffffff0|ー*&@7|い|!+&| |1|0| @2 +>い*0&#ffffff0|え*0#e08080255#600000255|ー@6| +&| +0#0000000#ffffff0|ー*&@7|い|!+&| |1| @3 +|い*&| +0#e08080255#600000255|カ*0#ffffff255&|ラ|フ|ル|な| +0#e08080255&|ー*&@1| +&| +0#0000000#ffffff0|ー*&@7|い|!+&| |2| @3 +|い*&| +0#e08080255#600000255|ポ*0#ffffff255#600030255|ッ|プ|ア|ッ|プ|で|─+0#e08080255&|╮| +0#0000000#ffffff0|ー*&@7|い|!+&| |3| @3 +|い*&| +0#e08080255#600000255|最*0#ffffff255#600030255|上|川| +0#e08080255&|ぼ*&|赤|い|な|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |4| @3 +|い*&| +0#e08080255#600000255|│+0�|あ*&|い|う|え|お|ー*0#a04070255&@1|│+0#e08080255&| +0#0000000#ffffff0|ー*&@7|い|!+&| |5| @3 +|い*&| +&|│+0#ffffff255#000060255|ー*0#8080e0255&@6|│+0#ffffff255&| +0#0000000#ffffff0|ー*&@7|い|!+&| |6| @3 +|い*&| +&|╰+0#ffffff255#000060255|─@13|╯| +0#0000000#ffffff0|ー*&@7|い|!+&| |7| @3 +|い*&|え|ー@15|い|!+&| |8| @3 +|い*&|え|ー@15|い|!+&| |9| @3 +|い*&|え|ー@15|い|!+&| |1|0| @2 |い*&|え|ー@15|い|!+&| |1@1| @2 |い*&|え|ー@15|い|!+&| |1|2| @2 |い*&|え|ー@15|い|!+&| |1|3| @2 diff --git a/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump b/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump index a6ffdb11de..df2e6c09b8 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump @@ -1,15 +1,15 @@ >い*0&#ffffff0|え|ー@15|い|!+&| |1| @3 |い*&|え|ー@15|い|!+&| |2| @3 -|い*&|え|ー@15|い|!+&| |3| @3 -|い*&|え|ー@15|い|!+&| |4| @3 -|い*&|え|ー@15|い|!+&| |5| @3 -|い*&| +&|╭+0#ffffff16#0000e05|─@13|╮| +0#0000000#ffffff0|ー*&@7|い|!+&| |6| @3 -|い*&| +&|│+0#ffffff16#0000e05|あ*&|め|ん|ぼ|赤|い|な|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |7| @3 -|い*&| +&|│+0#ffffff16#0000e05|あ*&|い|う|え|お| +&| +0&#e000002|ー*&|│+&| |ー*&@5|ー*0#0000000#ffffff0@1|い|!+&| |8| @3 -|い*&| +&|│+0#ffffff16#0000e05|ー*&@4| +&| +0&#e000002|カ*&|ラ|フ|ル|な|ー@1| +&@1|ー*0#0000000#ffffff0@1|い|!+&| |9| @3 -|い*&| +&|╰+0#ffffff16#0000e05|─@10|─+0&#e000002|ポ*&|ッ|プ|ア|ッ|プ|で| +&@1|ー*0#0000000#ffffff0@1|い|!+&| |1|0| @2 -|い*&|え|ー@4| +&| +0#ffffff16#e000002|最*&|上|川|ー@3| +&@1|ー*0#0000000#ffffff0@1|い|!+&| |1@1| @2 -|い*&|え|ー@4| +&| +0#ffffff16#e000002|ー*&@7|ー*0#0000000#ffffff0@1|い|!+&| |1|2| @2 +|い*&| +&|╭+0#ffffff255#000060255|─@13|╮| +0#0000000#ffffff0|ー*&@7|い|!+&| |3| @3 +|い*&| +&|│+0#ffffff255#000060255|あ*&|め|ん|ぼ|赤|い|な|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |4| @3 +|い*&| +&|│+0#ffffff255#000060255|あ*&|い|う|え|お|ー*0#8080e0255&@1|│+0#ffffff255&| +0#0000000#ffffff0|ー*&@7|い|!+&| |5| @3 +|い*&| +&|│+0#ffffff255#000060255|ー*0#8080e0255&@4| +&| +0#a04070255#600030255|ー*&|│+0#e08080255&| +0�|ー*&@5|ー*0#0000000#ffffff0@1|い|!+&| |6| @3 +|い*&| +&|╰+0#ffffff255#000060255|─@10|─+0#e08080255#600030255|カ*0#ffffff255&|ラ*0�|フ|ル|な|ー*0#e08080255&@2|ー*0#0000000#ffffff0@1|い|!+&| |7| @3 +|い*&|え|ー@4| +&| +0#e08080255#600000255|ポ*0#ffffff255&|ッ|プ|ア|ッ|プ|で|ー*0#e08080255&|ー*0#0000000#ffffff0@1|い|!+&| |8| @3 +|い*&|え|ー@4| +&| +0#e08080255#600000255|最*0#ffffff255&|上|川|ー*0#e08080255&@4|ー*0#0000000#ffffff0@1|い|!+&| |9| @3 +|い*&|え|ー@4| +&| +0#e08080255#600000255|ー*&@7|ー*0#0000000#ffffff0@1|い|!+&| |1|0| @2 +|い*&|え|ー@15|い|!+&| |1@1| @2 +|い*&|え|ー@15|い|!+&| |1|2| @2 |い*&|え|ー@15|い|!+&| |1|3| @2 |い*&|え|ー@15|い|!+&| |1|4| @2 |:| @25|1|,|1| @10|T|o|p| diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 023d890676..a134acabd9 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -4932,6 +4932,7 @@ func Test_popup_opacity_wide_char_overlap() " higher-zindex popup are properly blended (no holes or missing chars). let lines =<< trim END set encoding=utf-8 + set termguicolors for i in range(1, 20) call setline(i, 'いえーーーーーーーーーーーーーーーーい! ' .. i) endfor @@ -4939,7 +4940,7 @@ func Test_popup_opacity_wide_char_overlap() hi MyPopup2 ctermbg=darkred ctermfg=white let g:p1 = popup_create(['あめんぼ赤いな','あいうえお'], #{ \ opacity: 50, - \ line: 6, + \ line: 3, \ col: 4, \ border: [], \ borderchars: ['─','│','─','│','╭','╮','╯','╰'], @@ -4950,7 +4951,7 @@ func Test_popup_opacity_wide_char_overlap() \}) let g:p2 = popup_create(['カラフルな','ポップアップで','最上川'], #{ \ opacity: 50, - \ line: 4, + \ line: 1, \ col: 3, \ minwidth: 15, \ minheight: 3, @@ -4963,9 +4964,9 @@ func Test_popup_opacity_wide_char_overlap() let buf = RunVimInTerminal('-S XtestPopupOpacityWide', #{rows: 15, cols: 45}) call VerifyScreenDump(buf, 'Test_popupwin_opacity_wide_1', {}) - " Move popups far apart so they don't overlap. - " Tests right edge of popup where wide chars span content/padding boundary. - call term_sendkeys(buf, ":call popup_move(g:p2, #{line: 14, col: 16})\") + " Move p2 so it partially overlaps with p1 at a different position. + " Tests wide chars at the overlap boundary of two opacity popups. + call term_sendkeys(buf, ":call popup_move(g:p2, #{line: 6, col: 16})\") call TermWait(buf) call term_sendkeys(buf, ":\") call VerifyScreenDump(buf, 'Test_popupwin_opacity_wide_2', {}) diff --git a/src/version.c b/src/version.c index ccffba42d0..e2dbe11e88 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 332, /**/ 331, /**/ From 01e9670212db5aa7f9551485c6e716436db563a9 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Fri, 10 Apr 2026 18:37:25 +0000 Subject: [PATCH 018/545] runtime(doc): Update documentation on statusline click handler related: #19841 Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 3 ++- runtime/doc/tags | 1 - runtime/doc/various.txt | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 24776535cb..eb802ed75e 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 06 +*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -13300,6 +13300,7 @@ spell Compiled with spell checking support |spell|. startuptime Compiled with |--startuptime| support. statusline Compiled with support for 'statusline', 'rulerformat' and special formats of 'titlestring' and 'iconstring'. +statusline_click Click handlers in 'statusline' |stl-%[FuncName]| sun SunOS version of Vim. sun_workshop Support for Sun |workshop| has been removed. syntax Compiled with syntax highlighting support |syntax|. diff --git a/runtime/doc/tags b/runtime/doc/tags index 6505d1b88e..66f3731614 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -1524,7 +1524,6 @@ $quote eval.txt /*$quote* +spell various.txt /*+spell* +startuptime various.txt /*+startuptime* +statusline various.txt /*+statusline* -+statusline_click various.txt /*+statusline_click* +sun_workshop various.txt /*+sun_workshop* +syntax various.txt /*+syntax* +system() various.txt /*+system()* diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 9de6614522..880feb634a 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -496,7 +496,6 @@ N *+spell* spell checking support, see |spell| N *+startuptime* |--startuptime| argument N *+statusline* Options 'statusline', 'rulerformat' and special formats of 'titlestring' and 'iconstring' -N *+statusline_click* Click handlers in 'statusline' |stl-%[FuncName]| - *+sun_workshop* Removed: |workshop| N *+syntax* Syntax highlighting |syntax| *+system()* Unix only: opposite of |+fork| From 871e6c6cf9835b1f266461f3eb6c7bf07e841779 Mon Sep 17 00:00:00 2001 From: Nihaal Sangha Date: Fri, 10 Apr 2026 18:40:37 +0000 Subject: [PATCH 019/545] patch 9.2.0333: filetype: PklProject files are not recognized Problem: filetype: PklProject files are not recognized Solution: Detect PklProject files as pkl filetype (Nihaal Sangha). Reference: https://pkl-lang.org/ https://github.com/apple/pkl https://github.com/apple/pkl-pantry closes: #19948 Signed-off-by: Nihaal Sangha Signed-off-by: Christian Brabandt --- runtime/filetype.vim | 2 +- src/testdir/test_filetype.vim | 2 +- src/version.c | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 8bfd13da17..0c76a60a52 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -890,7 +890,7 @@ au BufNewFile,BufRead requirements/*.txt setf requirements au BufNewFile,BufRead requires/*.txt setf requirements " Pkl -au BufNewFile,BufRead *.pkl,*.pcf,pkl-lsp://* setf pkl +au BufNewFile,BufRead PklProject,*.pkl,*.pcf,pkl-lsp://* setf pkl " WIC kickstarter files au BufNewFile,BufRead *.wks,*.wks.in,*.wks.inc setf wks diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 04036b0eb4..70e269ee63 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -634,7 +634,7 @@ def s:GetFilenameChecks(): dict> pilrc: ['file.rcp'], pine: ['.pinerc', 'pinerc', '.pinercex', 'pinercex'], pinfo: ['/etc/pinforc', '/.pinforc', 'any/.pinforc', 'any/etc/pinforc'], - pkl: ['file.pkl', 'file.pcf'], + pkl: ['file.pkl', 'file.pcf', 'any/PklProject'], pli: ['file.pli', 'file.pl1'], plm: ['file.plm', 'file.p36', 'file.pac'], plp: ['file.plp'], diff --git a/src/version.c b/src/version.c index e2dbe11e88..13448796f3 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 333, /**/ 332, /**/ From dd40b1af5b5800cab81cb4b6566f028282e74031 Mon Sep 17 00:00:00 2001 From: Gary Johnson Date: Fri, 10 Apr 2026 21:23:38 +0000 Subject: [PATCH 020/545] patch 9.2.0334: GTK: window geometry shrinks with with client-side decorations Problem: On GTK3 with client-side decorations the window opens with wrong &columns/&lines, and each :tabnew/:tabclose cycle shrinks the size further. Solution: Measure and compensate for the CSD frame offset, discard spurious configure events from tabline show/hide (Gary Johnson). closes: #19853 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Gary Johnson Signed-off-by: Christian Brabandt --- src/gui.c | 51 +++++++++++--- src/gui_gtk_x11.c | 147 ++++++++++++++++++++++++++++++++++++--- src/testdir/test_gui.vim | 78 +++++++++++++++++++++ src/version.c | 2 + 4 files changed, 259 insertions(+), 19 deletions(-) diff --git a/src/gui.c b/src/gui.c index 1d60cd7f34..f62bf697e9 100644 --- a/src/gui.c +++ b/src/gui.c @@ -1659,9 +1659,11 @@ gui_resize_shell(int pixel_width, int pixel_height) gui_may_resize_shell(void) { if (new_pixel_height) + { // careful: gui_resize_shell() may postpone the resize again if we // were called indirectly by it gui_resize_shell(new_pixel_width, new_pixel_height); + } } int @@ -3737,12 +3739,24 @@ gui_init_which_components(char_u *oldval UNUSED) // Don't do this while starting up though. // Don't change Rows when adding menu/toolbar/tabline. // Don't change Columns when adding vertical toolbar. - if (!gui.starting && need_set_size != (RESIZE_VERT | RESIZE_HOR)) - (void)char_avail(); - if ((need_set_size & RESIZE_VERT) == 0) - Rows = prev_Rows; - if ((need_set_size & RESIZE_HOR) == 0) - Columns = prev_Columns; + { + // Save the size gui_set_shellsize() determined before + // char_avail() processes spurious configure events that may + // corrupt Rows/Columns. + long post_Rows = Rows; + long post_Columns = Columns; + + if (!gui.starting && need_set_size != (RESIZE_VERT | RESIZE_HOR)) + (void)char_avail(); + if ((need_set_size & RESIZE_VERT) == 0) + Rows = prev_Rows; + else + Rows = post_Rows; + if ((need_set_size & RESIZE_HOR) == 0) + Columns = prev_Columns; + else + Columns = post_Columns; + } #endif } // When the console tabline appears or disappears the window positions @@ -3793,14 +3807,29 @@ gui_update_tabline(void) out_flush(); if (!showit != !shown) + { + // Save Rows/Columns before showing/hiding the tabline. + // gui_mch_show_tabline() processes GTK events (via + // gui_mch_update) which may trigger a spurious configure event + // that temporarily corrupts Rows. Restore the original values so + // that gui_set_shellsize() uses the correct row/column count. + int save_Rows = Rows; + int save_Columns = Columns; + gui_mch_show_tabline(showit); + Rows = save_Rows; + Columns = save_Columns; + + // Resize the outer window to compensate for the tabline height + // change. This is also needed when called from draw_tabline() + // (e.g. after :tabclose), where no caller will do the resize. + // When called from gui_init_which_components() the subsequent + // gui_set_shellsize() call will redo this to the same dimensions, + // which is harmless. + gui_set_shellsize(FALSE, showit, RESIZE_VERT); + } if (showit != 0) gui_mch_update_tabline(); - - // When the tabs change from hidden to shown or from shown to - // hidden the size of the text area should remain the same. - if (!showit != !shown) - gui_set_shellsize(FALSE, showit, RESIZE_VERT); } } diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index 21e8768abc..5a9645a562 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -408,11 +408,13 @@ static int using_gnome = 0; * Width and height are of gui.mainwin. */ typedef struct resize_history { - int used; // If true, can't match for discard. Only matches once. + int used; // If true, can't match for discard. Only matches once. int width; int height; + int from_shellsize; // TRUE if recorded by gui_mch_set_shellsize (not + // a pre-record from gui_mch_show_tabline). # ifdef ENABLE_RESIZE_HISTORY_LOG - int seq; // for ch_log messages + int seq; // for ch_log messages # endif struct resize_history *next; } resize_hist_T; @@ -422,13 +424,28 @@ static resize_hist_T *latest_resize_hist; // list of stale resize requests static resize_hist_T *old_resize_hists; +// On Wayland (and GTK3 with CSD), gtk_window_resize(w, h) results in +// gtk_window_get_size() returning (w - csd_w, h - csd_h). These offsets are +// computed once from the first confirmed resize response and applied to all +// subsequent gtk_window_resize() calls so the form gets the intended size. +static int mch_csd_width = 0; +static int mch_csd_height = 0; + +// Expected form widget width for the most recent gui_mch_set_shellsize() +// call (set only after mch_csd_width is known). Used in +// form_configure_event() to clamp slightly-narrow configure responses that +// result from CSD under-compensation, preventing column loss. +static int mch_pending_form_w = 0; + /* * Used when calling gtk_window_resize(). * Create a resize request history item, put previous request on stale list. * Width/height are the size of the request for the gui.mainwin. + * from_shellsize is TRUE when called from gui_mch_set_shellsize (vs a + * stale pre-record from gui_mch_show_tabline). */ static void -alloc_resize_hist(int width, int height) +alloc_resize_hist(int width, int height, int from_shellsize) { // alloc a new resize hist, save current in list of old history resize_hist_T *prev_hist = latest_resize_hist; @@ -436,6 +453,7 @@ alloc_resize_hist(int width, int height) new_hist->width = width; new_hist->height = height; + new_hist->from_shellsize = from_shellsize; latest_resize_hist = new_hist; // previous hist item becomes head of list @@ -3102,7 +3120,25 @@ get_item_dimensions(GtkWidget *widget, GtkOrientation orientation) GtkAllocation allocation; gtk_widget_get_allocation(widget, &allocation); + if (allocation.height > 1) + return allocation.height; + + // Allocation hasn't been updated yet (widget just became visible, + // e.g. tab bar shown asynchronously on Wayland). Query the preferred + // height so the caller gets a valid value before the layout pass + // runs. Use the maximum of minimum and natural height: GTK may + // allocate min_h even when natural_h is smaller (e.g. GtkNotebook + // tab bar has min_h > natural_h due to CSS). +# if GTK_CHECK_VERSION(3,0,0) + { + gint min_h = 0, natural_h = 0; + + gtk_widget_get_preferred_height(widget, &min_h, &natural_h); + return MAX(min_h, natural_h); + } +# else return allocation.height; +# endif # else if (orientation == GTK_ORIENTATION_HORIZONTAL) return widget->allocation.height; @@ -3147,7 +3183,11 @@ get_menu_tool_height(void) height += get_item_dimensions(gui.toolbar, GTK_ORIENTATION_HORIZONTAL); #endif #ifdef FEAT_GUI_TABLINE - if (gui.tabline != NULL) + // Only include the tabline height when tabs are actually shown. After + // gtk_notebook_set_show_tabs(FALSE) the widget allocation is not updated + // until the GTK main loop runs, so reading it would give a stale value. + if (gui.tabline != NULL + && gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline))) height += get_item_dimensions(gui.tabline, GTK_ORIENTATION_HORIZONTAL); #endif @@ -3513,6 +3553,19 @@ gui_mch_show_tabline(int showit) if (!showit != !gtk_notebook_get_show_tabs(GTK_NOTEBOOK(gui.tabline))) { +# ifdef TRACK_RESIZE_HISTORY + // When the tabline visibility changes, GTK defers the formwin + // relayout. The resulting configure event fires while the mainwin is + // still at its current size. Pre-record that size so it becomes a + // stale (discardable) entry once gui_mch_set_shellsize() records the + // new target size. + { + int w, h; + + gtk_window_get_size(GTK_WINDOW(gui.mainwin), &w, &h); + alloc_resize_hist(w, h, FALSE); + } +# endif // Note: this may cause a resize event gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), showit); update_window_manager_hints(0, 0); @@ -4284,6 +4337,22 @@ gui_mch_new_colors(void) } } +/* + * One-shot idle callback to issue a corrective gtk_window_resize() after the + * startup configure event has been processed. At startup + * gui_mch_set_shellsize() runs before the CSD offsets are known (they are + * measured from the first configure response), so the window ends up + * mch_csd_height pixels too short and mch_csd_width pixels too narrow. This + * callback re-issues the shellsize with the now-known offsets so the physical + * window matches Vim's model. + */ + static gboolean +startup_resize_correction_cb(gpointer data UNUSED) +{ + gui_set_shellsize(FALSE, FALSE, RESIZE_BOTH); + return FALSE; // one-shot +} + /* * This signal informs us about the need to rearrange our sub-widgets. */ @@ -4321,6 +4390,41 @@ form_configure_event(GtkWidget *widget UNUSED, && match_stale_width_height(w, h)) // discard stale event return TRUE; + + // On Wayland (GTK3 CSD), gtk_window_resize(w, req_h) results in + // gtk_window_get_size() returning (req_w - csd_w, req_h - csd_h). + // Compute the offsets once from the first confirmed resize response. + if ((mch_csd_height == 0 || mch_csd_width == 0) + && latest_resize_hist != NULL + && !latest_resize_hist->used + && latest_resize_hist->from_shellsize + && gui.char_height > 0) + { + int pot_csd_w = latest_resize_hist->width - w; + int pot_csd_h = latest_resize_hist->height - h; + + if (pot_csd_w > 0 && pot_csd_w < gui.char_width) + { + mch_csd_width = pot_csd_w; + // The resize that triggered this startup event was issued without + // CSD compensation; retroactively set the expected form width so + // that the clamp below corrects Columns for this same event. + mch_pending_form_w = (int)Columns * gui.char_width + + gui_get_base_width(); + } + if (pot_csd_h > 0 && pot_csd_h < gui.char_height) + { + mch_csd_height = pot_csd_h; + // Similarly, correct the form height for this event so that Rows + // is computed correctly despite the missing CSD compensation. + usable_height += mch_csd_height; + } + // The window was resized without CSD compensation and is physically + // too small. Schedule a corrective resize (now that offsets are + // known) so the window actually fits the geometry Vim has just set. + if ((mch_csd_height > 0 || mch_csd_width > 0) && gtk_socket_id == 0) + g_idle_add(startup_resize_correction_cb, NULL); + } clear_resize_hists(); #endif @@ -4354,9 +4458,22 @@ form_configure_event(GtkWidget *widget UNUSED, if (gtk_socket_id != 0) usable_height -= (gui.char_height - (gui.char_height/2)); // sic. - gui_gtk_form_freeze(GTK_FORM(gui.formwin)); - gui_resize_shell(event->width, usable_height); - gui_gtk_form_thaw(GTK_FORM(gui.formwin)); + // If the configure event delivers a form width that is slightly less than + // the width we intended (mch_pending_form_w), the difference is within + // one char_width and is due to CSD under-compensation. Clamp to the + // intended width so that column count does not drift downward. + { + int use_width = event->width; + +#ifdef TRACK_RESIZE_HISTORY + if (mch_pending_form_w > event->width + && mch_pending_form_w - event->width < gui.char_width) + use_width = mch_pending_form_w; +#endif + gui_gtk_form_freeze(GTK_FORM(gui.formwin)); + gui_resize_shell(use_width, usable_height); + gui_gtk_form_thaw(GTK_FORM(gui.formwin)); + } return TRUE; } @@ -4855,8 +4972,22 @@ gui_mch_set_shellsize(int width, int height, width += get_menu_tool_width(); height += get_menu_tool_height(); + // Compensate for CSD frame: on Wayland (GTK3 with CSD), the compositor + // subtracts the decoration margin from the requested size. mch_csd_width + // and mch_csd_height are computed from the first startup resize response + // and are 0 on X11. + width += mch_csd_width; + height += mch_csd_height; + + // Record the form width we expect the compositor to deliver. Used in + // form_configure_event() to clamp configure responses that are slightly + // narrower than intended (due to CSD startup underestimate), preventing + // column loss. Only meaningful once mch_csd_width has been measured. + mch_pending_form_w = (mch_csd_width > 0) + ? width - get_menu_tool_width() - mch_csd_width : 0; + #ifdef TRACK_RESIZE_HISTORY - alloc_resize_hist(width, height); // track the resize request + alloc_resize_hist(width, height, TRUE); #endif if (gtk_socket_id == 0) gtk_window_resize(GTK_WINDOW(gui.mainwin), width, height); diff --git a/src/testdir/test_gui.vim b/src/testdir/test_gui.vim index c686094ca2..1098e00ff6 100644 --- a/src/testdir/test_gui.vim +++ b/src/testdir/test_gui.vim @@ -1889,4 +1889,82 @@ func Test_guioptions_clipboard() let &guioptions = save_guioptions endfunc +" Tests for GUI window geometry: initial size from -geometry option and +" size stability after :tabnew / :tabclose. +" +" Background: on GTK3 with client-side decorations (Wayland), the window +" compositor subtracts the CSD frame from the requested size, causing the +" window to open a few pixels too small (wrong &columns/&lines) and to shrink +" further with each :tabnew/:tabclose cycle. + +" Test that a GUI window opened with -geometry=WxH has exactly W columns +" and H lines. +" +" Without the CSD fix, on GTK3/Wayland the compositor subtracts the frame +" margin from the requested pixel size, so the window is a character cell too +" narrow and too short. +func Test_geometry_exact_size() + CheckCanRunGui + CheckFeature gui_gtk + + let after =<< trim [CODE] + call writefile([string(&columns), string(&lines)], 'Xtest_geomsize') + qall + [CODE] + + " Hide the menu bar so it does not widen the minimum window size. + if RunVim(['set guioptions-=m'], after, '-f -g -geometry 40x15') + let result = readfile('Xtest_geomsize') + call assert_equal('40', result[0], 'columns should match -geometry width') + call assert_equal('15', result[1], 'lines should match -geometry height') + endif + + call delete('Xtest_geomsize') +endfunc + +" Test that the window size is unchanged after opening and closing a tab. +" +" Each :tabnew/:tabclose cycle triggers a tabline show/hide, which causes +" asynchronous GTK layout events. Without the fix, stale configure events +" from these layout passes are mis-interpreted as user resizes, reducing +" &columns and &lines with every cycle. Three cycles are performed to +" amplify any drift. +func Test_tabnew_tabclose_size_stable() + CheckCanRunGui + CheckFeature gui_gtk + + let after =<< trim [CODE] + let cols0 = &columns + let rows0 = &lines + tabnew + sleep 300m + tabclose + sleep 300m + tabnew + sleep 300m + tabclose + sleep 300m + tabnew + sleep 300m + tabclose + sleep 300m + call writefile([string(cols0), string(rows0), string(&columns), string(&lines)], 'Xtest_tabsize') + qall + [CODE] + + if RunVim(['set guioptions-=m'], after, '-f -g -geometry 40x15') + let result = readfile('Xtest_tabsize') + call assert_equal('40', result[0], 'initial columns should match -geometry width') + call assert_equal('15', result[1], 'initial lines should match -geometry height') + call assert_equal(result[0], result[2], + \ 'columns changed after 3x tabnew/tabclose: ' + \ .. result[0] .. ' -> ' .. result[2]) + call assert_equal(result[1], result[3], + \ 'lines changed after 3x tabnew/tabclose: ' + \ .. result[1] .. ' -> ' .. result[3]) + endif + + call delete('Xtest_tabsize') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 13448796f3..83eb6ebec0 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 334, /**/ 333, /**/ From 71c10dcd58612fbbcf91b8ad74d6cc9d3fffafda Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 10 Apr 2026 21:37:44 +0000 Subject: [PATCH 021/545] patch 9.2.0335: json_encode() uses recursive algorithm Problem: json_encode() uses recursive algorithm Solution: Convert from recursive to iterative algorithm to prevent stack overflow on deep recursive levels (Yasuhiro Matsumoto). closes: #19839 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/json.c | 364 +++++++++++++++++++++++++++++++++++++------------- src/version.c | 2 + 2 files changed, 276 insertions(+), 90 deletions(-) diff --git a/src/json.c b/src/json.c index a21ed6e771..b0d98a290e 100644 --- a/src/json.c +++ b/src/json.c @@ -18,7 +18,7 @@ #if defined(FEAT_EVAL) -static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int depth); +static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options); /* * Encode "val" into a JSON format string. @@ -28,7 +28,7 @@ static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int option static int json_encode_gap(garray_T *gap, typval_T *val, int options) { - if (json_encode_item(gap, val, get_copyID(), options, 0) == FAIL) + if (json_encode_item(gap, val, get_copyID(), options) == FAIL) { ga_clear(gap); gap->ga_data = vim_strsave((char_u *)""); @@ -264,31 +264,61 @@ is_simple_key(char_u *key) return TRUE; } +typedef enum { + ENC_LIST, + ENC_TUPLE, + ENC_DICT, +} json_enc_type_T; + +typedef struct { + json_enc_type_T je_type; + int je_options; // options when this container was entered + union { + struct { + list_T *list; + listitem_T *li; // current item + } l; + struct { + tuple_T *tuple; + int idx; // current index + int len; + } t; + struct { + dict_T *dict; + hashitem_T *hi; // current entry + int todo; // remaining entries + } d; + } je_u; +} json_enc_frame_T; + /* * Encode "val" into "gap". + * Uses an explicit stack to avoid deep recursion on nested structures. * Return FAIL or OK. */ static int -json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int depth) +json_encode_item(garray_T *gap, typval_T *val, int copyID, int options) { - char_u numbuf[NUMBUFLEN]; - char_u *res; - blob_T *b; - list_T *l; - tuple_T *tuple; - dict_T *d; - int i; + char_u numbuf[NUMBUFLEN]; + char_u *res; + blob_T *b; + list_T *l; + tuple_T *tuple; + dict_T *d; + int i; + garray_T stack; + typval_T *cur_val; + json_enc_frame_T *frame; + + ga_init2(&stack, sizeof(json_enc_frame_T), 100); + cur_val = val; - if (depth > p_mfd) + for (;;) { - emsg(_(e_function_call_depth_is_higher_than_maxfuncdepth)); - return FAIL; - } - - switch (val->v_type) + switch (cur_val->v_type) { case VAR_BOOL: - switch ((long)val->vval.v_number) + switch ((long)cur_val->vval.v_number) { case VVAL_FALSE: GA_CONCAT_LITERAL(gap, "false"); break; case VVAL_TRUE: GA_CONCAT_LITERAL(gap, "true"); break; @@ -296,7 +326,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept break; case VAR_SPECIAL: - switch ((long)val->vval.v_number) + switch ((long)cur_val->vval.v_number) { case VVAL_NONE: if ((options & JSON_JS) != 0 && (options & JSON_NO_NONE) == 0) @@ -312,13 +342,13 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept size_t numbuflen; numbuflen = vim_snprintf_safelen((char *)numbuf, sizeof(numbuf), - "%lld", (varnumber_T)val->vval.v_number); + "%lld", (varnumber_T)cur_val->vval.v_number); ga_concat_len(gap, numbuf, numbuflen); } break; case VAR_STRING: - res = val->vval.v_string; + res = cur_val->vval.v_string; write_string(gap, res); break; @@ -330,11 +360,11 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept case VAR_CLASS: case VAR_OBJECT: case VAR_TYPEALIAS: - semsg(_(e_cannot_json_encode_str), vartype_name(val->v_type)); - return FAIL; + semsg(_(e_cannot_json_encode_str), vartype_name(cur_val->v_type)); + goto theend; case VAR_BLOB: - b = val->vval.v_blob; + b = cur_val->vval.v_blob; if (b == NULL || b->bv_ga.ga_len == 0) GA_CONCAT_LITERAL(gap, "[]"); else @@ -366,7 +396,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept break; case VAR_LIST: - l = val->vval.v_list; + l = cur_val->vval.v_list; if (l == NULL) GA_CONCAT_LITERAL(gap, "[]"); else @@ -375,26 +405,28 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept GA_CONCAT_LITERAL(gap, "[]"); else { - listitem_T *li; - l->lv_copyID = copyID; ga_append(gap, '['); CHECK_LIST_MATERIALIZE(l); - for (li = l->lv_first; li != NULL && !got_int; ) + if (l->lv_first != NULL) { - if (json_encode_item(gap, &li->li_tv, copyID, - options & JSON_JS, - depth + 1) == FAIL) - return FAIL; - if ((options & JSON_JS) - && li->li_next == NULL - && li->li_tv.v_type == VAR_SPECIAL - && li->li_tv.vval.v_number == VVAL_NONE) - // add an extra comma if the last item is v:none - ga_append(gap, ','); - li = li->li_next; - if (li != NULL) - ga_append(gap, ','); + if (stack.ga_len >= p_mfd) + { + emsg(_(e_function_call_depth_is_higher_than_maxfuncdepth)); + goto theend; + } + if (ga_grow(&stack, 1) == FAIL) + goto theend; + frame = ((json_enc_frame_T *)stack.ga_data) + + stack.ga_len; + frame->je_type = ENC_LIST; + frame->je_options = options; + frame->je_u.l.list = l; + frame->je_u.l.li = l->lv_first; + ++stack.ga_len; + options = options & JSON_JS; + cur_val = &l->lv_first->li_tv; + continue; } ga_append(gap, ']'); l->lv_copyID = 0; @@ -403,7 +435,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept break; case VAR_TUPLE: - tuple = val->vval.v_tuple; + tuple = cur_val->vval.v_tuple; if (tuple == NULL) GA_CONCAT_LITERAL(gap, "[]"); else @@ -414,33 +446,37 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept { int len = TUPLE_LEN(tuple); - tuple->tv_copyID = copyID; - ga_append(gap, '['); - for (i = 0; i < len && !got_int; i++) + if (len > 0) { - typval_T *t_item = TUPLE_ITEM(tuple, i); - if (json_encode_item(gap, t_item, copyID, - options & JSON_JS, - depth + 1) == FAIL) - return FAIL; - - if ((options & JSON_JS) - && i == len - 1 - && t_item->v_type == VAR_SPECIAL - && t_item->vval.v_number == VVAL_NONE) - // add an extra comma if the last item is v:none - ga_append(gap, ','); - if (i <= len - 2) - ga_append(gap, ','); + tuple->tv_copyID = copyID; + ga_append(gap, '['); + if (stack.ga_len >= p_mfd) + { + emsg(_(e_function_call_depth_is_higher_than_maxfuncdepth)); + goto theend; + } + if (ga_grow(&stack, 1) == FAIL) + goto theend; + frame = ((json_enc_frame_T *)stack.ga_data) + + stack.ga_len; + frame->je_type = ENC_TUPLE; + frame->je_options = options; + frame->je_u.t.tuple = tuple; + frame->je_u.t.idx = 0; + frame->je_u.t.len = len; + ++stack.ga_len; + options = options & JSON_JS; + cur_val = TUPLE_ITEM(tuple, 0); + continue; } - ga_append(gap, ']'); - tuple->tv_copyID = 0; + else + GA_CONCAT_LITERAL(gap, "[]"); } } break; case VAR_DICT: - d = val->vval.v_dict; + d = cur_val->vval.v_dict; if (d == NULL) GA_CONCAT_LITERAL(gap, "{}"); else @@ -449,46 +485,60 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept GA_CONCAT_LITERAL(gap, "{}"); else { - int first = TRUE; int todo = (int)d->dv_hashtab.ht_used; hashitem_T *hi; - d->dv_copyID = copyID; - ga_append(gap, '{'); + if (todo > 0) + { + d->dv_copyID = copyID; + ga_append(gap, '{'); - for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; - ++hi) - if (!HASHITEM_EMPTY(hi)) - { - --todo; - if (first) - first = FALSE; - else - ga_append(gap, ','); - if ((options & JSON_JS) + // Find first non-empty hash entry + for (hi = d->dv_hashtab.ht_array; + HASHITEM_EMPTY(hi); ++hi) + ; + --todo; + + // Write first key + if ((options & JSON_JS) && is_simple_key(hi->hi_key)) - ga_concat(gap, hi->hi_key); - else - write_string(gap, hi->hi_key); - ga_append(gap, ':'); - if (json_encode_item(gap, &dict_lookup(hi)->di_tv, - copyID, options | JSON_NO_NONE, - depth + 1) == FAIL) - return FAIL; + ga_concat(gap, hi->hi_key); + else + write_string(gap, hi->hi_key); + ga_append(gap, ':'); + + if (stack.ga_len >= p_mfd) + { + emsg(_(e_function_call_depth_is_higher_than_maxfuncdepth)); + goto theend; } - ga_append(gap, '}'); - d->dv_copyID = 0; + if (ga_grow(&stack, 1) == FAIL) + goto theend; + frame = ((json_enc_frame_T *)stack.ga_data) + + stack.ga_len; + frame->je_type = ENC_DICT; + frame->je_options = options; + frame->je_u.d.dict = d; + frame->je_u.d.hi = hi; + frame->je_u.d.todo = todo; + ++stack.ga_len; + options = options | JSON_NO_NONE; + cur_val = &dict_lookup(hi)->di_tv; + continue; + } + else + GA_CONCAT_LITERAL(gap, "{}"); } } break; case VAR_FLOAT: #if defined(HAVE_MATH_H) - if (isnan(val->vval.v_float)) + if (isnan(cur_val->vval.v_float)) GA_CONCAT_LITERAL(gap, "NaN"); - else if (isinf(val->vval.v_float)) + else if (isinf(cur_val->vval.v_float)) { - if (val->vval.v_float < 0.0) + if (cur_val->vval.v_float < 0.0) GA_CONCAT_LITERAL(gap, "-Infinity"); else GA_CONCAT_LITERAL(gap, "Infinity"); @@ -499,17 +549,151 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options, int dept size_t numbuflen; numbuflen = vim_snprintf_safelen((char *)numbuf, sizeof(numbuf), - "%g", val->vval.v_float); + "%g", cur_val->vval.v_float); ga_concat_len(gap, numbuf, numbuflen); } break; + case VAR_UNKNOWN: case VAR_ANY: case VAR_VOID: internal_error_no_abort("json_encode_item()"); - return FAIL; + goto theend; } - return OK; + + // Process the completed item by advancing containers or + // returning when the stack is empty. + for (;;) + { + int advance = FALSE; + + if (stack.ga_len == 0) + { + ga_clear(&stack); + return OK; + } + + frame = ((json_enc_frame_T *)stack.ga_data) + + stack.ga_len - 1; + switch (frame->je_type) + { + case ENC_LIST: + { + listitem_T *li = frame->je_u.l.li; + + // JSON_JS: add trailing comma if last item is v:none + if ((frame->je_options & JSON_JS) + && li->li_next == NULL + && li->li_tv.v_type == VAR_SPECIAL + && li->li_tv.vval.v_number == VVAL_NONE) + ga_append(gap, ','); + + li = li->li_next; + frame->je_u.l.li = li; + if (li != NULL && !got_int) + { + ga_append(gap, ','); + options = frame->je_options & JSON_JS; + cur_val = &li->li_tv; + advance = TRUE; + break; + } + ga_append(gap, ']'); + frame->je_u.l.list->lv_copyID = 0; + --stack.ga_len; + break; + } + + case ENC_TUPLE: + { + int idx = frame->je_u.t.idx; + int len = frame->je_u.t.len; + + // JSON_JS: add trailing comma if last item is v:none + if ((frame->je_options & JSON_JS) && idx == len - 1) + { + typval_T *t_item = + TUPLE_ITEM(frame->je_u.t.tuple, idx); + + if (t_item->v_type == VAR_SPECIAL + && t_item->vval.v_number == VVAL_NONE) + ga_append(gap, ','); + } + + ++idx; + frame->je_u.t.idx = idx; + if (idx < len && !got_int) + { + ga_append(gap, ','); + options = frame->je_options & JSON_JS; + cur_val = TUPLE_ITEM(frame->je_u.t.tuple, idx); + advance = TRUE; + break; + } + ga_append(gap, ']'); + frame->je_u.t.tuple->tv_copyID = 0; + --stack.ga_len; + break; + } + + case ENC_DICT: + { + hashitem_T *hi = frame->je_u.d.hi; + int todo = frame->je_u.d.todo; + + if (todo > 0 && !got_int) + { + // Advance to next non-empty entry + for (++hi; HASHITEM_EMPTY(hi); ++hi) + ; + --todo; + frame->je_u.d.hi = hi; + frame->je_u.d.todo = todo; + + ga_append(gap, ','); + if ((frame->je_options & JSON_JS) + && is_simple_key(hi->hi_key)) + ga_concat(gap, hi->hi_key); + else + write_string(gap, hi->hi_key); + ga_append(gap, ':'); + + options = frame->je_options | JSON_NO_NONE; + cur_val = &dict_lookup(hi)->di_tv; + advance = TRUE; + break; + } + ga_append(gap, '}'); + frame->je_u.d.dict->dv_copyID = 0; + --stack.ga_len; + break; + } + } + if (advance) + break; + } + } + +theend: + // Clean up copyIDs for remaining frames on error/failure + for (i = 0; i < stack.ga_len; i++) + { + frame = ((json_enc_frame_T *)stack.ga_data) + i; + switch (frame->je_type) + { + case ENC_LIST: + frame->je_u.l.list->lv_copyID = 0; + break; + case ENC_TUPLE: + frame->je_u.t.tuple->tv_copyID = 0; + break; + case ENC_DICT: + frame->je_u.d.dict->dv_copyID = 0; + break; + } + } + ga_clear(&stack); + return FAIL; } /* diff --git a/src/version.c b/src/version.c index 83eb6ebec0..0de4e4f14c 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 335, /**/ 334, /**/ From e29f33ef5115dbb66370ce18f46b3e01674e2180 Mon Sep 17 00:00:00 2001 From: Cimbali Date: Fri, 10 Apr 2026 22:02:09 +0000 Subject: [PATCH 022/545] patch 9.2.0336: libvterm: no terminal reflow support Problem: libvterm: no terminal reflow support Solution: Support for reflowing, sync libvterm to revision 843 (Cimbali) fixes: #2865 closes: #8365 closes: #19863 Co-authored-by: Paul "LeoNerd" Evans Signed-off-by: Cimbali Signed-off-by: Christian Brabandt --- Filelist | 1 + runtime/doc/version9.txt | 3 +- src/libvterm/README | 2 +- src/libvterm/include/vterm.h | 8 + src/libvterm/src/screen.c | 47 ++++- src/libvterm/src/state.c | 33 +++ src/libvterm/src/vterm_internal.h | 1 + src/libvterm/t/12state_scroll.test | 6 +- src/libvterm/t/13state_edit.test | 6 +- src/libvterm/t/62screen_damage.test | 2 +- src/libvterm/t/69screen_pushline.test | 17 ++ src/libvterm/t/harness.c | 25 ++- src/libvterm/t/run-test.pl | 2 +- src/terminal.c | 290 ++++++++++++++++++++++---- src/testdir/test_terminal.vim | 25 +++ src/version.c | 2 + 16 files changed, 411 insertions(+), 59 deletions(-) create mode 100644 src/libvterm/t/69screen_pushline.test diff --git a/Filelist b/Filelist index 6a21157647..1522e33740 100644 --- a/Filelist +++ b/Filelist @@ -440,6 +440,7 @@ SRC_ALL = \ src/libvterm/t/66screen_extent.test \ src/libvterm/t/67screen_dbl_wh.test \ src/libvterm/t/68screen_termprops.test \ + src/libvterm/t/69screen_pushline.test \ src/libvterm/t/69screen_reflow.test \ src/libvterm/t/90vttest_01-movement-1.test \ src/libvterm/t/90vttest_01-movement-2.test \ diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index e0976453d9..2b3decac78 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 09 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52615,6 +52615,7 @@ Other ~ bypassing the shell completely. - Allow mouse clickable regions in the |status-line| using the |stl-%[FuncName]| atom. +- Enable reflow support in the |:terminal|. Platform specific ~ ----------------- diff --git a/src/libvterm/README b/src/libvterm/README index 56c6e222be..875c448cbb 100644 --- a/src/libvterm/README +++ b/src/libvterm/README @@ -7,7 +7,7 @@ The original can be found: https://github.com/neovim/libvterm Modifications: -- revisions up to 839 have been included +- revisions up to 843 have been included - Added a .gitignore file. - use TRUE and FALSE instead of true and false - use int or unsigned int instead of bool diff --git a/src/libvterm/include/vterm.h b/src/libvterm/include/vterm.h index 48deebe25e..54e9e49242 100644 --- a/src/libvterm/include/vterm.h +++ b/src/libvterm/include/vterm.h @@ -452,6 +452,8 @@ typedef struct { int (*resize)(int rows, int cols, VTermStateFields *fields, void *user); int (*setlineinfo)(int row, const VTermLineInfo *newinfo, const VTermLineInfo *oldinfo, void *user); int (*sb_clear)(void *user); + // ABI-compat only enabled if vterm_state_callbacks_has_premove() is invoked + int (*premove)(VTermRect dest, void *user); } VTermStateCallbacks; // VIM: added @@ -488,6 +490,8 @@ VTermState *vterm_obtain_state(VTerm *vt); void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *callbacks, void *user); void *vterm_state_get_cbdata(VTermState *state); +void vterm_state_callbacks_has_premove(VTermState *state); + void vterm_state_set_unrecognised_fallbacks(VTermState *state, const VTermStateFallbacks *fallbacks, void *user); void *vterm_state_get_unrecognised_fbdata(VTermState *state); @@ -578,6 +582,8 @@ typedef struct { int (*sb_pushline)(int cols, const VTermScreenCell *cells, void *user); int (*sb_popline)(int cols, VTermScreenCell *cells, void *user); int (*sb_clear)(void* user); + /* ABI-compat this is only used if vterm_screen_callbacks_has_pushline4() is called */ + int (*sb_pushline4)(int cols, const VTermScreenCell *cells, int continuation, void *user); } VTermScreenCallbacks; // Return the screen of the vterm. @@ -590,6 +596,8 @@ VTermScreen *vterm_obtain_screen(VTerm *vt); void vterm_screen_set_callbacks(VTermScreen *screen, const VTermScreenCallbacks *callbacks, void *user); void *vterm_screen_get_cbdata(VTermScreen *screen); +void vterm_screen_callbacks_has_pushline4(VTermScreen *screen); + void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user); void *vterm_screen_get_unrecognised_fbdata(VTermScreen *screen); diff --git a/src/libvterm/src/screen.c b/src/libvterm/src/screen.c index fd76777c41..5ed8dd48ae 100644 --- a/src/libvterm/src/screen.c +++ b/src/libvterm/src/screen.c @@ -49,6 +49,7 @@ struct VTermScreen const VTermScreenCallbacks *callbacks; void *cbdata; + int callbacks_has_pushline4; VTermDamageSize damage_merge; /* start_row == -1 => no damage */ @@ -209,28 +210,41 @@ static int putglyph(VTermGlyphInfo *info, VTermPos pos, void *user) return 1; } -static void sb_pushline_from_row(VTermScreen *screen, int row) +static void sb_pushline_from_row(VTermScreen *screen, int row, int continuation) { VTermPos pos; pos.row = row; for(pos.col = 0; pos.col < screen->cols; pos.col++) vterm_screen_get_cell(screen, pos, screen->sb_buffer + pos.col); - (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata); + if(screen->callbacks_has_pushline4 && screen->callbacks->sb_pushline4) + (screen->callbacks->sb_pushline4)(screen->cols, screen->sb_buffer, continuation, screen->cbdata); + else + (screen->callbacks->sb_pushline)(screen->cols, screen->sb_buffer, screen->cbdata); } -static int moverect_internal(VTermRect dest, VTermRect src, void *user) +static int premove(VTermRect rect, void *user) { VTermScreen *screen = user; - if(screen->callbacks && screen->callbacks->sb_pushline && - dest.start_row == 0 && dest.start_col == 0 && // starts top-left corner - dest.end_col == screen->cols && // full width + if(((screen->callbacks && screen->callbacks->sb_pushline) || + (screen->callbacks_has_pushline4 && screen->callbacks && screen->callbacks->sb_pushline4)) && + rect.start_row == 0 && rect.start_col == 0 && // starts top-left corner + rect.end_col == screen->cols && // full width screen->buffer == screen->buffers[BUFIDX_PRIMARY]) { // not altscreen - for(int row = 0; row < src.start_row; row++) - sb_pushline_from_row(screen, row); + for(int row = 0; row < rect.end_row; row++) { + const VTermLineInfo *lineinfo = vterm_state_get_lineinfo(screen->state, row); + sb_pushline_from_row(screen, row, lineinfo->continuation); + } } + return 1; +} + +static int moverect_internal(VTermRect dest, VTermRect src, void *user) +{ + VTermScreen *screen = user; + int cols = src.end_col - src.start_col; int downward = src.start_row - dest.start_row; @@ -685,9 +699,12 @@ static void resize_buffer(VTermScreen *screen, int bufidx, int new_rows, int new if(old_row >= 0 && bufidx == BUFIDX_PRIMARY) { /* Push spare lines to scrollback buffer */ - if(screen->callbacks && screen->callbacks->sb_pushline) - for(int row = 0; row <= old_row; row++) - sb_pushline_from_row(screen, row); + if((screen->callbacks && screen->callbacks->sb_pushline) || + (screen->callbacks_has_pushline4 && screen->callbacks && screen->callbacks->sb_pushline4)) + for(int row = 0; row <= old_row; row++) { + const VTermLineInfo *lineinfo = old_lineinfo + row; + sb_pushline_from_row(screen, row, lineinfo->continuation); + } if(active) statefields->pos.row -= (old_row + 1); } @@ -880,6 +897,7 @@ static VTermStateCallbacks state_cbs = { &resize, // resize &setlineinfo, // setlineinfo &sb_clear, //sb_clear + &premove, // premove }; /* @@ -914,6 +932,7 @@ static VTermScreen *screen_new(VTerm *vt) screen->callbacks = NULL; screen->cbdata = NULL; + screen->callbacks_has_pushline4 = 0; screen->buffers[BUFIDX_PRIMARY] = alloc_buffer(screen, rows, cols); @@ -927,6 +946,7 @@ static VTermScreen *screen_new(VTerm *vt) } vterm_state_set_callbacks(screen->state, &state_cbs, screen); + vterm_state_callbacks_has_premove(screen->state); return screen; } @@ -1127,6 +1147,11 @@ void *vterm_screen_get_cbdata(VTermScreen *screen) return screen->cbdata; } +void vterm_screen_callbacks_has_pushline4(VTermScreen *screen) +{ + screen->callbacks_has_pushline4 = 1; +} + void vterm_screen_set_unrecognised_fallbacks(VTermScreen *screen, const VTermStateFallbacks *fallbacks, void *user) { vterm_state_set_unrecognised_fallbacks(screen->state, fallbacks, user); diff --git a/src/libvterm/src/state.c b/src/libvterm/src/state.c index fca833326e..34b37445c2 100644 --- a/src/libvterm/src/state.c +++ b/src/libvterm/src/state.c @@ -78,6 +78,7 @@ static VTermState *vterm_state_new(VTerm *vt) state->callbacks = NULL; state->cbdata = NULL; + state->callbacks_has_premove = 0; state->selection.callbacks = NULL; state->selection.user = NULL; @@ -133,6 +134,33 @@ static void scroll(VTermState *state, VTermRect rect, int downward, int rightwar else if(rightward < -cols) rightward = -cols; + if(state->callbacks_has_premove && state->callbacks && state->callbacks->premove) { + // TODO: technically this logic is wrong if both downward != 0 and rightward != 0 + + /* Work out what subsection of the destination area is about to be destroyed */ + if(downward > 0) + /* about to destroy the top */ + (*state->callbacks->premove)((VTermRect){ + .start_row = rect.start_row, .end_row = rect.start_row + downward, + .start_col = rect.start_col, .end_col = rect.end_col}, state->cbdata); + else if(downward < 0) + /* about to destroy the bottom */ + (*state->callbacks->premove)((VTermRect){ + .start_row = rect.end_row + downward, .end_row = rect.end_row, + .start_col = rect.start_col, .end_col = rect.end_col}, state->cbdata); + + if(rightward > 0) + /* about to destroy the left */ + (*state->callbacks->premove)((VTermRect){ + .start_row = rect.start_row, .end_row = rect.end_row, + .start_col = rect.start_col, .end_col = rect.start_col + rightward}, state->cbdata); + else if(rightward < 0) + /* about to destroy the right */ + (*state->callbacks->premove)((VTermRect){ + .start_row = rect.start_row, .end_row = rect.end_row, + .start_col = rect.end_col + rightward, .end_col = rect.end_col}, state->cbdata); + } + // Update lineinfo if full line if(rect.start_col == 0 && rect.end_col == state->cols && rightward == 0) { int height = rect.end_row - rect.start_row - abs(downward); @@ -2319,6 +2347,11 @@ void vterm_state_set_callbacks(VTermState *state, const VTermStateCallbacks *cal } } +void vterm_state_callbacks_has_premove(VTermState *state) +{ + state->callbacks_has_premove = 1; +} + void *vterm_state_get_cbdata(VTermState *state) { return state->cbdata; diff --git a/src/libvterm/src/vterm_internal.h b/src/libvterm/src/vterm_internal.h index 0f80fa4315..b9830b9db3 100644 --- a/src/libvterm/src/vterm_internal.h +++ b/src/libvterm/src/vterm_internal.h @@ -70,6 +70,7 @@ struct VTermState const VTermStateCallbacks *callbacks; void *cbdata; + int callbacks_has_premove; const VTermStateFallbacks *fallbacks; void *fbdata; diff --git a/src/libvterm/t/12state_scroll.test b/src/libvterm/t/12state_scroll.test index c1f2791d00..5605131bc7 100644 --- a/src/libvterm/t/12state_scroll.test +++ b/src/libvterm/t/12state_scroll.test @@ -127,24 +127,28 @@ PUSH "\e[100;105r\eD" PUSH "\e[5;2r\eD" RESET -WANTSTATE -s+me +WANTSTATE -s+Pme !Scroll Down move+erase emulation PUSH "\e[S" + premove 0..1,0..80 moverect 1..25,0..80 -> 0..24,0..80 erase 24..25,0..80 ?cursor = 0,0 PUSH "\e[2S" + premove 0..2,0..80 moverect 2..25,0..80 -> 0..23,0..80 erase 23..25,0..80 ?cursor = 0,0 !Scroll Up move+erase emulation PUSH "\e[T" + premove 24..25,0..80 moverect 0..24,0..80 -> 1..25,0..80 erase 0..1,0..80 ?cursor = 0,0 PUSH "\e[2T" + premove 23..25,0..80 moverect 0..23,0..80 -> 2..25,0..80 erase 0..2,0..80 ?cursor = 0,0 diff --git a/src/libvterm/t/13state_edit.test b/src/libvterm/t/13state_edit.test index d3f3e9e40b..84d22cc9ac 100644 --- a/src/libvterm/t/13state_edit.test +++ b/src/libvterm/t/13state_edit.test @@ -268,7 +268,7 @@ PUSH "\e[2\"q" PUSH "\eP\$q\"q\e\\" output "\eP1\$r2\"q\e\\" -WANTSTATE -s+m +WANTSTATE -s+Pm !ICH move+erase emuation RESET @@ -278,12 +278,14 @@ PUSH "ACD" PUSH "\e[2D" ?cursor = 0,1 PUSH "\e[@" + premove 0..1,79..80 moverect 0..1,1..79 -> 0..1,2..80 erase 0..1,1..2 ?cursor = 0,1 PUSH "B" ?cursor = 0,2 PUSH "\e[3@" + premove 0..1,77..80 moverect 0..1,2..77 -> 0..1,5..80 erase 0..1,2..5 @@ -295,10 +297,12 @@ PUSH "ABBC" PUSH "\e[3D" ?cursor = 0,1 PUSH "\e[P" + premove 0..1,1..2 moverect 0..1,2..80 -> 0..1,1..79 erase 0..1,79..80 ?cursor = 0,1 PUSH "\e[3P" + premove 0..1,1..4 moverect 0..1,4..80 -> 0..1,1..77 erase 0..1,77..80 ?cursor = 0,1 diff --git a/src/libvterm/t/62screen_damage.test b/src/libvterm/t/62screen_damage.test index 3b1b238527..2f11c759dd 100644 --- a/src/libvterm/t/62screen_damage.test +++ b/src/libvterm/t/62screen_damage.test @@ -146,9 +146,9 @@ PUSH "\e[25H\r\nABCDE\b\b\b\e[2P\r\n" sb_pushline 80 = moverect 1..25,0..80 -> 0..24,0..80 damage 24..25,0..80 = 24<41 42 43 44 45> + sb_pushline 80 = moverect 24..25,4..80 -> 24..25,2..78 damage 24..25,78..80 - sb_pushline 80 = DAMAGEFLUSH moverect 1..25,0..80 -> 0..24,0..80 damage 24..25,0..80 diff --git a/src/libvterm/t/69screen_pushline.test b/src/libvterm/t/69screen_pushline.test new file mode 100644 index 0000000000..735ea29479 --- /dev/null +++ b/src/libvterm/t/69screen_pushline.test @@ -0,0 +1,17 @@ +INIT +WANTSTATE +WANTSCREEN b + +RESET + +!Spillover text marks continuation on second line +PUSH "A"x85 +PUSH "\r\n" + ?lineinfo 0 = + ?lineinfo 1 = cont + +!Continuation mark sent to sb_pushline +PUSH "\n"x23 + sb_pushline 80 = 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 +PUSH "\n" + sb_pushline 80 cont = 41 41 41 41 41 diff --git a/src/libvterm/t/harness.c b/src/libvterm/t/harness.c index 5285d94ec0..6fc34fe043 100644 --- a/src/libvterm/t/harness.c +++ b/src/libvterm/t/harness.c @@ -326,6 +326,18 @@ static int movecursor(VTermPos pos, VTermPos oldpos UNUSED, int visible UNUSED, return 1; } +static int want_premove = 0; +static int premove(VTermRect rect, void *user UNUSED) +{ + if(!want_premove) + return 0; + + printf("premove %d..%d,%d..%d\n", + rect.start_row, rect.end_row, rect.start_col, rect.end_col); + + return 1; +} + static int want_scrollrect = 0; static int scrollrect(VTermRect rect, int downward, int rightward, void *user UNUSED) { @@ -509,6 +521,7 @@ VTermStateCallbacks state_cbs = { NULL, // resize state_setlineinfo, // setlineinfo state_sb_clear, // sb_clear + premove, // premove }; static int selection_set(VTermSelectionMask mask, VTermStringFragment frag, void *user UNUSED) @@ -590,7 +603,7 @@ static int screen_damage(VTermRect rect, void *user UNUSED) } static int want_screen_scrollback = 0; -static int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user UNUSED) +static int screen_sb_pushline4(int cols, const VTermScreenCell *cells, int continuation, void *user UNUSED) { int eol; int c; @@ -602,7 +615,7 @@ static int screen_sb_pushline(int cols, const VTermScreenCell *cells, void *user while(eol && !cells[eol-1].chars[0]) eol--; - printf("sb_pushline %d =", cols); + printf("sb_pushline %d%s =", cols, continuation ? " cont" : ""); for(c = 0; c < eol; c++) printf(" %02X", cells[c].chars[0]); printf("\n"); @@ -647,9 +660,10 @@ VTermScreenCallbacks screen_cbs = { settermprop, // settermprop NULL, // bell NULL, // resize - screen_sb_pushline, // sb_pushline + NULL, // sb_pushline screen_sb_popline, // sb_popline screen_sb_clear, // sb_clear + screen_sb_pushline4, // sb_pushline4 }; int main(int argc UNUSED, char **argv UNUSED) @@ -689,6 +703,7 @@ int main(int argc UNUSED, char **argv UNUSED) if(!state) { state = vterm_obtain_state(vt); vterm_state_set_callbacks(state, &state_cbs, NULL); + vterm_state_callbacks_has_premove(state); /* In some tests we want to check the behaviour of overflowing the * buffer, so make it nicely small */ @@ -713,6 +728,9 @@ int main(int argc UNUSED, char **argv UNUSED) case 's': want_scrollrect = sense; break; + case 'P': + want_premove = sense; + break; case 'm': want_moverect = sense; break; @@ -740,6 +758,7 @@ int main(int argc UNUSED, char **argv UNUSED) if(!screen) screen = vterm_obtain_screen(vt); vterm_screen_set_callbacks(screen, &screen_cbs, NULL); + vterm_screen_callbacks_has_pushline4(screen); while(line[i] == ' ') i++; diff --git a/src/libvterm/t/run-test.pl b/src/libvterm/t/run-test.pl index 3440465cda..819b5b4d4e 100644 --- a/src/libvterm/t/run-test.pl +++ b/src/libvterm/t/run-test.pl @@ -124,7 +124,7 @@ sub do_line elsif( $line =~ m/^putglyph (\S+) (.*)$/ ) { $line = "putglyph " . join( ",", map sprintf("%x", $_), eval($1) ) . " $2"; } - elsif( $line =~ m/^(?:movecursor|scrollrect|moverect|erase|damage|sb_pushline|sb_popline|sb_clear|settermprop|setmousefunc|selection-query) ?/ ) { + elsif( $line =~ m/^(?:movecursor|scrollrect|premove|moverect|erase|damage|sb_pushline|sb_popline|sb_clear|settermprop|setmousefunc|selection-query) ?/ ) { # no conversion } elsif( $line =~ m/^(selection-set) (.*?) (\[?)(.*?)(\]?)$/ ) { diff --git a/src/terminal.c b/src/terminal.c index 0310ae9aa2..6a9c286e29 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -61,9 +61,11 @@ typedef struct { typedef struct sb_line_S { int sb_cols; // can differ per line + int sb_bytes; // length in bytes of text cellattr_T *sb_cells; // allocated cellattr_T sb_fill_attr; // for short line char_u *sb_text; // for tl_scrollback_postponed + char_u continuation; } sb_line_T; #ifdef MSWIN @@ -148,6 +150,8 @@ struct terminal_S { garray_T tl_scrollback; int tl_scrollback_scrolled; garray_T tl_scrollback_postponed; + int tl_scrollback_snapshot; + int tl_buffer_scrolled; char_u *tl_highlight_name; // replaces "Terminal"; allocated @@ -1386,6 +1390,112 @@ update_cursor(term_T *term, int redraw) } } +/* + * Find the location of a scrollbackline in the buffer + */ + static void +scrollbackline_pos_in_buf(term_T *term, int row, linenr_T *lnum, int *start_col, size_t *start_pos) +{ + sb_line_T *lines = (sb_line_T *)term->tl_scrollback.ga_data; + linenr_T calc_lnum = term->tl_buffer_scrolled; + size_t calc_pos = 0; + int calc_col = 0; + int i; + + if (row < 0 || row >= term->tl_scrollback.ga_len) + return; + + if (row > term->tl_scrollback_scrolled) + { + // Lookback how far along in the top line we are + for (i = term->tl_scrollback_scrolled + 1; i > 0 && lines[i].continuation; --i) + { + calc_pos += lines[i - 1].sb_bytes; + calc_col += lines[i - 1].sb_cols; + } + i = term->tl_scrollback_scrolled + 1; + calc_lnum = term->tl_buffer_scrolled + 1; + // Do not count this line's bytes/cols twice + if (lines[i].continuation) + ++i; + } + else + { + i = 1; + calc_lnum = 1; + } + + for (; i <= row; ++i) + { + if (!lines[i].continuation) + { + ++calc_lnum; + calc_pos = 0; + calc_col = 0; + } + else + { + calc_pos += lines[i - 1].sb_bytes; + calc_col += lines[i - 1].sb_cols; + } + } + + *lnum = calc_lnum; + if (start_col) + *start_col = calc_col; + if (start_pos) + *start_pos = calc_pos; +} + +/* + * Find the location of a buffer line in the scrollback + */ + static void +bufline_pos_in_scrollback(term_T *term, linenr_T lnum, int col, int *row, int *wrapped_col) +{ + buf_T *buf = term->tl_buffer; + sb_line_T *lines = (sb_line_T *)term->tl_scrollback.ga_data; + linenr_T calc_row = term->tl_scrollback_scrolled; + int calc_col = col; + linenr_T l; + + if (lnum > buf->b_ml.ml_line_count) + return; + + if (lnum > term->tl_buffer_scrolled) + { + calc_row = term->tl_scrollback_scrolled; + l = term->tl_buffer_scrolled + 1; + + while (calc_row < term->tl_scrollback.ga_len && lines[calc_row].continuation) + ++calc_row; + } + else + { + calc_row = 0; + l = 1; + } + + while (calc_row < term->tl_scrollback.ga_len && l < lnum) + { + ++calc_row; + if (!lines[calc_row].continuation) + ++l; + } + + while (calc_row + 1 < term->tl_scrollback.ga_len && lines[calc_row + 1].continuation + && calc_col >= lines[calc_row].sb_cols) + { + calc_col -= lines[calc_row].sb_cols; + ++calc_row; + } + + if (row) + *row = calc_row; + if (wrapped_col) + *wrapped_col = calc_col; +} + /* * Invoked when "msg" output from a job was received. Write it to the terminal * of "buffer". @@ -1908,13 +2018,14 @@ term_try_stop_job(buf_T *buf) * Add the last line of the scrollback buffer to the buffer in the window. */ static void -add_scrollback_line_to_buffer(term_T *term, char_u *text, int len) +add_scrollback_line_to_buffer(term_T *term, char_u *text, int len, int append) { buf_T *buf = term->tl_buffer; int empty = (buf->b_ml.ml_flags & ML_EMPTY); linenr_T lnum = buf->b_ml.ml_line_count; #ifdef MSWIN + char_u *tmp = text; if (!enc_utf8 && enc_codepage > 0) { WCHAR *ret = NULL; @@ -1927,13 +2038,32 @@ add_scrollback_line_to_buffer(term_T *term, char_u *text, int len) WideCharToMultiByte_alloc(enc_codepage, 0, ret, length, (char **)&text, &len, 0, 0); vim_free(ret); - ml_append_buf(term->tl_buffer, lnum, text, len, FALSE); - vim_free(text); } } +#endif + + if (append) + { + char_u *prev_text = ml_get_buf(buf, lnum, FALSE); + size_t prev_len = STRLEN(prev_text); + + char_u *both = alloc(len + prev_len + 2); + if (both == NULL) + return; + vim_strncpy(both, prev_text, prev_len + 1); + vim_strncpy(both + prev_len, text, len + 1); + + curbuf = buf; + ml_replace(lnum, both, FALSE); + curbuf = curwin->w_buffer; + } else + ml_append_buf(buf, lnum, text, len + 1, FALSE); + +#ifdef MSWIN + if (tmp != text) + vim_free(text); #endif - ml_append_buf(term->tl_buffer, lnum, text, len + 1, FALSE); if (empty) { // Delete the empty line that was in the empty buffer. @@ -1989,6 +2119,7 @@ add_empty_scrollback(term_T *term, cellattr_T *fill_attr, int lnum) } } line->sb_cols = 0; + line->sb_bytes = 0; line->sb_cells = NULL; line->sb_fill_attr = *fill_attr; ++term->tl_scrollback.ga_len; @@ -2005,16 +2136,33 @@ cleanup_scrollback(term_T *term) { sb_line_T *line; garray_T *gap; + char_u *bufline; + size_t bufline_length; curbuf = term->tl_buffer; gap = &term->tl_scrollback; - while (curbuf->b_ml.ml_line_count > term->tl_scrollback_scrolled - && gap->ga_len > 0) + bufline = ml_get_buf(curbuf, curbuf->b_ml.ml_line_count, FALSE); + bufline_length = STRLEN(bufline); + while (term->tl_scrollback_snapshot && gap->ga_len > 0) { - ml_delete(curbuf->b_ml.ml_line_count); line = (sb_line_T *)gap->ga_data + gap->ga_len - 1; + if (line->sb_bytes < 0 || (size_t)line->sb_bytes > bufline_length) + break; + bufline_length -= line->sb_bytes; + if (!bufline_length) + { + ml_delete(curbuf->b_ml.ml_line_count); + bufline = ml_get_buf(curbuf, curbuf->b_ml.ml_line_count, FALSE); + bufline_length = STRLEN(bufline); + } vim_free(line->sb_cells); --gap->ga_len; + --term->tl_scrollback_snapshot; + } + if (bufline_length < STRLEN(bufline)) + { + char_u *shortened = vim_strnsave(bufline, bufline_length); + ml_replace(curbuf->b_ml.ml_line_count, shortened, FALSE); } curbuf = curwin->w_buffer; if (curbuf == term->tl_buffer) @@ -2028,6 +2176,7 @@ cleanup_scrollback(term_T *term) update_snapshot(term_T *term) { VTermScreen *screen; + VTermState *state; int len; int lines_skipped = 0; VTermPos pos; @@ -2043,6 +2192,7 @@ update_snapshot(term_T *term) cleanup_scrollback(term); screen = vterm_obtain_screen(term->tl_vterm); + state = vterm_obtain_state(term->tl_vterm); fill_attr = new_fill_attr = term->tl_default_color; for (pos.row = 0; pos.row < term->tl_rows; ++pos.row) { @@ -2067,7 +2217,10 @@ update_snapshot(term_T *term) // Line was skipped, add an empty line. --lines_skipped; if (add_empty_scrollback(term, &fill_attr, 0) == OK) - add_scrollback_line_to_buffer(term, (char_u *)"", 0); + { + add_scrollback_line_to_buffer(term, (char_u *)"", 0, 0); + ++term->tl_scrollback_snapshot; + } } if (len == 0) @@ -2079,9 +2232,12 @@ update_snapshot(term_T *term) { garray_T ga; int width; + const VTermLineInfo *lineinfo; sb_line_T *line = (sb_line_T *)term->tl_scrollback.ga_data + term->tl_scrollback.ga_len; + lineinfo = vterm_state_get_lineinfo(state, pos.row); + ga_init2(&ga, 1, 100); for (pos.col = 0; pos.col < len; pos.col += width) { @@ -2116,17 +2272,21 @@ update_snapshot(term_T *term) } } line->sb_cols = len; + line->sb_bytes = ga.ga_len; line->sb_cells = p; line->sb_fill_attr = new_fill_attr; + line->continuation = (char_u)lineinfo->continuation; fill_attr = new_fill_attr; ++term->tl_scrollback.ga_len; + ++term->tl_scrollback_snapshot; if (ga_grow(&ga, 1) == FAIL) - add_scrollback_line_to_buffer(term, (char_u *)"", 0); + add_scrollback_line_to_buffer(term, (char_u *)"", 0, 0); else { *((char_u *)ga.ga_data + ga.ga_len) = NUL; - add_scrollback_line_to_buffer(term, ga.ga_data, ga.ga_len); + add_scrollback_line_to_buffer(term, ga.ga_data, ga.ga_len, + lineinfo->continuation); } ga_clear(&ga); } @@ -2141,7 +2301,10 @@ update_snapshot(term_T *term) ++pos.row) { if (add_empty_scrollback(term, &fill_attr, 0) == OK) - add_scrollback_line_to_buffer(term, (char_u *)"", 0); + { + add_scrollback_line_to_buffer(term, (char_u *)"", 0, 0); + ++term->tl_scrollback_snapshot; + } } term->tl_dirty_snapshot = FALSE; @@ -2186,7 +2349,7 @@ may_move_terminal_to_buffer(term_T *term, int redraw) // Update the snapshot only if something changes or the buffer does not // have all the lines. if (term->tl_dirty_snapshot || term->tl_buffer->b_ml.ml_line_count - <= term->tl_scrollback_scrolled) + <= term->tl_buffer_scrolled) update_snapshot(term); if (redraw) @@ -2282,7 +2445,9 @@ cleanup_vterm(term_T *term) static void term_enter_normal_mode(void) { - term_T *term = curbuf->b_term; + term_T *term = curbuf->b_term; + linenr_T lnum; + int col; set_terminal_mode(term, TRUE); @@ -2291,15 +2456,16 @@ term_enter_normal_mode(void) // Move the window cursor to the position of the cursor in the // terminal. - curwin->w_cursor.lnum = term->tl_scrollback_scrolled - + term->tl_cursor_pos.row + 1; + lnum = term->tl_buffer_scrolled + 1 + term->tl_cursor_pos.row; + col = term->tl_cursor_pos.col; + scrollbackline_pos_in_buf(term, term->tl_cursor_pos.row + term->tl_scrollback_scrolled, &lnum, &col, NULL); + + curwin->w_cursor.lnum = lnum; check_cursor(); - if (coladvance(term->tl_cursor_pos.col) == FAIL) + if (coladvance(col) == FAIL) coladvance(MAXCOL); curwin->w_set_curswant = TRUE; - - // Display the same lines as in the terminal. - curwin->w_topline = term->tl_scrollback_scrolled + 1; + curwin->w_topline = term->tl_buffer_scrolled + 1; } /* @@ -3483,20 +3649,27 @@ limit_scrollback(term_T *term, garray_T *gap, int update_buffer) int todo = MAX(term->tl_buffer->b_p_twsl / 10, gap->ga_len - term->tl_buffer->b_p_twsl); int i; + sb_line_T *sb_lines = (sb_line_T *)gap->ga_data; curbuf = term->tl_buffer; for (i = 0; i < todo; ++i) { - vim_free(((sb_line_T *)gap->ga_data + i)->sb_cells); - if (update_buffer) + if (update_buffer && (!sb_lines[i].continuation || !i)) + { ml_delete(1); + --term->tl_buffer_scrolled; + } + vim_free(sb_lines[i].sb_cells); } + // Continue until end of wrapped line + for (; todo < gap->ga_len && sb_lines[todo].continuation; ++todo) + vim_free(sb_lines[todo].sb_cells); curbuf = curwin->w_buffer; gap->ga_len -= todo; mch_memmove(gap->ga_data, - (sb_line_T *)gap->ga_data + todo, - sizeof(sb_line_T) * gap->ga_len); + (sb_line_T *)gap->ga_data + todo, + sizeof(sb_line_T) * gap->ga_len); if (update_buffer) { win_T *curwin_save = curwin; @@ -3521,7 +3694,7 @@ limit_scrollback(term_T *term, garray_T *gap, int update_buffer) * Handle a line that is pushed off the top of the screen. */ static int -handle_pushline(int cols, const VTermScreenCell *cells, void *user) +handle_pushline(int cols, const VTermScreenCell *cells, int continuation, void *user) { term_T *term = (term_T *)user; garray_T *gap; @@ -3600,16 +3773,20 @@ handle_pushline(int cols, const VTermScreenCell *cells, void *user) *(text + text_len) = NUL; } if (update_buffer) - add_scrollback_line_to_buffer(term, text, text_len); + add_scrollback_line_to_buffer(term, text, text_len, continuation); line = (sb_line_T *)gap->ga_data + gap->ga_len; line->sb_cols = len; + line->sb_bytes = text_len; line->sb_cells = p; line->sb_fill_attr = fill_attr; + line->continuation = (char_u)continuation; if (update_buffer) { line->sb_text = NULL; ++term->tl_scrollback_scrolled; + if (!continuation) + ++term->tl_buffer_scrolled; ga_clear(&ga); // free the text } else @@ -3651,17 +3828,20 @@ handle_postponed_scrollback(term_T *term) text = pp_line->sb_text; if (text == NULL) text = (char_u *)""; - add_scrollback_line_to_buffer(term, text, (int)STRLEN(text)); + add_scrollback_line_to_buffer(term, text, (int)STRLEN(text), pp_line->continuation); vim_free(pp_line->sb_text); line = (sb_line_T *)term->tl_scrollback.ga_data + term->tl_scrollback.ga_len; line->sb_cols = pp_line->sb_cols; + line->sb_bytes = pp_line->sb_bytes; line->sb_cells = pp_line->sb_cells; line->sb_fill_attr = pp_line->sb_fill_attr; line->sb_text = NULL; ++term->tl_scrollback_scrolled; ++term->tl_scrollback.ga_len; + if (!pp_line->continuation) + ++term->tl_buffer_scrolled; } ga_clear(&term->tl_scrollback_postponed); @@ -3685,9 +3865,10 @@ static VTermScreenCallbacks screen_callbacks = { handle_settermprop, // settermprop handle_bell, // bell handle_resize, // resize - handle_pushline, // sb_pushline + NULL, // sb_pushline NULL, // sb_popline - NULL // sb_clear + NULL, // sb_clear + handle_pushline // sb_pushline4 }; /* @@ -4243,16 +4424,21 @@ term_get_attr(win_T *wp, linenr_T lnum, int col) term_T *term = buf->b_term; sb_line_T *line; cellattr_T *cellattr; + int sb_line = -1; + int sb_col = col; - if (lnum > term->tl_scrollback.ga_len) + if (term->tl_scrollback.ga_len) + bufline_pos_in_scrollback(term, lnum, col, &sb_line, &sb_col); + + if (sb_line < 0) cellattr = &term->tl_default_color; else { - line = (sb_line_T *)term->tl_scrollback.ga_data + lnum - 1; - if (col < 0 || col >= line->sb_cols) + line = (sb_line_T *)term->tl_scrollback.ga_data + sb_line; + if (sb_col < 0 || sb_col >= line->sb_cols) cellattr = &line->sb_fill_attr; else - cellattr = line->sb_cells + col; + cellattr = line->sb_cells + sb_col; } return cell2attr(term, wp, &cellattr->attrs, &cellattr->fg, &cellattr->bg); } @@ -4964,6 +5150,7 @@ create_vterm(term_T *term, int rows, int cols) vterm_screen_set_callbacks(screen, &screen_callbacks, term); vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCROLL); + vterm_screen_callbacks_has_pushline4(screen); // TODO: depends on 'encoding'. vterm_set_utf8(vterm, 1); @@ -5478,8 +5665,10 @@ read_dump_file(FILE *fd, VTermPos *cursor_pos) if (max_cells < ga_cell.ga_len) max_cells = ga_cell.ga_len; line->sb_cols = ga_cell.ga_len; + line->sb_bytes = ga_text.ga_len; line->sb_cells = ga_cell.ga_data; line->sb_fill_attr = term->tl_default_color; + line->continuation = 0; ++term->tl_scrollback.ga_len; ga_init(&ga_cell); @@ -6300,11 +6489,24 @@ f_term_getline(typval_T *argvars, typval_T *rettv) if (term->tl_vterm == NULL) { - linenr_T lnum = row + term->tl_scrollback_scrolled + 1; + linenr_T lnum = 0; + size_t offset = 0; + int sb_row = term->tl_scrollback_scrolled + row; + sb_line_T *line; + + if (sb_row < 0 || sb_row >= term->tl_scrollback.ga_len) + return; + line = (sb_line_T *)term->tl_scrollback.ga_data + sb_row; + + scrollbackline_pos_in_buf(term, sb_row, &lnum, NULL, &offset); // vterm is finished, get the text from the buffer if (lnum > 0 && lnum <= buf->b_ml.ml_line_count) - rettv->vval.v_string = vim_strsave(ml_get_buf(buf, lnum, FALSE)); + { + char_u *p = ml_get_buf(buf, lnum, FALSE); + if (STRLEN(p) >= offset + line->sb_bytes) + rettv->vval.v_string = vim_strnsave(p + offset, line->sb_bytes); + } } else { @@ -6343,7 +6545,7 @@ f_term_getscrolled(typval_T *argvars, typval_T *rettv) buf = term_get_buf(argvars, "term_getscrolled()"); if (buf == NULL) return; - rettv->vval.v_number = buf->b_term->tl_scrollback_scrolled; + rettv->vval.v_number = buf->b_term->tl_buffer_scrolled; } /* @@ -6557,12 +6759,22 @@ f_term_scrape(typval_T *argvars, typval_T *rettv) } else { - linenr_T lnum = pos.row + term->tl_scrollback_scrolled; + int sb_row = term->tl_scrollback_scrolled + pos.row; + linenr_T lnum = 0; + size_t offset = 0; - if (lnum < 0 || lnum >= term->tl_scrollback.ga_len) + scrollbackline_pos_in_buf(term, sb_row, &lnum, NULL, &offset); + + if (sb_row >= term->tl_scrollback.ga_len || lnum <= 0 || lnum > buf->b_ml.ml_line_count) return; - p = ml_get_buf(buf, lnum + 1, FALSE); - line = (sb_line_T *)term->tl_scrollback.ga_data + lnum; + + line = (sb_line_T *)term->tl_scrollback.ga_data + sb_row; + p = ml_get_buf(buf, lnum, FALSE); + + if (STRLEN(p) < offset + line->sb_bytes) + return; + + p += offset; } for (pos.col = 0; pos.col < term->tl_cols; ) diff --git a/src/testdir/test_terminal.vim b/src/testdir/test_terminal.vim index 6f8fb36626..c6521d3efe 100644 --- a/src/testdir/test_terminal.vim +++ b/src/testdir/test_terminal.vim @@ -2451,4 +2451,29 @@ func Test_terminal_disable_kitty_keyboard() bwipe! endfunc +func Test_terminal_unwraps() + CheckNotMSWindows + + 30vnew + + redraw + let buf = term_start("echo 1+2+3+4+5+6+7+8+9+10+11+12+13+14+15") + " Wait until both wrapped lines have appeared in the terminal + call WaitForAssert({-> assert_equal('14+15', term_getline(buf, 2))}) + + " A long wrapped line appears as 2 lines in libvterm + let l = term_getline(buf, 1) + call assert_equal('1+2+3+4+5+6+7+8+9+10+11+12+13+', l) + + let l = term_getline(buf, 2) + call assert_equal('14+15', l) + + call TermWait(buf) + " It should appear as a single buffer line in vim + let lastline = getline('$') + call assert_equal('1+2+3+4+5+6+7+8+9+10+11+12+13+14+15', lastline) + + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 0de4e4f14c..0b3a06d204 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 336, /**/ 335, /**/ From 01be43047f657b0afed488cf871a612d83abe087 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 11 Apr 2026 14:53:48 +0000 Subject: [PATCH 023/545] runtime(hlyank): verify winid in lambda before matchdelete() Signed-off-by: Christian Brabandt --- runtime/pack/dist/opt/hlyank/plugin/hlyank.vim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim b/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim index 4b568fae24..3e0cdb9bfb 100644 --- a/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim +++ b/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim @@ -1,7 +1,7 @@ vim9script # Highlight Yank plugin -# Last Change: 2025 Mar 22 +# Last Change: 2026 Apr 11 def HighlightedYank() @@ -28,7 +28,11 @@ def HighlightedYank() return [v[0][1], col_beg, col_end - col_beg] })) var winid = win_getid() - timer_start(duration, (_) => m->matchdelete(winid)) + timer_start(duration, (_) => { + if winbufnr(winid) != -1 + m->matchdelete(winid) + endif + }) endif enddef From 8fd37e42a65a36c7e0ea65f73095fa9da89e15e2 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Sat, 11 Apr 2026 15:00:50 +0000 Subject: [PATCH 024/545] patch 9.2.0337: list indexing broken on big-endian 32-bit platforms Problem: check_range_index_one() expects a long * but n1 is a varnumber_T. Casting varnumber_T * to long * is undefined behaviour and reads the wrong bytes on big-endian platforms (John Paul Adrian Glaubitz) Solution: Use a local long variable and pass that pointer to check_range_index_one() (James McCoy) fixes: #19798 closes: #19952 Signed-off-by: James McCoy Signed-off-by: Christian Brabandt --- src/list.c | 3 ++- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/list.c b/src/list.c index c9e9d686bd..3074bb93f2 100644 --- a/src/list.c +++ b/src/list.c @@ -1368,7 +1368,8 @@ list_slice_or_index( { // copy the item to "var1" to avoid that freeing the list makes it // invalid. - listitem_T *li = check_range_index_one(list, (long *)&n1, TRUE, TRUE); + long index = n1; + listitem_T *li = check_range_index_one(list, &index, TRUE, TRUE); if (li == NULL) return FAIL; copy_tv(&li->li_tv, &var1); diff --git a/src/version.c b/src/version.c index 0b3a06d204..f4e5ef7ec2 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 337, /**/ 336, /**/ From 0802e00f2a57fb62a0012a2f15a718e72ed806fd Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sat, 11 Apr 2026 15:22:24 +0000 Subject: [PATCH 025/545] patch 9.2.0338: Cannot handle mouseclicks in the tabline Problem: Cannot handle mouseclicks in the tabline Solution: Support %[FuncName] click regions in 'tabline', add "area" key to the click info dict (Yasuhiro Matsumoto). The previous implementation resolved and stored click regions only for per-window statuslines; the tabline path in win_redr_custom() (wp==NULL) parsed %[FuncName] but discarded the regions, and tabline clicks were dispatched via TabPageIdxs[] which didn't know about them. Add a global tabline_stl_click array populated from the tabline path, refactor stl_click_handler() to take the regions directly, and dispatch matching clicks from do_mouse() before falling through to tab selection. The winid entry in the callback dict is 0 for tabline clicks. related: #19841 closes: #19950 Supported by AI. Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 15 +++++-- src/globals.h | 4 ++ src/mouse.c | 70 +++++++++++++++++++++++++++------ src/screen.c | 46 +++++++++++++++------- src/testdir/test_statusline.vim | 57 +++++++++++++++++++++++++++ src/version.c | 2 + 6 files changed, 165 insertions(+), 29 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 300eebbc22..f083d6ff10 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 09 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -8626,7 +8626,8 @@ A jump table for the options with a short description can be found at |Q_op|. *stl-%[FuncName]* %[ defines clickable regions in the statusline. When the user clicks - on a region with the mouse, the specified function is called. + on a region with the mouse, the specified function is called. The + same syntax can also be used in 'tabline'. %[FuncName] Start of a clickable region. "FuncName" is the name of a Vim function to call when the region is clicked. @@ -8644,11 +8645,17 @@ A jump table for the options with a short description can be found at |Q_op|. "button" Mouse button: "l" (left), "m" (middle), "r" (right). "mods" Modifier keys: combination of "s" (shift), "c" (ctrl), "a" (alt). Empty string if no modifiers. - "winid" |window-ID| of the window whose statusline was clicked. + "winid" |window-ID| of the window whose statusline was clicked, + or 0 when the click was in 'tabline'. + "area" "statusline" or "tabline". Indicates which option the + clicked region belongs to. Useful when a single + callback is shared between 'statusline' and 'tabline'. If the function returns non-zero, the statusline is redrawn. Dragging the statusline to resize the window still works even when - click handlers are defined. + click handlers are defined. When used in 'tabline', clicks in + %[FuncName] regions are dispatched to the callback instead of the + default tab-selection behavior. Example: > func! ClickFile(info) diff --git a/src/globals.h b/src/globals.h index b7533c6322..7d873eea4e 100644 --- a/src/globals.h +++ b/src/globals.h @@ -104,6 +104,10 @@ EXTERN int redrawing_for_callback INIT(= 0); */ EXTERN short *TabPageIdxs INIT(= NULL); +// Click regions for 'tabline' (%[FuncName]). +EXTERN stl_click_region_T *tabline_stl_click INIT(= NULL); +EXTERN int tabline_stl_click_count INIT(= 0); + #ifdef FEAT_PROP_POPUP // Array with size Rows x Columns containing zindex of popups. EXTERN short *popup_mask INIT(= NULL); diff --git a/src/mouse.c b/src/mouse.c index 6bff3d7c28..1077c5ee72 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -21,6 +21,9 @@ static long mouse_hor_step = 6; static long mouse_vert_step = 3; static win_T *dragwin = NULL; // window being dragged static int stl_click_handler(win_T *wp, int mcol, int which_button, int mods); +static int stl_click_handler_regions(stl_click_region_T *regions, + int region_count, int winid, int mcol, + int which_button, int mods); void mouse_set_vert_scroll_step(long step) @@ -502,6 +505,16 @@ do_mouse( // Check for clicking in the tab page line. if (TabPageIdxs != NULL && mouse_row == 0 && firstwin->w_winrow > 0) { + // Dispatch 'tabline' %[FuncName] click regions before falling through + // to tab-page selection. On drag events fall through to the normal + // tab-drag handling. + if (is_click && !is_drag + && stl_click_handler_regions(tabline_stl_click, + tabline_stl_click_count, + 0, mouse_col, which_button, + mod_mask)) + return FALSE; + tp_label.just_in = true; tp_label.nr = TabPageIdxs[mouse_col]; @@ -1640,11 +1653,19 @@ mouse_model_popup(void) } /* - * Call a statusline click handler function. + * Call a click-region callback function. + * "regions"/"region_count" describe the resolved click regions, + * "winid" is stored as the "winid" key in the info dict (0 for tabline). * Returns TRUE if the function was called and handled the click. */ static int -stl_click_handler(win_T *wp, int mcol, int which_button, int mods) +stl_click_handler_regions( + stl_click_region_T *regions, + int region_count, + int winid, + int mcol, + int which_button, + int mods) { #ifdef FEAT_EVAL int n; @@ -1658,17 +1679,16 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods) funcexe_T funcexe; int col = mcol; - if (wp == NULL || wp->w_stl_click == NULL || wp->w_stl_click_count == 0) + if (regions == NULL || region_count == 0) return FALSE; // Find the click region at the given column. - for (n = 0; n < wp->w_stl_click_count; n++) + for (n = 0; n < region_count; n++) { - if (col >= wp->w_stl_click[n].col_start - && col < wp->w_stl_click[n].col_end) + if (col >= regions[n].col_start && col < regions[n].col_end) break; } - if (n >= wp->w_stl_click_count || wp->w_stl_click[n].funcname == NULL) + if (n >= region_count || regions[n].funcname == NULL) return FALSE; // Build the info dictionary. @@ -1676,7 +1696,7 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods) if (info == NULL) return FALSE; - dict_add_number(info, "minwid", wp->w_stl_click[n].minwid); + dict_add_number(info, "minwid", regions[n].minwid); // Determine number of clicks. // MOD_MASK_2CLICK=0x20, MOD_MASK_3CLICK=0x40, MOD_MASK_4CLICK=0x60 @@ -1705,7 +1725,13 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods) mods_str[mi] = NUL; dict_add_string(info, "mods", mods_str); - dict_add_number(info, "winid", wp->w_id); + dict_add_number(info, "winid", winid); + + // "area": which option the clicked region belongs to. Lets a shared + // dispatcher distinguish 'statusline' from 'tabline' (and future areas) + // without having to overload winid == 0. + dict_add_string(info, "area", + winid == 0 ? (char_u *)"tabline" : (char_u *)"statusline"); // Call the function with the info dict as argument. argvars[0].v_type = VAR_DICT; @@ -1718,7 +1744,7 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods) CLEAR_FIELD(funcexe); funcexe.fe_evaluate = TRUE; - (void)call_func(wp->w_stl_click[n].funcname, -1, + (void)call_func(regions[n].funcname, -1, &rettv, 1, argvars, &funcexe); n = (int)rettv.vval.v_number; @@ -1726,11 +1752,20 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods) dict_unref(info); if (n != 0) + { + // Make sure the tabline gets redrawn too when the callback asks for + // a redraw (redraw_statuslines() only redraws the tabline when + // redraw_tabline is set). + if (winid == 0) + redraw_tabline = TRUE; redraw_statuslines(); + } return TRUE; #else - (void)wp; + (void)regions; + (void)region_count; + (void)winid; (void)mcol; (void)which_button; (void)mods; @@ -1738,6 +1773,19 @@ stl_click_handler(win_T *wp, int mcol, int which_button, int mods) #endif } +/* + * Call a statusline click handler function for window "wp". + * Returns TRUE if the function was called and handled the click. + */ + static int +stl_click_handler(win_T *wp, int mcol, int which_button, int mods) +{ + if (wp == NULL) + return FALSE; + return stl_click_handler_regions(wp->w_stl_click, wp->w_stl_click_count, + wp->w_id, mcol, which_button, mods); +} + // dragwin is declared near the top of the file /* diff --git a/src/screen.c b/src/screen.c index f667e6eb24..ed28ac91ab 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1486,23 +1486,41 @@ win_redr_custom( TabPageIdxs[col++] = fillchar; } - // Resolve click function regions for statusline. - if (wp != NULL && !draw_ruler) + // Resolve click function regions for statusline or tabline. + if (!draw_ruler) { - int click_count = 0; + stl_click_region_T **out_regions; + int *out_count; + int base_col; + int click_count = 0; + + if (wp != NULL) + { + out_regions = &wp->w_stl_click; + out_count = &wp->w_stl_click_count; + base_col = wp->w_wincol; + } + else + { + // 'tabline': store regions in global state since there is no + // associated window. + out_regions = &tabline_stl_click; + out_count = &tabline_stl_click_count; + base_col = firstwin->w_wincol; + } // Count the click regions. for (n = 0; clicktab[n].start != NULL; n++) click_count++; // Free old click regions. - if (wp->w_stl_click != NULL) + if (*out_regions != NULL) { - for (n = 0; n < wp->w_stl_click_count; n++) - vim_free(wp->w_stl_click[n].funcname); - VIM_CLEAR(wp->w_stl_click); + for (n = 0; n < *out_count; n++) + vim_free((*out_regions)[n].funcname); + VIM_CLEAR(*out_regions); } - wp->w_stl_click_count = 0; + *out_count = 0; if (click_count > 0) { @@ -1514,7 +1532,7 @@ win_redr_custom( { char_u *cur_funcname = NULL; int cur_minwid = 0; - int region_start = wp->w_wincol; + int region_start = base_col; // Walk through click records converting buffer positions // to screen columns. @@ -1530,7 +1548,7 @@ win_redr_custom( if (cur_funcname != NULL) { regions[rcount].col_start = region_start; - regions[rcount].col_end = wp->w_wincol + len; + regions[rcount].col_end = base_col + len; regions[rcount].funcname = vim_strsave(cur_funcname); regions[rcount].minwid = cur_minwid; @@ -1539,22 +1557,22 @@ win_redr_custom( cur_funcname = clicktab[n].funcname; cur_minwid = clicktab[n].minwid; - region_start = wp->w_wincol + len; + region_start = base_col + len; } // Close final region if it extends to the end. if (cur_funcname != NULL) { regions[rcount].col_start = region_start; - regions[rcount].col_end = wp->w_wincol + maxwidth; + regions[rcount].col_end = base_col + maxwidth; regions[rcount].funcname = vim_strsave(cur_funcname); regions[rcount].minwid = cur_minwid; rcount++; } - wp->w_stl_click = regions; - wp->w_stl_click_count = rcount; + *out_regions = regions; + *out_count = rcount; } } diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 673a6ed125..27fef946af 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -752,6 +752,7 @@ func Test_statusline_click_handler() call assert_equal(1, g:stl_click_info.nclicks) call assert_equal(0, g:stl_click_info.minwid) call assert_equal(win_getid(), g:stl_click_info.winid) + call assert_equal('statusline', g:stl_click_info.area) unlet! g:stl_click_info " Click outside click region (on the filename part) @@ -873,4 +874,60 @@ func Test_statusline_click_linebreak_still_works() let &laststatus = save_ls endfunc +func Test_tabline_click_handler() + let save_mouse = &mouse + let save_tal = &tabline + let save_stal = &showtabline + if has('gui') + let save_go = &guioptions + set guioptions-=e + endif + set mouse=a + set showtabline=2 + + " Two adjacent click regions in 'tabline' with different minwid. + set tabline=%1[StlClickTestFunc][AAA]%[]%2[StlClickTestFunc][BBB]%[] + redraw! + + " Click on [AAA] region (tabline is row 1). + call test_setmouse(1, 2) + call feedkeys("\\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal('l', g:stl_click_info.button) + call assert_equal(1, g:stl_click_info.nclicks) + call assert_equal(1, g:stl_click_info.minwid) + " winid is 0 for tabline clicks (no associated window). + call assert_equal(0, g:stl_click_info.winid) + call assert_equal('tabline', g:stl_click_info.area) + unlet! g:stl_click_info + + " Click on [BBB] region. + call test_setmouse(1, 7) + call feedkeys("\\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal(2, g:stl_click_info.minwid) + unlet! g:stl_click_info + + " Middle click on [AAA]. + call test_setmouse(1, 2) + call feedkeys("\\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal('m', g:stl_click_info.button) + unlet! g:stl_click_info + + " Click outside any %[...] region: no callback, no error. + set tabline=xxx%1[StlClickTestFunc][YYY]%[] + redraw! + call test_setmouse(1, 1) + call feedkeys("\\", 'xt') + call assert_false(exists('g:stl_click_info')) + + let &mouse = save_mouse + let &tabline = save_tal + let &showtabline = save_stal + if has('gui') + let &guioptions = save_go + endif +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index f4e5ef7ec2..15f52077eb 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 338, /**/ 337, /**/ From 86ae6858ababe1f80476368c617dc1812df5b781 Mon Sep 17 00:00:00 2001 From: Emilia Date: Sat, 11 Apr 2026 15:34:53 +0000 Subject: [PATCH 026/545] runtime(jjdescription): allow to configure summary width Allow to configure max length for the summary line and fall back to gits setting. closes: #19905 Signed-off-by: Emilia Signed-off-by: Christian Brabandt --- runtime/doc/filetype.txt | 12 ++++++++++-- runtime/doc/tags | 1 + runtime/syntax/jjdescription.vim | 22 +++++++++++++++++----- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 790cfc2bd1..461f801ccc 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1,4 +1,4 @@ -*filetype.txt* For Vim version 9.2. Last change: 2026 Apr 09 +*filetype.txt* For Vim version 9.2. Last change: 2026 Apr 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -806,6 +806,14 @@ To enable the recognition of Markdown comments each time after removing re-source "javaformat.vim" for Vim versions greater than `8.2.1397`: > runtime autoload/javaformat.vim < +JUJUTSU DESCRIPTION *ft-jjdescription-plugin* + +The length of the first line of the commit message used for syntax +highlighting can be configured via `g:jjcommit_summary_length`, and will +fallback to `g:gitcommit_summary_length`. The default is 50. Example: > + + let g:jjcommit_summary_length = 70 +< JSON-FORMAT *ft-json-plugin* JSON filetype can be extended to use 'formatexpr' and "json.FormatExpr()" @@ -816,7 +824,7 @@ Add following lines to $HOME/.vim/ftplugin/json.vim: > vim9script import autoload 'dist/json.vim' setl formatexpr=json.FormatExpr() - +< LUA *ft-lua-plugin* *g:lua_folding* You can enable folding of Lua functions using |fold-expr| by: > diff --git a/runtime/doc/tags b/runtime/doc/tags index 66f3731614..1e0720b21a 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -7601,6 +7601,7 @@ ft-inform-syntax syntax.txt /*ft-inform-syntax* ft-java-plugin filetype.txt /*ft-java-plugin* ft-java-syntax syntax.txt /*ft-java-syntax* ft-javascript-omni insert.txt /*ft-javascript-omni* +ft-jjdescription-plugin filetype.txt /*ft-jjdescription-plugin* ft-jq-syntax syntax.txt /*ft-jq-syntax* ft-json-plugin filetype.txt /*ft-json-plugin* ft-json-syntax syntax.txt /*ft-json-syntax* diff --git a/runtime/syntax/jjdescription.vim b/runtime/syntax/jjdescription.vim index f7b76ba260..dcdcb39914 100644 --- a/runtime/syntax/jjdescription.vim +++ b/runtime/syntax/jjdescription.vim @@ -5,6 +5,7 @@ " 2025 Apr 17 by Vim Project (don't require space to start comments, #17130) " 2026 Apr 09 by Vim Project (anchor status regex to beginning of line, #19879) " 2026 Apr 09 by Vim Project (detect renames of files, #19879) +" 2026 Apr 11 by Vim Project (configure summary length, #19905) if exists('b:current_syntax') finish @@ -20,10 +21,21 @@ syn region jjComment start="^JJ:" end="$" contains=jjAdded,jjRemoved,jjChanged,j syn include @jjCommitDiff syntax/diff.vim syn region jjCommitDiff start=/\%(^diff --\%(git\|cc\|combined\) \)\@=/ end=/^\%(diff --\|$\|@@\@!\|[^[:alnum:]\ +-]\S\@!\)\@=/ fold contains=@jjCommitDiff -hi def link jjComment Comment -hi def link jjAdded Added -hi def link jjRemoved Removed -hi def link jjChanged Changed -hi def link jjRenamed Changed +if get(g:, 'jjcommit_summary_length', get(g:, 'gitcommit_summary_length', 0)) < 0 + syn match jjdescriptionSummary "^.*$" contained containedin=jjcommitFirstLine nextgroup=jjcommitOverflow contains=@Spell +elseif get(g:, 'jjcommit_summary_length', get(g:, 'gitcommit_summary_length', 1)) > 0 + exe 'syn match jjdescriptionSummary "^.*\%<' . (get(g:, 'jjcommit_summary_length', get(:g, 'gitcommit_summary_length', 50) + 1) . 'v." contained containedin=jjcommitFirstLine nextgroup=jjcommitOverflow contains=@Spell' +endif +syn match jjcommitOverflow ".*" contained contains=@Spell +syn match jjcommitBlank "^.\+" contained contains=@Spell +syn match jjcommitFirstLine "\%^.*" nextgroup=jjcommitBlank,jjComment skipnl + +hi def link jjcommitSummary Keyword +hi def link jjComment Comment +hi def link jjAdded Added +hi def link jjRemove Removed +hi def link jjChange Changed +hi def link jjRenamed Changed +hi def link jjcommitBlank Error let b:current_syntax = 'jjdescription' From 105d65e29b636981b2a92cd0205b19f85951d770 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 12 Apr 2026 16:12:22 +0000 Subject: [PATCH 027/545] patch 9.2.0339: regexp: nfa_regmatch() allocates and frees too often Problem: nfa_regmatch() allocates and frees two list buffers on every call, causing unnecessary memory allocation overhead for frequently used patterns. Solution: Cache the list buffers in the regprog struct and reuse them on subsequent top-level calls. Recursive calls still allocate their own buffers. Free cached buffers in nfa_regfree() (Yasuhiro Matsumoto). Benchmark: 10K lines, `:%s` x50 iterations | Pattern | Before | After | Improvement | |---|---|---|---| | `\<\(\w\+\%(ing\|tion\|ed\|ly\)\|\w\{3,}\)\>` (many matches) | 4.384s | 4.299s | -2% | | `\(foo\|bar\|baz\)\{3,}\(qux\|quux\|corge\)\{2,}...` (no match, high nstate) | 16.927s | 3.015s | -82% | closes: #19956 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/regexp.h | 2 ++ src/regexp_nfa.c | 35 ++++++++++++++++++++++++++++++----- src/version.c | 2 ++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/regexp.h b/src/regexp.h index 7a0c3abac6..aa685a54d0 100644 --- a/src/regexp.h +++ b/src/regexp.h @@ -123,6 +123,8 @@ typedef struct char_u *pattern; int nsubexp; // number of () int nstate; + void *listbuf[2]; // cached list buffers for + // nfa_regmatch() nfa_state_T state[1]; // actually longer.. } nfa_regprog_T; diff --git a/src/regexp_nfa.c b/src/regexp_nfa.c index 610c6bd678..4bd588b4e4 100644 --- a/src/regexp_nfa.c +++ b/src/regexp_nfa.c @@ -5797,9 +5797,21 @@ nfa_regmatch( // Allocate memory for the lists of nodes. size = (prog->nstate + 1) * sizeof(nfa_thread_T); - list[0].t = alloc(size); + // Reuse cached list buffers from prog when available (top-level call). + // Recursive calls must allocate their own buffers. + if (toplevel && prog->listbuf[0] != NULL) + { + list[0].t = (nfa_thread_T *)prog->listbuf[0]; + list[1].t = (nfa_thread_T *)prog->listbuf[1]; + prog->listbuf[0] = NULL; + prog->listbuf[1] = NULL; + } + else + { + list[0].t = alloc(size); + list[1].t = alloc(size); + } list[0].len = prog->nstate + 1; - list[1].t = alloc(size); list[1].len = prog->nstate + 1; if (list[0].t == NULL || list[1].t == NULL) goto theend; @@ -7287,9 +7299,18 @@ nfa_regmatch( #endif theend: - // Free memory - vim_free(list[0].t); - vim_free(list[1].t); + // Cache list buffers in prog for reuse, or free if prog already has + // cached buffers (recursive call case). + if (prog->listbuf[0] == NULL && list[0].t != NULL && list[1].t != NULL) + { + prog->listbuf[0] = list[0].t; + prog->listbuf[1] = list[1].t; + } + else + { + vim_free(list[0].t); + vim_free(list[1].t); + } vim_free(listids); #undef ADD_STATE_IF_MATCH #ifdef NFA_REGEXP_DEBUG_LOG @@ -7644,6 +7665,8 @@ nfa_regcomp(char_u *expr, int re_flags) goto fail; state_ptr = prog->state; prog->re_in_use = FALSE; + prog->listbuf[0] = NULL; + prog->listbuf[1] = NULL; /* * PASS 2 @@ -7707,6 +7730,8 @@ nfa_regfree(regprog_T *prog) vim_free(((nfa_regprog_T *)prog)->match_text); vim_free(((nfa_regprog_T *)prog)->pattern); + vim_free(((nfa_regprog_T *)prog)->listbuf[0]); + vim_free(((nfa_regprog_T *)prog)->listbuf[1]); vim_free(prog); } diff --git a/src/version.c b/src/version.c index 15f52077eb..fc48763819 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 339, /**/ 338, /**/ From dc4734448bd06247747fd9dd6ca8e75cfdd95030 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 12 Apr 2026 16:19:36 +0000 Subject: [PATCH 028/545] Fix a few typos closes: #19953 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/INSTALLpc.txt | 4 ++-- src/ex_docmd.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/INSTALLpc.txt b/src/INSTALLpc.txt index 5ea4d7a04e..5021496860 100644 --- a/src/INSTALLpc.txt +++ b/src/INSTALLpc.txt @@ -183,7 +183,7 @@ src/INSTALLpc.txt file for instructions. 2. MSYS2 with MinGW =================== -2.1 Buidling x86 32bit or 64bit versions. +2.1 Building x86 32bit or 64bit versions. 2.1.1. Setup the basic msys2 environment @@ -303,7 +303,7 @@ To build Vim with the address sanitizer (ASAN), execute the following command: CXX=clang++ make -f Make_ming.mak DEBUG=yes ASAN=yes -2.2 Buidling an Arch64 version of Vim +2.2 Building an Arch64 version of Vim 2.2.1. Setup the basic msys2 environment diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 7834f2bdbd..73ed9f2276 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -8366,7 +8366,7 @@ do_sleep(long msec, int hide_cursor) // actual time passed done = ELAPSED_FUNC(start_tv); #else - // guestimate time passed (will actually be more) + // guesstimate time passed (will actually be more) done += wait_now; #endif } From efedcd1e6f369e1dcc92ae0cc2c4c8020f08738e Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 12 Apr 2026 16:24:09 +0000 Subject: [PATCH 029/545] patch 9.2.0340: pum_redraw() may cause flicker Problem: pum_redraw() may cause flicker Solution: Wrap pum_redraw() in synchronized output to avoid flicker (Yasuhiro Matsumoto) closes: #19955 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/popupmenu.c | 6 ++++++ src/version.c | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/popupmenu.c b/src/popupmenu.c index 2ee720087d..a50faaa05b 100644 --- a/src/popupmenu.c +++ b/src/popupmenu.c @@ -953,6 +953,10 @@ pum_redraw(void) // Use current window for highlight overrides when using 'winhighlight' override_success = push_highlight_overrides(curwin->w_hl, curwin->w_hl_len); + // Batch the underlying screen update and the pum drawing into a single + // synchronized output frame to avoid flicker. + term_set_sync_output(TERM_SYNC_OUTPUT_ENABLE); + hlf_T hlfsNorm[3]; hlf_T hlfsSel[3]; // "word"/"abbr" @@ -1187,6 +1191,8 @@ pum_redraw(void) if (override_success) pop_highlight_overrides(); + + term_set_sync_output(TERM_SYNC_OUTPUT_DISABLE); } #if defined(FEAT_PROP_POPUP) && defined(FEAT_QUICKFIX) diff --git a/src/version.c b/src/version.c index fc48763819..560233fafc 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 340, /**/ 339, /**/ From 86dcb1878cdbd0a8af61664b14f034398e319026 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 13 Apr 2026 15:48:28 +0200 Subject: [PATCH 030/545] Revert "runtime(jjdescription): allow to configure summary width" This reverts commit 86ae6858ababe1f80476368c617dc1812df5b781. related: #19905 Signed-off-by: Christian Brabandt --- runtime/doc/filetype.txt | 12 ++---------- runtime/doc/tags | 1 - runtime/syntax/jjdescription.vim | 22 +++++----------------- 3 files changed, 7 insertions(+), 28 deletions(-) diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 461f801ccc..2c978ebc9a 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1,4 +1,4 @@ -*filetype.txt* For Vim version 9.2. Last change: 2026 Apr 11 +*filetype.txt* For Vim version 9.2. Last change: 2026 Apr 13 VIM REFERENCE MANUAL by Bram Moolenaar @@ -806,14 +806,6 @@ To enable the recognition of Markdown comments each time after removing re-source "javaformat.vim" for Vim versions greater than `8.2.1397`: > runtime autoload/javaformat.vim < -JUJUTSU DESCRIPTION *ft-jjdescription-plugin* - -The length of the first line of the commit message used for syntax -highlighting can be configured via `g:jjcommit_summary_length`, and will -fallback to `g:gitcommit_summary_length`. The default is 50. Example: > - - let g:jjcommit_summary_length = 70 -< JSON-FORMAT *ft-json-plugin* JSON filetype can be extended to use 'formatexpr' and "json.FormatExpr()" @@ -824,7 +816,7 @@ Add following lines to $HOME/.vim/ftplugin/json.vim: > vim9script import autoload 'dist/json.vim' setl formatexpr=json.FormatExpr() -< + LUA *ft-lua-plugin* *g:lua_folding* You can enable folding of Lua functions using |fold-expr| by: > diff --git a/runtime/doc/tags b/runtime/doc/tags index 1e0720b21a..66f3731614 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -7601,7 +7601,6 @@ ft-inform-syntax syntax.txt /*ft-inform-syntax* ft-java-plugin filetype.txt /*ft-java-plugin* ft-java-syntax syntax.txt /*ft-java-syntax* ft-javascript-omni insert.txt /*ft-javascript-omni* -ft-jjdescription-plugin filetype.txt /*ft-jjdescription-plugin* ft-jq-syntax syntax.txt /*ft-jq-syntax* ft-json-plugin filetype.txt /*ft-json-plugin* ft-json-syntax syntax.txt /*ft-json-syntax* diff --git a/runtime/syntax/jjdescription.vim b/runtime/syntax/jjdescription.vim index dcdcb39914..f7b76ba260 100644 --- a/runtime/syntax/jjdescription.vim +++ b/runtime/syntax/jjdescription.vim @@ -5,7 +5,6 @@ " 2025 Apr 17 by Vim Project (don't require space to start comments, #17130) " 2026 Apr 09 by Vim Project (anchor status regex to beginning of line, #19879) " 2026 Apr 09 by Vim Project (detect renames of files, #19879) -" 2026 Apr 11 by Vim Project (configure summary length, #19905) if exists('b:current_syntax') finish @@ -21,21 +20,10 @@ syn region jjComment start="^JJ:" end="$" contains=jjAdded,jjRemoved,jjChanged,j syn include @jjCommitDiff syntax/diff.vim syn region jjCommitDiff start=/\%(^diff --\%(git\|cc\|combined\) \)\@=/ end=/^\%(diff --\|$\|@@\@!\|[^[:alnum:]\ +-]\S\@!\)\@=/ fold contains=@jjCommitDiff -if get(g:, 'jjcommit_summary_length', get(g:, 'gitcommit_summary_length', 0)) < 0 - syn match jjdescriptionSummary "^.*$" contained containedin=jjcommitFirstLine nextgroup=jjcommitOverflow contains=@Spell -elseif get(g:, 'jjcommit_summary_length', get(g:, 'gitcommit_summary_length', 1)) > 0 - exe 'syn match jjdescriptionSummary "^.*\%<' . (get(g:, 'jjcommit_summary_length', get(:g, 'gitcommit_summary_length', 50) + 1) . 'v." contained containedin=jjcommitFirstLine nextgroup=jjcommitOverflow contains=@Spell' -endif -syn match jjcommitOverflow ".*" contained contains=@Spell -syn match jjcommitBlank "^.\+" contained contains=@Spell -syn match jjcommitFirstLine "\%^.*" nextgroup=jjcommitBlank,jjComment skipnl - -hi def link jjcommitSummary Keyword -hi def link jjComment Comment -hi def link jjAdded Added -hi def link jjRemove Removed -hi def link jjChange Changed -hi def link jjRenamed Changed -hi def link jjcommitBlank Error +hi def link jjComment Comment +hi def link jjAdded Added +hi def link jjRemoved Removed +hi def link jjChanged Changed +hi def link jjRenamed Changed let b:current_syntax = 'jjdescription' From 6836599733950e1f52864a9742ff3e5bca5820fd Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Tue, 14 Apr 2026 14:42:48 +0200 Subject: [PATCH 031/545] runtime(zip): Detect path traversal issues on Windows Signed-off-by: Christian Brabandt --- runtime/autoload/zip.vim | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/runtime/autoload/zip.vim b/runtime/autoload/zip.vim index f4482fd7fc..6a2d6daa3e 100644 --- a/runtime/autoload/zip.vim +++ b/runtime/autoload/zip.vim @@ -22,6 +22,7 @@ " 2026 Mar 08 by Vim Project: Make ZipUpdatePS() check for powershell " 2026 Apr 01 by Vim Project: Detect more path traversal attacks " 2026 Apr 05 by Vim Project: Detect more path traversal attacks +" 2026 Apr 14 by Vim Project: Detect more path traversal attacks on Windows " License: Vim License (see vim's :help license) " Copyright: Copyright (C) 2005-2019 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, @@ -405,7 +406,12 @@ fun! zip#Write(fname) else let zipfile = substitute(a:fname,'^.\{-}zipfile://\(.\{-}\)::[^\\].*$','\1','') let fname = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','') - " TODO: what to check on MS-Windows to avoid writing absolute paths? + " fname should not start with drive leter or a UNC path + if fname =~ '^\%(\%(\a:[\\/]\)\|[\\/]\{2}\)' + call s:Mess('Error', "***error*** (zip#Write) Path Traversal Attack detected, not writing!") + call s:ChgDir(curdir,s:WARNING,"(zip#Write) unable to return to ".curdir."!") + return + endif endif if fname =~ '^[.]\{1,2}/' let gnu_cmd = g:zip_zipcmd . ' -d ' . s:Escape(fnamemodify(zipfile,":p"),0) . ' ' . s:Escape(fname,0) From fcc4276db31bc35239186e62921b4ff5a15fb18e Mon Sep 17 00:00:00 2001 From: q1uf3ng Date: Tue, 14 Apr 2026 15:58:40 +0000 Subject: [PATCH 032/545] patch 9.2.0341: some functions can be run from the sandbox Problem: some functions can be run from the sandbox Solution: Block them, so they are not accessible from a modeline (q1uf3ng) closes: #19975 Co-authored-by: zeertzjq Signed-off-by: q1uf3ng Signed-off-by: Christian Brabandt --- src/evalfunc.c | 3 +++ src/ex_cmds.h | 2 +- src/filepath.c | 13 +++++++++++++ src/testdir/test_excmd.vim | 7 +++++++ src/version.c | 2 ++ 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index f61135f77c..a0fb99b738 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -4391,6 +4391,9 @@ f_echoraw(typval_T *argvars, typval_T *rettv UNUSED) { char_u *str; + if (check_secure()) + return; + if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL) return; diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 7613d18aa1..1621569732 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -552,7 +552,7 @@ EXCMD(CMD_echomsg, "echomsg", ex_execute, EX_EXTRA|EX_NOTRLCOM|EX_EXPR_ARG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_echoconsole, "echoconsole", ex_execute, - EX_EXTRA|EX_NOTRLCOM|EX_EXPR_ARG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, + EX_EXTRA|EX_NOTRLCOM|EX_EXPR_ARG|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_echon, "echon", ex_echo, EX_EXTRA|EX_NOTRLCOM|EX_EXPR_ARG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK, diff --git a/src/filepath.c b/src/filepath.c index 721dbc05d7..492e0ad3ad 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -823,6 +823,9 @@ f_chdir(typval_T *argvars, typval_T *rettv) rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; + if (check_secure()) + return; + if (argvars[0].v_type != VAR_STRING) { // Returning an empty string means it failed. @@ -1727,6 +1730,8 @@ f_readdir(typval_T *argvars, typval_T *rettv) if (rettv_list_alloc(rettv) == FAIL) return; + if (check_secure()) + return; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL @@ -1780,6 +1785,8 @@ f_readdirex(typval_T *argvars, typval_T *rettv) if (rettv_list_alloc(rettv) == FAIL) return; + if (check_secure()) + return; if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL @@ -2051,6 +2058,9 @@ read_file_or_blob(typval_T *argvars, typval_T *rettv, int always_blob) void f_readblob(typval_T *argvars, typval_T *rettv) { + if (check_secure()) + return; + if (in_vim9script() && (check_for_string_arg(argvars, 0) == FAIL || check_for_opt_number_arg(argvars, 1) == FAIL @@ -2067,6 +2077,9 @@ f_readblob(typval_T *argvars, typval_T *rettv) void f_readfile(typval_T *argvars, typval_T *rettv) { + if (check_secure()) + return; + if (in_vim9script() && (check_for_nonempty_string_arg(argvars, 0) == FAIL || check_for_opt_string_arg(argvars, 1) == FAIL diff --git a/src/testdir/test_excmd.vim b/src/testdir/test_excmd.vim index c671adfe94..cc877ce8b6 100644 --- a/src/testdir/test_excmd.vim +++ b/src/testdir/test_excmd.vim @@ -673,6 +673,13 @@ func Sandbox_tests() if has('unix') call assert_fails('cd `pwd`', 'E48:') endif + call assert_fails("call echoraw('test')", 'E48:') + call assert_fails("echoconsole 'test'", 'E48:') + call assert_fails("call readfile('Xsomefile')", 'E48:') + call assert_fails("call readblob('Xsomefile')", 'E48:') + call assert_fails("call readdir('.')", 'E48:') + call assert_fails("call readdirex('.')", 'E48:') + call assert_fails("call chdir('.')", 'E48:') " some options cannot be changed in a sandbox call assert_fails('set exrc', 'E48:') call assert_fails('set cdpath', 'E48:') diff --git a/src/version.c b/src/version.c index 560233fafc..ff43fbef01 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 341, /**/ 340, /**/ From c922202ea20a8c379a6c6c6c201af4bf5e861df7 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Tue, 14 Apr 2026 16:10:43 +0000 Subject: [PATCH 033/545] patch 9.2.0342: tests: test_excmd.vim leaves swapfiles behind Problem: tests: test_excmd.vim leaves swapfiles behind Solution: Close open buffer using :bw! related: #19975 Signed-off-by: Christian Brabandt --- src/testdir/test_excmd.vim | 16 ++++++++-------- src/version.c | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/testdir/test_excmd.vim b/src/testdir/test_excmd.vim index cc877ce8b6..0de0771f78 100644 --- a/src/testdir/test_excmd.vim +++ b/src/testdir/test_excmd.vim @@ -61,7 +61,7 @@ func Test_copy() exe "normal! gg4:yank\" call assert_equal("L1\nL2\nL1\nL2\n", @") - close! + bw! endfunc " Test for the :file command @@ -105,7 +105,7 @@ func Test_drop_cmd() call assert_equal(1, winnr('$')) " Check for setting the argument list call assert_equal(['Xdropfile'], argv()) - enew | only! + enew | only! | bw! Xdropfile endfunc " Test for the :append command @@ -133,7 +133,7 @@ func Test_append_cmd() call assert_equal([' L1', ' L2', ' L3'], getline(1, '$')) call assert_true(&autoindent) set autoindent& - close! + bw! endfunc func Test_append_cmd_empty_buf() @@ -181,7 +181,7 @@ func Test_insert_cmd() call assert_equal([' L2', ' L3', ' L1'], getline(1, '$')) call assert_true(&autoindent) set autoindent& - close! + bw! endfunc func Test_insert_cmd_empty_buf() @@ -229,7 +229,7 @@ func Test_change_cmd() call assert_equal([' L4', ' L5', 'L2', 'L3'], getline(1, '$')) call assert_true(&autoindent) set autoindent& - close! + bw! endfunc " Test for the :language command @@ -537,14 +537,14 @@ func Test_read_cmd() edit Xcmdfile read call assert_equal(['one', 'one'], getline(1, '$')) - close! + bw! new read Xcmdfile call assert_equal(['', 'one'], getline(1, '$')) call deletebufline('', 1, '$') call feedkeys("Qr Xcmdfile\visual\", 'xt') call assert_equal(['one'], getline(1, '$')) - close! + bw! endfunc " Test for running Ex commands when text is locked. @@ -611,7 +611,7 @@ func Test_excmd_delete() call assert_equal([' bar'], split(execute('deletp'), "\n")) call setline(1, ['foo', "\tbar"]) call assert_equal([' bar'], split(execute('deletep'), "\n")) - close! + bw! endfunc " Test for commands that are blocked in a sandbox diff --git a/src/version.c b/src/version.c index ff43fbef01..a2c470932e 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 342, /**/ 341, /**/ From 9d95410aa4079e78c07d8a99c7e2e072f133d5c2 Mon Sep 17 00:00:00 2001 From: James McCoy Date: Tue, 14 Apr 2026 16:25:50 +0000 Subject: [PATCH 034/545] patch 9.2.0343: tests: test_clientserver may fail on slower systems Problem: tests: test_clientserver may fail on slower systems Solution: Wait for argc() before checking argv() (James McCoy). On slower systems, the argv() check may run before the server has populated the arg list. Add a wait for argc() to be 3 to be more tolerant of such systems closes: #19974 Signed-off-by: James McCoy Signed-off-by: Christian Brabandt --- src/testdir/test_clientserver.vim | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/testdir/test_clientserver.vim b/src/testdir/test_clientserver.vim index 8aa9428f2a..0ced9cbc41 100644 --- a/src/testdir/test_clientserver.vim +++ b/src/testdir/test_clientserver.vim @@ -145,6 +145,7 @@ func Test_client_server() " Edit multiple files using --remote call system(cmd .. ' --remote Xclientfile1 Xclientfile2 Xclientfile3') + call WaitForAssert({-> assert_equal('3', remote_expr(name, 'argc()'))}) call assert_match(".*Xclientfile1\n.*Xclientfile2\n.*Xclientfile3\n", remote_expr(name, 'argv()')) eval name->remote_send(":%bw!\") diff --git a/src/version.c b/src/version.c index a2c470932e..0028692f28 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 343, /**/ 342, /**/ From 4b6f3f1d165ea3119e9e5570a46b3c746c83039d Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Tue, 14 Apr 2026 16:31:10 +0000 Subject: [PATCH 035/545] runtime(doc): Tweak documentation style in options.txt closes: #19971 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index f083d6ff10..5f8b4292bc 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 11 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 14 VIM REFERENCE MANUAL by Bram Moolenaar @@ -8620,11 +8620,11 @@ A jump table for the options with a short description can be found at |Q_op|. applied to StatusLineNC for the statusline of non-current windows. The number N must be between 1 and 9. See |hl-User1..9| - *stl-%@* + *stl-%@* @ - Inserts a newline. This only takes effect when the "maxheight" value of 'statuslineopt' is greater than 1, or for |tabpanel|. - *stl-%[FuncName]* + *stl-%[FuncName]* %[ defines clickable regions in the statusline. When the user clicks on a region with the mouse, the specified function is called. The same syntax can also be used in 'tabline'. @@ -8655,7 +8655,7 @@ A jump table for the options with a short description can be found at |Q_op|. Dragging the statusline to resize the window still works even when click handlers are defined. When used in 'tabline', clicks in %[FuncName] regions are dispatched to the callback instead of the - default tab-selection behavior. + default tab page selection behavior. Example: > func! ClickFile(info) From 962a540d7653b0153b9250f8fafb1562bbc5211f Mon Sep 17 00:00:00 2001 From: Zdenek Dohnal Date: Tue, 14 Apr 2026 16:37:25 +0000 Subject: [PATCH 036/545] patch 9.2.0344: channel: ch_listen() can bind to network interface Problem: channel: ch_listen() can bind to network interface Solution: Only allow to use Unix domain sockets or localhost interface (Zdenek Dohnal) related: #19231 related: #19799 closes: #19973 Signed-off-by: Zdenek Dohnal Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 5 +- runtime/doc/channel.txt | 18 +++--- src/channel.c | 105 +++++------------------------------ src/proto/channel.pro | 2 +- src/testdir/test_channel.vim | 30 ++++------ src/version.c | 2 + 6 files changed, 41 insertions(+), 121 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index eb802ed75e..f929b9e4c6 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 10 +*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 14 VIM REFERENCE MANUAL by Bram Moolenaar @@ -107,7 +107,8 @@ ch_getbufnr({handle}, {what}) Number get buffer number for {handle}/{what} ch_getjob({channel}) Job get the Job of {channel} ch_info({handle}) Dict info about channel {handle} ch_listen({address} [, {options}]) - Channel listen on {address} + Channel listen on {address} - port on loopback + or UNIX domain socket ch_log({msg} [, {handle}]) none write {msg} in the channel log file ch_logfile({fname} [, {mode}]) none start logging channel activity ch_open({address} [, {options}]) diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 55b95c632f..b8aaa92768 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -1,4 +1,4 @@ -*channel.txt* For Vim version 9.2. Last change: 2026 Apr 06 +*channel.txt* For Vim version 9.2. Last change: 2026 Apr 14 VIM REFERENCE MANUAL by Bram Moolenaar @@ -132,7 +132,7 @@ Start Vim and create a listening channel: > endfunc " Start listening on port 8765 - let server = ch_listen('localhost:8765', {"callback": "OnAccept"}) + let server = ch_listen('8765', {"callback": "OnAccept"}) From another Vim instance (or any program) you can connect to it: > let channel = ch_open('localhost:8765') @@ -637,8 +637,7 @@ ch_info({handle}) *ch_info()* "status" "open", "buffered" or "closed", like ch_status() When opened with ch_open(): - "hostname" the hostname of the address - "port" the port of the address + "port" the port on loopback "path" the path of the Unix-domain socket "sock_status" "open" or "closed" "sock_mode" "NL", "RAW", "JSON" or "JS" @@ -668,14 +667,15 @@ ch_info({handle}) *ch_info()* Return type: dict ch_listen({address} [, {options}]) *E1573* *E1574* *ch_listen()* - Listen on {address} for incoming channel connections. - This creates a server-side channel, unlike |ch_open()| - which connects to an existing server. + Listen on {address} - port on loopback or UNIX domain socket + for incoming channel connections. This creates a server-side + channel, unlike |ch_open()|which connects to an existing server. Returns a Channel. Use |ch_status()| to check for failure. {address} is a String, see |channel-address| for the possible - accepted forms, however binding to all interfaces is not - allowed for security reasons. + accepted forms, however in case of TCP sockets it allows to + set only a port and binds to loopback address for + security reasons. Note: IPv6 is not yet supported. If {options} is given it must be a |Dictionary|. diff --git a/src/channel.c b/src/channel.c index 6b4bd3cf2b..edf6d84c7a 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1400,8 +1400,7 @@ channel_open_func(typval_T *argvars) channel_T * channel_listen_func(typval_T *argvars) { - char_u *address; - char_u *p; + char_u *arg; char *rest; int port; int is_unix = FALSE; @@ -1409,62 +1408,31 @@ channel_listen_func(typval_T *argvars) channel_T *channel = NULL; if (in_vim9script() - && (check_for_string_arg(argvars, 0) == FAIL - || check_for_opt_dict_arg(argvars, 1) == FAIL)) - return NULL; - - address = tv_get_string(&argvars[0]); - if (argvars[1].v_type != VAR_UNKNOWN - && check_for_nonnull_dict_arg(argvars, 1) == FAIL) + && check_for_string_arg(argvars, 0) == FAIL) return NULL; - if (*address == NUL) + arg = tv_get_string(&argvars[0]); + if (*arg == NUL) { - semsg(_(e_invalid_argument_str), address); + semsg(_(e_invalid_argument_str), arg); return NULL; } - if (!STRNCMP(address, "unix:", 5)) + if (!STRNCMP(arg, "unix:", 5)) { is_unix = TRUE; - address += 5; + arg += 5; port = 0; } - else if (*address == '[') - { - // ipv6 address - p = vim_strchr(address + 1, ']'); - if (p == NULL || *++p != ':') - { - semsg(_(e_invalid_argument_str), address); - return NULL; - } - port = strtol((char *)(p + 1), &rest, 10); - if (port < 0 || port >= 65536 || *rest != NUL) - { - semsg(_(e_invalid_argument_str), address); - return NULL; - } - // strip '[' and ']' - ++address; - *(p - 1) = NUL; - } else { - // ipv4 address - p = vim_strchr(address, ':'); - if (p == NULL) - { - semsg(_(e_invalid_argument_str), address); - return NULL; - } - port = strtol((char *)(p + 1), &rest, 10); + port = strtol((char *)(arg), &rest, 10); if (port < 0 || port >= 65536 || *rest != NUL) { - semsg(_(e_invalid_argument_str), address); + semsg(_(e_invalid_argument_str), arg); return NULL; } - *p = NUL; + *arg = NUL; } // parse options @@ -1481,9 +1449,9 @@ channel_listen_func(typval_T *argvars) } if (is_unix) - channel = channel_listen_unix((char *)address, NULL); + channel = channel_listen_unix((char *)arg, NULL); else - channel = channel_listen((char *)address, port, NULL); + channel = channel_listen(port, NULL); if (channel != NULL) { opt.jo_set = JO_ALL; @@ -1501,7 +1469,6 @@ channel_listen_func(typval_T *argvars) */ channel_T * channel_listen( - char *hostname, int port_in, void (*nb_close_cb)(void)) { @@ -1513,12 +1480,6 @@ channel_listen( int val = 1; channel_T *channel; - if (hostname == NULL || *hostname == NUL) - { - ch_error(NULL, "Hostname/address not defined."); - return NULL; - } - #ifdef MSWIN channel_init_winsock(); #endif @@ -1535,42 +1496,7 @@ channel_listen( vim_memset((char *)&server, 0, sizeof(server)); server.sin_family = AF_INET; server.sin_port = htons(port_in); - -#ifdef FEAT_IPV6 - struct addrinfo hints; - struct addrinfo *res = NULL; - int err; - - CLEAR_FIELD(hints); - hints.ai_family = AF_INET; - hints.ai_socktype = SOCK_STREAM; - if ((err = getaddrinfo(hostname, NULL, &hints, &res)) != 0) - { - ch_error(channel, "in getaddrinfo() in channel_listen()"); - PERROR(_(e_gethostbyname_in_channel_listen)); - channel_free(channel); - return NULL; - } - memcpy(&server.sin_addr, - &((struct sockaddr_in *)res->ai_addr)->sin_addr, - sizeof(server.sin_addr)); - freeaddrinfo(res); -#else - if ((host = gethostbyname(hostname)) == NULL) - { - ch_error(channel, "in gethostbyname() in channel_listen()"); - PERROR(_(e_gethostbyname_in_channel_listen)); - channel_free(channel); - return NULL; - } - - char *p; - - // When using host->h_addr_list[0] directly ubsan warns for it to - // not be aligned. First copy the pointer to avoid that. - memcpy(&p, &host->h_addr_list[0], sizeof(p)); - memcpy((char *)&server.sin_addr, p, host->h_length); -#endif + server.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sd = socket(AF_INET, SOCK_STREAM, 0); if (sd == -1) @@ -1632,7 +1558,7 @@ channel_listen( channel->ch_listen = TRUE; channel->CH_SOCK_FD = (sock_T)sd; channel->ch_nb_close_cb = nb_close_cb; - channel->ch_hostname = (char *)vim_strsave((char_u *)hostname); + channel->ch_hostname = (char *)vim_strsave((char_u *)""); channel->ch_port = port_in; channel->ch_to_be_closed |= (1U << PART_SOCK); @@ -3796,10 +3722,7 @@ channel_info(channel_T *channel, dict_T *dict) if (channel->ch_hostname != NULL) { if (channel->ch_port) - { - dict_add_string(dict, "hostname", (char_u *)channel->ch_hostname); dict_add_number(dict, "port", channel->ch_port); - } else // Unix-domain socket. dict_add_string(dict, "path", (char_u *)channel->ch_hostname); diff --git a/src/proto/channel.pro b/src/proto/channel.pro index d0c2cd0424..0b5d17a137 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -9,7 +9,7 @@ void free_unused_channels(int copyID, int mask); void channel_gui_register_all(void); channel_T *channel_open(const char *hostname, int port, int waittime, void (*nb_close_cb)(void)); channel_T *channel_listen_func(typval_T *argvars); -channel_T *channel_listen(char *hostname, int port_in, void (*nb_close_cb)(void)); +channel_T *channel_listen(int port_in, void (*nb_close_cb)(void)); channel_T *channel_listen_unix(char *path, void (*nb_close_cb)(void)); void ch_close_part(channel_T *channel, ch_part_T part); void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index 6cd55ca9f9..9110a141c7 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -2778,7 +2778,7 @@ endfunction func Test_listen() call ch_log('Test_listen()') - let server = ch_listen('127.0.0.1:12345', {'callback': function('s:test_listen_accept')}) + let server = ch_listen('12345', {'callback': function('s:test_listen_accept')}) if ch_status(server) == 'fail' call assert_report("Can't listen channel") return @@ -2799,35 +2799,29 @@ func Test_listen() call assert_match('127.0.0.1:', g:server_received_addr) endfunc -func Test_listen_invalid_address() - call ch_log('Test_listen_invalid_address()') - - " empty address - call assert_fails("call ch_listen('')", 'E475:') +func Test_listen_invalid_port() + call ch_log('Test_listen_invalid_port()') " missing port - call assert_fails("call ch_listen('localhost')", 'E475:') + call assert_fails("call ch_listen('')", 'E475:') " port number too large - call assert_fails("call ch_listen('localhost:99999')", 'E475:') + call assert_fails("call ch_listen('99999')", 'E475:') " port number zero should let the OS assign an available port - let ch = ch_listen('localhost:0') + let ch = ch_listen('0') call assert_equal('open', ch_status(ch)) call assert_notequal(0, ch_info(ch).port) call ch_close(ch) " port number negative - call assert_fails("call ch_listen('localhost:-1')", 'E475:') - - " invalid ipv6 format (missing closing bracket) - call assert_fails("call ch_listen('[::1:8765')", 'E475:') - - " invalid ipv6 format (missing port) - call assert_fails("call ch_listen('[::1]')", 'E475:') + call assert_fails("call ch_listen('-1')", 'E475:') +endfunc - " TODO: IPv6 should actually work - call assert_fails("call ch_listen('[::1]:9999')", 'E1574:') +func Test_listen_info_no_hostname() + let ch = ch_listen('0') + call assert_fails("call ch_info(ch).hostname", 'E716:') + call ch_close(ch) endfunc func Test_channel_lsp_mode() diff --git a/src/version.c b/src/version.c index 0028692f28..1694045111 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 344, /**/ 343, /**/ From efbd482116d0f1a244b436a46ce3544b016de04b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 14 Apr 2026 16:48:55 +0000 Subject: [PATCH 037/545] patch 9.2.0345: Wrong autoformatting with 'autocomplete' Problem: Wrong autoformatting with 'autocomplete'. Solution: Don't trigger autoformatting when ending autocompletion without selecting an item (zeertzjq). fixes: #19954 closes: #19970 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/insexpand.c | 2 +- src/testdir/test_ins_complete.vim | 16 ++++++++++++++++ src/version.c | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/insexpand.c b/src/insexpand.c index 3fade6cb16..abdcafa7bf 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -2956,7 +2956,7 @@ ins_compl_stop(int c, int prev_mode, int retval) want_cindent = FALSE; // don't do it again } } - else + else if (!compl_autocomplete || compl_used_match) { int prev_col = curwin->w_cursor.col; diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index bf16a93617..a32bc45cca 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -6289,4 +6289,20 @@ func Test_ins_register_preinsert_autocomplete() delfunc TestOmni endfunc +func Test_autocomplete_with_auto_format() + call test_override("char_avail", 1) + new + setlocal formatoptions=tcq textwidth=9 autocomplete noautoindent + call feedkeys("ia b c d\ie f g\", 'tx') + call assert_equal(['a b c e f', 'gd'], getline(1, '$')) + + %delete + setlocal autoindent + call feedkeys("ia b c d\ie f g\", 'tx') + call assert_equal(['a b c e f', 'gd'], getline(1, '$')) + + bw! + call test_override("char_avail", 0) +endfunc + " vim: shiftwidth=2 sts=2 expandtab nofoldenable diff --git a/src/version.c b/src/version.c index 1694045111..f9899cb4fc 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 345, /**/ 344, /**/ From c4fe1e958a2051d443abe072c8a5366a887da9b3 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Tue, 14 Apr 2026 16:56:03 +0000 Subject: [PATCH 038/545] patch 9.2.0346: Wrong cursor position when entering command line window Problem: Wrong cursor position when entering command line window Solution: Add check_cursor() command to verify the cursor position (Hirohito Higashi). When opening the command-line window with CTRL-F after typing a command that fills the screen width, the cursor was placed past the end of the line. Add check_cursor() after setting State to MODE_NORMAL so the cursor is adjusted to the last character. Also fix the cmdwin prefix character (e.g. ':') being drawn on wrapped continuation rows. Draw an empty space instead so that the text alignment is preserved. closes: #19964 Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/drawline.c | 15 +++++--- src/ex_getln.c | 1 + .../dumps/Test_cmdwin_wrap_prefix.dump | 12 +++++++ src/testdir/dumps/Test_wildmenu_pum_22.dump | 2 +- src/testdir/test_cmdline.vim | 2 +- src/testdir/test_cmdwin.vim | 34 +++++++++++++++++++ src/version.c | 2 ++ 7 files changed, 62 insertions(+), 6 deletions(-) create mode 100644 src/testdir/dumps/Test_cmdwin_wrap_prefix.dump diff --git a/src/drawline.c b/src/drawline.c index ee93e07837..419412bd1f 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -2053,12 +2053,19 @@ win_line( wlv.draw_state = WL_CMDLINE; if (wp == cmdwin_win) { - // Draw the cmdline character. wlv.n_extra = 1; - wlv.c_extra = cmdwin_type; wlv.c_final = NUL; - wlv.char_attr = - hl_combine_attr(get_win_attr(wp), HL_ATTR(HLF_AT)); + if (wlv.row == wlv.startrow) + { + wlv.c_extra = cmdwin_type; + wlv.char_attr = hl_combine_attr( + get_win_attr(wp), HL_ATTR(HLF_AT)); + } + else + { + wlv.c_extra = ' '; + wlv.char_attr = get_win_attr(wp); + } } } #ifdef FEAT_FOLDING diff --git a/src/ex_getln.c b/src/ex_getln.c index e1983de738..f89bcecc50 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -4867,6 +4867,7 @@ open_cmdwin(void) exmode_active = 0; State = MODE_NORMAL; + check_cursor(); setmouse(); clear_showcmd(); diff --git a/src/testdir/dumps/Test_cmdwin_wrap_prefix.dump b/src/testdir/dumps/Test_cmdwin_wrap_prefix.dump new file mode 100644 index 0000000000..19ed264481 --- /dev/null +++ b/src/testdir/dumps/Test_cmdwin_wrap_prefix.dump @@ -0,0 +1,12 @@ +| +0&#ffffff0@39 +|~+0#4040ff13&| @38 +|[+1#0000000&|N|o| |N|a|m|e|]| @12|0|,|0|-|1| @9|A|l@1 +|:+0#4040ff13&|e+0#af5f00255&|c|h|o| +0#0000000&|"+0#e000002&|a@31|" +| +0#0000000&|X|Y>Z| @35 +|~+0#4040ff13&| @38 +|~| @38 +|~| @38 +|~| @38 +|~| @38 +|[+3#0000000&|C|o|m@1|a|n|d| |L|i|n|e|]| @7|1|,|4|2| @10|A|l@1 +| +0&&@39 diff --git a/src/testdir/dumps/Test_wildmenu_pum_22.dump b/src/testdir/dumps/Test_wildmenu_pum_22.dump index e774d5dda2..463195169e 100644 --- a/src/testdir/dumps/Test_wildmenu_pum_22.dump +++ b/src/testdir/dumps/Test_wildmenu_pum_22.dump @@ -3,7 +3,7 @@ |:+0#4040ff13&|s+0#af5f00255&|e|t| +0#0000000&|w+0#e000e06&|i|l|d|m|o|d|e|=+0#af5f00255&|l+0#0000000&|o|n|g|e|s|t|,+0#e000e06&|f+0#0000000&|u|l@1| @48 |:+0#4040ff13&|s+0#af5f00255&|e|t| +0#0000000&|w+0#e000e06&|i|l|d|m|o|d|e|=+0#af5f00255&|f+0#0000000&|u|l@1| @56 |:+0#4040ff13&|s+0#af5f00255&|i|g|n| +0#0000000&|d|e|f|i|n|e| @62 -|:+0#4040ff13&|s+0#af5f00255&|i|g|n| +0#0000000&|d|e|f|i|n|e> @62 +|:+0#4040ff13&|s+0#af5f00255&|i|g|n| +0#0000000&|d|e|f|i|n>e| @62 |~+0#4040ff13&| @73 |~| @73 |[+3#0000000&|C|o|m@1|a|n|d| |L|i|n|e|]| @60 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 2810e0b7e8..7dbb1d2084 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -2947,7 +2947,7 @@ func Test_wildmenu_pum() " When the popup is open, entering the cmdline window should close the popup call term_sendkeys(buf, "\sign \\") - call WaitForTermCurPosAndLinesToMatch(buf, [6, (strlen(':sign define') + 1)], g:test_timeout, (rows, '^You discovered the command-line window! You can close it with ":q"\.')) + call WaitForTermCurPosAndLinesToMatch(buf, [6, strlen(':sign define')], g:test_timeout, (rows, '^You discovered the command-line window! You can close it with ":q"\.')) call WaitFor({buf_ -> {-> term_scrape(buf, 6)->slice(1, 5)->filter({_, v -> v.fg == '#af5f00'})->len() == 4}}(buf), g:test_timeout) call VerifyScreenDump(buf, 'Test_wildmenu_pum_22', {}) call term_sendkeys(buf, ":q\") diff --git a/src/testdir/test_cmdwin.vim b/src/testdir/test_cmdwin.vim index b25862dad7..360dc67bb8 100644 --- a/src/testdir/test_cmdwin.vim +++ b/src/testdir/test_cmdwin.vim @@ -611,4 +611,38 @@ func Test_cmdwin_showcmd() call StopVimInTerminal(buf) endfunc +func Test_cmdwin_cursor_position() + " When the cmdline fills the screen width exactly, pressing CTRL-F to open + " the cmdwin should place the cursor on the last character, not past it. + let cmd = 'echo "' .. repeat('a', &columns - 8) .. '"' + call assert_equal(&columns - 1, len(cmd)) + let g:cmdwin_col = 0 + let g:cmdwin_line = '' + call feedkeys(':' .. cmd .. "\" .. + \ ":let g:cmdwin_col = col('.')\" .. + \ ":let g:cmdwin_line = getline('.')\" .. + \ ":q\", 'x!') + call assert_equal(len(cmd), g:cmdwin_col) + call assert_equal(cmd, g:cmdwin_line) + unlet g:cmdwin_col g:cmdwin_line +endfunc + +func Test_cmdwin_no_prefix_on_wrapped_line() + CheckScreendump + + let lines =<< trim END + augroup vimHints | au! | augroup END + END + call writefile(lines, 'Xcmdwin_wrap', 'D') + + let buf = RunVimInTerminal('-S Xcmdwin_wrap', #{rows: 12, cols: 40}) + let cmd = 'echo "' .. repeat('a', 40 - 8) .. '"XYZ' + call term_sendkeys(buf, ':' .. cmd .. "\") + call TermWait(buf, 100) + call VerifyScreenDump(buf, 'Test_cmdwin_wrap_prefix', {}) + + call term_sendkeys(buf, ":q\") + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index f9899cb4fc..c07927ba2b 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 346, /**/ 345, /**/ From 39875daec2e5fbd9444860601a678b2b7924f1e5 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Tue, 14 Apr 2026 17:02:21 +0000 Subject: [PATCH 039/545] patch 9.2.0347: Vim9: script-local variable not found Problem: Vim9: script-local variable not found after function call (Mao-Yining) Solution: Accept a script local variable in a function which overrides a previous block-scope variable (Yegappan Lakshmanan) fixes: #19959 closes: #19963 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- src/testdir/test_vim9_script.vim | 22 ++++++++++++++++++++++ src/version.c | 2 ++ src/vim9compile.c | 5 +++++ 3 files changed, 29 insertions(+) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index eefb6174d8..30c2589ef2 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -5759,6 +5759,28 @@ def Test_multikey_dict_in_block() unlet g:TestDict enddef +" Test for overriding a block level variable with a new script level variable +" and referring to it in a function. +def Test_block_var_override_with_script_var() + var lines =<< trim END + vim9script + + if true + var lines = ['a'] + lines->filter((_, _) => true) + endif + + var lines = [] + + def Fx() + lines->add('b') + enddef + Fx() + assert_equal(['b'], lines) + END + v9.CheckSourceSuccess(lines) +enddef + " Test for using the type() function with void def Test_type_func_with_void() var lines =<< trim END diff --git a/src/version.c b/src/version.c index c07927ba2b..63d8715aa5 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 347, /**/ 346, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index c9976160e4..ea56f4ec5b 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -253,6 +253,11 @@ find_script_var(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack) { int idx; + if (ufunc->uf_block_depth == 0 && sav->sav_block_id == 0) + // If the function was defined at the script level (not inside a + // block), script-scope variables are always visible. + return sav; + // Go over the blocks that this function was defined in. If the // variable block ID matches it was visible to the function. for (idx = 0; idx < ufunc->uf_block_depth; ++idx) From f9cb0d14dc3d825646cf75558bc6f5323f654ead Mon Sep 17 00:00:00 2001 From: Ozaki Kiichi Date: Tue, 14 Apr 2026 18:13:32 +0000 Subject: [PATCH 040/545] CI: Separate out ASan tests closes: #19962 Signed-off-by: Ozaki Kiichi Signed-off-by: Christian Brabandt --- .github/actions/build_vim_on_linux/action.yml | 324 ++++++++++++++++ .github/actions/test_artifacts/action.yml | 9 +- .github/workflows/ci.yml | 353 +++++------------- Filelist | 2 + ci/gen_testset.py | 43 +++ 5 files changed, 464 insertions(+), 267 deletions(-) create mode 100644 .github/actions/build_vim_on_linux/action.yml create mode 100644 ci/gen_testset.py diff --git a/.github/actions/build_vim_on_linux/action.yml b/.github/actions/build_vim_on_linux/action.yml new file mode 100644 index 0000000000..9a2105164b --- /dev/null +++ b/.github/actions/build_vim_on_linux/action.yml @@ -0,0 +1,324 @@ +name: Build Vim on Linux +description: Build Vim on Linux + +inputs: + features: + description: Vim features + required: true + compiler: + description: Compiler + required: true + architecture: + description: Architecture + required: false + extra: + description: Extra flags + required: true + shadow: + description: Shadow directory + required: false + interface: + description: Interface of language interpreter + required: false + lua_ver: + description: Lua version + required: false + python3: + description: Python3 ABI type + required: false + coverage: + description: Enable coverage + required: false + +runs: + using: "composite" + steps: + - name: Check Filelist (for packaging) + shell: bash + run: | + echo '::group::Check Filelist (for packaging)' + # If any files in the repository are not listed in Filelist this will + # exit with an error code and list the missing entries. + make -f ci/unlisted.make + echo '::endgroup::' + + - name: Check hlgroups (are any new hlgroups added, but not handled in highlight.c) + shell: bash + run: | + echo '::group::Check hlgroups (are any new hlgroups added, but not handled in highlight.c)' + # 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 + echo '::endgroup::' + + - name: Report executable syntax tests + if: contains(fromJSON(inputs.extra), 'syn_test_execs') + shell: bash + run: | + echo '::group::Report executable syntax tests' + # Search and list all found executable syntax tests, and exit with + # an error code. + make -C runtime/syntax executables + echo '::endgroup::' + + - name: Add i386 packages to dpkg + if: inputs.architecture == 'i386' + shell: bash + run: | + echo '::group::Add i386 packages to dpkg' + sudo dpkg --add-architecture i386 + echo '::endgroup::' + + - name: Uninstall snap + shell: bash + run: | + echo '::group::Uninstall snap' + sudo bash ci/remove_snap.sh + echo '::endgroup::' + + - name: Enable debug packages + shell: bash + run: | + echo '::group::Enable debug packages' + # Some of the ASAN suppressions are in libraries linked with dlopen + # and symbolization of them requires these debug packages. + sudo apt install ubuntu-dbgsym-keyring + sudo cp ci/ddebs.list /etc/apt/sources.list.d/ddebs.list + sudo cp ci/pinned-pkgs /etc/apt/preferences.d/pinned-pkgs + echo '::endgroup::' + + - name: Install packages + shell: bash + run: | + echo '::group::Install packages' + # This is added by default, and it is often broken, but we don't need anything from it + sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list + PKGS=( \ + gettext \ + x11-utils \ + labwc \ + wl-clipboard \ + wayland-utils \ + libgtk-3-dev:${{ inputs.architecture }} \ + libgtk-3-bin:${{ inputs.architecture }} \ + desktop-file-utils \ + libc6-dbgsym:${{ inputs.architecture }} \ + libtool-bin \ + libncurses-dev:${{ inputs.architecture }} \ + libxt-dev:${{ inputs.architecture }} \ + libegl-mesa0:${{ inputs.architecture }} \ + libegl1:${{ inputs.architecture }} \ + libegl1-mesa-dev:${{ inputs.architecture }} \ + libepoxy-dev:${{ inputs.architecture }} \ + libwayland-egl1:${{ inputs.architecture }} \ + libwayland-client0:${{ inputs.architecture }} \ + libwayland-cursor0:${{ inputs.architecture }} \ + locales-all \ + software-properties-common \ + ) + if ${{ contains(fromJSON(inputs.extra), 'asan') }} && ${{ contains(inputs.architecture, 'native') }}; then + PKGS+=( \ + libepoxy0-dbgsym:${{ inputs.architecture }} \ + libxdamage1-dbgsym:${{ inputs.architecture }} \ + libxcb1-dbgsym:${{ inputs.architecture }} \ + libgtk-3-bin-dbgsym:${{ inputs.architecture }} \ + libgtk-3-0t64-dbgsym:${{ inputs.architecture }} \ + libglib2.0-0t64-dbgsym:${{ inputs.architecture }} \ + libglib2.0-bin-dbgsym:${{ inputs.architecture }} \ + libglib2.0-dev-bin-dbgsym:${{ inputs.architecture }} \ + ) + fi + if ${{ inputs.features == 'huge' }}; then + LUA_VER=${{ inputs.lua_ver || '5.4' }} + PKGS+=( \ + autoconf \ + gdb \ + lcov \ + libcanberra-dev \ + libperl-dev \ + python3-dev \ + liblua${LUA_VER}-dev \ + lua${LUA_VER} \ + ruby-dev \ + tcl-dev \ + cscope \ + libsodium-dev \ + attr \ + libattr1-dev + ) + fi + if ${{ contains(fromJSON(inputs.extra), 'proto') }}; then + PKGS+=( python3-clang ) + fi + sudo apt-get update && sudo apt-get upgrade -y --allow-downgrades && sudo apt-get install -y --allow-downgrades "${PKGS[@]}" + + - name: Install gcc-${{ env.GCC_VER }} + if: inputs.compiler == 'gcc' + shell: bash + run: | + echo '::group::Install gcc-${{ env.GCC_VER }}' + # ubuntu-toolchain-r/test PPA for gcc-13 compiler + # disabled because the installation failed, causing test failures + # sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y + sudo apt-get update -y + sudo apt-get install -y gcc-${{ env.GCC_VER }}:${{ inputs.architecture }} + sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{ env.GCC_VER }} 100 + sudo update-alternatives --set gcc /usr/bin/gcc-${{ env.GCC_VER }} + echo '::endgroup::' + + - name: Install clang-${{ env.CLANG_VER }} + if: inputs.compiler == 'clang' + shell: bash + run: | + echo '::group::Install clang-${{ env.CLANG_VER }}' + . /etc/lsb-release + curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/llvm-archive-keyring.gpg > /dev/null + echo "Types: deb + URIs: https://apt.llvm.org/${DISTRIB_CODENAME}/ + Suites: llvm-toolchain-${DISTRIB_CODENAME}-${{ env.CLANG_VER }} + Components: main + Signed-By: /usr/share/keyrings/llvm-archive-keyring.gpg" | sudo tee /etc/apt/sources.list.d/llvm-toolchain.sources > /dev/null + sudo apt-get update -y + sudo apt-get install -y clang-${{ env.CLANG_VER }} llvm-${{ env.CLANG_VER }} + sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${{ env.CLANG_VER }} 100 + sudo update-alternatives --set clang /usr/bin/clang-${{ env.CLANG_VER }} + sudo update-alternatives --install /usr/bin/llvm-cov llvm-cov /usr/bin/llvm-cov-${{ env.CLANG_VER }} 100 + sudo update-alternatives --install /usr/bin/asan_symbolize asan_symbolize /usr/bin/asan_symbolize-${{ env.CLANG_VER }} 100 + echo '::endgroup::' + + - name: Set up environment + shell: bash + run: | + echo '::group::Set up environment' + mkdir -p "${LOG_DIR}" + mkdir -p "${HOME}/bin" + echo "${HOME}/bin" >> $GITHUB_PATH + ( + echo "LINUX_VERSION=$(uname -r)" + echo "NPROC=$(getconf _NPROCESSORS_ONLN)" + echo "TMPDIR=$(mktemp -d -p /tmp)" + + case "${{ inputs.features }}" in + tiny) + echo "TEST=testtiny" + if ${{ contains(fromJSON(inputs.extra), 'nogui') }}; then + CONFOPT="--disable-gui" + fi + ;; + normal) + ;; + huge) + echo "TEST=scripttests test_libvterm indenttest syntaxtest" + INTERFACE=${{ inputs.interface || 'yes' }} + if ${{ inputs.python3 == 'stable-abi' }}; then + PYTHON3_CONFOPT="--with-python3-stable-abi=3.8" + fi + # The ubuntu-24.04 CI runner does not provide a python2 package. + CONFOPT="--enable-perlinterp=${INTERFACE} --enable-pythoninterp=no --enable-python3interp=${INTERFACE} --enable-rubyinterp=${INTERFACE} --enable-luainterp=${INTERFACE} --enable-tclinterp=${INTERFACE} ${PYTHON3_CONFOPT}" + ;; + esac + + if ${{ contains(fromJSON(inputs.extra), 'no_x11_wl') }}; then + CONFOPT="${CONFOPT} --without-x --disable-gui --without-wayland --enable-socketserver" + fi + if ${{ inputs.coverage == true }}; then + CFLAGS="${CFLAGS} --coverage -DUSE_GCOV_FLUSH" + echo "LDFLAGS=--coverage" + fi + if ${{ contains(fromJSON(inputs.extra), 'uchar') }}; then + CFLAGS="${CFLAGS} -funsigned-char" + fi + if ${{ contains(fromJSON(inputs.extra), 'testgui') }}; then + echo "TEST=-C src testgui" + fi + if ${{ contains(fromJSON(inputs.extra), 'unittests') }}; then + echo "TEST=unittests" + fi + if ${{ contains(fromJSON(inputs.extra), 'asan') }}; then + echo "SANITIZER_CFLAGS=-g -O0 -DABORT_ON_INTERNAL_ERROR -DEXITFREE -fsanitize-recover=all -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" + echo "ASAN_OPTIONS=print_stacktrace=1:log_path=${LOG_DIR}/asan" + echo "UBSAN_OPTIONS=print_stacktrace=1:log_path=${LOG_DIR}/ubsan" + echo "LSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/src/testdir/lsan-suppress.txt" + fi + if ${{ contains(fromJSON(inputs.extra), 'vimtags') }}; then + echo "TEST=-C runtime/doc vimtags VIMEXE=../../${SRCDIR}/vim" + fi + if ${{ contains(fromJSON(inputs.extra), 'proto') }}; then + echo "TEST=-C src protoclean proto" + fi + echo "CFLAGS=${CFLAGS}" + echo "CONFOPT=${CONFOPT}" + # Disables GTK attempt to integrate with the accessibility service that does run in CI. + echo "NO_AT_BRIDGE=1" + ) >> $GITHUB_ENV + echo '::endgroup::' + + - name: Set up system + shell: bash + run: | + echo '::group::Set up system' + if [[ ${CC} = clang ]]; then + # Use llvm-cov instead of gcov when compiler is clang. + ln -fs /usr/bin/llvm-cov ${HOME}/bin/gcov + fi + sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0 + sudo usermod -a -G audio "${USER}" + sudo bash ci/setup-xvfb.sh + echo '::endgroup::' + + - name: Check autoconf + if: contains(fromJSON(inputs.extra), 'unittests') + shell: bash + run: | + echo '::group::Check autoconf' + make -C src autoconf + echo '::endgroup::' + + - name: Set up shadow dir + if: inputs.shadow + shell: bash + run: | + echo '::group::Set up shadow dir' + make -C src shadow + echo "SRCDIR=${{ inputs.shadow }}" >> $GITHUB_ENV + echo "SHADOWOPT=-C ${{ inputs.shadow }}" >> $GITHUB_ENV + echo '::endgroup::' + + - name: Configure + shell: bash + run: | + echo '::group::Configure' + ./configure --with-features=${{ inputs.features }} ${CONFOPT} --enable-fail-if-missing + # Append various warning flags to CFLAGS. + sed -i -f ci/config.mk.sed ${SRCDIR}/auto/config.mk + sed -i -f ci/config.mk.${CC}.sed ${SRCDIR}/auto/config.mk + if [[ ${CC} = clang ]]; then + # Suppress some warnings produced by clang 12 and later. + sed -i -f ci/config.mk.clang-12.sed ${SRCDIR}/auto/config.mk + fi + echo '::endgroup::' + + - name: Build + if: (!contains(fromJSON(inputs.extra), 'unittests')) + shell: bash + run: | + echo '::group::Build' + make ${SHADOWOPT} -j${NPROC} + echo '::endgroup::' + + - name: Check version + if: (!contains(fromJSON(inputs.extra), 'unittests')) + shell: bash + run: | + echo '::group::Check version' + "${SRCDIR}"/vim --version + "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-1.vim -c quit + "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-2.vim -c quit + if ${{ inputs.features == 'huge' }}; then + # Also check that optional and dynamic features are configured and working + "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 \ + -c "let g:required=['gettext', 'sodium', 'sound', 'perl', 'python3', 'lua', 'ruby', 'tcl']" \ + -S ci/if_feat_check.vim -c quit + fi + echo '::endgroup::' diff --git a/.github/actions/test_artifacts/action.yml b/.github/actions/test_artifacts/action.yml index 79d1aa82f6..eb4313de81 100644 --- a/.github/actions/test_artifacts/action.yml +++ b/.github/actions/test_artifacts/action.yml @@ -1,4 +1,4 @@ -name: 'test_artifacts' +name: "test_artifacts" description: "Upload failed test artifacts" runs: using: "composite" @@ -40,9 +40,10 @@ runs: # A file, directory or wildcard pattern that describes what # to upload. path: | - ${{ github.workspace }}/runtime/indent/testdir/*.fail - ${{ github.workspace }}/runtime/syntax/testdir/failed/* - ${{ github.workspace }}/src/testdir/failed/* + ${{ github.workspace }}/logs/**/*.symbolized + ${{ github.workspace }}/runtime/indent/testdir/*.fail + ${{ github.workspace }}/runtime/syntax/testdir/failed/* + ${{ github.workspace }}/src/testdir/failed/* # The desired behavior if no files are found using the # provided path. if-no-files-found: ignore diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f09f06ee2..c2a9003a19 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: GitHub CI on: push: - branches: ['**'] + branches: ["**"] pull_request: # Cancels all previous workflow runs for pull requests that have not completed. @@ -28,7 +28,7 @@ jobs: LEAK_CFLAGS: -DEXITFREE LOG_DIR: ${{ github.workspace }}/logs TERM: xterm - DISPLAY: ':99' + DISPLAY: ":99" DEBIAN_FRONTEND: noninteractive strategy: @@ -67,12 +67,6 @@ jobs: coverage: true interface: dynamic extra: [uchar, testgui] - - features: huge - compiler: clang - # Lua5.1 is the most widely used version (since it's what LuaJIT is - # compatible with), so ensure it works - lua_ver: '5.1' - extra: [asan] - features: huge compiler: gcc coverage: true @@ -90,254 +84,26 @@ jobs: extra: [no_x11_wl] steps: - - name: Checkout repository from github + - name: Checkout repository from GitHub uses: actions/checkout@v6 - - name: Check Filelist (for packaging) - run: | - # If any files in the repository are not listed in Filelist this will - # 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 - - - name: Report executable syntax tests - if: contains(matrix.extra, 'syn_test_execs') - run: | - # Search and list all found executable syntax tests, and exit with - # an error code. - make -C runtime/syntax executables - - - run: sudo dpkg --add-architecture i386 - if: matrix.architecture == 'i386' - - - name: Uninstall snap - run: | - sudo bash ci/remove_snap.sh - - - name: Enable debug packages - run: | - # Some of the ASAN suppressions are in libraries linked with dlopen - # and symbolization of them requires these debug packages. - sudo apt install ubuntu-dbgsym-keyring - sudo cp ci/ddebs.list /etc/apt/sources.list.d/ddebs.list - sudo cp ci/pinned-pkgs /etc/apt/preferences.d/pinned-pkgs - - - name: Install packages - run: | - # This is added by default, and it is often broken, but we don't need anything from it - sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list - PKGS=( \ - gettext \ - x11-utils \ - labwc \ - wl-clipboard \ - wayland-utils \ - libgtk-3-dev:${{ matrix.architecture }} \ - libgtk-3-bin:${{ matrix.architecture }} \ - desktop-file-utils \ - libc6-dbgsym:${{ matrix.architecture }} \ - libtool-bin \ - libncurses-dev:${{ matrix.architecture }} \ - libxt-dev:${{ matrix.architecture }} \ - libegl-mesa0:${{ matrix.architecture }} \ - libegl1:${{ matrix.architecture }} \ - libegl1-mesa-dev:${{ matrix.architecture }} \ - libepoxy-dev:${{ matrix.architecture }} \ - libwayland-egl1:${{ matrix.architecture }} \ - libwayland-client0:${{ matrix.architecture }} \ - libwayland-cursor0:${{ matrix.architecture }} \ - locales-all \ - software-properties-common \ - ) - if ${{ contains(matrix.extra, 'asan') }} && ${{ contains(matrix.architecture, 'native') }}; then - PKGS+=( \ - libepoxy0-dbgsym:${{ matrix.architecture }} \ - libxdamage1-dbgsym:${{ matrix.architecture }} \ - libxcb1-dbgsym:${{ matrix.architecture }} \ - libgtk-3-bin-dbgsym:${{ matrix.architecture }} \ - libgtk-3-0t64-dbgsym:${{ matrix.architecture }} \ - libglib2.0-0t64-dbgsym:${{ matrix.architecture }} \ - libglib2.0-bin-dbgsym:${{ matrix.architecture }} \ - libglib2.0-dev-bin-dbgsym:${{ matrix.architecture }} \ - ) - fi - if ${{ matrix.features == 'huge' }}; then - LUA_VER=${{ matrix.lua_ver || '5.4' }} - PKGS+=( \ - autoconf \ - gdb \ - lcov \ - libcanberra-dev \ - libperl-dev \ - python3-dev \ - liblua${LUA_VER}-dev \ - lua${LUA_VER} \ - ruby-dev \ - tcl-dev \ - cscope \ - libsodium-dev \ - attr \ - libattr1-dev - ) - fi - if ${{ contains(matrix.extra, 'proto') }}; then - PKGS+=( python3-clang ) - fi - sudo apt-get update && sudo apt-get upgrade -y --allow-downgrades && sudo apt-get install -y --allow-downgrades "${PKGS[@]}" - - - name: Install gcc-${{ env.GCC_VER }} - if: matrix.compiler == 'gcc' - run: | - # ubuntu-toolchain-r/test PPA for gcc-13 compiler - # disabled because the installation failed, causing test failures - # sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get update -y - sudo apt-get install -y gcc-${{ env.GCC_VER }}:${{ matrix.architecture }} - sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{ env.GCC_VER }} 100 - sudo update-alternatives --set gcc /usr/bin/gcc-${{ env.GCC_VER }} - - - name: Install clang-${{ env.CLANG_VER }} - if: matrix.compiler == 'clang' - run: | - . /etc/lsb-release - curl -fsSL https://apt.llvm.org/llvm-snapshot.gpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/llvm-archive-keyring.gpg > /dev/null - echo "Types: deb - URIs: https://apt.llvm.org/${DISTRIB_CODENAME}/ - Suites: llvm-toolchain-${DISTRIB_CODENAME}-${{ env.CLANG_VER }} - Components: main - Signed-By: /usr/share/keyrings/llvm-archive-keyring.gpg" | sudo tee /etc/apt/sources.list.d/llvm-toolchain.sources > /dev/null - sudo apt-get update -y - sudo apt-get install -y clang-${{ env.CLANG_VER }} llvm-${{ env.CLANG_VER }} - sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${{ env.CLANG_VER }} 100 - sudo update-alternatives --set clang /usr/bin/clang-${{ env.CLANG_VER }} - sudo update-alternatives --install /usr/bin/llvm-cov llvm-cov /usr/bin/llvm-cov-${{ env.CLANG_VER }} 100 - sudo update-alternatives --install /usr/bin/asan_symbolize asan_symbolize /usr/bin/asan_symbolize-${{ env.CLANG_VER }} 100 - - - name: Set up environment - run: | - mkdir -p "${LOG_DIR}" - mkdir -p "${HOME}/bin" - echo "${HOME}/bin" >> $GITHUB_PATH - ( - echo "LINUX_VERSION=$(uname -r)" - echo "NPROC=$(getconf _NPROCESSORS_ONLN)" - echo "TMPDIR=$(mktemp -d -p /tmp)" - - case "${{ matrix.features }}" in - tiny) - echo "TEST=testtiny" - if ${{ contains(matrix.extra, 'nogui') }}; then - CONFOPT="--disable-gui" - fi - ;; - normal) - ;; - huge) - echo "TEST=scripttests test_libvterm indenttest syntaxtest" - INTERFACE=${{ matrix.interface || 'yes' }} - if ${{ matrix.python3 == 'stable-abi' }}; then - PYTHON3_CONFOPT="--with-python3-stable-abi=3.8" - fi - # The ubuntu-24.04 CI runner does not provide a python2 package. - CONFOPT="--enable-perlinterp=${INTERFACE} --enable-pythoninterp=no --enable-python3interp=${INTERFACE} --enable-rubyinterp=${INTERFACE} --enable-luainterp=${INTERFACE} --enable-tclinterp=${INTERFACE} ${PYTHON3_CONFOPT}" - ;; - esac - - if ${{ contains(matrix.extra, 'no_x11_wl') }}; then - CONFOPT="${CONFOPT} --without-x --disable-gui --without-wayland --enable-socketserver" - fi - if ${{ matrix.coverage == true }}; then - CFLAGS="${CFLAGS} --coverage -DUSE_GCOV_FLUSH" - echo "LDFLAGS=--coverage" - fi - if ${{ contains(matrix.extra, 'uchar') }}; then - CFLAGS="${CFLAGS} -funsigned-char" - fi - if ${{ contains(matrix.extra, 'testgui') }}; then - echo "TEST=-C src testgui" - fi - if ${{ contains(matrix.extra, 'unittests') }}; then - echo "TEST=unittests" - fi - if ${{ contains(matrix.extra, 'asan') }}; then - echo "SANITIZER_CFLAGS=-g -O0 -DABORT_ON_INTERNAL_ERROR -DEXITFREE -fsanitize-recover=all -fsanitize=address -fsanitize=undefined -fno-omit-frame-pointer" - echo "ASAN_OPTIONS=print_stacktrace=1:log_path=${LOG_DIR}/asan" - echo "UBSAN_OPTIONS=print_stacktrace=1:log_path=${LOG_DIR}/ubsan" - echo "LSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/src/testdir/lsan-suppress.txt" - fi - if ${{ contains(matrix.extra, 'vimtags') }}; then - echo "TEST=-C runtime/doc vimtags VIMEXE=../../${SRCDIR}/vim" - fi - if ${{ contains(matrix.extra, 'proto') }}; then - echo "TEST=-C src protoclean proto" - fi - echo "CFLAGS=${CFLAGS}" - echo "CONFOPT=${CONFOPT}" - # Disables GTK attempt to integrate with the accessibility service that does run in CI. - echo "NO_AT_BRIDGE=1" - ) >> $GITHUB_ENV - - - name: Set up system - run: | - if [[ ${CC} = clang ]]; then - # Use llvm-cov instead of gcov when compiler is clang. - ln -fs /usr/bin/llvm-cov ${HOME}/bin/gcov - fi - sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=0 - sudo usermod -a -G audio "${USER}" - sudo bash ci/setup-xvfb.sh - - - name: Check autoconf - if: contains(matrix.extra, 'unittests') - run: | - make -C src autoconf - - - name: Set up shadow dir - if: matrix.shadow - run: | - make -C src shadow - echo "SRCDIR=${{ matrix.shadow }}" >> $GITHUB_ENV - echo "SHADOWOPT=-C ${{ matrix.shadow }}" >> $GITHUB_ENV - - - name: Configure - run: | - ./configure --with-features=${{ matrix.features }} ${CONFOPT} --enable-fail-if-missing - # Append various warning flags to CFLAGS. - sed -i -f ci/config.mk.sed ${SRCDIR}/auto/config.mk - sed -i -f ci/config.mk.${CC}.sed ${SRCDIR}/auto/config.mk - if [[ ${CC} = clang ]]; then - # Suppress some warnings produced by clang 12 and later. - sed -i -f ci/config.mk.clang-12.sed ${SRCDIR}/auto/config.mk - fi - - name: Build - if: (!contains(matrix.extra, 'unittests')) - run: | - make ${SHADOWOPT} -j${NPROC} - - - name: Check version - if: (!contains(matrix.extra, 'unittests')) - run: | - "${SRCDIR}"/vim --version - "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-1.vim -c quit - "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-2.vim -c quit - if ${{ matrix.features == 'huge' }}; then - # Also check that optional and dynamic features are configured and working - "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 \ - -c "let g:required=['gettext', 'sodium', 'sound', 'perl', 'python3', 'lua', 'ruby', 'tcl']" \ - -S ci/if_feat_check.vim -c quit - fi + timeout-minutes: 15 + uses: ./.github/actions/build_vim_on_linux + with: + features: ${{ matrix.features }} + compiler: ${{ matrix.compiler }} + architecture: ${{ matrix.architecture }} + extra: ${{ toJSON(matrix.extra) }} + shadow: ${{ matrix.shadow }} + interface: ${{ matrix.interface }} + lua_ver: ${{ matrix.lua_ver }} + python3: ${{ matrix.python3 }} + coverage: ${{ matrix.coverage }} - name: Test - timeout-minutes: 45 - run: | - make ${SHADOWOPT} ${TEST} + timeout-minutes: 20 + run: make ${SHADOWOPT} ${TEST} # Enable to debug failing tests live and ssh into the CI runners # - name: Setup tmate session @@ -346,7 +112,8 @@ jobs: # with: # limit-access-to-actor: true - - if: ${{ !cancelled() }} + - name: Upload failed test artifacts + if: ${{ !cancelled() }} uses: ./.github/actions/test_artifacts - name: Vim tags @@ -410,15 +177,73 @@ jobs: flags: linux,${{ matrix.features }}-${{ matrix.compiler }}-${{ join(matrix.extra, '-') }} token: ${{ secrets.CODECOV_TOKEN }} + linux-asan: + runs-on: ubuntu-24.04 + + env: + CC: clang + CLANG_VER: 21 + SRCDIR: ./src + LEAK_CFLAGS: -DEXITFREE + LOG_DIR: ${{ github.workspace }}/logs + TERM: xterm + DISPLAY: ":99" + DEBIAN_FRONTEND: noninteractive + + strategy: + fail-fast: false + matrix: + testset: + - tinytests + - newtests.1 + - newtests.2 + + steps: + - name: Checkout repository from GitHub + uses: actions/checkout@v6 + + - name: Build + timeout-minutes: 15 + uses: ./.github/actions/build_vim_on_linux + with: + features: huge + compiler: clang + extra: '["asan"]' + # Lua5.1 is the most widely used version (since it's what LuaJIT is + # compatible with), so ensure it works + lua_ver: "5.1" + + - name: Test (tinytests) + if: matrix.testset == 'tinytests' + timeout-minutes: 20 + run: make ${TEST} NEW_TESTS_RES= + + - name: Test (newtests) + if: startsWith(matrix.testset, 'newtests') + timeout-minutes: 20 + env: + JOB_INDEX: ${{ strategy.job-index }} + JOB_TOTAL: ${{ strategy.job-total }} + run: | + set -x + make -C src/testdir SCRIPTS_TINY_OUT= \ + NEW_TESTS_RES="$(python3 ci/gen_testset.py $((JOB_TOTAL-1)) | jq -r --argjson i $((JOB_INDEX-1)) '.[$i]|join(" ")')" + - name: ASan logs - if: contains(matrix.extra, 'asan') && !cancelled() + if: ${{ !cancelled() }} run: | for f in $(grep -lR '#[[:digit:]]* *0x[[:xdigit:]]*' "${LOG_DIR}"); do - echo "$f" - asan_symbolize -l "$f" + ( + echo "$f" + asan_symbolize -l "$f" + ) | tee "$f".symbolized false # in order to fail a job done + - name: Upload failed test artifacts + if: ${{ !cancelled() }} + uses: ./.github/actions/test_artifacts + macos: runs-on: ${{ matrix.runner }} @@ -436,7 +261,7 @@ jobs: runner: [macos-15-intel, macos-26] steps: - - name: Checkout repository from github + - name: Checkout repository from GitHub uses: actions/checkout@v6 - name: Install packages @@ -503,7 +328,8 @@ jobs: run: | make ${TEST} - - if: ${{ !cancelled() }} + - name: Upload failed test artifacts + if: ${{ !cancelled() }} uses: ./.github/actions/test_artifacts windows: @@ -513,7 +339,7 @@ jobs: # Interfaces # Lua LUA_VER: 54 - LUA_VER_DOT: '5.4' + LUA_VER_DOT: "5.4" LUA_RELEASE: 5.4.2 LUA32_URL: https://downloads.sourceforge.net/luabinaries/lua-%LUA_RELEASE%_Win32_dllw6_lib.zip LUA64_URL: https://downloads.sourceforge.net/luabinaries/lua-%LUA_RELEASE%_Win64_dllw6_lib.zip @@ -522,16 +348,16 @@ jobs: LUA_DIR_SLASH: D:/Lua # Python 2 PYTHON_VER: 27 - PYTHON_VER_DOT: '2.7' + PYTHON_VER_DOT: "2.7" PYTHON_DIR: 'C:\Python27' # Python 3 PYTHON3_VER: 313 - PYTHON3_VER_DOT: '3.13' + PYTHON3_VER_DOT: "3.13" # Other dependencies # winpty WINPTY_URL: https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip # libsodium - SODIUM_VER: '1.0.20' + SODIUM_VER: "1.0.20" # SODIUM_MSVC_URL: https://download.libsodium.org/libsodium/releases/libsodium-%SODIUM_VER%-stable-msvc.zip SODIUM_MSVC_URL: https://github.com/jedisct1/libsodium/releases/download/%SODIUM_VER%-RELEASE/libsodium-%SODIUM_VER%-msvc.zip SODIUM_MSVC_VER: v143 @@ -619,7 +445,7 @@ jobs: msystem: ${{ env.MSYSTEM }} release: false - - name: Checkout repository from github + - name: Checkout repository from GitHub uses: actions/checkout@v6 - name: Create a list of download URLs @@ -833,7 +659,8 @@ jobs: nmake -nologo -f Make_mvc.mak tiny VIMPROG=..\vim || exit 1 ) - - if: ${{ !cancelled() }} + - name: Upload failed test artifacts + if: ${{ !cancelled() }} uses: ./.github/actions/test_artifacts - name: Generate gcov files diff --git a/Filelist b/Filelist index 1522e33740..4832028577 100644 --- a/Filelist +++ b/Filelist @@ -13,6 +13,7 @@ SRC_ALL = \ .github/workflows/codeql-analysis.yml \ .github/workflows/coverity.yml \ .github/workflows/link-check.yml \ + .github/actions/build_vim_on_linux/action.yml \ .github/actions/test_artifacts/action.yml \ .github/dependabot.yml \ .gitignore \ @@ -23,6 +24,7 @@ SRC_ALL = \ .editorconfig \ ci/appveyor.bat \ ci/config.mk*.sed \ + ci/gen_testset.py \ ci/if_ver*.vim \ ci/if_feat_check.vim \ ci/lychee.toml \ diff --git a/ci/gen_testset.py b/ci/gen_testset.py new file mode 100644 index 0000000000..91eeca111e --- /dev/null +++ b/ci/gen_testset.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +import argparse +import itertools +import json +import re +import subprocess + + +def generate_testset(n): + cp = subprocess.run(["make", "-C", "src/testdir", "-npq"], capture_output=True) + + tests = set() + for line in cp.stdout.decode().split("\n"): + if re.match(r"^(NEW_TESTS_RES|TEST_VIM9_RES) = ", line): + tests.update(re.findall(r"\btest\w+\.res\b", line)) + + tests = sorted(list(tests)) + # move test_alot*.res to the end + tests = ( + [t for t in tests if not t.startswith("test_alot")] + + [t for t in tests if t.startswith("test_alot_")] + + ["test_alot.res"] + ) + targets = tests + + if n > 1: + targets = [ts for ts in itertools.batched(tests, n)] + targets = [[t for t in ts if t] for ts in itertools.zip_longest(*targets)] + + return targets + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("n", type=int, nargs="?", default=1) + args = parser.parse_args() + + print(json.dumps(generate_testset(args.n))) + + +if __name__ == "__main__": + main() From 91b402f57575ed33649285043a3c631701165f4a Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Tue, 14 Apr 2026 18:18:36 +0000 Subject: [PATCH 041/545] patch 9.2.0348: potential buffer underrun when setting statusline like option Problem: potential buffer underrun when settings statusline like option (q1uf3ng) Solution: Validate that p > out before accessing p[-1] closes: #19961 Signed-off-by: Christian Brabandt --- src/buffer.c | 2 +- src/testdir/test_cmdline.vim | 10 ++++++++++ src/testdir/test_options.vim | 8 +++++--- src/testdir/test_statusline.vim | 10 ++++++++++ src/testdir/test_tabline.vim | 10 ++++++++++ src/testdir/test_tabpanel.vim | 10 ++++++++++ src/version.c | 2 ++ 7 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index cbee49e1aa..20f8dcc454 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -5028,7 +5028,7 @@ build_stl_str_hl_local( if (*s != '}') // missing '}' or out of space break; s++; - if (reevaluate) + if (reevaluate && p > out) p[-1] = NUL; // remove the % at the end of %{% expr %} else *p = NUL; diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 7dbb1d2084..270f1ddbcb 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -5390,4 +5390,14 @@ func Test_breaklist_args_fails() call assert_fails(':breaklist extra', 'E488:') endfunc +func Test_rulerformat_empty() + set ruler rulerformat=%!'%{}%' + try + redraw + catch + endtry + set ruler& + set rulerformat& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index d33c062528..7bd07ca0bc 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -870,9 +870,6 @@ func Test_set_option_errors() call assert_fails('set commentstring=x', 'E537:') call assert_fails('let &commentstring = "x"', 'E537:') call assert_fails('set complete=x', 'E539:') - call assert_fails('set rulerformat=%-', 'E539:') - call assert_fails('set rulerformat=%(', 'E542:') - call assert_fails('set rulerformat=%15(%%', 'E542:') " Test for 'statusline' errors call assert_fails('set statusline=%$', 'E539:') @@ -890,6 +887,11 @@ func Test_set_option_errors() call assert_fails('set tabline=%(', 'E542:') call assert_fails('set tabline=%)', 'E542:') + " Test for 'rulerformat' errors + call assert_fails('set rulerformat=%-', 'E539:') + call assert_fails('set rulerformat=%(', 'E542:') + call assert_fails('set rulerformat=%15(%%', 'E542:') + if has('cursorshape') " This invalid value for 'guicursor' used to cause Vim to crash. call assert_fails('set guicursor=i-ci,r-cr:h', 'E545:') diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 27fef946af..6936ca8078 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -930,4 +930,14 @@ func Test_tabline_click_handler() endif endfunc +func Test_statusline_empty() + set laststatus=2 statusline=%!'%{}%' + try + redraw! + catch + endtry + set laststatus& + set statusline& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_tabline.vim b/src/testdir/test_tabline.vim index 21f66cfcde..7ae0602489 100644 --- a/src/testdir/test_tabline.vim +++ b/src/testdir/test_tabline.vim @@ -250,4 +250,14 @@ func Test_tabline_mouse_enable() endfor endfunc +func Test_tabline_empty() + set showtabline=2 tabline=%!'%{}%' + try + redraw! + catch + endtry + set showtabline& + set tabline& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_tabpanel.vim b/src/testdir/test_tabpanel.vim index f983e2b37d..4bb7f39eb0 100644 --- a/src/testdir/test_tabpanel.vim +++ b/src/testdir/test_tabpanel.vim @@ -923,4 +923,14 @@ func Test_tabpanel_variable_height() %bwipeout! endfunc +func Test_tabpanel_empty() + set showtabpanel=2 tabpanel=%!'%{}%' + try + redraw! + catch + endtry + set showtabpanel& + set tabpanel& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 63d8715aa5..dec32640e3 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 348, /**/ 347, /**/ From c72196529bbe73c4dbb966ed9e83c2f620f864ce Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Tue, 14 Apr 2026 18:39:52 +0000 Subject: [PATCH 042/545] patch 9.2.0349: cannot style non-current window separator Problem: cannot style non-current window separator Solution: Add the VertSplitNC highlighting group (Hirohito Higashi). Add VertSplitNC highlight group for vertical separators of non-current windows, similar to StatusLine/StatusLineNC distinction. The separator adjacent to the current window uses VertSplit, others use VertSplitNC. Default: linked to VertSplit. At the current window's status line rows, the separator cell is drawn as a space with the StatusLine highlight so it blends into the status line without a stray fillchar glyph. Also: - :redrawstatus[!] now also redraws vertical separators. - statusline height changes trigger vsep redraw. closes: #19951 Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 5 +- runtime/doc/syntax.txt | 11 +- runtime/doc/tags | 1 + runtime/doc/version9.txt | 6 +- runtime/syntax/vim.vim | 6 +- src/drawscreen.c | 27 +++- src/ex_docmd.c | 1 + src/globals.h | 1 + src/highlight.c | 1 + src/optiondefs.h | 2 +- src/proto/screen.pro | 3 +- src/screen.c | 122 ++++++++++++++++-- src/testdir/dumps/Test_VertSplitNC_1.dump | 12 ++ src/testdir/dumps/Test_VertSplitNC_2.dump | 12 ++ src/testdir/dumps/Test_VertSplitNC_3.dump | 12 ++ src/testdir/dumps/Test_VertSplitNC_fc_1.dump | 12 ++ src/testdir/dumps/Test_VertSplitNC_fc_2.dump | 12 ++ src/testdir/dumps/Test_VertSplitNC_stl_1.dump | 20 +++ src/testdir/dumps/Test_VertSplitNC_stl_2.dump | 20 +++ src/testdir/dumps/Test_VertSplitNC_stl_3.dump | 20 +++ src/testdir/dumps/Test_diff_anchors_07.dump | 2 +- ...st_diff_anchors_scrollbind_topline_01.dump | 2 +- ...st_diff_anchors_scrollbind_topline_02.dump | 2 +- ...st_diff_anchors_scrollbind_topline_03.dump | 2 +- ...st_diff_anchors_scrollbind_topline_04.dump | 2 +- ...st_diff_anchors_scrollbind_topline_05.dump | 2 +- ...st_diff_anchors_scrollbind_topline_06.dump | 2 +- ...st_diff_anchors_scrollbind_topline_07.dump | 2 +- ...st_diff_anchors_scrollbind_topline_08.dump | 2 +- ...st_diff_anchors_scrollbind_topline_13.dump | 2 +- ...st_diff_anchors_scrollbind_topline_14.dump | 2 +- ...st_diff_anchors_scrollbind_topline_15.dump | 2 +- ...st_diff_anchors_scrollbind_topline_16.dump | 2 +- ...st_diff_anchors_scrollbind_topline_17.dump | 2 +- .../dumps/Test_diff_get_put_linematch_16.dump | 2 +- .../dumps/Test_diff_get_put_linematch_17.dump | 2 +- .../dumps/Test_diff_get_put_linematch_18.dump | 2 +- .../dumps/Test_diff_get_put_linematch_19.dump | 2 +- .../dumps/Test_diff_get_put_linematch_3.dump | 2 +- .../dumps/Test_diff_get_put_linematch_4.dump | 2 +- .../dumps/Test_diff_inline_multiline_07.dump | 2 +- .../dumps/Test_diff_inline_multiline_08.dump | 2 +- .../dumps/Test_diff_inline_multiline_09.dump | 2 +- .../dumps/Test_diff_inline_multiline_10.dump | 2 +- src/testdir/dumps/Test_diff_rnu_01.dump | 2 +- src/testdir/dumps/Test_diff_rnu_02.dump | 2 +- src/testdir/dumps/Test_diff_rnu_03.dump | 2 +- src/testdir/dumps/Test_diff_scroll_1.dump | 2 +- src/testdir/dumps/Test_diff_scroll_2.dump | 2 +- .../dumps/Test_diff_scroll_change_03.dump | 2 +- src/testdir/dumps/Test_diff_topline_1.dump | 2 +- src/testdir/dumps/Test_diff_topline_2.dump | 2 +- src/testdir/dumps/Test_diff_topline_4.dump | 2 +- .../dumps/Test_diff_with_cul_bri_01.dump | 2 +- .../dumps/Test_diff_with_cul_bri_02.dump | 2 +- .../dumps/Test_diff_with_cul_bri_03.dump | 2 +- .../dumps/Test_diff_with_cul_bri_04.dump | 2 +- .../dumps/Test_diff_with_cursorline_01.dump | 2 +- .../dumps/Test_diff_with_cursorline_02.dump | 2 +- .../dumps/Test_diff_with_cursorline_03.dump | 2 +- .../dumps/Test_display_fillchars_1.dump | 4 +- .../dumps/Test_display_fillchars_2.dump | 4 +- src/testdir/dumps/Test_hor_scroll_1.dump | 2 +- src/testdir/dumps/Test_hor_scroll_2.dump | 2 +- src/testdir/dumps/Test_hor_scroll_3.dump | 2 +- src/testdir/dumps/Test_hor_scroll_4.dump | 2 +- src/testdir/dumps/Test_hor_scroll_5.dump | 2 +- .../Test_laststatus_vsplit_row_height3_1.dump | 2 +- src/testdir/dumps/Test_linematch_3diffs1.dump | 2 +- src/testdir/dumps/Test_listchars_01.dump | 2 +- src/testdir/dumps/Test_listchars_02.dump | 2 +- src/testdir/dumps/Test_listchars_03.dump | 2 +- src/testdir/dumps/Test_listchars_04.dump | 2 +- src/testdir/dumps/Test_listchars_05.dump | 2 +- src/testdir/dumps/Test_listchars_06.dump | 2 +- src/testdir/dumps/Test_listchars_07.dump | 2 +- .../dumps/Test_popup_prop_not_visible_01.dump | 2 +- .../Test_popup_prop_not_visible_01a.dump | 2 +- .../Test_popup_prop_not_visible_01b.dump | 2 +- .../dumps/Test_popup_prop_not_visible_02.dump | 2 +- .../dumps/Test_popup_prop_not_visible_03.dump | 2 +- src/testdir/dumps/Test_prop_diff_mode_2.dump | 2 +- .../dumps/Test_smooth_diff_change_line_1.dump | 2 +- .../dumps/Test_smooth_diff_change_line_2.dump | 2 +- .../dumps/Test_smooth_diff_change_line_3.dump | 2 +- .../Test_smooth_diff_change_line_3a.dump | 2 +- .../dumps/Test_smooth_diff_change_line_4.dump | 2 +- .../dumps/Test_switchwin_clear_pum_02.dump | 2 +- .../Test_tabpanel_drawing_scrolling_3.dump | 2 +- ...Test_terminal_wincolor_split_MyWinCol.dump | 4 +- ...est_terminal_wincolor_split_MyWinCol2.dump | 4 +- src/testdir/dumps/Test_winhighlight_14.dump | 2 +- src/testdir/dumps/Test_winhighlight_15.dump | 2 +- src/testdir/dumps/Test_winhighlight_2.dump | 2 +- src/testdir/dumps/Test_winhighlight_6.dump | 2 +- .../dumps/Test_winhighlight_hlsearch_2.dump | 2 +- .../dumps/Test_winscrolled_once_only_1.dump | 2 +- src/testdir/test_highlight.vim | 108 ++++++++++++++++ src/testdir/test_options.vim | 2 +- src/testdir/test_statusline.vim | 6 +- src/testdir/test_window_cmd.vim | 4 +- src/version.c | 2 + src/vim.h | 3 +- src/window.c | 10 ++ 104 files changed, 490 insertions(+), 113 deletions(-) create mode 100644 src/testdir/dumps/Test_VertSplitNC_1.dump create mode 100644 src/testdir/dumps/Test_VertSplitNC_2.dump create mode 100644 src/testdir/dumps/Test_VertSplitNC_3.dump create mode 100644 src/testdir/dumps/Test_VertSplitNC_fc_1.dump create mode 100644 src/testdir/dumps/Test_VertSplitNC_fc_2.dump create mode 100644 src/testdir/dumps/Test_VertSplitNC_stl_1.dump create mode 100644 src/testdir/dumps/Test_VertSplitNC_stl_2.dump create mode 100644 src/testdir/dumps/Test_VertSplitNC_stl_3.dump diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 5f8b4292bc..4d40b8aa55 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -4757,7 +4757,8 @@ A jump table for the options with a short description can be found at |Q_op|. l:Search,m:MoreMsg,M:ModeMsg,n:LineNr, a:LineNrAbove,b:LineNrBelow, N:CursorLineNr,r:Question,s:StatusLine, - S:StatusLineNC,c:VertSplit,t:Title, + S:StatusLineNC,c:VertSplit, + |:VertSplitNC,t:Title, v:Visual,V:VisualNOS,w:WarningMsg, W:WildMenu,f:Folded,F:FoldColumn, A:DiffAdd,C:DiffChange,D:DiffDelete, @@ -4805,6 +4806,8 @@ A jump table for the options with a short description can be found at |Q_op|. |hl-StatusLineNC| S status lines of not-current windows |hl-Title| t Titles for output from ":set all", ":autocmd" etc. |hl-VertSplit| c column used to separate vertically split windows + |hl-VertSplitNC| | column separating non-current vertically split + windows |hl-Visual| v Visual mode |hl-VisualNOS| V Visual mode when Vim is "Not Owning the Selection" Only X11 Gui's |gui-x11|, diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index c771a400ab..2102debf23 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 Mar 22 +*syntax.txt* For Vim version 9.2. Last change: 2026 Apr 14 VIM REFERENCE MANUAL by Bram Moolenaar @@ -5978,7 +5978,14 @@ EndOfBuffer Filler lines (~) after the last line in the buffer. *hl-ErrorMsg* ErrorMsg Error messages on the command line. *hl-VertSplit* -VertSplit Column separating vertically split windows. +VertSplit Column separating vertically split windows that are adjacent + to the current window. On the current window's status line + rows, the separator cell is drawn as a space with the + |hl-StatusLine| highlight so it blends into the status line + without a stray fillchar glyph. + *hl-VertSplitNC* +VertSplitNC Column separating vertically split windows of non-current + windows. By default, highlighted like |hl-VertSplit|. *hl-Folded* Folded Line used for closed folds. *hl-FoldColumn* diff --git a/runtime/doc/tags b/runtime/doc/tags index 66f3731614..c4bc58fc94 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -8487,6 +8487,7 @@ hl-User1 syntax.txt /*hl-User1* hl-User1..9 syntax.txt /*hl-User1..9* hl-User9 syntax.txt /*hl-User9* hl-VertSplit syntax.txt /*hl-VertSplit* +hl-VertSplitNC syntax.txt /*hl-VertSplitNC* hl-Visual syntax.txt /*hl-Visual* hl-VisualNOS syntax.txt /*hl-VisualNOS* hl-WarningMsg syntax.txt /*hl-WarningMsg* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 2b3decac78..54449a5424 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 10 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 14 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52659,6 +52659,10 @@ Options: ~ 'termsync' Enable support for terminal DEC 2026 sync mode. 'winhighlight' Window-local highlight group mappings. +Highlighting: ~ + +|hl-VertSplitNC| Column separator of non-current window. + ============================================================================== PATCHES *patches-9.3* *bug-fixes-9.3* *patches-after-9.2* diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 6b0658e3d1..23bdda6511 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -2,7 +2,7 @@ " Language: Vim script " Maintainer: Hirohito Higashi " Doug Kearns -" Last Change: 2026 Apr 07 +" Last Change: 2026 Apr 14 " Former Maintainer: Charles E. Campbell " DO NOT CHANGE DIRECTLY. @@ -146,8 +146,8 @@ syn keyword vimGroup contained Added Bold BoldItalic Boolean Changed Character C " Default highlighting groups {{{2 " GEN_SYN_VIM: vimHLGroup, START_STR='syn keyword vimHLGroup contained', END_STR='' -syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit VisualNOS DiffText DiffTextAdd PmenuSbar TabLineSel TabLineFill TabPanel TabPanelSel TabPanelFill Cursor lCursor TitleBar TitleBarNC QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel PmenuBorder PopupSelected MessageWindow PopupNotification PreInsert Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb PmenuShadow Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC ToolbarLine -syn keyword vimHLGroup contained ToolbarButton TitleBar TitleBarNC Menu Tooltip Scrollbar CursorIM ComplMatchIns LineNrAbove LineNrBelow MsgArea Terminal User1 User2 User3 User4 User5 User6 User7 User8 User9 +syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit VertSplitNC VisualNOS DiffText DiffTextAdd PmenuSbar TabLineSel TabLineFill TabPanel TabPanelSel TabPanelFill Cursor lCursor TitleBar TitleBarNC QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel PmenuBorder PopupSelected MessageWindow PopupNotification PreInsert Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb PmenuShadow Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC +syn keyword vimHLGroup contained ToolbarLine ToolbarButton TitleBar TitleBarNC Menu Tooltip Scrollbar CursorIM ComplMatchIns LineNrAbove LineNrBelow MsgArea Terminal User1 User2 User3 User4 User5 User6 User7 User8 User9 syn match vimHLGroup contained "\" syn case match diff --git a/src/drawscreen.c b/src/drawscreen.c index d619859bd0..0a745cd885 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -371,6 +371,16 @@ update_screen(int type_arg) pum_will_redraw = save_pum_will_redraw; pum_may_redraw(); + // Redraw vertical separators to update VertSplit/VertSplitNC highlights + // when the current window has changed. + if (redraw_vseps) + { + redraw_vseps = FALSE; + FOR_ALL_WINDOWS(wp) + if (wp->w_vsep_width > 0) + draw_vsep_win(wp, 0); + } + // Reset b_mod_set flags. Going through all windows is probably faster // than going through all buffers (there could be many buffers). FOR_ALL_WINDOWS(wp) @@ -600,12 +610,12 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED) */ if (wp->w_vsep_width != 0 && wp->w_status_height != 0 && redrawing()) { - if (stl_connected(wp)) - fillchar = fillchar_status(&attr, wp); - else - fillchar = fillchar_vsep(&attr, wp); for (i = 0; i < wp->w_status_height; i++) - screen_putchar(fillchar, row + i, W_ENDCOL(wp), attr); + { + int r = row + i; + fillchar = sep_cell_at_row(&attr, wp, r); + screen_putchar(fillchar, r, W_ENDCOL(wp), attr); + } } busy = FALSE; } @@ -3414,6 +3424,13 @@ redraw_statuslines(void) if (ret) pop_highlight_overrides(); } + if (redraw_vseps) + { + redraw_vseps = FALSE; + FOR_ALL_WINDOWS(wp) + if (wp->w_vsep_width > 0) + draw_vsep_win(wp, 0); + } if (redraw_tabline) draw_tabline(); diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 73ed9f2276..ade93579dc 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -9016,6 +9016,7 @@ ex_redrawstatus(exarg_T *eap UNUSED) status_redraw_all(); else status_redraw_curbuf(); + redraw_vseps = TRUE; if (msg_scrolled && (State & MODE_CMDLINE)) return; // redraw later diff --git a/src/globals.h b/src/globals.h index 7d873eea4e..20450aa68c 100644 --- a/src/globals.h +++ b/src/globals.h @@ -1090,6 +1090,7 @@ EXTERN tabpage_T *first_tabpage; EXTERN tabpage_T *curtab; EXTERN tabpage_T *lastused_tabpage; EXTERN int redraw_tabline INIT(= FALSE); // need to redraw tabline +EXTERN int redraw_vseps INIT(= FALSE); // need to redraw vseps #if defined(FEAT_TABPANEL) EXTERN int redraw_tabpanel INIT(= FALSE); // need to redraw tabpanel diff --git a/src/highlight.c b/src/highlight.c index 73b39e98b2..8baa44afe1 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -272,6 +272,7 @@ static char *(highlight_init_both[]) = { "default link EndOfBuffer NonText", CENT("VertSplit term=reverse cterm=reverse", "VertSplit term=reverse cterm=reverse gui=reverse"), + "default link VertSplitNC VertSplit", #ifdef FEAT_CLIPBOARD CENT("VisualNOS term=underline,bold cterm=underline,bold", "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"), diff --git a/src/optiondefs.h b/src/optiondefs.h index a5e1fe99df..9fd2fe5cd3 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -312,7 +312,7 @@ struct vimoption # define ISP_LATIN1 (char_u *)"@,161-255" #endif -#define HIGHLIGHT_INIT "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,E:DiffTextAdd,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,j:PmenuBorder,H:PmenuShadow,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm,Z:StatusLineTermNC,g:MsgArea,h:ComplMatchIns,%:TabPanel,^:TabPanelSel,&:TabPanelFill,I:PreInsert" +#define HIGHLIGHT_INIT "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,|:VertSplitNC,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,E:DiffTextAdd,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,j:PmenuBorder,H:PmenuShadow,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm,Z:StatusLineTermNC,g:MsgArea,h:ComplMatchIns,%:TabPanel,^:TabPanelSel,&:TabPanelFill,I:PreInsert" // Default python version for pyx* commands #if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) diff --git a/src/proto/screen.pro b/src/proto/screen.pro index 2dce886917..afaa86287a 100644 --- a/src/proto/screen.pro +++ b/src/proto/screen.pro @@ -48,7 +48,8 @@ void clearmode(void); void draw_tabline(void); void get_trans_bufname(buf_T *buf); int fillchar_status(int *attr, win_T *wp); -int fillchar_vsep(int *attr, win_T *wp); +int fillchar_vsep(int *attr, win_T *wp, int row); +int sep_cell_at_row(int *attr, win_T *wp, int row); int redrawing(void); int messaging(void); void comp_col(void); diff --git a/src/screen.c b/src/screen.c index ed28ac91ab..8005b3cc33 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1126,9 +1126,7 @@ screen_line( { if (!skip_for_popup(row, col + coloff)) { - int c; - - c = fillchar_vsep(&hl, wp); + int c = sep_cell_at_row(&hl, wp, row); if (ScreenLines[off_to] != (schar_T)c || (enc_utf8 && (int)ScreenLinesUC[off_to] != (c >= 0x80 ? c : 0)) @@ -1179,21 +1177,23 @@ rl_mirror(char_u *str) #endif /* - * Draw the verticap separator right of window "wp" starting with line "row". + * Draw the vertical separator right of window "wp" starting with line "row". */ void draw_vsep_win(win_T *wp, int row) { int hl; - int c; if (!wp->w_vsep_width) return; - // draw the vertical separator right of this window - c = fillchar_vsep(&hl, wp); - screen_fill(W_WINROW(wp) + row, W_WINROW(wp) + wp->w_height, - W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); + // Draw the vertical separator right of this window, row by row, so + // that the cell can differ per row depending on adjacent windows. + for (int r = W_WINROW(wp) + row; r < W_WINROW(wp) + wp->w_height; ++r) + { + int c = sep_cell_at_row(&hl, wp, r); + screen_fill(r, r + 1, W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); + } } /* @@ -5117,16 +5117,41 @@ fillchar_status(int *attr, win_T *wp) return fill; } +/* + * Return true if the vertical separator of "wp" at screen row "row" is + * adjacent to the current window. The separator is owned by "wp" and drawn + * at its right edge. + */ + static bool +vsep_row_is_curwin(win_T *wp, int row) +{ + if (wp == curwin) + return true; + + // Check if curwin is immediately to the right of wp's separator and + // "row" is within curwin's row range. + if (curwin->w_wincol == W_ENDCOL(wp) + wp->w_vsep_width + && row >= W_WINROW(curwin) + && row < W_WINROW(curwin) + curwin->w_height) + return true; + + return false; +} + /* * Get the character to use in a separator between vertically split windows. * Get its attributes in "*attr". + * "row" is the screen row number used to determine VertSplit or VertSplitNC. */ int -fillchar_vsep(int *attr, win_T *wp) +fillchar_vsep(int *attr, win_T *wp, int row) { bool override_success = push_highlight_overrides(wp->w_hl, wp->w_hl_len); - *attr = HL_ATTR(HLF_C); + if (vsep_row_is_curwin(wp, row)) + *attr = HL_ATTR(HLF_C); + else + *attr = HL_ATTR(HLF_CNC); if (override_success) pop_highlight_overrides(); @@ -5136,6 +5161,81 @@ fillchar_vsep(int *attr, win_T *wp) return wp->w_fill_chars.vert; } +/* + * Find the window immediately to the right of "wp"'s right separator at + * screen row "row". Returns NULL if there is none. + */ + static win_T * +right_neighbor_at_row(win_T *wp, int row) +{ + win_T *rn; + int rcol = W_ENDCOL(wp) + wp->w_vsep_width; + + FOR_ALL_WINDOWS(rn) + if (rn->w_wincol == rcol + && row >= W_WINROW(rn) + && row < W_WINROW(rn) + rn->w_height + rn->w_status_height) + return rn; + return NULL; +} + +/* + * Return true if window "wn" has its status line at screen row "row". + */ + static bool +win_status_at(win_T *wn, int row) +{ + return wn != NULL && wn->w_status_height > 0 + && row >= W_WINROW(wn) + wn->w_height + && row < W_WINROW(wn) + wn->w_height + wn->w_status_height; +} + +/* + * Decide the character and highlight to draw at the separator cell on the + * right edge of window "wp" at screen row "row". Returns the character via + * the return value and sets "*attr" to the highlight attribute. + * + * Rule: + * 1. If curwin is on either side of the separator at this row: + * - At curwin's status line row: draw a space with StatusLine highlight + * so curwin's status line extends across the separator. + * - Otherwise (curwin's content row): draw the vsep char with VertSplit. + * 2. If curwin is not adjacent here: + * - If the right neighbor has its status line at this row: draw a space + * with the right neighbor's StatusLineNC. + * - Else if "wp" has its status line at this row: draw a space with + * wp's StatusLineNC. + * - Else (both sides have content): draw the vsep char with VertSplitNC. + */ + int +sep_cell_at_row(int *attr, win_T *wp, int row) +{ + win_T *rn = right_neighbor_at_row(wp, row); + bool curwin_adjacent = (wp == curwin) || (rn == curwin); + + if (curwin_adjacent) + { + if (win_status_at(curwin, row)) + { + (void)fillchar_status(attr, curwin); + return ' '; + } + return fillchar_vsep(attr, wp, row); + } + + if (win_status_at(rn, row)) + { + (void)fillchar_status(attr, rn); + return ' '; + } + if (win_status_at(wp, row)) + { + (void)fillchar_status(attr, wp); + return ' '; + } + return fillchar_vsep(attr, wp, row); +} + /* * Return TRUE if redrawing should currently be done. */ diff --git a/src/testdir/dumps/Test_VertSplitNC_1.dump b/src/testdir/dumps/Test_VertSplitNC_1.dump new file mode 100644 index 0000000000..804bf60eac --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_1.dump @@ -0,0 +1,12 @@ +>V+0&#ffffff0|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|<+0#ffffff16#0000e05|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p +| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_2.dump b/src/testdir/dumps/Test_VertSplitNC_2.dump new file mode 100644 index 0000000000..8afa526ac0 --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_2.dump @@ -0,0 +1,12 @@ +|V+0&#ffffff0|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&>V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| +0#ffffff16#0000e05|<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p +| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_3.dump b/src/testdir/dumps/Test_VertSplitNC_3.dump new file mode 100644 index 0000000000..614de055ea --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_3.dump @@ -0,0 +1,12 @@ +|V+0&#ffffff0|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&>V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 +|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| +0#ffffff16#0000e05|<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p +| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_fc_1.dump b/src/testdir/dumps/Test_VertSplitNC_fc_1.dump new file mode 100644 index 0000000000..6aa0dbf449 --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_fc_1.dump @@ -0,0 +1,12 @@ +>f+0&#ffffff0|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|<+0#ffffff16#0000e05|o| |N|a|m|e|]| |[|+|]|=|1|,|1|=@5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]|-|1|,|1|-@5|T|o|p| |<| |N|a|m|e|]| |[|+|]|-|1|,|1|-@5|T|o|p +| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_fc_2.dump b/src/testdir/dumps/Test_VertSplitNC_fc_2.dump new file mode 100644 index 0000000000..58652b933b --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_fc_2.dump @@ -0,0 +1,12 @@ +|f+0&#ffffff0|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&>f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 +|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]|-|1|,|1|-@5|T|o|p| +0#ffffff16#0000e05|<| |N|a|m|e|]| |[|+|]|=|1|,|1|=@5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]|-|1|,|1|-@5|T|o|p +| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_stl_1.dump b/src/testdir/dumps/Test_VertSplitNC_stl_1.dump new file mode 100644 index 0000000000..5e4d8bb009 --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_stl_1.dump @@ -0,0 +1,20 @@ +|m+0&#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&>m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p| |[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p +| @74 +@75 +@75 +| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_stl_2.dump b/src/testdir/dumps/Test_VertSplitNC_stl_2.dump new file mode 100644 index 0000000000..c7ceac388b --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_stl_2.dump @@ -0,0 +1,20 @@ +|m+0&#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&>m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p| |[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p +| @74 +@75 +@75 +| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_stl_3.dump b/src/testdir/dumps/Test_VertSplitNC_stl_3.dump new file mode 100644 index 0000000000..2d4189ed6b --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_stl_3.dump @@ -0,0 +1,20 @@ +|m+0&#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 +|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&>m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 +|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p| +0#ffffff16#0000e05|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p +| +0#0000001#a8a8a8255@36| +0#ffffff16#0000e05@37 +| +0#0000001#a8a8a8255@36| +0#ffffff16#0000e05@37 +| +0#0000001#a8a8a8255@36| +0#ffffff16#0000e05@37 +| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_diff_anchors_07.dump b/src/testdir/dumps/Test_diff_anchors_07.dump index 310fa6544b..eda58e9fd7 100644 --- a/src/testdir/dumps/Test_diff_anchors_07.dump +++ b/src/testdir/dumps/Test_diff_anchors_07.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@22||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255| @20||+1&#ffffff0| +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@21 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@22||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|3+0#0000000#5fd7ff255| @20||+1&#ffffff0| +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@21 |~+0&#ffffff0| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump index a0ffa9f301..5616d5f371 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump index 9e5c9dc94e..3d66db8d6e 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump index 76f8617683..fdab541440 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump index 1c069d8ded..705446a3dd 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump index 87bbc572e5..5daf6573d3 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump index 1f5917cf47..6cae25e1d4 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump index 1c069d8ded..705446a3dd 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump index aadabe077d..a2ade506e4 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump index 99cb0ded0f..af99924276 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump index 3c35c7741a..c6a418a4b4 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump index 432cadb94d..cf6bce8d0d 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump index b95c2661a6..4bff316f42 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump index 7daaf7a9ac..9496933153 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_16.dump b/src/testdir/dumps/Test_diff_get_put_linematch_16.dump index d4649d2052..eeb984d682 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_16.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_16.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|D+0#0000000#5fd7ff255|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|6|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|6|,|1| @11|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_17.dump b/src/testdir/dumps/Test_diff_get_put_linematch_17.dump index 69737fa58c..b8426a39a4 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_17.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_17.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|D+0#0000000#5fd7ff255|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|8|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|8|,|1| @11|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_18.dump b/src/testdir/dumps/Test_diff_get_put_linematch_18.dump index 49ab243871..2b94da9551 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_18.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_18.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|D+0#0000000#5fd7ff255|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|6|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|9|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|6|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|9|,|1| @11|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_19.dump b/src/testdir/dumps/Test_diff_get_put_linematch_19.dump index cbea3e5602..8fdd9f608a 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_19.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_19.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|D+0#0000000#ffffff0|E|F| @31||+1&&| +0#0000e05#a8a8a8255@1|D+0#0000000#ffffff0|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1>s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|1|3|,|1| @10|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|7|,|1| @10|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|1|3|,|1| @10|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|7|,|1| @10|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_3.dump b/src/testdir/dumps/Test_diff_get_put_linematch_3.dump index cb62a9ef71..ce905130e9 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_3.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_3.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 |:+0&&|5|,|1|0|d|i|f@1|g|e|t| @62 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_4.dump b/src/testdir/dumps/Test_diff_get_put_linematch_4.dump index 16c2cd874d..c6ea6ddc85 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_4.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_4.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 |:+0&&|4|,|1|7|d|i|f@1|g|e|t| @62 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_07.dump b/src/testdir/dumps/Test_diff_inline_multiline_07.dump index de58a6ac30..b76d549b27 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_07.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_07.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_08.dump b/src/testdir/dumps/Test_diff_inline_multiline_08.dump index 901ea4699d..51c35f5afd 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_08.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_08.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_09.dump b/src/testdir/dumps/Test_diff_inline_multiline_09.dump index 91f59cda25..dc36ed8ffc 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_09.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_09.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_10.dump b/src/testdir/dumps/Test_diff_inline_multiline_10.dump index 212c247975..ad86c4684d 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_10.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_10.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_rnu_01.dump b/src/testdir/dumps/Test_diff_rnu_01.dump index dc7c0f9101..d51d66674c 100644 --- a/src/testdir/dumps/Test_diff_rnu_01.dump +++ b/src/testdir/dumps/Test_diff_rnu_01.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_rnu_02.dump b/src/testdir/dumps/Test_diff_rnu_02.dump index 32ffc4003d..d33a2ee0ab 100644 --- a/src/testdir/dumps/Test_diff_rnu_02.dump +++ b/src/testdir/dumps/Test_diff_rnu_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_rnu_03.dump b/src/testdir/dumps/Test_diff_rnu_03.dump index 8c8b938449..0d29b84081 100644 --- a/src/testdir/dumps/Test_diff_rnu_03.dump +++ b/src/testdir/dumps/Test_diff_rnu_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_scroll_1.dump b/src/testdir/dumps/Test_diff_scroll_1.dump index ee53c35bbb..9713461012 100644 --- a/src/testdir/dumps/Test_diff_scroll_1.dump +++ b/src/testdir/dumps/Test_diff_scroll_1.dump @@ -8,5 +8,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|d+0#0000000#5fd7ff255|o|l|o|r| @29 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|s+0#0000000#5fd7ff255|i|t| @31 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|a+0#0000000#5fd7ff255|m|e|t|,| @29 -|X+1&#ffffff0|l|e|f|t| @13|5|,|0|-|1| @9|T|o|p| |X+3&&|r|i|g|h|t| @12|5|,|0|-|1| @9|T|o|p +|X+1&#ffffff0|l|e|f|t| @13|5|,|0|-|1| @9|T|o|p| +3&&|X|r|i|g|h|t| @12|5|,|0|-|1| @9|T|o|p |"+0&&|X|r|i|g|h|t|"| |3@1|L|,| |2|6|3|B| @56 diff --git a/src/testdir/dumps/Test_diff_scroll_2.dump b/src/testdir/dumps/Test_diff_scroll_2.dump index 4cac386a6c..d6cd1b9c99 100644 --- a/src/testdir/dumps/Test_diff_scroll_2.dump +++ b/src/testdir/dumps/Test_diff_scroll_2.dump @@ -8,5 +8,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|d+0#0000000#5fd7ff255|o|l|o|r| @29 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|s+0#0000000#5fd7ff255|i|t| @31 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|a+0#0000000#5fd7ff255|m|e|t|,| @29 -|X+1&#ffffff0|l|e|f|t| @13|6|,|1| @11|T|o|p| |X+3&&|r|i|g|h|t| @12|6|,|1| @11|T|o|p +|X+1&#ffffff0|l|e|f|t| @13|6|,|1| @11|T|o|p| +3&&|X|r|i|g|h|t| @12|6|,|1| @11|T|o|p |"+0&&|X|r|i|g|h|t|"| |3@1|L|,| |2|6|3|B| @56 diff --git a/src/testdir/dumps/Test_diff_scroll_change_03.dump b/src/testdir/dumps/Test_diff_scroll_change_03.dump index 6d0c8600f6..5a4088975b 100644 --- a/src/testdir/dumps/Test_diff_scroll_change_03.dump +++ b/src/testdir/dumps/Test_diff_scroll_change_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|7|,|3| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|5|,|3| @10|B|o|t +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|7|,|3| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|5|,|3| @10|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_topline_1.dump b/src/testdir/dumps/Test_diff_topline_1.dump index 1d4c9145f0..5b5122621c 100644 --- a/src/testdir/dumps/Test_diff_topline_1.dump +++ b/src/testdir/dumps/Test_diff_topline_1.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|4| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|5| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|6| @32 -|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% +|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_topline_2.dump b/src/testdir/dumps/Test_diff_topline_2.dump index 2d28bd373a..a0f3db79c0 100644 --- a/src/testdir/dumps/Test_diff_topline_2.dump +++ b/src/testdir/dumps/Test_diff_topline_2.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|4| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|5| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|6| @32 -|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% +|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% |9+0&&| @73 diff --git a/src/testdir/dumps/Test_diff_topline_4.dump b/src/testdir/dumps/Test_diff_topline_4.dump index 6043d7ad73..94414b1e79 100644 --- a/src/testdir/dumps/Test_diff_topline_4.dump +++ b/src/testdir/dumps/Test_diff_topline_4.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|1+0#0000000#5fd7ff255|9| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|0| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|1| @32 -|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p +|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_01.dump b/src/testdir/dumps/Test_diff_with_cul_bri_01.dump index 8151088e04..3855fa17c5 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_01.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_01.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_02.dump b/src/testdir/dumps/Test_diff_with_cul_bri_02.dump index 62126f6b39..c1ae50d6d4 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_02.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_02.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_03.dump b/src/testdir/dumps/Test_diff_with_cul_bri_03.dump index f32be7385b..bdf8be065e 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_03.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_03.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_04.dump b/src/testdir/dumps/Test_diff_with_cul_bri_04.dump index 9489f9ab04..aa3ce8e5d4 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_04.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_04.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cursorline_01.dump b/src/testdir/dumps/Test_diff_with_cursorline_01.dump index 31d8b2a189..0b9bcb9bda 100644 --- a/src/testdir/dumps/Test_diff_with_cursorline_01.dump +++ b/src/testdir/dumps/Test_diff_with_cursorline_01.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cursorline_02.dump b/src/testdir/dumps/Test_diff_with_cursorline_02.dump index 9aef7d1fa3..68014acb2c 100644 --- a/src/testdir/dumps/Test_diff_with_cursorline_02.dump +++ b/src/testdir/dumps/Test_diff_with_cursorline_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cursorline_03.dump b/src/testdir/dumps/Test_diff_with_cursorline_03.dump index 5bc9e13167..595de46ce1 100644 --- a/src/testdir/dumps/Test_diff_with_cursorline_03.dump +++ b/src/testdir/dumps/Test_diff_with_cursorline_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|4|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|4|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_display_fillchars_1.dump b/src/testdir/dumps/Test_display_fillchars_1.dump index b0b1642bff..9568e09c5f 100644 --- a/src/testdir/dumps/Test_display_fillchars_1.dump +++ b/src/testdir/dumps/Test_display_fillchars_1.dump @@ -2,11 +2,11 @@ |w|i|n|d|o|w| |2| @28|++1&&|w+0&&|i|n|d|o|w| |1| @28 |w|i|n|d|o|w| |2| @28|++1&&|w+0&&|i|n|d|o|w| |1| @28 |y+0#4040ff13&| @35|++1#0000000&|x+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]|b@5|1|,|1|b@11|A|l@1|b|[|N|o| |N|a|m|e|]| |[|+|]|a@5|1|,|1|a@11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]|b@5|1|,|1|b@11|A|l@1| |[|N|o| |N|a|m|e|]| |[|+|]|a@5|1|,|1|a@11|A|l@1 >w+0&&|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |w|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |w|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |o+0#4040ff13&| @35|>+1#0000000&|z+0#4040ff13&| @35 |o| @35|>+1#0000000&|z+0#4040ff13&| @35 -|[+3#0000000&|N|o| |N|a|m|e|]| |[|+|]|4@5|1|,|1|4@11|A|l@1|4|[+1&&|N|o| |N|a|m|e|]| |[|+|]|c@5|1|,|1|c@11|A|l@1 +|[+3#0000000&|N|o| |N|a|m|e|]| |[|+|]|4@5|1|,|1|4@11|A|l@1| |[+1&&|N|o| |N|a|m|e|]| |[|+|]|c@5|1|,|1|c@11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_display_fillchars_2.dump b/src/testdir/dumps/Test_display_fillchars_2.dump index b983face03..f62488861f 100644 --- a/src/testdir/dumps/Test_display_fillchars_2.dump +++ b/src/testdir/dumps/Test_display_fillchars_2.dump @@ -2,11 +2,11 @@ |w|i|n|d|o|w| |2| @28|++1&&|w+0&&|i|n|d|o|w| |1| @28 |w|i|n|d|o|w| |2| @28|++1&&|w+0&&|i|n|d|o|w| |1| @28 |y+0#4040ff13&| @35|++1#0000000&|x+0#4040ff13&| @35 -|[+3#0000000&|N|o| |N|a|m|e|]| |[|+|]|2@5|1|,|1|2@11|A|l@1|2|[+1&&|N|o| |N|a|m|e|]| |[|+|]|a@5|1|,|1|a@11|A|l@1 +|[+3#0000000&|N|o| |N|a|m|e|]| |[|+|]|2@5|1|,|1|2@11|A|l@1| |[+1&&|N|o| |N|a|m|e|]| |[|+|]|a@5|1|,|1|a@11|A|l@1 |w+0&&|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |w|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |w|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |o+0#4040ff13&| @35|>+1#0000000&|z+0#4040ff13&| @35 |o| @35|>+1#0000000&|z+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]|d@5|1|,|1|d@11|A|l@1|d|[|N|o| |N|a|m|e|]| |[|+|]|c@5|1|,|1|c@11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]|d@5|1|,|1|d@11|A|l@1| |[|N|o| |N|a|m|e|]| |[|+|]|c@5|1|,|1|c@11|A|l@1 |:+0&&|w|i|n|c|m|d| |k| @65 diff --git a/src/testdir/dumps/Test_hor_scroll_1.dump b/src/testdir/dumps/Test_hor_scroll_1.dump index 615afb8e12..65b16f3ccc 100644 --- a/src/testdir/dumps/Test_hor_scroll_1.dump +++ b/src/testdir/dumps/Test_hor_scroll_1.dump @@ -4,5 +4,5 @@ @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_hor_scroll_2.dump b/src/testdir/dumps/Test_hor_scroll_2.dump index dfe2e98ae0..2b6afa0ecd 100644 --- a/src/testdir/dumps/Test_hor_scroll_2.dump +++ b/src/testdir/dumps/Test_hor_scroll_2.dump @@ -4,5 +4,5 @@ @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_hor_scroll_3.dump b/src/testdir/dumps/Test_hor_scroll_3.dump index db9fd6ff1b..93f94a0ecc 100644 --- a/src/testdir/dumps/Test_hor_scroll_3.dump +++ b/src/testdir/dumps/Test_hor_scroll_3.dump @@ -4,5 +4,5 @@ @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 |:+0&&|w|i|n|d|o| |:|s|e|t| |c|u|r|s|o|r|l|i|n|e| @52 diff --git a/src/testdir/dumps/Test_hor_scroll_4.dump b/src/testdir/dumps/Test_hor_scroll_4.dump index 81821604d0..830423ad35 100644 --- a/src/testdir/dumps/Test_hor_scroll_4.dump +++ b/src/testdir/dumps/Test_hor_scroll_4.dump @@ -4,5 +4,5 @@ @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 |:+0&&|w|i|n|d|o| |:|s|e|t| |c|u|r|s|o|r|l|i|n|e| @52 diff --git a/src/testdir/dumps/Test_hor_scroll_5.dump b/src/testdir/dumps/Test_hor_scroll_5.dump index f2986f3796..d706910fc0 100644 --- a/src/testdir/dumps/Test_hor_scroll_5.dump +++ b/src/testdir/dumps/Test_hor_scroll_5.dump @@ -4,5 +4,5 @@ @20||+1&&| +0&&@53 @20||+1&&| +0&&@53 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|4|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|4|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|4|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|4|1| @10|A|l@1 |:+0&&|w|i|n|d|o| |:|s|e|t| |n|o|c|u|r|s|o|r|l|i|n|e| |n|o|c|u|r|s|o|r|c|o|l|u|m|n| @35 diff --git a/src/testdir/dumps/Test_laststatus_vsplit_row_height3_1.dump b/src/testdir/dumps/Test_laststatus_vsplit_row_height3_1.dump index 6d24637e32..82ea8b8f8b 100644 --- a/src/testdir/dumps/Test_laststatus_vsplit_row_height3_1.dump +++ b/src/testdir/dumps/Test_laststatus_vsplit_row_height3_1.dump @@ -3,6 +3,6 @@ |~| @73 |[+3#0000000&|N|o| |N|a|m|e|]| @47|0|,|0|-|1| @9|A|l@1 | +0&&@36||+1&&| +0&&@36 -|~+0#4040ff13&| @35||+1#0000000&|[|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 +|~+0#4040ff13&| @35| +1#0000000&|[|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 |[|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1| @37 | +0&&@74 diff --git a/src/testdir/dumps/Test_linematch_3diffs1.dump b/src/testdir/dumps/Test_linematch_3diffs1.dump index d8f23e84bd..a010d27617 100644 --- a/src/testdir/dumps/Test_linematch_3diffs1.dump +++ b/src/testdir/dumps/Test_linematch_3diffs1.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|0|-|1| @3|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|0|-|1| @3|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|0|-|1| @3|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|0|-|1| @3|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|0|-|1| @3|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|0|-|1| @3|A|l@1 |"+0&&|X|d|i|f|i|l|e|3|"| |5|L|,| |4|5|B| @56 diff --git a/src/testdir/dumps/Test_listchars_01.dump b/src/testdir/dumps/Test_listchars_01.dump index 72fff585c2..a7de310690 100644 --- a/src/testdir/dumps/Test_listchars_01.dump +++ b/src/testdir/dumps/Test_listchars_01.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|]| |1|,| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|]| |1|,| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_02.dump b/src/testdir/dumps/Test_listchars_02.dump index b7be52e890..d7e05df9ae 100644 --- a/src/testdir/dumps/Test_listchars_02.dump +++ b/src/testdir/dumps/Test_listchars_02.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @2||+1#0000000&|~+0#4040ff13&| @32 |~| @18||+1#0000000&|~+0#4040ff13&| @2||+1#0000000&|~+0#4040ff13&| @32 |~| @18||+1#0000000&|~+0#4040ff13&| @2||+1#0000000&|~+0#4040ff13&| @32 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1|,| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1|,| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @10|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_03.dump b/src/testdir/dumps/Test_listchars_03.dump index 13aca6d2f7..d54b3e50fa 100644 --- a/src/testdir/dumps/Test_listchars_03.dump +++ b/src/testdir/dumps/Test_listchars_03.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @1||+1#0000000&|~+0#4040ff13&| @33 |~| @18||+1#0000000&|~+0#4040ff13&| @1||+1#0000000&|~+0#4040ff13&| @33 |~| @18||+1#0000000&|~+0#4040ff13&| @1||+1#0000000&|~+0#4040ff13&| @33 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @10|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_04.dump b/src/testdir/dumps/Test_listchars_04.dump index e62c5aff20..694e6eadca 100644 --- a/src/testdir/dumps/Test_listchars_04.dump +++ b/src/testdir/dumps/Test_listchars_04.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| ||+1#0000000&|~+0#4040ff13&| @34 |~| @18||+1#0000000&|~+0#4040ff13&| ||+1#0000000&|~+0#4040ff13&| @34 |~| @18||+1#0000000&|~+0#4040ff13&| ||+1#0000000&|~+0#4040ff13&| @34 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @11|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @11|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_05.dump b/src/testdir/dumps/Test_listchars_05.dump index 89b759fb42..7ecb79738a 100644 --- a/src/testdir/dumps/Test_listchars_05.dump +++ b/src/testdir/dumps/Test_listchars_05.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_06.dump b/src/testdir/dumps/Test_listchars_06.dump index 9f4b5d30e2..9176c5fded 100644 --- a/src/testdir/dumps/Test_listchars_06.dump +++ b/src/testdir/dumps/Test_listchars_06.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<+3&&|]| |1|,| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| +3&&|<|]| |1|,| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 |:+0&&|s|e|t| |n|o|w|r|a|p| |f|o|l|d|c|o|l|u|m|n|=|4| @35 diff --git a/src/testdir/dumps/Test_listchars_07.dump b/src/testdir/dumps/Test_listchars_07.dump index ad81656a50..572bf248ff 100644 --- a/src/testdir/dumps/Test_listchars_07.dump +++ b/src/testdir/dumps/Test_listchars_07.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<+3&&| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| +3&&|<| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 |:+0&&|s|e|t| |n|o|w|r|a|p| |f|o|l|d|c|o|l|u|m|n|=|4| @35 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_01.dump b/src/testdir/dumps/Test_popup_prop_not_visible_01.dump index dbcfda1161..20f4d46586 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_01.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_01.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 -|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump b/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump index 0b97cb832d..e048949de9 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 -|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 |:+0&&|c|a|l@1| |p|o|p|u|p|_|h|i|d|e|(|g|:|s|o|m|e|_|i|d|)| @47 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump b/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump index e8a5c2e353..c91b201003 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 -|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 |:+0&&|c|a|l@1| |p|o|p|u|p|_|s|h|o|w|(|g|:|s|o|m|e|_|i|d|)| @47 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_02.dump b/src/testdir/dumps/Test_popup_prop_not_visible_02.dump index 1d1a5531c9..e4b2aec90f 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_02.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_02.dump @@ -6,5 +6,5 @@ |~+0#4040ff13#ffffff0| @55||+1#0000000&|~+0#4040ff13&| @15 |~| @55||+1#0000000&|~+0#4040ff13&| @15 |~| @55||+1#0000000&|~+0#4040ff13&| @15 -|[+1#0000000&|N|o| |N|a|m|e|]| @29|0|,|0|-|1| @9|A|l@1| |<+3&&|m|e|]| |[|+|]| |1|,|0|-|1| @2 +|[+1#0000000&|N|o| |N|a|m|e|]| @29|0|,|0|-|1| @9|A|l@1| +3&&|<|m|e|]| |[|+|]| |1|,|0|-|1| @2 |:+0&&|v|e|r|t| |r|e|s|i|z|e| |-|1|4| @58 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_03.dump b/src/testdir/dumps/Test_popup_prop_not_visible_03.dump index caaa881fe3..a056cd867a 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_03.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_03.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @63||+1#0000000&|~+0#4040ff13&| @7 |~| @63||+1#0000000&|~+0#4040ff13&| @7 |~| @63||+1#0000000&|~+0#4040ff13&| @7 -|[+1#0000000&|N|o| |N|a|m|e|]| @37|0|,|0|-|1| @9|A|l@1| |<+3&&|[|+|]| |1|,|0|- +|[+1#0000000&|N|o| |N|a|m|e|]| @37|0|,|0|-|1| @9|A|l@1| +3&&|<|[|+|]| |1|,|0|- |:+0&&|v|e|r|t| |r|e|s|i|z|e| |-|8| @59 diff --git a/src/testdir/dumps/Test_prop_diff_mode_2.dump b/src/testdir/dumps/Test_prop_diff_mode_2.dump index 243ec0dc18..453665d998 100644 --- a/src/testdir/dumps/Test_prop_diff_mode_2.dump +++ b/src/testdir/dumps/Test_prop_diff_mode_2.dump @@ -6,5 +6,5 @@ |~+0#4040ff13#ffffff0| @28||+1#0000000&| +0#af5f00255&@1|5| |0+0#0000000#ffd7ff255@2|9+2&#ff404010| +0&#ffd7ff255@20 |~+0#4040ff13#ffffff0| @28||+1#0000000&| +0#af5f00255&@3|<+0#0000000#ffd7ff255|t|e|x|t|>| @18 |~+0#4040ff13#ffffff0| @28||+1#0000000&|~+0#4040ff13&| @27 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1|-|7| @5|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1|-|7| @5|A|l@1 |:+0&&|w|i|n|d|o| |s|e|t| |n|u|m|b|e|r| @42 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_1.dump b/src/testdir/dumps/Test_smooth_diff_change_line_1.dump index c634f1dd31..28f2e1d995 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_1.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_1.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_2.dump b/src/testdir/dumps/Test_smooth_diff_change_line_2.dump index 691fe289fd..4c2d28d0e6 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_2.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_2.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|7| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|7| @11|A|l@1 |-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@62 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_3.dump b/src/testdir/dumps/Test_smooth_diff_change_line_3.dump index c4178404d8..e47fb5d34b 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_3.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_3.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump b/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump index d13141eb28..0c7e70c273 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_4.dump b/src/testdir/dumps/Test_smooth_diff_change_line_4.dump index b789df3fb7..37ce70a758 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_4.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_4.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_switchwin_clear_pum_02.dump b/src/testdir/dumps/Test_switchwin_clear_pum_02.dump index 39013c09fd..cf5f9dbc9c 100644 --- a/src/testdir/dumps/Test_switchwin_clear_pum_02.dump +++ b/src/testdir/dumps/Test_switchwin_clear_pum_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|w+1#0000000&|i|n|_|b| |[|+|]| @9|1|,|1|0| @10|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|9| @11|A|l@1 +|w+1#0000000&|i|n|_|b| |[|+|]| @9|1|,|1|0| @10|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|9| @11|A|l@1 |-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@62 diff --git a/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump b/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump index b041b672f6..0ee2efe9db 100644 --- a/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump +++ b/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump @@ -6,5 +6,5 @@ | +1&&@19| +0#af5f00255&@1|6| ||+1#0000000&| +0#af5f00255&|3|0| |t+0#0000000&|e|x|t| |t|e|x|t| |t|e|x|t| |t | +1&&@19| +0#af5f00255&@1|7| ||+1#0000000&| +0#af5f00255&|3|1| |t+0#0000000&|e|x|t| |t|e|x|t| |t|e|x|t| |t | +1&&@19| +0#af5f00255&@1|8| ||+1#0000000&| +0#af5f00255&|3|2| |t+0#0000000&|e|x|t| |t|e|x|t| |t|e|x|t| |t -| +1&&@19|<| |1|,| |<+3&&|.|t|x|t| |[|+|]| |2|8|,|1| @2|2|6|% +| +1&&@19|<| |1|,| +3&&|<|.|t|x|t| |[|+|]| |2|8|,|1| @2|2|6|% | +1&&@19| +0&&@24 diff --git a/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol.dump b/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol.dump index 3633ffe540..d2e69dc822 100644 --- a/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol.dump +++ b/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol.dump @@ -4,12 +4,12 @@ | +0#ff404010#e0e0004@36||+1#0000000#ffffff0|3+0&&| @35 | +0#ff404010#e0e0004@36||+1#0000000#ffffff0|4+0&&| @35 | +0#ff404010#e0e0004@36||+1#0000000#ffffff0|5+0&&| @35 -|!+2#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22||+1#0000000#ffffff0|6+0&&| @35 +|!+2#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @23|6+0#0000000#ffffff0| @35 |h+0#00e0003#5fd7ff255|e|l@1|o| @31||+1#0000000#ffffff0|7+0&&| @35 |h+0#00e0003#5fd7ff255|e|l@1|o| @31||+1#0000000#ffffff0|8+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|9+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|1+0&&|0| @34 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|1+0&&@1| @34 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|1+0&&|2| @34 -|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @23|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @23 +|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22| +1#0000000#ffffff0|[|N|o| |N|a|m|e|]| |[|+|]| @23 |:+0&&|s|e|t| |w|i|n|c|o|l|o|r|=|M|y|W|i|n|C|o|l| @52 diff --git a/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol2.dump b/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol2.dump index ce69e3b4df..8e5796f622 100644 --- a/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol2.dump +++ b/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol2.dump @@ -4,12 +4,12 @@ | +0#ff404010#e0e0004@36||+1#0000000#ffffff0| +0#0000001#4040ff13@36 | +0#ff404010#e0e0004@36||+1#0000000#ffffff0| +0#0000001#4040ff13@36 | +0#ff404010#e0e0004@36||+1#0000000#ffffff0| +0#0000001#4040ff13@36 -|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22||+1#0000000#ffffff0|!+2#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22 +|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22| +2&&|!|c|a|t| |[|r|u|n@1|i|n|g|]| @22 |h+0#00e0003#5fd7ff255|e|l@1|o| @31||+1#0000000#ffffff0|0+0&&| @35 |h+0#00e0003#5fd7ff255|e|l@1|o| @31||+1#0000000#ffffff0|1+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|2+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|3+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|4+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|5+0&&| @35 -|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @23|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @23 +|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22| +1#0000000#ffffff0|[|N|o| |N|a|m|e|]| |[|+|]| @23 |:+0&&|s|e|t| |w|i|n|c|o|l|o|r|=|M|y|W|i|n|C|o|l|2| @51 diff --git a/src/testdir/dumps/Test_winhighlight_14.dump b/src/testdir/dumps/Test_winhighlight_14.dump index 1ea869d656..176bf6ea99 100644 --- a/src/testdir/dumps/Test_winhighlight_14.dump +++ b/src/testdir/dumps/Test_winhighlight_14.dump @@ -4,5 +4,5 @@ |S+8&&|i|x| @33||+1&&|S+0&&|i|x| @33 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 |:+0&&|w|i|n|c|m|d| |l| @65 diff --git a/src/testdir/dumps/Test_winhighlight_15.dump b/src/testdir/dumps/Test_winhighlight_15.dump index a90302a86f..7d69f21b56 100644 --- a/src/testdir/dumps/Test_winhighlight_15.dump +++ b/src/testdir/dumps/Test_winhighlight_15.dump @@ -4,5 +4,5 @@ |S+8#0000000#ffffff0|i|x| @33||+1&&|S+0#ffffff16#e000002|i|x| @33 |~+0#4040ff13#ffffff0| @35||+1#0000000&|~+0#4040ff13#e000002| @35 |~+0&#ffffff0| @35||+1#0000000&|~+0#4040ff13#e000002| @35 -|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 +|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 |:+0&&|s|e|t|l|o|c|a|l| |w|h|l|=|N|o|r|m|a|l|:|E|r@1|o|r|M|s|g| @45 diff --git a/src/testdir/dumps/Test_winhighlight_2.dump b/src/testdir/dumps/Test_winhighlight_2.dump index b84ef70531..72f754114e 100644 --- a/src/testdir/dumps/Test_winhighlight_2.dump +++ b/src/testdir/dumps/Test_winhighlight_2.dump @@ -4,5 +4,5 @@ |S|i|x| @33||+1&&|S+0&&|i|x| @33 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 |:+0&&|w|i|n|c|m|d| |l| @65 diff --git a/src/testdir/dumps/Test_winhighlight_6.dump b/src/testdir/dumps/Test_winhighlight_6.dump index f5753e1187..e482465ca4 100644 --- a/src/testdir/dumps/Test_winhighlight_6.dump +++ b/src/testdir/dumps/Test_winhighlight_6.dump @@ -4,5 +4,5 @@ |S|i|x| @33||+1&&|S+0&&|i|x| @33 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 |:+0&&|s|e|t|l|o|c|a|l| |w|h|l|=|V|e|r|t|S|p|l|i|t|:|E|r@1|o|r|M|s|g| @42 diff --git a/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump b/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump index 30d643854e..37a9087bba 100644 --- a/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump +++ b/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 |/+0&&|F> @72 diff --git a/src/testdir/dumps/Test_winscrolled_once_only_1.dump b/src/testdir/dumps/Test_winscrolled_once_only_1.dump index 56d640107e..7407f46863 100644 --- a/src/testdir/dumps/Test_winscrolled_once_only_1.dump +++ b/src/testdir/dumps/Test_winscrolled_once_only_1.dump @@ -5,6 +5,6 @@ |a+0#0000000&@2| @26||+1&&|~+0#4040ff13&| @27 |b+0#0000000&@2| @26||+1&&|~+0#4040ff13&| @27 |~| @28||+1#0000000&|~+0#4040ff13&| @27 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @1|2|,|1| @7|B|o|t +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @1|2|,|1| @7|B|o|t |1+0&&| |1|0@2| |[|'|r|o|w|'|,| |[@1|'|c|o|l|'|,| |[@1|'|l|e|a|f|'|,| |1|0@1|2|]|,| |[|'|l|e|a|f|'|,| |1|0@1|1|]@2|,| |[ |'|l|e|a|f|'|,| |1|0@2|]@2| @44 diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim index 6b45e8ea82..584693f807 100644 --- a/src/testdir/test_highlight.vim +++ b/src/testdir/test_highlight.vim @@ -1680,4 +1680,112 @@ func Test_winhighlight_occasion() call StopVimInTerminal(buf) endfunc +func Test_VertSplitNC() + CheckScreendump + + let lines =<< trim END + hi StatusLine ctermfg=White ctermbg=DarkBlue cterm=NONE + hi StatusLineNC ctermfg=Black ctermbg=Gray cterm=NONE + hi VertSplit ctermfg=Green ctermbg=NONE cterm=NONE + hi VertSplitNC ctermfg=DarkGray ctermbg=NONE cterm=NONE + call setline(1, repeat(['VertSplitNC test'], 20)) + vsplit + vsplit + END + call writefile(lines, 'Xtest_vertsplitNC', 'D') + + let buf = RunVimInTerminal('-S Xtest_vertsplitNC', {'rows': 12}) + call TermWait(buf) + + " Left window is current: left separator is VertSplit, right is VertSplitNC + call VerifyScreenDump(buf, 'Test_VertSplitNC_1', {}) + + " Move to middle window: both separators should be VertSplit + call term_sendkeys(buf, "\l") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_VertSplitNC_2', {}) + + " Move to right window: right separator is VertSplitNC, left is VertSplit + call term_sendkeys(buf, "\l") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_VertSplitNC_3', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_VertSplitNC_multiline_stl() + CheckScreendump + + let lines =<< trim END + hi StatusLine ctermfg=White ctermbg=DarkBlue cterm=NONE + hi StatusLineNC ctermfg=Black ctermbg=Gray cterm=NONE + hi VertSplit ctermfg=Green ctermbg=NONE cterm=NONE + hi VertSplitNC ctermfg=DarkGray ctermbg=NONE cterm=NONE + set statuslineopt=maxheight:4,fixedheight + set statusline=%f%=%l,%c\ %P + call setline(1, repeat(['multi stl test'], 20)) + vsplit + wincmd l + sp + sp + wincmd k + wincmd = + END + call writefile(lines, 'Xtest_vertsplitNC_stl', 'D') + + let buf = RunVimInTerminal('-S Xtest_vertsplitNC_stl', {'rows': 20}) + call TermWait(buf) + + " Right-top window is current: the separator cell on its status line + " row should be a space with StatusLine highlight. Other status + " line rows should use VertSplitNC. + call VerifyScreenDump(buf, 'Test_VertSplitNC_stl_1', {}) + + " Move to right-middle window + call term_sendkeys(buf, "\j") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_VertSplitNC_stl_2', {}) + + " Move to right-bottom window + call term_sendkeys(buf, "\j") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_VertSplitNC_stl_3', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_VertSplitNC_fillchars() + CheckScreendump + + let lines =<< trim END + hi StatusLine ctermfg=White ctermbg=DarkBlue cterm=NONE + hi StatusLineNC ctermfg=Black ctermbg=Gray cterm=NONE + hi VertSplit ctermfg=Green ctermbg=NONE cterm=NONE + hi VertSplitNC ctermfg=DarkGray ctermbg=NONE cterm=NONE + set fillchars=vert:\|,stl:=,stlnc:- + call setline(1, repeat(['fillchars test'], 20)) + vsplit + vsplit + END + call writefile(lines, 'Xtest_vertsplitNC_fc', 'D') + + let buf = RunVimInTerminal('-S Xtest_vertsplitNC_fc', {'rows': 12}) + call TermWait(buf) + + " Left window is current. Non-status-line rows show '|' with + " VertSplit (left sep) and VertSplitNC (right sep). On the status + " line row, the separator cell is a space (not '=' from stl, and + " not '|' from vert) with StatusLine highlight. + call VerifyScreenDump(buf, 'Test_VertSplitNC_fc_1', {}) + + " Move to middle window: both separators on non-status rows use + " VertSplit. On the status line row both separator cells are + " spaces with StatusLine highlight. + call term_sendkeys(buf, "\l") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_VertSplitNC_fc_2', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index 7bd07ca0bc..828a5ca710 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -667,7 +667,7 @@ func Test_set_completion_string_values() " highlight: special parsing, including auto-completing highlight groups " after ':' - call assert_equal([&hl, '8'], getcompletion('set hl=', 'cmdline')[0:1]) + call assert_equal([escape(&hl, '|'), '8'], getcompletion('set hl=', 'cmdline')[0:1]) call assert_equal('8', getcompletion('set hl+=', 'cmdline')[0]) call assert_equal(['8:', '8b', '8i'], getcompletion('set hl+=8', 'cmdline')[0:2]) call assert_equal('8bi', getcompletion('set hl+=8b', 'cmdline')[0]) diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 6936ca8078..79e857cf52 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -447,7 +447,7 @@ func Test_statusline() set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:- vsplit set statusline=x%=y - call assert_match('^x^\+y^x=\+y$', s:get_statusline()) + call assert_match('^x^\+y x=\+y$', s:get_statusline()) set fillchars& close @@ -548,9 +548,9 @@ func Test_statusline_mbyte_fillchar() set statusline=a%=b call assert_match('^a\+━\+b$', s:get_statusline()) vnew - call assert_match('^a\+━\+b━a\+═\+b$', s:get_statusline()) + call assert_match('^a\+━\+b a\+═\+b$', s:get_statusline()) wincmd w - call assert_match('^a\+═\+b═a\+━\+b$', s:get_statusline()) + call assert_match('^a\+═\+b a\+━\+b$', s:get_statusline()) set statusline& fillchars& %bw! endfunc diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index aa631b2905..98e9ba83db 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -284,7 +284,7 @@ func Test_window_split_no_room() call setwinvar(winnr('k'), '&statusline', '@#') let last_stl_row = win_screenpos(0)[0] - 1 redraw - call assert_equal('@#|', GetScreenStr(last_stl_row)) + call assert_equal('@# ', GetScreenStr(last_stl_row)) call assert_equal('~ |', GetScreenStr(&lines - &cmdheight)) call assert_fails('wincmd H', 'E36:') @@ -292,7 +292,7 @@ func Test_window_split_no_room() call assert_equal(info, s:win_layout_info()) call setwinvar(winnr('k'), '&statusline', '=-') redraw - call assert_equal('=-|', GetScreenStr(last_stl_row)) + call assert_equal('=- ', GetScreenStr(last_stl_row)) call assert_equal('~ |', GetScreenStr(&lines - &cmdheight)) %bw! diff --git a/src/version.c b/src/version.c index dec32640e3..657dc3f471 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 349, /**/ 348, /**/ diff --git a/src/vim.h b/src/vim.h index 51d0cc1f32..143642d044 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1533,6 +1533,7 @@ typedef enum , HLF_S // status lines , HLF_SNC // status lines of not-current windows , HLF_C // column to separate vertically split windows + , HLF_CNC // column separator for not-current windows , HLF_T // Titles for output from ":set all", ":autocmd" etc. , HLF_V // Visual mode , HLF_VNC // Visual mode, autoselecting and not clipboard owner @@ -1584,7 +1585,7 @@ typedef enum // The HL_FLAGS must be in the same order as the HLF_ enums! // When changing this also adjust the default for 'highlight'. #define HL_FLAGS {'8', '~', '@', 'd', 'e', 'h', 'i', 'l', 'y', 'm', 'M', \ - 'n', 'a', 'b', 'N', 'G', 'O', 'r', 's', 'S', 'c', 't', 'v', 'V', \ + 'n', 'a', 'b', 'N', 'G', 'O', 'r', 's', 'S', 'c', '|', 't', 'v', 'V', \ 'w', 'W', 'f', 'F', 'A', 'C', 'D', 'T', 'E', '-', '>', \ 'B', 'P', 'R', 'L', \ '+', '=', 'k', '<','[', ']', '{', '}', 'x', 'X', 'j', 'H', \ diff --git a/src/window.c b/src/window.c index b42845ecf2..1d0d478442 100644 --- a/src/window.c +++ b/src/window.c @@ -5825,6 +5825,14 @@ win_enter_ext(win_T *wp, int flags) redraw_mode = TRUE; #endif redraw_tabline = TRUE; + redraw_vseps = TRUE; + // Need to redraw all status lines so that the vsep character at + // status line rows is updated for the new current window. + { + win_T *ww; + FOR_ALL_WINDOWS(ww) + ww->w_redr_status = TRUE; + } #if defined(FEAT_TABPANEL) redraw_tabpanel = TRUE; #endif @@ -7879,6 +7887,8 @@ frame_change_statusline_height(void) tabpage_T *tp; int global_stlh; + redraw_vseps = TRUE; + // First pass: find space-constrained global height. global_stlh = stlo_mh; FOR_ALL_TABPAGES(tp) From 4c287947164fe26a6bd32e1c9362668388ae7457 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Tue, 14 Apr 2026 18:51:54 +0000 Subject: [PATCH 043/545] patch 9.2.0350: Enabling modelines poses a risk Problem: Enabling modelines poses a risk, cannot whitelist specific modelines Solution: Include the 'modelinestrict' option, enabled by default, that allows only a few very specific modelines, all others will be ignored When set (which it is by default), only the following settings will be applied, all others will be ignored: 'autoindent' 'cindent' 'commentstring' 'expandtab' 'filetype' 'foldcolumn' 'foldenable' 'foldmethod' 'modifiable' 'readonly' 'rightleft' 'shiftwidth' 'smartindent' 'softtabstop' 'spell' 'spelllang' 'tabstop' 'textwidth' 'varsofttabstop' 'vartabstop' Supported by AI closes: #19875 Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 42 ++++++ runtime/doc/quickref.txt | 3 +- runtime/doc/tags | 4 + runtime/doc/version9.txt | 3 + runtime/optwin.vim | 4 +- .../testdir/dumps/python2_strings_05.dump | 2 +- .../syntax/testdir/input/python2_strings.py | 2 +- runtime/syntax/vim.vim | 32 ++--- src/option.c | 43 ++++++ src/option.h | 1 + src/optiondefs.h | 3 + src/po/vim.pot | 5 +- src/testdir/test_fold.vim | 8 +- src/testdir/test_indent.vim | 4 +- src/testdir/test_modeline.vim | 127 +++++++++++++++++- src/testdir/test_quickfix.vim | 4 +- src/version.c | 2 + 17 files changed, 255 insertions(+), 34 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4d40b8aa55..627d965402 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6199,6 +6199,48 @@ A jump table for the options with a short description can be found at |Q_op|. NOTE: 'modeline' is set to the Vi default value when 'compatible' is set and to the Vim default value when 'compatible' is reset. + *'modelinestrict'* *'mlst'* *'nomodelinestrict'* *'nomlst'* +'modelinestrict' 'mlst' boolean (default: on) + global + When on, only a safe subset of options can be set from a |modeline|. + The following options are allowed: + 'autoindent' + 'cindent' + 'commentstring' + 'expandtab' + 'filetype' + 'foldcolumn' + 'foldenable' + 'foldmethod' + 'modifiable' + 'readonly' + 'rightleft' + 'shiftwidth' + 'smartindent' + 'softtabstop' + 'spell' + 'spelllang' + 'tabstop' + 'textwidth' + 'varsofttabstop' + 'vartabstop' + + Any other option set from a modeline will be silently ignored. + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. + + The behaviour of 'modeline', 'modelinestrict' and 'modelineexpr' is + as follows: + + 'modeline'| 'modelinestrict'| 'modelineexpr' | Meaning + ----------+-----------------+-------------------+--------~ + on | off | on | All options can be set + on | on | any | Only whitelisted + | | | options can be set + on | off | off | All options except for + | | | expr options can be set + off | any | any | No options can be set + *'modifiable'* *'ma'* *'nomodifiable'* *'noma'* *E21* 'modifiable' 'ma' boolean (default on) diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 770983780f..8f6aca0463 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -1,4 +1,4 @@ -*quickref.txt* For Vim version 9.2. Last change: 2026 Apr 07 +*quickref.txt* For Vim version 9.2. Last change: 2026 Apr 09 VIM REFERENCE MANUAL by Bram Moolenaar @@ -823,6 +823,7 @@ Short explanation of each option: *option-list* 'modeline' 'ml' recognize modelines at start or end of file 'modelineexpr' 'mle' allow setting expression options from a modeline 'modelines' 'mls' number of lines checked for modelines +'modelinestrict' 'mlst' only allow safe options in modelines 'modifiable' 'ma' changes to the text are not possible 'modified' 'mod' buffer has been modified 'more' pause listings when the whole screen is filled diff --git a/runtime/doc/tags b/runtime/doc/tags index c4bc58fc94..4d31cc51a6 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -531,6 +531,7 @@ $quote eval.txt /*$quote* 'ml' options.txt /*'ml'* 'mle' options.txt /*'mle'* 'mls' options.txt /*'mls'* +'mlst' options.txt /*'mlst'* 'mm' options.txt /*'mm'* 'mmd' options.txt /*'mmd'* 'mmp' options.txt /*'mmp'* @@ -539,6 +540,7 @@ $quote eval.txt /*$quote* 'modeline' options.txt /*'modeline'* 'modelineexpr' options.txt /*'modelineexpr'* 'modelines' options.txt /*'modelines'* +'modelinestrict' options.txt /*'modelinestrict'* 'modifiable' options.txt /*'modifiable'* 'modified' options.txt /*'modified'* 'mopt' options.txt /*'mopt'* @@ -706,9 +708,11 @@ $quote eval.txt /*$quote* 'nomh' options.txt /*'nomh'* 'noml' options.txt /*'noml'* 'nomle' options.txt /*'nomle'* +'nomlst' options.txt /*'nomlst'* 'nomod' options.txt /*'nomod'* 'nomodeline' options.txt /*'nomodeline'* 'nomodelineexpr' options.txt /*'nomodelineexpr'* +'nomodelinestrict' options.txt /*'nomodelinestrict'* 'nomodifiable' options.txt /*'nomodifiable'* 'nomodified' options.txt /*'nomodified'* 'nomore' options.txt /*'nomore'* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 54449a5424..fa8c5355c7 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52634,6 +52634,8 @@ Changed ~ - |js_decode()| rejects lone surrogates - virtual text properties on lines deleted by a multi-line substitute are moved to the resulting joined line instead of being dropped. +- Only a few whitelisted options are allowed to be set by a modeline to + improve overall security, see for details: 'modelinestrict' *added-9.3* Added ~ @@ -52650,6 +52652,7 @@ Autocommands: ~ Options: ~ +'modelinestrict' Only allow safe options to be set from a modeline. 'pumopt' Additional options for the popup menu 'statuslineopt' Extra window-local options for the 'statusline', to configure the height. diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 522ffed54d..73243e77f3 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: The Vim Project -" Last Change: 2026 Apr 07 +" Last Change: 2026 Apr 09 " Former Maintainer: Bram Moolenaar " If there already is an option window, jump to that one. @@ -1108,6 +1108,8 @@ call append("$", "\t" .. s:local_to_buffer) call BinOptionL("ml") call AddOption("modelineexpr", gettext("allow setting expression options from a modeline")) call BinOptionG("mle", &mle) +call AddOption("modelinestrict", gettext("only allow safe options to be set from a modeline")) +call BinOptionG("modelinestrict", &modelinestrict) call AddOption("modelines", gettext("number of lines to check for modelines")) call append("$", " \tset mls=" . &mls) call AddOption("binary", gettext("binary file editing")) diff --git a/runtime/syntax/testdir/dumps/python2_strings_05.dump b/runtime/syntax/testdir/dumps/python2_strings_05.dump index 0ed41d8b3a..730e067fdf 100644 --- a/runtime/syntax/testdir/dumps/python2_strings_05.dump +++ b/runtime/syntax/testdir/dumps/python2_strings_05.dump @@ -1,7 +1,7 @@ |a+0#e000002#ffffff0|n|d| |l|i|t|e|r|a|l| |\|t| |a|n|d| |\|0|4|0| |a|n|d| |\|x|F@1| +0#0000000&@42 |a+0#e000002&|n|d| |e|s|c|a|p|e|s| |\+0#e000e06&|u|0@1|A|1| +0#e000002&|a|n|d| |\+0#e000e06&|U|0@2|1|0|6|0|5|"+0#e000002&@2| +0#0000000&@38 @75 ->#+0#0000e05&| |v|i|m|:| |s|y|n|t|a|x|=|p|y|t|h|o|n|2| +0#0000000&@53 +>#+0#0000e05&| |v|i|m|:| |f|t|=|p|y|t|h|o|n|2| +0#0000000&@57 |~+0#4040ff13&| @73 |~| @73 |~| @73 diff --git a/runtime/syntax/testdir/input/python2_strings.py b/runtime/syntax/testdir/input/python2_strings.py index a5625b7d65..7ac32da161 100644 --- a/runtime/syntax/testdir/input/python2_strings.py +++ b/runtime/syntax/testdir/input/python2_strings.py @@ -79,4 +79,4 @@ and literal \t and \040 and \xFF and escapes \u00A1 and \U00010605""" -# vim: syntax=python2 +# vim: ft=python2 diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 23bdda6511..f213710c37 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -68,25 +68,25 @@ syn keyword vimOption contained al aleph ari allowrevins ambw ambiwidth arab ara syn keyword vimOption contained co columns com comments cms commentstring cp compatible cpt complete cfu completefunc cia completeitemalign cot completeopt cpp completepopup csl completeslash cto completetimeout cocu concealcursor cole conceallevel cf confirm ci copyindent cpo cpoptions cm cryptmethod cspc cscopepathcomp csprg cscopeprg csqf cscopequickfix csre cscoperelative cst cscopetag csto cscopetagorder csverb cscopeverbose crb cursorbind cuc cursorcolumn cul cursorline culopt cursorlineopt debug def define deco delcombine dict dictionary diff dia diffanchors dex diffexpr dip diffopt dg digraph dir directory dy display ead eadirection ed edcompatible emo emoji enc encoding eof endoffile eol endofline ea equalalways ep equalprg eb errorbells ef errorfile skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained efm errorformat ek esckeys ei eventignore eiw eventignorewin et expandtab ex exrc fenc fileencoding fencs fileencodings ff fileformat ffs fileformats fic fileignorecase ft filetype fcs fillchars ffu findfunc fixeol fixendofline fcl foldclose fdc foldcolumn fen foldenable fde foldexpr fdi foldignore fdl foldlevel fdls foldlevelstart fmr foldmarker fdm foldmethod fml foldminlines fdn foldnestmax fdo foldopen fdt foldtext fex formatexpr flp formatlistpat fo formatoptions fp formatprg fs fsync gd gdefault gfm grepformat gp grepprg gcr guicursor gfn guifont gfs guifontset gfw guifontwide ghr guiheadroom gli guiligatures go guioptions guipty gtl guitablabel gtt guitabtooltip hf helpfile hh helpheight hlg helplang hid hidden hl highlight skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine mfd maxfuncdepth skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset pmbfn printmbfont skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth shm shortmess sn shortname skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding tgc termguicolors skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines mlst modelinestrict ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth shm shortmess skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod " vimOptions: These are the turn-off setting variants {{{2 " GEN_SYN_VIM: vimOption turn-off, START_STR='syn keyword vimOption contained', END_STR='' syn keyword vimOption contained noari noallowrevins noarab noarabic noarshape noarabicshape noacd noautochdir noac noautocomplete noai noautoindent noar noautoread noasd noautoshelldir noaw noautowrite noawa noautowriteall nobk nobackup nobeval noballooneval nobevalterm noballoonevalterm nobin nobinary nobomb nobri nobreakindent nobl nobuflisted nocdh nocdhome nocin nocindent nocp nocompatible nocf noconfirm noci nocopyindent nocsre nocscoperelative nocst nocscopetag nocsverb nocscopeverbose nocrb nocursorbind nocuc nocursorcolumn nocul nocursorline nodeco nodelcombine nodiff nodg nodigraph noed noedcompatible noemo noemoji noeof noendoffile noeol noendofline noea noequalalways noeb noerrorbells noek noesckeys noet noexpandtab noex noexrc nofic nofileignorecase -syn keyword vimOption contained nofixeol nofixendofline nofen nofoldenable nofs nofsync nogd nogdefault noguipty nohid nohidden nohk nohkmap nohkp nohkmapp nohls nohlsearch noicon noic noignorecase noimc noimcmdline noimd noimdisable nois noincsearch noinf noinfercase noim noinsertmode nojs nojoinspaces nolnr nolangnoremap nolrm nolangremap nolz nolazyredraw nolbr nolinebreak nolisp nolist nolpl noloadplugins nomagic noml nomodeline nomle nomodelineexpr noma nomodifiable nomod nomodified nomore nomousef nomousefocus nomh nomousehide nomousemev nomousemoveevent nonu nonumber noodev noopendevice nopaste nopi nopreserveindent nopvw nopreviewwindow noprompt noro noreadonly nornu norelativenumber noremap nors norestorescreen nori norevins norl norightleft noru noruler -syn keyword vimOption contained noscb noscrollbind noscf noscrollfocus nosecure nossl noshellslash nostmp noshelltemp nosr noshiftround nosn noshortname nosc noshowcmd nosft noshowfulltag nosm noshowmatch nosmd noshowmode noscs nosmartcase nosi nosmartindent nosta nosmarttab nosms nosmoothscroll nospell nosb nosplitbelow nospr nosplitright nosol nostartofline noswf noswapfile notbs notagbsearch notr notagrelative notgst notagstack notbidi notermbidi notgc notermguicolors notsy notermsync noterse nota notextauto notx notextmode notop notildeop noto notimeout notitle nottimeout notbi nottybuiltin notf nottyfast noudf noundofile novb novisualbell nowarn nowiv noweirdinvert nowic nowildignorecase nowmnu nowildmenu nowfb nowinfixbuf nowfh nowinfixheight nowfw nowinfixwidth -syn keyword vimOption contained nowst nowlsteal nowrap nows nowrapscan nowrite nowa nowriteany nowb nowritebackup noxtermcodes +syn keyword vimOption contained nofixeol nofixendofline nofen nofoldenable nofs nofsync nogd nogdefault noguipty nohid nohidden nohk nohkmap nohkp nohkmapp nohls nohlsearch noicon noic noignorecase noimc noimcmdline noimd noimdisable nois noincsearch noinf noinfercase noim noinsertmode nojs nojoinspaces nolnr nolangnoremap nolrm nolangremap nolz nolazyredraw nolbr nolinebreak nolisp nolist nolpl noloadplugins nomagic noml nomodeline nomle nomodelineexpr nomlst nomodelinestrict noma nomodifiable nomod nomodified nomore nomousef nomousefocus nomh nomousehide nomousemev nomousemoveevent nonu nonumber noodev noopendevice nopaste nopi nopreserveindent nopvw nopreviewwindow noprompt noro noreadonly nornu norelativenumber noremap nors norestorescreen nori norevins norl norightleft +syn keyword vimOption contained noru noruler noscb noscrollbind noscf noscrollfocus nosecure nossl noshellslash nostmp noshelltemp nosr noshiftround nosn noshortname nosc noshowcmd nosft noshowfulltag nosm noshowmatch nosmd noshowmode noscs nosmartcase nosi nosmartindent nosta nosmarttab nosms nosmoothscroll nospell nosb nosplitbelow nospr nosplitright nosol nostartofline noswf noswapfile notbs notagbsearch notr notagrelative notgst notagstack notbidi notermbidi notgc notermguicolors notsy notermsync noterse nota notextauto notx notextmode notop notildeop noto notimeout notitle nottimeout notbi nottybuiltin notf nottyfast noudf noundofile novb novisualbell nowarn nowiv noweirdinvert nowic nowildignorecase nowmnu nowildmenu nowfb nowinfixbuf nowfh nowinfixheight +syn keyword vimOption contained nowfw nowinfixwidth nowst nowlsteal nowrap nows nowrapscan nowrite nowa nowriteany nowb nowritebackup noxtermcodes " vimOptions: These are the invertible variants {{{2 " GEN_SYN_VIM: vimOption invertible, START_STR='syn keyword vimOption contained', END_STR='' syn keyword vimOption contained invari invallowrevins invarab invarabic invarshape invarabicshape invacd invautochdir invac invautocomplete invai invautoindent invar invautoread invasd invautoshelldir invaw invautowrite invawa invautowriteall invbk invbackup invbeval invballooneval invbevalterm invballoonevalterm invbin invbinary invbomb invbri invbreakindent invbl invbuflisted invcdh invcdhome invcin invcindent invcp invcompatible invcf invconfirm invci invcopyindent invcsre invcscoperelative invcst invcscopetag invcsverb invcscopeverbose invcrb invcursorbind invcuc invcursorcolumn invcul invcursorline invdeco invdelcombine invdiff invdg invdigraph inved invedcompatible invemo invemoji inveof invendoffile inveol invendofline invea invequalalways inveb inverrorbells -syn keyword vimOption contained invek invesckeys invet invexpandtab invex invexrc invfic invfileignorecase invfixeol invfixendofline invfen invfoldenable invfs invfsync invgd invgdefault invguipty invhid invhidden invhk invhkmap invhkp invhkmapp invhls invhlsearch invicon invic invignorecase invimc invimcmdline invimd invimdisable invis invincsearch invinf invinfercase invim invinsertmode invjs invjoinspaces invlnr invlangnoremap invlrm invlangremap invlz invlazyredraw invlbr invlinebreak invlisp invlist invlpl invloadplugins invmagic invml invmodeline invmle invmodelineexpr invma invmodifiable invmod invmodified invmore invmousef invmousefocus invmh invmousehide invmousemev invmousemoveevent invnu invnumber invodev invopendevice invpaste invpi invpreserveindent -syn keyword vimOption contained invpvw invpreviewwindow invprompt invro invreadonly invrnu invrelativenumber invremap invrs invrestorescreen invri invrevins invrl invrightleft invru invruler invscb invscrollbind invscf invscrollfocus invsecure invssl invshellslash invstmp invshelltemp invsr invshiftround invsn invshortname invsc invshowcmd invsft invshowfulltag invsm invshowmatch invsmd invshowmode invscs invsmartcase invsi invsmartindent invsta invsmarttab invsms invsmoothscroll invspell invsb invsplitbelow invspr invsplitright invsol invstartofline invswf invswapfile invtbs invtagbsearch invtr invtagrelative invtgst invtagstack invtbidi invtermbidi invtgc invtermguicolors invtsy invtermsync invterse invta invtextauto invtx invtextmode invtop invtildeop invto invtimeout -syn keyword vimOption contained invtitle invttimeout invtbi invttybuiltin invtf invttyfast invudf invundofile invvb invvisualbell invwarn invwiv invweirdinvert invwic invwildignorecase invwmnu invwildmenu invwfb invwinfixbuf invwfh invwinfixheight invwfw invwinfixwidth invwst invwlsteal invwrap invws invwrapscan invwrite invwa invwriteany invwb invwritebackup invxtermcodes +syn keyword vimOption contained invek invesckeys invet invexpandtab invex invexrc invfic invfileignorecase invfixeol invfixendofline invfen invfoldenable invfs invfsync invgd invgdefault invguipty invhid invhidden invhk invhkmap invhkp invhkmapp invhls invhlsearch invicon invic invignorecase invimc invimcmdline invimd invimdisable invis invincsearch invinf invinfercase invim invinsertmode invjs invjoinspaces invlnr invlangnoremap invlrm invlangremap invlz invlazyredraw invlbr invlinebreak invlisp invlist invlpl invloadplugins invmagic invml invmodeline invmle invmodelineexpr invmlst invmodelinestrict invma invmodifiable invmod invmodified invmore invmousef invmousefocus invmh invmousehide invmousemev invmousemoveevent invnu invnumber invodev invopendevice invpaste +syn keyword vimOption contained invpi invpreserveindent invpvw invpreviewwindow invprompt invro invreadonly invrnu invrelativenumber invremap invrs invrestorescreen invri invrevins invrl invrightleft invru invruler invscb invscrollbind invscf invscrollfocus invsecure invssl invshellslash invstmp invshelltemp invsr invshiftround invsn invshortname invsc invshowcmd invsft invshowfulltag invsm invshowmatch invsmd invshowmode invscs invsmartcase invsi invsmartindent invsta invsmarttab invsms invsmoothscroll invspell invsb invsplitbelow invspr invsplitright invsol invstartofline invswf invswapfile invtbs invtagbsearch invtr invtagrelative invtgst invtagstack invtbidi invtermbidi invtgc invtermguicolors invtsy invtermsync invterse invta invtextauto invtx invtextmode +syn keyword vimOption contained invtop invtildeop invto invtimeout invtitle invttimeout invtbi invttybuiltin invtf invttyfast invudf invundofile invvb invvisualbell invwarn invwiv invweirdinvert invwic invwildignorecase invwmnu invwildmenu invwfb invwinfixbuf invwfh invwinfixheight invwfw invwinfixwidth invwst invwlsteal invwrap invws invwrapscan invwrite invwa invwriteany invwb invwritebackup invxtermcodes " termcap codes (which can also be set) {{{2 " GEN_SYN_VIM: vimOption term output code, START_STR='syn keyword vimOption contained', END_STR='skipwhite nextgroup=vimSetEqual,vimSetMod' syn keyword vimOption contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo t_BS t_ES skipwhite nextgroup=vimSetEqual,vimSetMod @@ -107,11 +107,11 @@ syn keyword vimOptionVarName contained al aleph ari allowrevins ambw ambiwidth a syn keyword vimOptionVarName contained co columns com comments cms commentstring cp compatible cpt complete cfu completefunc cia completeitemalign cot completeopt cpp completepopup csl completeslash cto completetimeout cocu concealcursor cole conceallevel cf confirm ci copyindent cpo cpoptions cm cryptmethod cspc cscopepathcomp csprg cscopeprg csqf cscopequickfix csre cscoperelative cst cscopetag csto cscopetagorder csverb cscopeverbose crb cursorbind cuc cursorcolumn cul cursorline culopt cursorlineopt debug def define deco delcombine dict dictionary diff dia diffanchors dex diffexpr dip diffopt dg digraph dir directory dy display ead eadirection ed edcompatible emo emoji enc encoding eof endoffile eol endofline ea equalalways ep equalprg eb errorbells ef errorfile syn keyword vimOptionVarName contained efm errorformat ek esckeys ei eventignore eiw eventignorewin et expandtab ex exrc fenc fileencoding fencs fileencodings ff fileformat ffs fileformats fic fileignorecase ft filetype fcs fillchars ffu findfunc fixeol fixendofline fcl foldclose fdc foldcolumn fen foldenable fde foldexpr fdi foldignore fdl foldlevel fdls foldlevelstart fmr foldmarker fdm foldmethod fml foldminlines fdn foldnestmax fdo foldopen fdt foldtext fex formatexpr flp formatlistpat fo formatoptions fp formatprg fs fsync gd gdefault gfm grepformat gp grepprg gcr guicursor gfn guifont gfs guifontset gfw guifontwide ghr guiheadroom gli guiligatures go guioptions guipty gtl guitablabel gtt guitabtooltip hf helpfile hh helpheight hlg helplang hid hidden hl highlight syn keyword vimOptionVarName contained hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine -syn keyword vimOptionVarName contained mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset -syn keyword vimOptionVarName contained pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth -syn keyword vimOptionVarName contained shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi -syn keyword vimOptionVarName contained tenc termencoding tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu -syn keyword vimOptionVarName contained wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes +syn keyword vimOptionVarName contained mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines mlst modelinestrict ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader +syn keyword vimOptionVarName contained pmbcs printmbcharset pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround +syn keyword vimOptionVarName contained sw shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll +syn keyword vimOptionVarName contained term tbidi termbidi tenc termencoding tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore +syn keyword vimOptionVarName contained wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes " GEN_SYN_VIM: vimOption term output code variable, START_STR='syn keyword vimOptionVarName contained', END_STR='' syn keyword vimOptionVarName contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo t_BS t_ES syn keyword vimOptionVarName contained t_F1 t_F2 t_F3 t_F4 t_F5 t_F6 t_F7 t_F8 t_F9 t_k1 t_K1 t_k2 t_k3 t_K3 t_k4 t_K4 t_k5 t_K5 t_k6 t_K6 t_k7 t_K7 t_k8 t_K8 t_k9 t_K9 t_KA t_kb t_kB t_KB t_KC t_kd t_kD t_KD t_KE t_KF t_KG t_kh t_KH t_kI t_KI t_KJ t_KK t_kl t_KL t_kN t_kP t_kr t_ku diff --git a/src/option.c b/src/option.c index ab1166098c..46009341d1 100644 --- a/src/option.c +++ b/src/option.c @@ -1543,6 +1543,44 @@ get_opt_op(char_u *arg) return op; } +// Options that are allowed in a modeline when 'modelinestrict' is on. +static char *modeline_whitelist[] = +{ + "autoindent", + "cindent", + "commentstring", + "expandtab", + "filetype", + "foldcolumn", + "foldenable", + "foldmethod", + "modifiable", + "readonly", + "rightleft", + "shiftwidth", + "smartindent", + "softtabstop", + "spell", + "spelllang", + "tabstop", + "textwidth", + "varsofttabstop", + "vartabstop", + NULL +}; + +/* + * Return TRUE if option "name" is in the modeline whitelist. + */ + static bool +is_modeline_whitelisted(char *name) +{ + for (int i = 0; modeline_whitelist[i] != NULL; i++) + if (STRCMP(name, modeline_whitelist[i]) == 0) + return true; + return false; +} + /* * Validate whether the value of the option in "opt_idx" can be changed. * Returns FAIL if the option can be skipped or cannot be changed. Returns OK @@ -1575,6 +1613,11 @@ validate_opt_idx(int opt_idx, int opt_flags, long_u flags, char **errmsg) *errmsg = e_not_allowed_in_modeline_when_modelineexpr_is_off; return FAIL; } + // When 'modelinestrict' is on, only whitelisted options may be + // set from a modeline. Silently skip others. + if (p_mlstr && opt_idx >= 0 + && !is_modeline_whitelisted(options[opt_idx].fullname)) + return FAIL; #ifdef FEAT_DIFF // In diff mode some options are overruled. This avoids that // 'foldmethod' becomes "marker" instead of "diff" and that diff --git a/src/option.h b/src/option.h index 7165ea80c6..6bf6169bbf 100644 --- a/src/option.h +++ b/src/option.h @@ -807,6 +807,7 @@ EXTERN char_u *p_msm; // 'mkspellmem' EXTERN int p_ml; // 'modeline' EXTERN int p_mle; // 'modelineexpr' EXTERN long p_mls; // 'modelines' +EXTERN int p_mlstr; // 'modelinestrict' EXTERN int p_ma; // 'modifiable' EXTERN int p_mod; // 'modified' EXTERN char_u *p_mouse; // 'mouse' diff --git a/src/optiondefs.h b/src/optiondefs.h index 9fd2fe5cd3..38962790bf 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -1785,6 +1785,9 @@ static struct vimoption options[] = {"modelines", "mls", P_NUM|P_VI_DEF, (char_u *)&p_mls, PV_NONE, NULL, NULL, {(char_u *)5L, (char_u *)0L} SCTX_INIT}, + {"modelinestrict", "mlst", P_BOOL|P_VI_DEF|P_SECURE, + (char_u *)&p_mlstr, PV_NONE, NULL, NULL, + {(char_u *)TRUE, (char_u *)0L} SCTX_INIT}, {"modifiable", "ma", P_BOOL|P_VI_DEF|P_NOGLOB, (char_u *)&p_ma, PV_MA, did_set_modifiable, NULL, {(char_u *)TRUE, (char_u *)0L} SCTX_INIT}, diff --git a/src/po/vim.pot b/src/po/vim.pot index b2e3b0f647..b09ade556d 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-04-07 19:50+0000\n" +"POT-Creation-Date: 2026-04-09 20:33+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -10223,6 +10223,9 @@ msgstr "" msgid "allow setting expression options from a modeline" msgstr "" +msgid "only allow safe options to be set from a modeline" +msgstr "" + msgid "number of lines to check for modelines" msgstr "" diff --git a/src/testdir/test_fold.vim b/src/testdir/test_fold.vim index c377a95737..d4b3a5093b 100644 --- a/src/testdir/test_fold.vim +++ b/src/testdir/test_fold.vim @@ -1766,7 +1766,7 @@ func Test_foldtext_in_modeline() bw! endfunc - set modeline modelineexpr + set modeline modelineexpr nomodelinestrict call Check_foldtext_in_modeline('setlocal') call Check_foldtext_in_modeline('set') @@ -1792,7 +1792,7 @@ func Test_foldtext_in_modeline() call assert_equal(['after'], readfile('Xmodelinefoldtext_write')) bwipe! - set modeline& modelineexpr& + set modeline& modelineexpr& modelinestrict& delfunc ModelineFoldText delfunc Check_foldtext_in_modeline endfunc @@ -1849,7 +1849,7 @@ func Test_foldexpr_in_modeline() bw! endfunc - set modeline modelineexpr + set modeline modelineexpr nomodelinestrict call Check_foldexpr_in_modeline('setlocal') call Check_foldexpr_in_modeline('set') @@ -1875,7 +1875,7 @@ func Test_foldexpr_in_modeline() call assert_equal(['after'], readfile('Xmodelinefoldexpr_write')) bwipe! - set modeline& modelineexpr& + set modeline& modelineexpr& modelinestrict& delfunc ModelineFoldExpr delfunc Check_foldexpr_in_modeline endfunc diff --git a/src/testdir/test_indent.vim b/src/testdir/test_indent.vim index c3f7953caa..c19be6328e 100644 --- a/src/testdir/test_indent.vim +++ b/src/testdir/test_indent.vim @@ -195,7 +195,7 @@ endfunc " Test for setting the 'indentexpr' from a modeline func Test_modeline_indent_expr() let modeline = &modeline - set modeline + set modeline nomodelinestrict func GetIndent() return line('.') * 2 endfunc @@ -206,7 +206,7 @@ func Test_modeline_indent_expr() exe "normal Oa\nb\n" call assert_equal([' a', ' b'], getline(1, 2)) - set modelineexpr& + set modelineexpr& modelinestrict& delfunc GetIndent let &modeline = modeline bw! diff --git a/src/testdir/test_modeline.vim b/src/testdir/test_modeline.vim index 79fc7d14d5..b78a4258f0 100644 --- a/src/testdir/test_modeline.vim +++ b/src/testdir/test_modeline.vim @@ -47,7 +47,7 @@ endfunc func Test_modeline_syntax() call writefile(['vim: set syn=c :', 'nothing'], 'Xmodeline_syntax', 'D') let modeline = &modeline - set modeline + set modeline nomodelinestrict syntax enable split Xmodeline_syntax call assert_equal("c", &syntax) @@ -55,6 +55,7 @@ func Test_modeline_syntax() bwipe! let &modeline = modeline + set modelinestrict syntax off endfunc @@ -62,13 +63,14 @@ func Test_modeline_keymap() CheckFeature keymap call writefile(['vim: set keymap=greek :', 'nothing'], 'Xmodeline_keymap', 'D') let modeline = &modeline - set modeline + set modeline nomodelinestrict split Xmodeline_keymap call assert_equal("greek", &keymap) call assert_match('greek\|grk', b:keymap_name) bwipe! let &modeline = modeline + set modelinestrict set keymap= iminsert=0 imsearch=-1 endfunc @@ -145,7 +147,8 @@ endfunc func Test_modeline_colon() let modeline = &modeline - set modeline + let modelinestrict = &modelinestrict + set modeline nomodelinestrict call writefile(['// vim: set showbreak=\: ts=2: sw=2'], 'Xmodeline_colon', 'D') edit Xmodeline_colon @@ -159,6 +162,7 @@ func Test_modeline_colon() call assert_equal(8, &sw) let &modeline = modeline + let &modelinestrict = modelinestrict endfunc func s:modeline_fails(what, text, error) @@ -170,7 +174,8 @@ func s:modeline_fails(what, text, error) let fname = "Xmodeline_fails_" . a:what call writefile(['vim: set ' . a:text . ' :', 'nothing'], fname, 'D') let modeline = &modeline - set modeline + let modelinestrict = &modelinestrict + set modeline nomodelinestrict filetype plugin on syntax enable call assert_fails('split ' . fname, a:error) @@ -179,6 +184,7 @@ func s:modeline_fails(what, text, error) bwipe! let &modeline = modeline + let &modelinestrict = modelinestrict filetype plugin off syntax off endfunc @@ -348,20 +354,23 @@ endfunc " Some options cannot be set from the modeline when 'diff' option is set func Test_modeline_diff_buffer() + set nomodelinestrict call writefile(['vim: diff foldmethod=marker wrap'], 'Xmdifile', 'D') set foldmethod& nowrap new Xmdifile call assert_equal('manual', &foldmethod) call assert_false(&wrap) set wrap& + set modelinestrict bw endfunc func Test_modeline_disable() - set modeline + set modeline nomodelinestrict call writefile(['vim: sw=2', 'vim: nomodeline', 'vim: sw=3'], 'Xmodeline_disable', 'D') edit Xmodeline_disable call assert_equal(2, &sw) + set modelinestrict endfunc " If 'nowrap' is set from a modeline, '>' is used forcibly as lcs-extends. @@ -373,6 +382,7 @@ func Test_modeline_nowrap_lcs_extends() \ 'ddd vim: nowrap', \ ], 'Xmodeline_nowrap', 'D') set noequalalways + set nomodelinestrict 11new | 20vsplit func Check_modeline_nowrap(expect_insecure, expect_secure, set_cmd) @@ -493,4 +503,111 @@ func Test_modeline_nowrap_lcs_extends() set equalalways& endfunc +func Test_modeline_strict_allowed() + let modeline = &modeline + set modeline modelinestrict + + " Whitelisted options should work + call writefile(['vim: set ts=2 sw=4 et :'], 'Xmodeline_strict', 'D') + split Xmodeline_strict + call assert_equal(2, &ts) + call assert_equal(4, &sw) + call assert_equal(1, &et) + bwipe! + + " 'filetype' should work + call writefile(['vim: set ft=python :'], 'Xmodeline_strict') + filetype plugin on + split Xmodeline_strict + call assert_equal("python", &filetype) + bwipe! + filetype plugin off + + " 'spell' and 'spelllang' should work + call writefile(['vim: set spell spelllang=de :'], 'Xmodeline_strict') + split Xmodeline_strict + call assert_equal(1, &spell) + call assert_equal("de", &spelllang) + bwipe! + + " 'foldmethod' should work + call writefile(['vim: set fdm=marker :'], 'Xmodeline_strict') + split Xmodeline_strict + call assert_equal("marker", &foldmethod) + bwipe! + + " 'autoindent' and 'cindent' should work + call writefile(['vim: set ai cin :'], 'Xmodeline_strict') + split Xmodeline_strict + call assert_equal(1, &ai) + call assert_equal(1, &cin) + bwipe! + + " 'textwidth' + call writefile(['vim: set tw=10 :'], 'Xmodeline_strict') + split Xmodeline_strict + call assert_equal(10, &textwidth) + bwipe! + + let &modeline = modeline + set modelinestrict +endfunc + +func Test_modeline_strict_blocked() + let modeline = &modeline + set modeline modelinestrict + + " 'wrap' is not whitelisted, should be silently skipped + set wrap + call writefile(['vim: set nowrap :'], 'Xmodeline_strict_fail') + split Xmodeline_strict_fail + call assert_equal(1, &wrap) + bwipe! + + " 'number' is not whitelisted, should be silently skipped + set nonumber + call writefile(['vim: set number :'], 'Xmodeline_strict_fail') + split Xmodeline_strict_fail + call assert_equal(0, &number) + bwipe! + + " Whitelisted options still work alongside blocked ones + set wrap nonumber + call writefile(['vim: set nowrap ts=3 number :'], 'Xmodeline_strict_fail') + split Xmodeline_strict_fail + call assert_equal(1, &wrap) + call assert_equal(3, &ts) + call assert_equal(0, &number) + bwipe! + + let &modeline = modeline +endfunc + +func Test_modeline_strict_off() + let modeline = &modeline + set modeline nomodelinestrict + + " With modelinestrict off, non-whitelisted options should work + call writefile(['vim: set number :'], 'Xmodeline_strict_off', 'D') + split Xmodeline_strict_off + call assert_equal(1, &number) + bwipe! + + let &modeline = modeline + set modelinestrict& +endfunc + +func Test_modeline_strict_cannot_be_set_from_modeline() + let modeline = &modeline + set modeline modelinestrict + + " 'modelinestrict' itself cannot be set from a modeline (P_SECURE) + call writefile(['vim: set nomodelinestrict :'], 'Xmodeline_strict_ml', 'D') + call assert_fails('split Xmodeline_strict_ml', 'E520:') + call assert_equal(1, &modelinestrict) + bwipe! + + let &modeline = modeline +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 5326c3dfb6..69a4f6dafa 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -2271,7 +2271,7 @@ func Test_switchbuf() " If opening a file changes 'switchbuf', then the new value should be " retained. - set modeline&vim + set modeline&vim nomodelinestrict call writefile(["vim: switchbuf=split"], 'Xqftestfile1', 'D') enew | only set switchbuf&vim @@ -2290,7 +2290,7 @@ func Test_switchbuf() call delete('Xqftestfile2') call delete('Xqftestfile3') - set switchbuf&vim + set switchbuf&vim modelinestrict enew | only endfunc diff --git a/src/version.c b/src/version.c index 657dc3f471..9aef76d65e 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 350, /**/ 349, /**/ From 351a16c88f56aeeca5e06095624dd701b264b2a9 Mon Sep 17 00:00:00 2001 From: q1uf3ng Date: Wed, 15 Apr 2026 04:03:02 +0000 Subject: [PATCH 044/545] runtime(zip): also block single leading slash and absolute paths in Extract zip#Write(): the Windows path check did not match a single leading slash (/path), which resolves to the current drive root on Windows. Simplify the regex to match any leading slash or backslash. zip#Extract(): add absolute path checks for both Unix and Windows, matching the existing checks in zip#Write(). closes: #19976 Signed-off-by: q1uf3ng Signed-off-by: Christian Brabandt --- runtime/autoload/zip.vim | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/zip.vim b/runtime/autoload/zip.vim index 6a2d6daa3e..aad548239a 100644 --- a/runtime/autoload/zip.vim +++ b/runtime/autoload/zip.vim @@ -23,6 +23,7 @@ " 2026 Apr 01 by Vim Project: Detect more path traversal attacks " 2026 Apr 05 by Vim Project: Detect more path traversal attacks " 2026 Apr 14 by Vim Project: Detect more path traversal attacks on Windows +" 2026 Apr 15 by Vim Project: Detect more path traversal attacks on Windows " License: Vim License (see vim's :help license) " Copyright: Copyright (C) 2005-2019 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, @@ -406,8 +407,8 @@ fun! zip#Write(fname) else let zipfile = substitute(a:fname,'^.\{-}zipfile://\(.\{-}\)::[^\\].*$','\1','') let fname = substitute(a:fname,'^.\{-}zipfile://.\{-}::\([^\\].*\)$','\1','') - " fname should not start with drive leter or a UNC path - if fname =~ '^\%(\%(\a:[\\/]\)\|[\\/]\{2}\)' + " fname should not start with drive letter, UNC path, or leading slash + if fname =~ '^\%(\a:[\\/]\|[\\/]\)' call s:Mess('Error', "***error*** (zip#Write) Path Traversal Attack detected, not writing!") call s:ChgDir(curdir,s:WARNING,"(zip#Write) unable to return to ".curdir."!") return @@ -505,6 +506,18 @@ fun! zip#Extract() call s:Mess('Error', "***error*** (zip#Browse) Path Traversal Attack detected, not extracting!") return endif + " block absolute paths + if has("unix") + if fname =~ '^/' + call s:Mess('Error', "***error*** (zip#Extract) Path Traversal Attack detected, not extracting!") + return + endif + else + if fname =~ '^\%(\a:[\\/]\|[\\/]\)' + call s:Mess('Error', "***error*** (zip#Extract) Path Traversal Attack detected, not extracting!") + return + endif + endif if filereadable(fname) call s:Mess('Error', "***error*** (zip#Extract) <" .. fname .."> already exists in directory, not overwriting!") return From bfa46a52f6a93cb99ec55d56ad43493d875c6dc2 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 15 Apr 2026 04:12:55 +0000 Subject: [PATCH 045/545] patch 9.2.0351: repeat_string() can be improved Problem: repeat_string() can be improved Solution: Replace the for() loop by an exponential growing while loop (Yasuhiro Matsumoto) closes: #19977 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/evalfunc.c | 15 ++++++++++++--- src/version.c | 2 ++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/evalfunc.c b/src/evalfunc.c index a0fb99b738..04774128dd 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -10584,7 +10584,7 @@ repeat_string(typval_T *str_tv, int n, typval_T *rettv) int slen; int len; char_u *r; - int i; + int done; p = tv_get_string(str_tv); rettv->v_type = VAR_STRING; @@ -10599,8 +10599,17 @@ repeat_string(typval_T *str_tv, int n, typval_T *rettv) if (r == NULL) return; - for (i = 0; i < n; i++) - mch_memmove(r + i * slen, p, (size_t)slen); + mch_memmove(r, p, (size_t)slen); + done = slen; + while (done < len) + { + int copy_len = done; + + if (copy_len > len - done) + copy_len = len - done; + mch_memmove(r + done, r, (size_t)copy_len); + done += copy_len; + } r[len] = NUL; rettv->vval.v_string = r; diff --git a/src/version.c b/src/version.c index 9aef76d65e..3c64afaa92 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 351, /**/ 350, /**/ From 2252d2c1e5fe0250923adf52dc7a3e874e8f6690 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Wed, 15 Apr 2026 16:52:09 +0000 Subject: [PATCH 046/545] patch 9.2.0352: 'winhighlight' of left window blends into right window Problem: 'winhighlight' of left window blends into right window Solution: Allow to push a NULL highlight override (Foxe Chen) closes: #19980 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/highlight.c | 13 +++++++---- src/testdir/dumps/Test_VertSplitNC_whl1.dump | 12 ++++++++++ src/testdir/dumps/Test_VertSplitNC_whl2.dump | 12 ++++++++++ src/testdir/test_highlight.vim | 24 ++++++++++++++++++++ src/version.c | 2 ++ 5 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 src/testdir/dumps/Test_VertSplitNC_whl1.dump create mode 100644 src/testdir/dumps/Test_VertSplitNC_whl2.dump diff --git a/src/highlight.c b/src/highlight.c index 8baa44afe1..eb7707c81f 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -184,7 +184,8 @@ typedef struct typedef struct hl_overrides_S hl_overrides_T; struct hl_overrides_S { - hl_override_T *arr; // May be NULL if "arr" was freed + hl_override_T *arr; // May be NULL if "arr" was freed or no highlight + // overrides (all values set to default) int len; hl_overrides_T *next; // Used to handle recursive calls @@ -5804,9 +5805,10 @@ set_highlight_attr(hl_override_T *arr, int len, bool update_ids) bool push_highlight_overrides(hl_override_T *arr, int len) { - // Don't want to do anything if "arr" is NULL or if "arr" is already the - // current override. - if (arr == NULL || (overrides != NULL && overrides->arr == arr)) + // Don't want to do anything if "arr" is already the current override. If + // "arr" is NULL (but overrides->arr is not), then still push an override, + // but "->arr" will just be NULL so any previous overrides are cleared. + if (overrides != NULL && overrides->arr == arr) return false; hl_overrides_T *set; @@ -5831,7 +5833,8 @@ push_highlight_overrides(hl_override_T *arr, int len) memcpy(highlight_attr, highlight_attr_raw, sizeof(highlight_attr)); // Update highlight_attr[] array - set_highlight_attr(arr, len, false); + if (arr != NULL) + set_highlight_attr(arr, len, false); return true; } diff --git a/src/testdir/dumps/Test_VertSplitNC_whl1.dump b/src/testdir/dumps/Test_VertSplitNC_whl1.dump new file mode 100644 index 0000000000..b6267ef1ae --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_whl1.dump @@ -0,0 +1,12 @@ +> +0&#ffffff0@36||+1&&| +0&&@36 +|~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|[+0#ffffff16#e000002|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1| |[+1#0000000#ffffff0|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 +| +0&&@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_whl2.dump b/src/testdir/dumps/Test_VertSplitNC_whl2.dump new file mode 100644 index 0000000000..825775e591 --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_whl2.dump @@ -0,0 +1,12 @@ +| +0&#ffffff0@36||+1&&> +0&&@36 +|~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|~| @35||+1#0000000&|~+0#4040ff13&| @35 +|[+1#0000000&|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 +| +0&&@74 diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim index 584693f807..6968e72b6a 100644 --- a/src/testdir/test_highlight.vim +++ b/src/testdir/test_highlight.vim @@ -1788,4 +1788,28 @@ func Test_VertSplitNC_fillchars() call StopVimInTerminal(buf) endfunc +" Test that 'winhighlight' of window left of separator does not apply when +" drawing the window to the right of the separator. +func Test_VertSplitNC_winhighlight() + CheckScreendump + + let lines =<< trim END + vsplit + set winhighlight=StatusLine:ErrorMsg + END + call writefile(lines, 'Xtest_vertsplitNC_winhighlight', 'D') + + let buf = RunVimInTerminal('-S Xtest_vertsplitNC_winhighlight', {'rows': 12}) + call TermWait(buf) + + call VerifyScreenDump(buf, 'Test_VertSplitNC_whl1', {}) + + call term_sendkeys(buf, "\\") " Go to window with empty winhighlight + call TermWait(buf) + + call VerifyScreenDump(buf, 'Test_VertSplitNC_whl2', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 3c64afaa92..2eb306c56b 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 352, /**/ 351, /**/ From 60e925ca0edf4cb12190cade3a4ae6b07259ca31 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Wed, 15 Apr 2026 17:00:19 +0000 Subject: [PATCH 047/545] runtime(doc): Tweak documentation style in channel.txt closes: #19978 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/channel.txt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index b8aaa92768..ce0ceb45be 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -1,4 +1,4 @@ -*channel.txt* For Vim version 9.2. Last change: 2026 Apr 14 +*channel.txt* For Vim version 9.2. Last change: 2026 Apr 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -668,22 +668,22 @@ ch_info({handle}) *ch_info()* ch_listen({address} [, {options}]) *E1573* *E1574* *ch_listen()* Listen on {address} - port on loopback or UNIX domain socket - for incoming channel connections. This creates a server-side - channel, unlike |ch_open()|which connects to an existing server. + for incoming channel connections. This creates a server-side + channel, unlike |ch_open()| which connects to an existing + server. Returns a Channel. Use |ch_status()| to check for failure. {address} is a String, see |channel-address| for the possible accepted forms, however in case of TCP sockets it allows to - set only a port and binds to loopback address for - security reasons. + set only a port and binds to loopback address for security + reasons. Note: IPv6 is not yet supported. If {options} is given it must be a |Dictionary|. See |channel-open-options|. - The "callback" in {options} is invoked when a new - connection is accepted. It receives two arguments: the - new Channel and the client address as a String (e.g. - "127.0.0.1:12345"). + The "callback" in {options} is invoked when a new connection + is accepted. It receives two arguments: the new Channel and + the client address as a String (e.g. "127.0.0.1:12345"). Use |ch_open()| to connect to an existing server instead. From 7cc73a6c668e43b2018b145af235801e53b95b45 Mon Sep 17 00:00:00 2001 From: John Marriott Date: Wed, 15 Apr 2026 17:54:22 +0000 Subject: [PATCH 048/545] patch 9.2.0353: Missing out-of-memory check in register.c Problem: Missing out-of-memory check in register.c Solution: Check for memory allocation failure and return NULL (John Marriott). closes: #19949 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/register.c | 10 ++++++++++ src/version.c | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/register.c b/src/register.c index a20e5c1d0f..0215f0153d 100644 --- a/src/register.c +++ b/src/register.c @@ -352,6 +352,16 @@ get_register( { reg->y_array[i].string = vim_strnsave(y_current->y_array[i].string, y_current->y_array[i].length); + if (reg->y_array[i].string == NULL) + { + // The allocation failed so clean up and exit + while (--i >= 0) + vim_free(reg->y_array[i].string); + vim_free(reg->y_array); + vim_free(reg); + return (void *)NULL; + } + reg->y_array[i].length = y_current->y_array[i].length; } } diff --git a/src/version.c b/src/version.c index 2eb306c56b..2f48f475e9 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 353, /**/ 352, /**/ From 2041478e2a4f58269847551f4a9e0206364ae121 Mon Sep 17 00:00:00 2001 From: Martin Schwan Date: Wed, 15 Apr 2026 18:07:18 +0000 Subject: [PATCH 049/545] runtime(bitbake): support forward-slashes in bitbake varflags Enable syntax highlighting for forward-slashes in Bitbake variables with varflags. Bitbake allows for forward-slashes in both the variable name and their potential varflags. E.g. the following should match: FOO_BAR[baz] = "foobar" FOO_BAR_foo/bar[baz] = "foobar" FOO_BAR_foo/bar[baz/bazzer] = "foobar" Also allow plus-signs in variable names, to be in line with normal variable names. related: #19983 Signed-off-by: Martin Schwan Signed-off-by: Christian Brabandt --- runtime/syntax/bitbake.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/syntax/bitbake.vim b/runtime/syntax/bitbake.vim index 9fea3e291a..ac494cf441 100644 --- a/runtime/syntax/bitbake.vim +++ b/runtime/syntax/bitbake.vim @@ -7,6 +7,7 @@ " Last Change: 2022 Jul 25 " 2025 Oct 13 by Vim project: update multiline function syntax #18565 " 2026 Apr 07 by Vim project: update syntax script #19931 +" 2026 Apr 15 by Vim project: allow forward-slashes in bitbake varflags #19983 " " This file is licensed under the MIT license, see COPYING.MIT in " this source distribution for the terms. @@ -59,7 +60,7 @@ syn match bbVarValue ".*$" contained contains=bbString,bbVarDeref,bbV syn region bbVarPyValue start=+${@+ skip=+\\$+ end=+}+ contained contains=@python " Vars metadata flags -syn match bbVarFlagDef "^\([a-zA-Z0-9\-_\.]\+\)\(\[[a-zA-Z0-9\-_\.+]\+\]\)\@=" contains=bbIdentifier nextgroup=bbVarFlagFlag +syn match bbVarFlagDef "^\([a-zA-Z0-9\-_\.+/]\+\)\(\[[a-zA-Z0-9\-_\.+/]\+\]\)\@=" contains=bbIdentifier nextgroup=bbVarFlagFlag syn region bbVarFlagFlag matchgroup=bbArrayBrackets start="\[" end="\]\s*\(:=\|=\|.=\|=.|+=\|=+\|?=\)\@=" contained contains=bbIdentifier nextgroup=bbVarEq " Includes and requires From 0e02be191909df88a8d6ef0a1b091c67c49b1888 Mon Sep 17 00:00:00 2001 From: Martin Schwan Date: Wed, 15 Apr 2026 18:08:32 +0000 Subject: [PATCH 050/545] patch 9.2.0354: filetype: not all Bitbake include files are recognized Problem: filetype: not all Bitbake include files are recognized Solution: Enhance the file detection logic and consider varflags (Martin Schwan) closes: #19983 Signed-off-by: Martin Schwan Signed-off-by: Christian Brabandt --- runtime/autoload/dist/ft.vim | 4 ++-- src/testdir/test_filetype.vim | 10 ++++++++++ src/version.c | 2 ++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index b1c209400e..f0330c205c 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -3,7 +3,7 @@ vim9script # Vim functions for file type detection # # Maintainer: The Vim Project -# Last Change: 2026 Apr 03 +# Last Change: 2026 Apr 15 # Former Maintainer: Bram Moolenaar # These functions are moved here from runtime/filetype.vim to make startup @@ -915,7 +915,7 @@ export def FTinc() elseif line =~ '^\s*\%({\|(\*\)' || line =~? ft_pascal_keywords setf pascal return - elseif line =~# '\<\%(require\|inherit\)\>' || line =~# '[A-Z][A-Za-z0-9_:${}/]*\s\+\%(??\|[?:+.]\)\?=.\? ' + elseif line =~# '\<\%(require\|inherit\)\>' || line =~# '[A-Z][A-Za-z0-9_:${}/]*\(\[[A-Za-z0-9_:/]\+\]\)*\s\+\%(??\|[?:+.]\)\?=.\? ' setf bitbake return endif diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 70e269ee63..892cd32388 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -2853,6 +2853,16 @@ func Test_inc_file() call assert_equal('bitbake', &filetype) bwipe! + call writefile(['FOO_BAR[baz] = "foobar"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + + call writefile(['FOO_BAR_foo/bar[baz/bazzer] = "foobar"'], 'Xfile.inc') + split Xfile.inc + call assert_equal('bitbake', &filetype) + bwipe! + call writefile(['MACHINEOVERRIDES =. "qemuall:"'], 'Xfile.inc') split Xfile.inc call assert_equal('bitbake', &filetype) diff --git a/src/version.c b/src/version.c index 2f48f475e9..513632bd8d 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 354, /**/ 353, /**/ From 490b737f3e172c3c66744c7531ee85f19618419d Mon Sep 17 00:00:00 2001 From: q1uf3ng Date: Wed, 15 Apr 2026 18:20:55 +0000 Subject: [PATCH 051/545] patch 9.2.0355: runtime(tar): missing path traversal checks in tar#Extract() Problem: runtime(tar): missing path traversal checks in tar#Extract() Solution: Add check for leading slash, however gnu tar should already detect this (q1uf3ng) tar#Extract() did not check for ../ sequences or absolute paths, unlike zip#Extract() which was patched in recent commits. Add the same checks: ../ (relative traversal), leading slash (Unix), drive letter and UNC/leading slash (Windows). closes: #19981 Signed-off-by: q1uf3ng Signed-off-by: Christian Brabandt --- runtime/autoload/tar.vim | 19 +++++++++++++++++++ src/testdir/test_plugin_tar.vim | 5 +++++ src/version.c | 2 ++ 3 files changed, 26 insertions(+) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index 722a0ab680..6aa3489e53 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -23,6 +23,7 @@ " 2026 Apr 06 by Vim Project: fix bugs with lz4 support (#19925) " 2026 Apr 09 by Vim Project: fix bugs with zstd support (#19930) " 2026 Apr 09 by Vim Project: fix bug with dotted filename (#19930) +" 2026 Apr 15 by Vim Project: fix more path traversal issues (#19981) " " Contains many ideas from Michael Toren's " @@ -612,6 +613,24 @@ fun! tar#Extract() let &report= repkeep return endif + if fname =~ '^[.]\?[.]/' || simplify(fname) =~ '\.\.[/\\]' + call s:Msg('tar#Extract', 'error', "Path Traversal Attack detected, not extracting!") + let &report= repkeep + return + endif + if has("unix") + if fname =~ '^/' + call s:Msg('tar#Extract', 'error', "Path Traversal Attack detected, not extracting!") + let &report= repkeep + return + endif + else + if fname =~ '^\%(\a:[\\/]\|[\\/]\)' + call s:Msg('tar#Extract', 'error', "Path Traversal Attack detected, not extracting!") + let &report= repkeep + return + endif + endif let extractcmd= s:WinPath(g:tar_extractcmd) let tarball = expand("%") diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim index 80b7a76d6d..56f25df2ae 100644 --- a/src/testdir/test_plugin_tar.vim +++ b/src/testdir/test_plugin_tar.vim @@ -86,6 +86,11 @@ def g:Test_tar_evil() assert_equal("X.tar", @%) assert_equal(1, b:leading_slash) + ### Press x to extract + :6 + var mess = execute(":normal x", '') + assert_match('(tar#Extract) Path Traversal Attack detected, not extracting!', mess) + ### Check ENTER on file :6 exe ":normal \" diff --git a/src/version.c b/src/version.c index 513632bd8d..c0f826a17d 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 355, /**/ 354, /**/ From a414630393f81c9a5b8fa4d0fcc1287155f67751 Mon Sep 17 00:00:00 2001 From: McAuley Penney Date: Wed, 15 Apr 2026 19:11:12 +0000 Subject: [PATCH 052/545] patch 9.2.0356: Cannot apply 'scrolloff' context lines at end of file Problem: Cannot apply 'scrolloff' context lines at end of file Solution: Add the 'scrolloffpad' option to keep 'scrolloff' context even when at the end of the file (McAuley Penney). closes: #19040 Signed-off-by: McAuley Penney Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 25 +- runtime/doc/quickref.txt | 3 +- runtime/doc/tags | 2 + runtime/doc/version9.txt | 3 +- runtime/optwin.vim | 4 +- runtime/syntax/vim.vim | 12 +- src/move.c | 80 ++- src/option.c | 38 +- src/option.h | 2 + src/optiondefs.h | 4 + src/po/vim.pot | 5 +- src/popupwin.c | 1 + src/proto/option.pro | 1 + src/structs.h | 2 + .../dumps/Test_scrolloffpad_basic_1.dump | 20 + .../dumps/Test_scrolloffpad_basic_2.dump | 20 + .../dumps/Test_scrolloffpad_basic_3.dump | 20 + .../dumps/Test_scrolloffpad_folds_1.dump | 20 + .../dumps/Test_scrolloffpad_folds_2.dump | 20 + .../dumps/Test_scrolloffpad_folds_3.dump | 20 + .../Test_scrolloffpad_smoothscroll_1.dump | 20 + .../Test_scrolloffpad_smoothscroll_2.dump | 20 + src/testdir/test_cursor_func.vim | 3 +- src/testdir/test_options.vim | 39 ++ src/testdir/test_popupwin.vim | 33 +- src/testdir/test_scroll_opt.vim | 651 ++++++++++++++++++ src/testdir/util/gen_opt_test.vim | 2 + src/version.c | 2 + src/window.c | 1 + 29 files changed, 1035 insertions(+), 38 deletions(-) create mode 100644 src/testdir/dumps/Test_scrolloffpad_basic_1.dump create mode 100644 src/testdir/dumps/Test_scrolloffpad_basic_2.dump create mode 100644 src/testdir/dumps/Test_scrolloffpad_basic_3.dump create mode 100644 src/testdir/dumps/Test_scrolloffpad_folds_1.dump create mode 100644 src/testdir/dumps/Test_scrolloffpad_folds_2.dump create mode 100644 src/testdir/dumps/Test_scrolloffpad_folds_3.dump create mode 100644 src/testdir/dumps/Test_scrolloffpad_smoothscroll_1.dump create mode 100644 src/testdir/dumps/Test_scrolloffpad_smoothscroll_2.dump diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 627d965402..31a9629c0b 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 14 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -7577,8 +7577,8 @@ A jump table for the options with a short description can be found at |Q_op|. Minimal number of screen lines to keep above and below the cursor. This will make some context visible around where you are working. If you set it to a very large value (999) the cursor line will always be - in the middle of the window (except at the start or end of the file or - when long lines wrap). + in the middle of the window (except at the start or end of the file, + see 'scrolloffpad', or when long lines wrap). After using the local value, go back the global value with one of these two: > setlocal scrolloff< @@ -7586,7 +7586,24 @@ A jump table for the options with a short description can be found at |Q_op|. < For scrolling horizontally see 'sidescrolloff'. NOTE: This option is set to 0 when 'compatible' is set. - *'scrollopt'* *'sbo'* + *'scrolloffpad'* *'sop'* +'scrolloffpad' 'sop' number (default 0) + global or local to window |global-local| + When 'scrolloff' and 'scrolloffpad' are greater than zero, allow + the cursor to remain centered when at the end of the file. + Normally, 'scrolloff' will not keep the cursor centered at the + end of the file. + + A value of 0 disables this feature. Any value above 0 enables it. + For a window-local value, -1 means to use the global value. + Values below -1 are invalid. + + After using the local value, go back the global value with one of + these two: > + setlocal scrolloffpad< + setlocal scrolloffpad=-1 + +< *'scrollopt'* *'sbo'* 'scrollopt' 'sbo' string (default "ver,jump") global This is a comma-separated list of words that specifies how diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 8f6aca0463..1ada2bf9b3 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -1,4 +1,4 @@ -*quickref.txt* For Vim version 9.2. Last change: 2026 Apr 09 +*quickref.txt* For Vim version 9.2. Last change: 2026 Apr 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -895,6 +895,7 @@ Short explanation of each option: *option-list* 'scrollfocus' 'scf' scroll wheel applies to window under pointer 'scrolljump' 'sj' minimum number of lines to scroll 'scrolloff' 'so' minimum nr. of lines above and below cursor +'scrolloffpad' 'sop' keep 'scrolloff' context at end of file 'scrollopt' 'sbo' how 'scrollbind' should behave 'sections' 'sect' nroff macros that separate sections 'secure' secure mode for reading .vimrc in current dir diff --git a/runtime/doc/tags b/runtime/doc/tags index 4d31cc51a6..7effaf824f 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -953,6 +953,7 @@ $quote eval.txt /*$quote* 'scrollfocus' options.txt /*'scrollfocus'* 'scrolljump' options.txt /*'scrolljump'* 'scrolloff' options.txt /*'scrolloff'* +'scrolloffpad' options.txt /*'scrolloffpad'* 'scrollopt' options.txt /*'scrollopt'* 'scs' options.txt /*'scs'* 'sect' options.txt /*'sect'* @@ -1011,6 +1012,7 @@ $quote eval.txt /*$quote* 'so' options.txt /*'so'* 'softtabstop' options.txt /*'softtabstop'* 'sol' options.txt /*'sol'* +'sop' options.txt /*'sop'* 'sourceany' vi_diff.txt /*'sourceany'* 'sp' options.txt /*'sp'* 'spc' options.txt /*'spc'* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index fa8c5355c7..ee24e29010 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 14 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52654,6 +52654,7 @@ Options: ~ 'modelinestrict' Only allow safe options to be set from a modeline. 'pumopt' Additional options for the popup menu +'scrolloffpad' Keep 'scrolloff' context at end of file 'statuslineopt' Extra window-local options for the 'statusline', to configure the height. 't_BS' Begin synchronized update. diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 73243e77f3..62c2ec2e55 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: The Vim Project -" Last Change: 2026 Apr 09 +" Last Change: 2026 Apr 15 " Former Maintainer: Bram Moolenaar " If there already is an option window, jump to that one. @@ -351,6 +351,8 @@ call append("$", "\t" .. s:local_to_window) call BinOptionL("sms") call AddOption("scrolloff", gettext("number of screen lines to show around the cursor")) call append("$", " \tset so=" . &so) +call AddOption("scrolloffpad", gettext("keep 'scrolloff' context even at end of file")) +call append("$", " \tset sop=" . &sop) call AddOption("wrap", gettext("long lines wrap")) call append("$", "\t" .. s:local_to_window) call BinOptionL("wrap") diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index f213710c37..a390df78ed 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -2,7 +2,7 @@ " Language: Vim script " Maintainer: Hirohito Higashi " Doug Kearns -" Last Change: 2026 Apr 14 +" Last Change: 2026 Apr 15 " Former Maintainer: Charles E. Campbell " DO NOT CHANGE DIRECTLY. @@ -69,8 +69,8 @@ syn keyword vimOption contained co columns com comments cms commentstring cp com syn keyword vimOption contained efm errorformat ek esckeys ei eventignore eiw eventignorewin et expandtab ex exrc fenc fileencoding fencs fileencodings ff fileformat ffs fileformats fic fileignorecase ft filetype fcs fillchars ffu findfunc fixeol fixendofline fcl foldclose fdc foldcolumn fen foldenable fde foldexpr fdi foldignore fdl foldlevel fdls foldlevelstart fmr foldmarker fdm foldmethod fml foldminlines fdn foldnestmax fdo foldopen fdt foldtext fex formatexpr flp formatlistpat fo formatoptions fp formatprg fs fsync gd gdefault gfm grepformat gp grepprg gcr guicursor gfn guifont gfs guifontset gfw guifontwide ghr guiheadroom gli guiligatures go guioptions guipty gtl guitablabel gtt guitabtooltip hf helpfile hh helpheight hlg helplang hid hidden hl highlight skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine mfd maxfuncdepth skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines mlst modelinestrict ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth shm shortmess skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sop scrolloffpad sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod @@ -108,9 +108,9 @@ syn keyword vimOptionVarName contained co columns com comments cms commentstring syn keyword vimOptionVarName contained efm errorformat ek esckeys ei eventignore eiw eventignorewin et expandtab ex exrc fenc fileencoding fencs fileencodings ff fileformat ffs fileformats fic fileignorecase ft filetype fcs fillchars ffu findfunc fixeol fixendofline fcl foldclose fdc foldcolumn fen foldenable fde foldexpr fdi foldignore fdl foldlevel fdls foldlevelstart fmr foldmarker fdm foldmethod fml foldminlines fdn foldnestmax fdo foldopen fdt foldtext fex formatexpr flp formatlistpat fo formatoptions fp formatprg fs fsync gd gdefault gfm grepformat gp grepprg gcr guicursor gfn guifont gfs guifontset gfw guifontwide ghr guiheadroom gli guiligatures go guioptions guipty gtl guitablabel gtt guitabtooltip hf helpfile hh helpheight hlg helplang hid hidden hl highlight syn keyword vimOptionVarName contained hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine syn keyword vimOptionVarName contained mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines mlst modelinestrict ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader -syn keyword vimOptionVarName contained pmbcs printmbcharset pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround -syn keyword vimOptionVarName contained sw shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll -syn keyword vimOptionVarName contained term tbidi termbidi tenc termencoding tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore +syn keyword vimOptionVarName contained pmbcs printmbcharset pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sop scrolloffpad sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote +syn keyword vimOptionVarName contained sr shiftround sw shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack +syn keyword vimOptionVarName contained tcldll term tbidi termbidi tenc termencoding tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore syn keyword vimOptionVarName contained wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes " GEN_SYN_VIM: vimOption term output code variable, START_STR='syn keyword vimOptionVarName contained', END_STR='' syn keyword vimOptionVarName contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo t_BS t_ES diff --git a/src/move.c b/src/move.c index ee9a8eb5aa..1e35101303 100644 --- a/src/move.c +++ b/src/move.c @@ -279,6 +279,31 @@ update_topline_redraw(void) update_screen(0); } +/* + * Return true when 'scrolloffpad' may augment 'scrolloff'. + * This only applies to automatic cursor visibility correction. + * For now 'scrolloffpad' is treated as boolean: 0 disables, > 0 enables. + */ + static bool +use_scrolloffpad(void) +{ + return get_scrolloff_value() > 0 && get_scrolloffpad_value() > 0; +} + +/* + * Return TRUE when there are not enough real buffer lines below "lnum" to + * satisfy the requested "so" context. + */ + static bool +scrolloffpad_eof_pressure(linenr_T lnum, long so) +{ + if (!use_scrolloffpad() || so <= 0) + return false; + + // Use subtraction to avoid signed overflow in "lnum + so". + return lnum > curbuf->b_ml.ml_line_count - so; +} + /* * Update curwin->w_topline to move the cursor onto the screen. */ @@ -295,6 +320,7 @@ update_topline(void) int check_botline = FALSE; long *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so; int save_so = *so_ptr; + bool eof_pressure; // Cursor is updated instead when this is TRUE for 'splitkeep'. if (skip_update_topline) @@ -318,6 +344,7 @@ update_topline(void) // When dragging with the mouse, don't scroll that quickly if (mouse_dragging > 0) *so_ptr = mouse_dragging - 1; + eof_pressure = scrolloffpad_eof_pressure(curwin->w_cursor.lnum, *so_ptr); linenr_T old_topline = curwin->w_topline; #ifdef FEAT_DIFF @@ -405,11 +432,21 @@ update_topline(void) // cursor in the middle of the window. Otherwise put the cursor // near the top of the window. if (n >= halfheight) - scroll_cursor_halfway(FALSE, FALSE); + { + if (eof_pressure) + scroll_cursor_halfway(TRUE, TRUE); + else + scroll_cursor_halfway(FALSE, FALSE); + } else { - scroll_cursor_top(scrolljump_value(), FALSE); - check_botline = TRUE; + if (eof_pressure) + scroll_cursor_halfway(TRUE, TRUE); + else + { + scroll_cursor_top(scrolljump_value(), FALSE); + check_botline = TRUE; + } } } @@ -436,7 +473,7 @@ update_topline(void) if (!(curwin->w_valid & VALID_BOTLINE_AP)) validate_botline(); - if (curwin->w_botline <= curbuf->b_ml.ml_line_count) + if (curwin->w_botline <= curbuf->b_ml.ml_line_count || use_scrolloffpad()) { if (curwin->w_cursor.lnum < curwin->w_botline) { @@ -452,7 +489,7 @@ update_topline(void) // Cursor is (a few lines) above botline, check if there are // 'scrolloff' window lines below the cursor. If not, need to // scroll. - n = curwin->w_empty_rows; + n = eof_pressure ? 0 : curwin->w_empty_rows; loff.lnum = curwin->w_cursor.lnum; #ifdef FEAT_FOLDING // In a fold go to its last line. @@ -473,14 +510,14 @@ update_topline(void) if (n >= *so_ptr) break; botline_forw(&loff); + } + if (n >= *so_ptr && !eof_pressure) + // sufficient context, no need to scroll + check_botline = FALSE; } - if (n >= *so_ptr) + else // sufficient context, no need to scroll check_botline = FALSE; - } - else - // sufficient context, no need to scroll - check_botline = FALSE; } if (check_botline) { @@ -506,9 +543,14 @@ update_topline(void) line_count = curwin->w_cursor.lnum - curwin->w_botline + 1 + *so_ptr; if (line_count <= curwin->w_height + 1) - scroll_cursor_bot(scrolljump_value(), FALSE); + { + if (eof_pressure) + scroll_cursor_halfway(TRUE, TRUE); + else + scroll_cursor_bot(scrolljump_value(), FALSE); + } else - scroll_cursor_halfway(FALSE, FALSE); + scroll_cursor_halfway(eof_pressure, eof_pressure); } } } @@ -2350,9 +2392,11 @@ botline_forw(lineoff_T *lp) else #ifdef FEAT_FOLDING if (hasFolding(lp->lnum, NULL, &lp->lnum)) - // Add a closed fold - lp->height = 1; - else + { + // Add a closed fold. + lp->height = 1; + } + else #endif lp->height = PLINES_NOFILL(lp->lnum); } @@ -2804,8 +2848,9 @@ scroll_cursor_bot(int min_scroll, int set_topbot) * Scroll up if the cursor is off the bottom of the screen a bit. * Otherwise put it at 1/2 of the screen. */ + bool eof_pressure = scrolloffpad_eof_pressure(cln, so); if (line_count >= curwin->w_height && line_count > min_scroll) - scroll_cursor_halfway(FALSE, TRUE); + scroll_cursor_halfway(eof_pressure, TRUE); else if (line_count > 0) { if (do_sms) @@ -3046,7 +3091,8 @@ cursor_correct(void) if (curwin->w_botline == curbuf->b_ml.ml_line_count + 1 && mouse_dragging == 0) { - below_wanted = 0; + if (!use_scrolloffpad()) + below_wanted = 0; max_off = (curwin->w_height - 1) / 2; if (above_wanted > max_off) above_wanted = max_off; diff --git a/src/option.c b/src/option.c index 46009341d1..dc6f0966d5 100644 --- a/src/option.c +++ b/src/option.c @@ -804,8 +804,9 @@ set_option_default( long def_val = (long)(long_i)options[opt_idx].def_val[dvi]; if ((long *)varp == &curwin->w_p_so - || (long *)varp == &curwin->w_p_siso) - // 'scrolloff' and 'sidescrolloff' local values have a + || (long *)varp == &curwin->w_p_siso + || (long *)varp == &curwin->w_p_sop) + // 'scrolloff', 'sidescrolloff', and 'scrolloffpad' local values have a // different default value than the global default. *(long *)varp = -1; else @@ -2581,8 +2582,9 @@ do_set_option_numeric( value = NO_LOCAL_UNDOLEVEL; else if (opt_flags == OPT_LOCAL && ((long *)varp == &curwin->w_p_siso - || (long *)varp == &curwin->w_p_so)) - // for 'scrolloff'/'sidescrolloff' -1 means using the global value + || (long *)varp == &curwin->w_p_so + || (long *)varp == &curwin->w_p_sop)) + // for 'scrolloff'/'sidescrolloff'/'scrolloffpad' -1 means using the global value value = -1; else value = *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL); @@ -2624,6 +2626,12 @@ do_set_option_numeric( else if (op == OP_REMOVING) value = *(long *)varp - value; + if ((long *)varp == &curwin->w_p_sop && value < -1) + { + errmsg = e_invalid_argument; + goto skip; + } + errmsg = set_num_option(opt_idx, varp, value, errbuf, errbuflen, opt_flags); @@ -5401,6 +5409,11 @@ check_num_option_bounds( errmsg = e_argument_must_be_positive; p_so = 0; } + if (p_sop < 0 && full_screen) + { + errmsg = e_invalid_argument; + p_sop = 0; + } if (p_siso < 0 && full_screen) { errmsg = e_argument_must_be_positive; @@ -6818,6 +6831,9 @@ unset_global_local_option(char_u *name, void *from) case PV_SO: curwin->w_p_so = -1; break; + case PV_SOP: + curwin->w_p_sop = -1; + break; # ifdef FEAT_FIND_ID case PV_DEF: clear_string_option(&buf->b_p_def); @@ -6959,6 +6975,7 @@ get_varp_scope(struct vimoption *p, int scope) case PV_TC: return (char_u *)&(curbuf->b_p_tc); case PV_SISO: return (char_u *)&(curwin->w_p_siso); case PV_SO: return (char_u *)&(curwin->w_p_so); + case PV_SOP: return (char_u *)&(curwin->w_p_sop); #ifdef FEAT_FIND_ID case PV_DEF: return (char_u *)&(curbuf->b_p_def); case PV_INC: return (char_u *)&(curbuf->b_p_inc); @@ -7044,6 +7061,8 @@ get_varp(struct vimoption *p) ? (char_u *)&(curwin->w_p_siso) : p->var; case PV_SO: return curwin->w_p_so >= 0 ? (char_u *)&(curwin->w_p_so) : p->var; + case PV_SOP: return curwin->w_p_sop != -1 + ? (char_u *)&(curwin->w_p_sop) : p->var; #ifdef FEAT_FIND_ID case PV_DEF: return *curbuf->b_p_def != NUL ? (char_u *)&(curbuf->b_p_def) : p->var; @@ -7445,6 +7464,7 @@ copy_winopt(winopt_T *from, winopt_T *to) to->wo_crb_save = from->wo_crb_save; to->wo_siso = from->wo_siso; to->wo_so = from->wo_so; + to->wo_sop = from->wo_sop; #ifdef FEAT_SPELL to->wo_spell = from->wo_spell; #endif @@ -9072,6 +9092,16 @@ get_scrolloff_value(void) return curwin->w_p_so < 0 ? p_so : curwin->w_p_so; } +/* + * Return the effective 'scrolloffpad' value for the current window, using the + * global value when appropriate. + */ + long +get_scrolloffpad_value(void) +{ + return curwin->w_p_sop == -1 ? p_sop : curwin->w_p_sop; +} + /* * Return the effective 'sidescrolloff' value for the current window, using the * global value when appropriate. diff --git a/src/option.h b/src/option.h index 6bf6169bbf..e5f9de8e16 100644 --- a/src/option.h +++ b/src/option.h @@ -901,6 +901,7 @@ EXTERN long p_sj; // 'scrolljump' EXTERN int p_scf; // 'scrollfocus' #endif EXTERN long p_so; // 'scrolloff' +EXTERN long p_sop; // 'scrolloffpad' EXTERN char_u *p_sbo; // 'scrollopt' EXTERN char_u *p_sections; // 'sections' EXTERN int p_secure; // 'secure' @@ -1378,6 +1379,7 @@ enum , WV_SMS , WV_SISO , WV_SO + , WV_SOP #ifdef FEAT_SPELL , WV_SPELL #endif diff --git a/src/optiondefs.h b/src/optiondefs.h index 38962790bf..0214a34b1d 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -209,6 +209,7 @@ #define PV_SMS OPT_WIN(WV_SMS) #define PV_SISO OPT_BOTH(OPT_WIN(WV_SISO)) #define PV_SO OPT_BOTH(OPT_WIN(WV_SO)) +#define PV_SOP OPT_BOTH(OPT_WIN(WV_SOP)) #ifdef FEAT_SPELL # define PV_SPELL OPT_WIN(WV_SPELL) #endif @@ -2266,6 +2267,9 @@ static struct vimoption options[] = {"scrolloff", "so", P_NUM|P_VI_DEF|P_VIM|P_RALL, (char_u *)&p_so, PV_SO, NULL, NULL, {(char_u *)0L, (char_u *)0L} SCTX_INIT}, + {"scrolloffpad", "sop", P_NUM|P_VI_DEF|P_VIM|P_RALL, + (char_u *)&p_sop, PV_SOP, NULL, NULL, + {(char_u *)0L, (char_u *)0L} SCTX_INIT}, {"scrollopt", "sbo", P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP, (char_u *)&p_sbo, PV_NONE, did_set_scrollopt, expand_set_scrollopt, {(char_u *)"ver,jump", (char_u *)0L} diff --git a/src/po/vim.pot b/src/po/vim.pot index b09ade556d..936961c92f 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-04-09 20:33+0000\n" +"POT-Creation-Date: 2026-04-15 19:10+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -9383,6 +9383,9 @@ msgstr "" msgid "number of screen lines to show around the cursor" msgstr "" +msgid "keep 'scrolloff' context even at end of file" +msgstr "" + msgid "long lines wrap" msgstr "" diff --git a/src/popupwin.c b/src/popupwin.c index bdf53cdc4f..319d882c55 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -2399,6 +2399,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type) } wp->w_p_wrap = TRUE; // 'wrap' is default on wp->w_p_so = 0; // 'scrolloff' zero + wp->w_p_sop = 0; // 'scrolloffpad' zero if (tp != NULL) { diff --git a/src/proto/option.pro b/src/proto/option.pro index 892a6ce3b2..ae586ea978 100644 --- a/src/proto/option.pro +++ b/src/proto/option.pro @@ -145,6 +145,7 @@ int option_was_set(char_u *name); int reset_option_was_set(char_u *name); int can_bs(int what); long get_scrolloff_value(void); +long get_scrolloffpad_value(void); long get_sidescrolloff_value(void); unsigned int get_bkc_flags(buf_T *buf); char_u *get_flp_value(buf_T *buf); diff --git a/src/structs.h b/src/structs.h index 955b9f1001..648ceec905 100644 --- a/src/structs.h +++ b/src/structs.h @@ -349,6 +349,8 @@ typedef struct #define w_p_siso w_onebuf_opt.wo_siso // 'sidescrolloff' local value long wo_so; #define w_p_so w_onebuf_opt.wo_so // 'scrolloff' local value + long wo_sop; +#define w_p_sop w_onebuf_opt.wo_sop // 'scrolloffpad' local value #ifdef FEAT_TERMINAL char_u *wo_twk; # define w_p_twk w_onebuf_opt.wo_twk // 'termwinkey' diff --git a/src/testdir/dumps/Test_scrolloffpad_basic_1.dump b/src/testdir/dumps/Test_scrolloffpad_basic_1.dump new file mode 100644 index 0000000000..af38b016a3 --- /dev/null +++ b/src/testdir/dumps/Test_scrolloffpad_basic_1.dump @@ -0,0 +1,20 @@ +|l+0&#ffffff0|i|n|e| |9|1| @70 +|l|i|n|e| |9|2| @70 +|l|i|n|e| |9|3| @70 +|l|i|n|e| |9|4| @70 +|l|i|n|e| |9|5| @70 +|l|i|n|e| |9|6| @70 +|l|i|n|e| |9|7| @70 +|l|i|n|e| |9|8| @70 +|l|i|n|e| |9@1| @70 +>l|i|n|e| |1|0@1| @69 +|~+0#4040ff13&| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +| +0#0000000&@59|1|0@1|,|1| @8|B|o|t| diff --git a/src/testdir/dumps/Test_scrolloffpad_basic_2.dump b/src/testdir/dumps/Test_scrolloffpad_basic_2.dump new file mode 100644 index 0000000000..0702019465 --- /dev/null +++ b/src/testdir/dumps/Test_scrolloffpad_basic_2.dump @@ -0,0 +1,20 @@ +>l+0&#ffffff0|i|n|e| |1| @71 +|l|i|n|e| |2| @71 +|l|i|n|e| |3| @71 +|l|i|n|e| |4| @71 +|l|i|n|e| |5| @71 +|l|i|n|e| |6| @71 +|l|i|n|e| |7| @71 +|l|i|n|e| |8| @71 +|l|i|n|e| |9| @71 +|l|i|n|e| |1|0| @70 +|l|i|n|e| |1@1| @70 +|l|i|n|e| |1|2| @70 +|l|i|n|e| |1|3| @70 +|l|i|n|e| |1|4| @70 +|l|i|n|e| |1|5| @70 +|l|i|n|e| |1|6| @70 +|l|i|n|e| |1|7| @70 +|l|i|n|e| |1|8| @70 +|l|i|n|e| |1|9| @70 +@60|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_scrolloffpad_basic_3.dump b/src/testdir/dumps/Test_scrolloffpad_basic_3.dump new file mode 100644 index 0000000000..72eed1e33f --- /dev/null +++ b/src/testdir/dumps/Test_scrolloffpad_basic_3.dump @@ -0,0 +1,20 @@ +|l+0&#ffffff0|i|n|e| |8|2| @70 +|l|i|n|e| |8|3| @70 +|l|i|n|e| |8|4| @70 +|l|i|n|e| |8|5| @70 +|l|i|n|e| |8|6| @70 +|l|i|n|e| |8|7| @70 +|l|i|n|e| |8@1| @70 +|l|i|n|e| |8|9| @70 +|l|i|n|e| |9|0| @70 +|l|i|n|e| |9|1| @70 +|l|i|n|e| |9|2| @70 +|l|i|n|e| |9|3| @70 +|l|i|n|e| |9|4| @70 +|l|i|n|e| |9|5| @70 +|l|i|n|e| |9|6| @70 +|l|i|n|e| |9|7| @70 +|l|i|n|e| |9|8| @70 +|l|i|n|e| |9@1| @70 +>l|i|n|e| |1|0@1| @69 +@60|1|0@1|,|1| @8|B|o|t| diff --git a/src/testdir/dumps/Test_scrolloffpad_folds_1.dump b/src/testdir/dumps/Test_scrolloffpad_folds_1.dump new file mode 100644 index 0000000000..1c64333006 --- /dev/null +++ b/src/testdir/dumps/Test_scrolloffpad_folds_1.dump @@ -0,0 +1,20 @@ +|l+0&#ffffff0|i|n|e| |1@2| @69 +|l|i|n|e| |1@1|2| @69 +|l|i|n|e| |1@1|3| @69 +|l|i|n|e| |1@1|4| @69 +|l|i|n|e| |1@1|5| @69 +|l|i|n|e| |1@1|6| @69 +|l|i|n|e| |1@1|7| @69 +|l|i|n|e| |1@1|8| @69 +|l|i|n|e| |1@1|9| @69 +>l|i|n|e| |1|2|0| @69 +|~+0#4040ff13&| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +| +0#0000000&@59|1|2|0|,|1| @8|B|o|t| diff --git a/src/testdir/dumps/Test_scrolloffpad_folds_2.dump b/src/testdir/dumps/Test_scrolloffpad_folds_2.dump new file mode 100644 index 0000000000..63ac801b4a --- /dev/null +++ b/src/testdir/dumps/Test_scrolloffpad_folds_2.dump @@ -0,0 +1,20 @@ +|l+0&#ffffff0|i|n|e| |5|1| @70 +|l|i|n|e| |5|2| @70 +|l|i|n|e| |5|3| @70 +|l|i|n|e| |5|4| @70 +|l|i|n|e| |5@1| @70 +|l|i|n|e| |5|6| @70 +|l|i|n|e| |5|7| @70 +|l|i|n|e| |5|8| @70 +|l|i|n|e| |5|9| @70 +>++0#0000e05#a8a8a8255|-@1| |5|1| |l|i|n|e|s|:| |l|i|n|e| |6|0|-@56 +|l+0#0000000#ffffff0|i|n|e| |1@2| @69 +|l|i|n|e| |1@1|2| @69 +|l|i|n|e| |1@1|3| @69 +|l|i|n|e| |1@1|4| @69 +|l|i|n|e| |1@1|5| @69 +|l|i|n|e| |1@1|6| @69 +|l|i|n|e| |1@1|7| @69 +|l|i|n|e| |1@1|8| @69 +|l|i|n|e| |1@1|9| @69 +@60|6|0|,|1| @9|9|8|%| diff --git a/src/testdir/dumps/Test_scrolloffpad_folds_3.dump b/src/testdir/dumps/Test_scrolloffpad_folds_3.dump new file mode 100644 index 0000000000..1c64333006 --- /dev/null +++ b/src/testdir/dumps/Test_scrolloffpad_folds_3.dump @@ -0,0 +1,20 @@ +|l+0&#ffffff0|i|n|e| |1@2| @69 +|l|i|n|e| |1@1|2| @69 +|l|i|n|e| |1@1|3| @69 +|l|i|n|e| |1@1|4| @69 +|l|i|n|e| |1@1|5| @69 +|l|i|n|e| |1@1|6| @69 +|l|i|n|e| |1@1|7| @69 +|l|i|n|e| |1@1|8| @69 +|l|i|n|e| |1@1|9| @69 +>l|i|n|e| |1|2|0| @69 +|~+0#4040ff13&| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +| +0#0000000&@59|1|2|0|,|1| @8|B|o|t| diff --git a/src/testdir/dumps/Test_scrolloffpad_smoothscroll_1.dump b/src/testdir/dumps/Test_scrolloffpad_smoothscroll_1.dump new file mode 100644 index 0000000000..af38b016a3 --- /dev/null +++ b/src/testdir/dumps/Test_scrolloffpad_smoothscroll_1.dump @@ -0,0 +1,20 @@ +|l+0&#ffffff0|i|n|e| |9|1| @70 +|l|i|n|e| |9|2| @70 +|l|i|n|e| |9|3| @70 +|l|i|n|e| |9|4| @70 +|l|i|n|e| |9|5| @70 +|l|i|n|e| |9|6| @70 +|l|i|n|e| |9|7| @70 +|l|i|n|e| |9|8| @70 +|l|i|n|e| |9@1| @70 +>l|i|n|e| |1|0@1| @69 +|~+0#4040ff13&| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +| +0#0000000&@59|1|0@1|,|1| @8|B|o|t| diff --git a/src/testdir/dumps/Test_scrolloffpad_smoothscroll_2.dump b/src/testdir/dumps/Test_scrolloffpad_smoothscroll_2.dump new file mode 100644 index 0000000000..3b66ce6218 --- /dev/null +++ b/src/testdir/dumps/Test_scrolloffpad_smoothscroll_2.dump @@ -0,0 +1,20 @@ +|l+0&#ffffff0|i|n|e| |9|2| @70 +|l|i|n|e| |9|3| @70 +|l|i|n|e| |9|4| @70 +|l|i|n|e| |9|5| @70 +|l|i|n|e| |9|6| @70 +|l|i|n|e| |9|7| @70 +|l|i|n|e| |9|8| @70 +|l|i|n|e| |9@1| @70 +|L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| >L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N +|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| |L|O|N|G| @6 +|~+0#4040ff13&| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +|~| @76 +| +0#0000000&@59|1|0@1|,|4|1| @7|B|o|t| diff --git a/src/testdir/test_cursor_func.vim b/src/testdir/test_cursor_func.vim index 73169649df..e541da886d 100644 --- a/src/testdir/test_cursor_func.vim +++ b/src/testdir/test_cursor_func.vim @@ -124,7 +124,8 @@ func Test_screenpos() setlocal nonumber display=lastline so=0 exe "normal G\\" redraw - call assert_equal({'row': winrow + wininfo.height - 1, + let winbar_height = get(wininfo, 'winbar', 0) + call assert_equal({'row': winrow + wininfo.height - 1 + winbar_height, \ 'col': wincol + 7, \ 'curscol': wincol + 7, \ 'endcol': wincol + 7}, winid->screenpos(line('$'), 8)) diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index 828a5ca710..76a1bff344 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -1495,6 +1495,45 @@ func Test_local_scrolloff() set siso& endfunc +func Test_local_scrolloffpad() + let save_g_sop = &g:sop + let save_l_sop = &l:sop + set sop=0 + call assert_equal(0, &g:sop) + call assert_equal(-1, &l:sop) + call assert_equal(0, &sop) + setglobal sop=1 + call assert_equal(1, &g:sop) + call assert_equal(1, &sop) + split + call assert_equal(1, &g:sop) + call assert_equal(-1, &l:sop) + call assert_equal(1, &sop) + setlocal sop=0 + call assert_equal(0, &l:sop) + call assert_equal(0, &sop) + call assert_equal(1, &g:sop) + wincmd p + call assert_equal(1, &sop) + wincmd p + setlocal sop< + call assert_equal(-1, &l:sop) + call assert_equal(1, &sop) + setlocal sop=2 + call assert_equal(2, &l:sop) + call assert_equal(2, &sop) + setlocal sop=-1 + call assert_equal(-1, &l:sop) + call assert_equal(1, &sop) " Uses global value because local is -1 + call assert_fails("setlocal sop=-2", 'E474:') + call assert_equal(-1, &l:sop) + call assert_equal(1, &sop) + call assert_fails("setlocal sop=foo", 'E521:') + close + let &g:sop = save_g_sop + let &l:sop = save_l_sop +endfunc + func Test_writedelay() CheckFunction reltimefloat diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index a134acabd9..030e9e6598 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -1412,6 +1412,18 @@ endfunc func Test_popup_option_values() new + " Save old values + let save_g_so = &g:so + let save_l_so = &l:so + let save_g_sop = &g:sop + let save_l_sop = &l:sop + + let save_nu = &l:nu + let save_wrap = &l:wrap + let save_ofu = &l:ofu + let save_path = &l:path + let save_stl = &l:stl + " window-local setlocal number setlocal nowrap @@ -1421,6 +1433,8 @@ func Test_popup_option_values() setlocal path=/there " global/window-local setlocal statusline=2 + set scrolloff=5 + set scrolloffpad=1 let winid = popup_create('hello', {}) call assert_equal(0, getwinvar(winid, '&number')) @@ -1429,12 +1443,27 @@ func Test_popup_option_values() call assert_equal(&g:path, getwinvar(winid, '&path')) call assert_equal(&g:statusline, getwinvar(winid, '&statusline')) - " 'scrolloff' is reset to zero + " 'scrolloff' and 'scrolloffpad' are reset to zero call assert_equal(5, &scrolloff) call assert_equal(0, getwinvar(winid, '&scrolloff')) + call assert_equal(1, &scrolloffpad) + call assert_equal(0, getwinvar(winid, '&scrolloffpad')) call popup_close(winid) - bwipe + + " Restore old values + let &g:so = save_g_so + let &l:so = save_l_so + let &g:sop = save_g_sop + let &l:sop = save_l_sop + + let &l:nu = save_nu + let &l:wrap = save_wrap + let &l:ofu = save_ofu + let &l:path = save_path + let &l:stl = save_stl + + bwipe! endfunc func Test_popup_atcursor() diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim index 99fd32a009..390a8b59a2 100644 --- a/src/testdir/test_scroll_opt.vim +++ b/src/testdir/test_scroll_opt.vim @@ -1439,4 +1439,655 @@ func Test_smoothscroll_listchars_eol() bwipe! endfunc +" scrolloffpad contract: +" - augment scrolloff only under EOF pressure (insufficient real lines below); +" - do not change explicit "z" viewport placement command semantics; +" - current scope is EOF-only, so BOF behavior remains unchanged. +func Test_scrolloffpad_zb_keeps_bottom_command_semantics() + new + resize 12 + setlocal scrolloff=10 + call setline(1, map(range(1, 300), 'printf("line %d", v:val)')) + + setlocal scrolloffpad=0 + normal! gg150Gzb + let baseline = [line('.'), line('w$'), winline()] + + setlocal scrolloffpad=1 + normal! gg150Gzb + call assert_equal(baseline, [line('.'), line('w$'), winline()]) + + bwipe! +endfunc + +func Test_scrolloffpad_zminus_keeps_bottom_beginline_semantics() + new + resize 12 + setlocal scrolloff=10 + call setline(1, map(range(1, 300), 'printf(" line %d", v:val)')) + + setlocal scrolloffpad=0 + normal! gg150Gz- + let baseline = [line('.'), line('w$'), winline(), col('.')] + call assert_equal(match(getline('.'), '\S') + 1, col('.')) + + setlocal scrolloffpad=1 + normal! gg150Gz- + call assert_equal(baseline, [line('.'), line('w$'), winline(), col('.')]) + call assert_equal(match(getline('.'), '\S') + 1, col('.')) + + bwipe! +endfunc + +func Test_scrolloffpad_zb_is_one_shot_then_scrolloff_reapplies() + new + resize 12 + setlocal scrolloff=10 + call setline(1, map(range(1, 300), 'printf("line %d", v:val)')) + + let after_zb = {} + let after_j = {} + for sop in [0, 1] + let &l:scrolloffpad = sop + normal! gg150Gzb + let after_zb[sop] = [line('.'), line('w$'), winline(), winsaveview().topline] + + normal! j + let after_j[sop] = [line('.'), line('w$'), winline(), winsaveview().topline] + call assert_notequal(after_zb[sop][3], after_j[sop][3]) + call assert_true(line('.') < line('w$')) + endfor + call assert_equal(after_zb[0], after_zb[1]) + call assert_equal(after_j[0], after_j[1]) + + bwipe! +endfunc + +func Test_scrolloffpad_has_no_mid_buffer_effect() + new + resize 12 + setlocal scrolloff=10 scrolloffpad=0 + call setline(1, map(range(1, 500), 'printf("line %d", v:val)')) + + normal! gg150G + let topline_without_pad = winsaveview().topline + + setlocal scrolloffpad=1 + normal! gg150G + let topline_with_pad = winsaveview().topline + + call assert_equal(topline_without_pad, topline_with_pad) + + bwipe! +endfunc + +func Test_scrolloffpad_changes_eof_pressure_only() + new + resize 12 + setlocal scrolloff=10 scrolloffpad=0 + call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) + + normal! ggG + let view_without_pad = winsaveview() + let cursor_without_pad = line('.') + let row_without_pad = winline() + + setlocal scrolloffpad=1 + normal! ggG + let view_with_pad = winsaveview() + let row_with_pad = winline() + + call assert_equal(line('$'), line('.')) + call assert_equal(cursor_without_pad, line('.')) + call assert_notequal(view_without_pad.topline, view_with_pad.topline) + call assert_true(row_with_pad < row_without_pad) + + bwipe! +endfunc + +func Test_scrolloffpad_large_scrolloff_no_overflow() + new + resize 12 + call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) + setlocal scrolloff=2147483647 scrolloffpad=0 + + normal! ggG + let view_without_pad = winsaveview() + let row_without_pad = winline() + + setlocal scrolloffpad=1 + normal! ggG + let view_with_pad = winsaveview() + let row_with_pad = winline() + + call assert_equal(line('$'), line('.')) + call assert_notequal(view_without_pad.topline, view_with_pad.topline) + call assert_true(row_with_pad < row_without_pad) + + bwipe! +endfunc + +func Test_scrolloffpad_boolean_gate_values() + new + resize 12 + setlocal scrolloff=10 + call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) + + let views = {} + let rows = {} + for sop in [0, 1, 2] + let &l:scrolloffpad = sop + normal! ggG + let views[sop] = winsaveview() + let rows[sop] = winline() + call assert_equal(line('$'), line('.')) + endfor + + call assert_equal(views[1].topline, views[2].topline) + call assert_equal(rows[1], rows[2]) + call assert_notequal(views[0].topline, views[1].topline) + call assert_true(rows[1] < rows[0]) + + bwipe! +endfunc + +func Test_scrolloffpad_requires_scrolloff_nonzero() + new + resize 12 + call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) + + let states = {} + for so in [0, 10] + let states[so] = {} + for sop in [0, 1] + let &l:scrolloff = so + let &l:scrolloffpad = sop + normal! ggG + let states[so][sop] = [line('.'), line('w0'), line('w$'), winline()] + call assert_equal(line('$'), line('.')) + endfor + endfor + + call assert_equal(states[0][0], states[0][1]) + call assert_notequal(states[10][0], states[10][1]) + call assert_true(states[10][1][3] < states[10][0][3]) + + bwipe! +endfunc + +func Test_scrolloffpad_search_to_eof() + new + resize 12 + setlocal scrolloff=10 + call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) + call setline(line('$'), 'EOF TARGET') + + let states = {} + for sop in [0, 1] + let &l:scrolloffpad = sop + normal! gg + call assert_true(search('EOF TARGET') > 0) + let states[sop] = [line('.'), line('w0'), line('w$'), winline()] + call assert_equal(line('$'), line('.')) + endfor + + call assert_notequal(states[0], states[1]) + call assert_true(states[1][3] < states[0][3]) + + bwipe! +endfunc + +func Test_scrolloffpad_paging_to_eof() + new + resize 12 + setlocal scrolloff=10 + call setline(1, map(range(1, 240), 'printf("line %d", v:val)')) + + let states = {} + for sop in [0, 1] + let &l:scrolloffpad = sop + normal! gg + + let prev = -1 + for _ in range(1, 200) + execute "normal! \" + if line('.') == prev + break + endif + let prev = line('.') + endfor + + let states[sop] = [line('.'), line('w0'), line('w$'), winline()] + call assert_equal(line('$'), line('w$')) + endfor + + call assert_notequal(states[0], states[1]) + call assert_true(states[1][3] < states[0][3]) + + bwipe! +endfunc + +func Test_scrolloffpad_autocmd_append_at_eof() + let states = {} + for sop in [0, 1] + new + resize 12 + setlocal scrolloff=10 + let &l:scrolloffpad = sop + call setline(1, map(range(1, 120), 'printf("line %d", v:val)')) + + let b:scrolloffpad_appended = 0 + augroup ScrolloffpadAppendAtEof + autocmd! + autocmd CursorMoved if b:scrolloffpad_appended == 0 && line('.') == line('$') | call append('$', 'appended') | let b:scrolloffpad_appended = 1 | endif + augroup END + + normal! ggG + doautocmd CursorMoved + let states[sop] = [ + \ line('.'), + \ line('$'), + \ line('w0'), + \ line('w$'), + \ winline(), + \ b:scrolloffpad_appended, + \ ] + + call assert_equal(1, b:scrolloffpad_appended) + call assert_equal(states[sop][1] - 1, states[sop][0]) + + augroup ScrolloffpadAppendAtEof + autocmd! + augroup END + bwipe! + endfor + + call assert_notequal(states[0], states[1]) + call assert_true(states[1][4] < states[0][4]) + +endfunc + +func Test_scrolloffpad_eof_no_reverse_scroll_on_j() + new + resize 20 + setlocal scrolloff=20 scrolloffpad=1 + call setline(1, map(range(1, 80), 'printf("line %d", v:val)')) + + normal! gg + let prev_topline = winsaveview().topline + for lnum in range(2, line('$')) + normal! j + let cur_topline = winsaveview().topline + call assert_true( + \ cur_topline >= prev_topline, + \ printf('topline moved backwards at line %d: %d -> %d', + \ lnum, prev_topline, cur_topline)) + let prev_topline = cur_topline + endfor + + bwipe! +endfunc + +func Test_scrolloffpad_bof_unchanged() + new + resize 12 + setlocal scrolloff=10 scrolloffpad=0 + call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) + + normal! Ggg + let view_without_pad = winsaveview() + let w0_without_pad = line('w0') + + setlocal scrolloffpad=1 + normal! Ggg + let view_with_pad = winsaveview() + let w0_with_pad = line('w0') + + call assert_equal(1, w0_without_pad) + call assert_equal(1, w0_with_pad) + call assert_equal(view_without_pad.topline, view_with_pad.topline) + + bwipe! +endfunc + +func Test_scrolloffpad_mouse_drag_uses_drag_scrolloff() + CheckFeature mouse + + let save_mouse = &mouse + set mouse=a + + new + resize 20 + call setline(1, map(range(1, 240), 'printf("line %d", v:val)')) + setlocal scrolloff=50 + + let after_drag = {} + for sop in [0, 1] + let &l:scrolloffpad = sop + normal! gg160Gzt + normal! v + call test_setmouse(2, 1) + call feedkeys("\", 'xt') + call test_setmouse(3, 1) + call feedkeys("\", 'xt') + let after_drag[sop] = [winsaveview().topline, line('.'), winline()] + call feedkeys("\", 'xt') + endfor + + call assert_equal(after_drag[0], after_drag[1]) + + bwipe! + let &mouse = save_mouse +endfunc + +func Test_scrolloffpad_basic() + CheckScreendump + CheckRunVimInTerminal + + let save_termwinsize = &termwinsize + set termwinsize= + + let lines =<< trim END + set scrolloff=10 + set scrolloffpad=5 + enew! + call setline(1, map(range(1, 100), 'printf("line %d", v:val)')) + normal! gg + END + call writefile(lines, 'XScrolloffpadBasic', 'D') + + let buf = RunVimInTerminal('-S XScrolloffpadBasic', {'rows': 20, 'cols': 78}) + + " Enabled: scrolloffpad > 0, expect EOF centering/padding + call term_sendkeys(buf, "\:\normal! G\") + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_scrolloffpad_basic_1', {}) + + " Beginning-of-file is unchanged (Top) + call term_sendkeys(buf, "\:\normal! gg\") + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_scrolloffpad_basic_2', {}) + + " Gating: disable scrolloffpad, then go to EOF again + " Expect normal EOF behavior (no extra centering/padding) + call term_sendkeys(buf, "\:\set scrolloffpad=0\") + call term_sendkeys(buf, "\:\normal! G\") + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_scrolloffpad_basic_3', {}) + + call StopVimInTerminal(buf) + let &termwinsize = save_termwinsize +endfunc + +func Test_scrolloffpad_smoothscroll() + CheckScreendump + CheckRunVimInTerminal + + let save_termwinsize = &termwinsize + set termwinsize= + + let lines =<< trim END + set smoothscroll scrolloff=10 scrolloffpad=1 + enew! + call setline(1, map(range(1, 100), 'printf("line %d", v:val)')) + normal! gg + END + call writefile(lines, 'XScrolloffpadSmoothscroll', 'D') + + let buf = RunVimInTerminal('-S XScrolloffpadSmoothscroll', #{rows: 20, cols: 78}) + + call term_sendkeys(buf, "\:\normal! G\") + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_scrolloffpad_smoothscroll_1', {}) + + call term_sendkeys(buf, "\:\call setline(line('$'), repeat('LONG ', 30))\") + call term_sendkeys(buf, "\:\normal! 41|\") + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_scrolloffpad_smoothscroll_2', {}) + + call StopVimInTerminal(buf) + let &termwinsize = save_termwinsize +endfunc + +func Test_scrolloffpad_insert_eof() + let save_so = &scrolloff + let save_sop = &scrolloffpad + + set scrolloff=10 scrolloffpad=1 + enew! + call setline(1, map(range(1, 200), 'printf("line %d", v:val)')) + normal! G + + let topline_before = winsaveview().topline + call feedkeys("i\", 'xt') + call assert_equal(topline_before, winsaveview().topline) + + exe "normal! \" + let topline_after = winsaveview().topline + call feedkeys("i\", 'xt') + call assert_equal(topline_after, winsaveview().topline) + + let &scrolloff = save_so + let &scrolloffpad = save_sop + bwipe! +endfunc + +func Test_scrolloffpad_in_diff_mode() + CheckFeature diff + + let save_so = &scrolloff + let save_sop = &scrolloffpad + let save_splitright = &splitright + + set nosplitright + set scrolloff=10 + set scrolloffpad=0 + + enew + call setline(1, map(range(1, 100), {_, v -> 'line ' .. v})) + diffthis + + vnew + call setline(1, map(range(1, 100), {_, v -> 'line ' .. v})) + " Make buffers minimally different to avoid diff folding everything. + call setline(50, 'DIFF LINE 50') + diffthis + + windo normal! zR + windo normal! gg + wincmd = + + let rows_without = [] + let rows_with = [] + let near_states = [] + let eof_states = [] + for sop in [0, 1] + let &scrolloffpad = sop + + " Near EOF with real text visible in both windows. + windo normal! 99G + for w in range(1, winnr('$')) + execute w .. 'wincmd w' + let state = [line('.'), line('w0'), line('w$'), winline()] + call assert_equal(99, state[0]) + call assert_equal(100, state[2]) + if sop == 0 + call add(near_states, state) + endif + endfor + call assert_equal(near_states[0], near_states[1]) + + " EOF in both windows: scrolloffpad should raise the cursor row. + windo normal! G + for w in range(1, winnr('$')) + execute w .. 'wincmd w' + let state = [line('.'), line('w0'), line('w$'), winline()] + call assert_equal(line('$'), state[0]) + if sop == 0 + call add(eof_states, state) + call add(rows_without, state[3]) + else + call add(rows_with, state[3]) + endif + endfor + call assert_equal(eof_states[0], eof_states[1]) + endfor + + call assert_true(rows_with[0] < rows_without[0]) + call assert_true(rows_with[1] < rows_without[1]) + + windo diffoff + %bwipe! + let &scrolloff = save_so + let &scrolloffpad = save_sop + let &splitright = save_splitright +endfunc + +func Test_scrolloffpad_diff_eof_filler_behavior() + CheckFeature diff + + let save_so = &scrolloff + let save_sop = &scrolloffpad + let save_diffopt = &diffopt + let save_splitright = &splitright + + set diffopt+=filler + set scrolloff=10 + set scrolloffpad=0 + set nosplitright + + 20new + call setline(1, map(range(1, 100), {_, v -> 'left ' .. v})) + diffthis + let short_wid = win_getid() + + vnew + call setline(1, map(range(1, 120), {_, v -> 'right ' .. v})) + diffthis + let long_wid = win_getid() + + call assert_true(win_gotoid(short_wid)) + let short_height = winheight(0) + call assert_true(win_gotoid(long_wid)) + let long_height = winheight(0) + call assert_equal(short_height, long_height) + call assert_equal(20, short_height) + + let ordered_diff_wids = [long_wid, short_wid] + let states = {} + for sop in [0, 1] + execute 'set scrolloffpad=' .. sop + for wid in ordered_diff_wids + call assert_true(win_gotoid(wid)) + normal! gg + endfor + for wid in ordered_diff_wids + call assert_true(win_gotoid(wid)) + normal! G + endfor + + call assert_true(win_gotoid(short_wid)) + let short_view = winsaveview() + let short_state = [ + \ line('.'), + \ line('$'), + \ winline(), + \ short_view.topline, + \ short_view.topfill, + \ diff_filler(line('$') + 1), + \ ] + call assert_equal(short_state[1], short_state[0]) + call assert_true(short_state[5] > 0) + + call assert_true(win_gotoid(long_wid)) + let long_view = winsaveview() + let long_state = [ + \ line('.'), + \ line('$'), + \ winline(), + \ long_view.topline, + \ long_view.topfill, + \ ] + call assert_true(long_state[0] > 0 && long_state[0] <= long_state[1]) + call assert_equal(short_state[0], long_state[0]) + + let states[sop] = [short_state, long_state] + endfor + + let short_without = states[0][0] + let short_with = states[1][0] + " Environment/layout can shift direction of movement; require only that + " scrolloffpad changes the short-window viewport state under EOF filler. + call assert_true(short_with[2] != short_without[2] + \ || short_with[3] != short_without[3] + \ || short_with[4] != short_without[4]) + + windo diffoff + call assert_true(win_gotoid(short_wid)) + only! + %bwipe! + let &scrolloff = save_so + let &scrolloffpad = save_sop + let &diffopt = save_diffopt + let &splitright = save_splitright +endfunc + +func Test_scrolloffpad_with_folds() + CheckScreendump + CheckRunVimInTerminal + CheckFeature folding + + let save_termwinsize = &termwinsize + set termwinsize= + + let lines =<< trim END + set scrolloff=10 + set scrolloffpad=1 + + enew + call setline(1, map(range(1, 120), {_, v -> 'line ' . v})) + + " Create a large fold near the end of the file. + " Fold lines 60-110, leaving 111-120 visible after the fold. + set foldmethod=manual + set foldenable + normal! gg + normal! 60G + normal! zf50j + normal! gg + END + call writefile(lines, 'XScrolloffpadFolds', 'D') + + let buf = RunVimInTerminal('-S XScrolloffpadFolds', #{rows: 20, cols: 78}) + + " Case 1: Jump to end-of-file + " With folds present, scrolloffpad should still + " keep the cursor positioned with padding below EOF + call term_sendkeys(buf, "\:\normal! G\") + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_1', {}) + + " Case 2: Move to the folded line to ensure the fold is actually in view + call term_sendkeys(buf, "\:\normal! 60G\") + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_2', {}) + + " Case 3: Close the fold explicitly and go to EOF again + " Behavior should remain stable with closed folds + call term_sendkeys(buf, "\:\normal! zc\") + call term_sendkeys(buf, "\:\normal! G\") + call term_sendkeys(buf, "\") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_3', {}) + + call StopVimInTerminal(buf) + let &termwinsize = save_termwinsize +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/util/gen_opt_test.vim b/src/testdir/util/gen_opt_test.vim index 6a02c531bd..bc54d272d8 100644 --- a/src/testdir/util/gen_opt_test.vim +++ b/src/testdir/util/gen_opt_test.vim @@ -24,6 +24,7 @@ while search("^'[^']*'.*\\n.*|global-local", 'W') endwhile call extend(global_locals, #{ \ scrolloff: -1, + \ scrolloffpad: -1, \ sidescrolloff: -1, \ undolevels: -123456, \}) @@ -93,6 +94,7 @@ let test_values = { \ 'scroll': [[0, 1, 2, 15], [-1, 999]], \ 'scrolljump': [[-100, -1, 0, 1, 2, 15], [-101, 999]], \ 'scrolloff': [[0, 1, 8, 999], [-1]], + \ 'scrolloffpad': [[0, 1, 2, 3], [-1]], \ 'shiftwidth': [[0, 1, 8, 999], [-1]], \ 'showtabpanel': [[0, 1, 2], []], \ 'sidescroll': [[0, 1, 8, 999], [-1]], diff --git a/src/version.c b/src/version.c index c0f826a17d..0e22232f13 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 356, /**/ 355, /**/ diff --git a/src/window.c b/src/window.c index 1d0d478442..26d923d9c0 100644 --- a/src/window.c +++ b/src/window.c @@ -5974,6 +5974,7 @@ win_alloc(win_T *after, int hidden) // use global option value for global-local options new_wp->w_allbuf_opt.wo_so = new_wp->w_p_so = -1; + new_wp->w_allbuf_opt.wo_sop = new_wp->w_p_sop = -1; new_wp->w_allbuf_opt.wo_siso = new_wp->w_p_siso = -1; // We won't calculate w_fraction until resizing the window From c78194e41d5a0b05b0ddf383b6679b1503f977fb Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 15 Apr 2026 20:17:17 +0000 Subject: [PATCH 053/545] patch 9.2.0357: [security]: command injection via backticks in tag files Problem: [security]: command injection via backticks in tag files (Srinivas Piskala Ganesh Babu, Andy Ngo) Solution: Disallow backticks before attempting to expand filenames. Github Advisory: https://github.com/vim/vim/security/advisories/GHSA-cwgx-gcj7-6qh8 Supported by AI Signed-off-by: Christian Brabandt --- src/tag.c | 4 +++- src/testdir/test_tagjump.vim | 22 ++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/tag.c b/src/tag.c index d3e27e6023..0f12e384b5 100644 --- a/src/tag.c +++ b/src/tag.c @@ -4137,8 +4137,10 @@ expand_tag_fname(char_u *fname, char_u *tag_fname, int expand) /* * Expand file name (for environment variables) when needed. + * Disallow backticks, they could execute arbitrary shell + * commands. This is not needed for tag filenames. */ - if (expand && mch_has_wildcard(fname)) + if (expand && mch_has_wildcard(fname) && vim_strchr(fname, '`') == NULL) { ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index bbab3c70e8..c0fa7b02e6 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -1693,4 +1693,26 @@ func Test_tag_excmd_with_number_vim9script() bwipe! endfunc +" Test that backtick expressions in tag filenames are not expanded. +" This prevents command injection via malicious tags files. +func Test_tag_backtick_filename_not_expanded() + let pwned_file = 'Xtags_pwnd' + call assert_false(filereadable(pwned_file)) + + let tagline = "main\t`touch " .. pwned_file .. "`\t/^int main/;\"\tf" + call writefile([tagline], 'Xbt_tags', 'D') + call writefile(['int main(int argc, char **argv) {', '}'], 'Xbt_main.c', 'D') + + set tags=Xbt_tags + sp Xbt_main.c + + " The :tag command should fail to find the file, but must NOT execute + " the backtick shell command. + call assert_fails('tag main', 'E429:') + call assert_false(filereadable(pwned_file)) + + set tags& + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 0e22232f13..6716d470b3 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 357, /**/ 356, /**/ From e6a84bb6b0e45ddb106f689054b0d96aba161f67 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 16 Apr 2026 14:28:18 +0200 Subject: [PATCH 054/545] runtime(tar): missing g:tar_secure in tar#Extract() Signed-off-by: Christian Brabandt --- runtime/autoload/tar.vim | 25 +++++++++++++------------ runtime/doc/pi_tar.txt | 11 +++++++++-- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index 6aa3489e53..1347371b51 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -24,6 +24,7 @@ " 2026 Apr 09 by Vim Project: fix bugs with zstd support (#19930) " 2026 Apr 09 by Vim Project: fix bug with dotted filename (#19930) " 2026 Apr 15 by Vim Project: fix more path traversal issues (#19981) +" 2026 Apr 16 by Vim Project: use g:tar_secure in tar#Extract() " " Contains many ideas from Michael Toren's " @@ -640,7 +641,7 @@ fun! tar#Extract() endif if tarball =~# "\.tar$" - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -649,7 +650,7 @@ fun! tar#Extract() elseif tarball =~# "\.tgz$" let extractcmd= substitute(extractcmd,"-","-z","") - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -658,7 +659,7 @@ fun! tar#Extract() elseif tarball =~# "\.tar\.gz$" let extractcmd= substitute(extractcmd,"-","-z","") - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -667,7 +668,7 @@ fun! tar#Extract() elseif tarball =~# "\.tbz$" let extractcmd= substitute(extractcmd,"-","-j","") - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -676,7 +677,7 @@ fun! tar#Extract() elseif tarball =~# "\.tar\.bz2$" let extractcmd= substitute(extractcmd,"-","-j","") - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -685,7 +686,7 @@ fun! tar#Extract() elseif tarball =~# "\.tar\.bz3$" let extractcmd= substitute(extractcmd,"-","-j","") - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -694,7 +695,7 @@ fun! tar#Extract() elseif tarball =~# "\.txz$" let extractcmd= substitute(extractcmd,"-","-J","") - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -703,7 +704,7 @@ fun! tar#Extract() elseif tarball =~# "\.tar\.xz$" let extractcmd= substitute(extractcmd,"-","-J","") - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -712,7 +713,7 @@ fun! tar#Extract() elseif tarball =~# "\.tzst$" let extractcmd= substitute(extractcmd,"-","--zstd -","") - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -721,7 +722,7 @@ fun! tar#Extract() elseif tarball =~# "\.tar\.zst$" let extractcmd= substitute(extractcmd,"-","--zstd -","") - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -732,7 +733,7 @@ fun! tar#Extract() if has("linux") let extractcmd= substitute(extractcmd,"-","-I lz4 -","") endif - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else @@ -743,7 +744,7 @@ fun! tar#Extract() if has("linux") let extractcmd= substitute(extractcmd,"-","-I lz4 -","") endif - call system(extractcmd." ".shellescape(tarball)." ".shellescape(fname)) + call system(extractcmd." ".shellescape(tarball)." ".g:tar_secure.shellescape(fname)) if v:shell_error != 0 call s:Msg('tar#Extract', 'error', $"{extractcmd} {tarball} {fname}: failed!") else diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt index 0bba3c37d3..5a4dae8f31 100644 --- a/runtime/doc/pi_tar.txt +++ b/runtime/doc/pi_tar.txt @@ -1,4 +1,4 @@ -*pi_tar.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*pi_tar.txt* For Vim version 9.2. Last change: 2026 Apr 16 +====================+ | Tar File Interface | @@ -101,10 +101,17 @@ Copyright 2005-2017: *tar-copyright* 4. History *tar-history* unreleased: + Apr 16, 2026 * add missing g:tar_secure into tar#Extract + Apr 15, 2026 * add path traversal checks in tar#Extract() + Apr 09, 2026 * fix zstd support and dotted filename support + Apr 06, 2026 * fix lz4 support + Feb 07, 2026 * make path traversal detection more robust + Feb 06, 2026 * fix bug with nowrapscan + Jul 16, 2025 * update minimum required Vim version Jul 13, 2025 * drop leading / May 19, 2025 * restore working directory after read/write Apr 16, 2025 * decouple from netrw by adding s:WinPath() - instead of shelling out to file(1) + instead of shelling out to file(1) Mar 02, 2025 * determine the compression using readblob() Mar 02, 2025 * escape the filename before using :read Mar 01, 2025 * fix syntax error in tar#Read() From 21c0cd29f808e32a7c159889c6dcf7a6fbf189f7 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 15 Apr 2026 20:46:19 +0000 Subject: [PATCH 055/545] runtime(netrw): add missing escape() calls Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/pack/dist/opt/netrw/autoload/netrw.vim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index 8e5fdb5397..0e6fe6cea6 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -24,6 +24,7 @@ " 2026 Mar 01 by Vim Project include portnumber in hostname checking #19533 " 2026 Apr 01 by Vim Project use fnameescape() with netrw#FileUrlEdit() " 2026 Apr 05 by Vim Project Fix netrw#RFC2396() #19913 +" 2026 Apr 15 by Vim Project Add missing escape() " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -5304,6 +5305,8 @@ function s:NetrwMarkFileCompress(islocal) if a:islocal if g:netrw_keepdir let fname= netrw#os#Escape(netrw#fs#ComposePath(curdir,fname)) + else + let fname= netrw#os#Escape(fname) endif call system(exe." ".fname) if v:shell_error @@ -5638,6 +5641,8 @@ function s:NetrwMarkFileExe(islocal,enbloc) if a:islocal if g:netrw_keepdir let fname= netrw#os#Escape(netrw#fs#WinPath(netrw#fs#ComposePath(curdir,fname))) + else + let fname= netrw#os#Escape(netrw#fs#WinPath(fname)) endif else let fname= netrw#os#Escape(netrw#fs#WinPath(b:netrw_curdir.fname)) From ab02d65b1f045607e51a28317894c39ae9512d1d Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 16 Apr 2026 19:21:56 +0000 Subject: [PATCH 056/545] runtime(gzip): Remove compatibility fall-backs, harden random filename generation Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/autoload/gzip.vim | 57 ++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/runtime/autoload/gzip.vim b/runtime/autoload/gzip.vim index a6fbe2c336..09f35c95ba 100644 --- a/runtime/autoload/gzip.vim +++ b/runtime/autoload/gzip.vim @@ -1,6 +1,6 @@ " Vim autoload file for editing compressed files. " Maintainer: The Vim Project -" Last Change: 2024 Nov 25 +" Last Change: 2026 Apr 26 " Former Maintainer: Bram Moolenaar " These functions are used by the gzip plugin. @@ -82,13 +82,8 @@ fun gzip#read(cmd) let empty = line("'[") == 1 && line("']") == line("$") let tmp = tempname() let tmpe = tmp . "." . expand(":e") - if exists('*fnameescape') - let tmp_esc = fnameescape(tmp) - let tmpe_esc = fnameescape(tmpe) - else - let tmp_esc = escape(tmp, ' ') - let tmpe_esc = escape(tmpe, ' ') - endif + let tmp_esc = fnameescape(tmp) + let tmpe_esc = fnameescape(tmpe) " write the just read lines to a temp file "'[,']w tmp.gz" execute "silent '[,']w " . tmpe_esc " uncompress the temp file: call system("gzip -dn tmp.gz") @@ -101,22 +96,14 @@ fun gzip#read(cmd) let ok = 1 " delete the compressed lines; remember the line number let l = line("'[") - 1 - if exists(":lockmarks") - lockmarks '[,']d _ - else - '[,']d _ - endif + lockmarks '[,']d _ " read in the uncompressed lines "'[-1r tmp" " Use ++edit if the buffer was empty, keep the 'ff' and 'fenc' options. setlocal nobin - if exists(":lockmarks") - if empty - execute "silent lockmarks " . l . "r ++edit " . tmp_esc - else - execute "silent lockmarks " . l . "r " . tmp_esc - endif + if empty + execute "silent lockmarks " . l . "r ++edit " . tmp_esc else - execute "silent " . l . "r " . tmp_esc + execute "silent lockmarks " . l . "r " . tmp_esc endif " if buffer became empty, delete trailing blank line @@ -143,11 +130,7 @@ fun gzip#read(cmd) " When uncompressed the whole buffer, do autocommands if ok && empty - if exists('*fnameescape') - let fname = fnameescape(expand("%:r")) - else - let fname = escape(expand("%:r"), " \t\n*?[{`$\\%#'\"|!<") - endif + let fname = fnameescape(expand("%:r")) if filereadable(undofile(expand("%"))) exe "sil rundo " . fnameescape(undofile(expand("%"))) endif @@ -191,8 +174,9 @@ fun gzip#appre(cmd) call s:set_compression(readfile(nm, "b", 1)[0]) endif - " Rename to a weird name to avoid the risk of overwriting another file - let nmt = expand(":p:h") . "/X~=@l9q5" + " Rename to a unique name to avoid the risk of overwriting another file + " or being targeted by a symlink in a shared directory. + let nmt = s:samedir_tempname(nm) let nmte = nmt . "." . expand(":e") if rename(nm, nmte) == 0 if &patchmode != "" && getfsize(nm . &patchmode) == -1 @@ -208,22 +192,27 @@ fun gzip#appre(cmd) endfun " find a file name for the file to be compressed. Use "name" without an -" extension if possible. Otherwise use a weird name to avoid overwriting an -" existing file. +" extension if possible. Otherwise use a unique name to avoid overwriting an +" existing file or following a symlink set up by another user. fun s:tempname(name) let fn = fnamemodify(a:name, ":r") if !filereadable(fn) && !isdirectory(fn) return fn endif - return fnamemodify(a:name, ":p:h") . "/X~=@l9q5" + return s:samedir_tempname(a:name) +endfun + +" Generate an unpredictable file name in the same directory as "name", using +" the random component of tempname() to avoid symlink attacks in shared +" directories (e.g. /tmp). +fun s:samedir_tempname(name) + let tmp = tempname() + return fnamemodify(a:name, ":p:h") . "/" . fnamemodify(tmp, ":h:t") . fnamemodify(tmp, ":t") endfun fun s:escape(name) " shellescape() was added by patch 7.0.111 - if exists("*shellescape") - return shellescape(a:name) - endif - return "'" . a:name . "'" + return shellescape(a:name) endfun " vim: set sw=2 : From b076c49282f4415ac90b692332887a57a3e8f178 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 16 Apr 2026 20:03:39 +0000 Subject: [PATCH 057/545] patch 9.2.0358: runtime(vimball): still path traversal attacks possible Problem: runtime(vimball): still path traversal attacks possible Solution: block Windows driver letter paths (Yasuhiro Matsumoto) The path traversal check in vimball#Vimball() did not reject file names starting with a Windows drive letter (e.g. "C:/foo"). Backslashes are normalized to forward slashes earlier, so UNC paths are caught by the leading-slash check, but absolute drive-letter paths slipped through and could write outside of g:vimball_home on Windows. Add a "^\a:" check next to the existing "^/" check, and cover it with a new test. closes: #19989 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/autoload/vimball.vim | 12 ++++++------ runtime/doc/pi_vimball.txt | 7 ++++++- src/testdir/test_plugin_vimball.vim | 14 ++++++++++++++ src/version.c | 2 ++ 4 files changed, 28 insertions(+), 7 deletions(-) diff --git a/runtime/autoload/vimball.vim b/runtime/autoload/vimball.vim index d661ded631..8cc206f1d4 100644 --- a/runtime/autoload/vimball.vim +++ b/runtime/autoload/vimball.vim @@ -1,13 +1,10 @@ " vimball.vim : construct a file containing both paths and files " Maintainer: This runtime file is looking for a new maintainer. " Original Author: Charles E. Campbell -" Date: Apr 11, 2016 +" Date: Apr 16, 2026 " Version: 37 (with modifications from the Vim Project) " GetLatestVimScripts: 1502 1 :AutoInstall: vimball.vim " Last Change: -" 2025 Feb 28 by Vim Project: add support for bzip3 (#16755) -" 2026 Apr 05 by Vim Project: Detect path traversal attacks -" 2026 Apr 09 by Vim Project: Detect more path traversal attacks " Copyright: (c) 2004-2011 by Charles E. Campbell " The VIM LICENSE applies to Vimball.vim, and Vimball.txt " (see |copyright|) except use "Vimball" instead of "Vim". @@ -230,8 +227,11 @@ fun! vimball#Vimball(really,...) let fsize = substitute(getline(linenr+1),'^\(\d\+\).\{-}$','\1','')+0 let fenc = substitute(getline(linenr+1),'^\d\+\s*\(\S\{-}\)$','\1','') let filecnt = filecnt + 1 - " Do not allow a leading / or .. anywhere in the file name - if fname =~ '\.\.' || fname =~ '^/' + " Do not allow a leading /, .. anywhere, or a Windows drive letter + " (e.g. C:/foo) in the file name. Backslashes were already converted + " to forward slashes above, so this also catches \\server\share UNC + " paths via the leading-slash check. + if fname =~ '\.\.' || fname =~ '^/' || fname =~ '^\a:' echomsg "(Vimball) Path Traversal Attack detected, aborting..." exe "tabn ".curtabnr bw! Vimball diff --git a/runtime/doc/pi_vimball.txt b/runtime/doc/pi_vimball.txt index 07fc68f7eb..e1770e289e 100644 --- a/runtime/doc/pi_vimball.txt +++ b/runtime/doc/pi_vimball.txt @@ -1,4 +1,4 @@ -*pi_vimball.txt* For Vim version 9.2. Last change: 2026 Apr 05 +*pi_vimball.txt* For Vim version 9.2. Last change: 2026 Apr 16 ---------------- Vimball Archiver @@ -166,6 +166,11 @@ WINDOWS *vimball-windows* ============================================================================== 4. Vimball History *vimball-history* {{{1 + unreleased: + Feb 28, 2025 * add support for bzip3 (#16755) + Apr 05, 2026 * Detect path traversal attacks + Apr 09, 2026 * Detect more path traversal attacks + Apr 16, 2026 * Block Windows drive letter paths 37 : Jul 18, 2014 * (by request of T. Miedema) added augroup around the autocmds in vimballPlugin.vim Jul 06, 2015 * there are two uses of tabc; changed to tabc! diff --git a/src/testdir/test_plugin_vimball.vim b/src/testdir/test_plugin_vimball.vim index 2d5b4ba768..ab740d1d2b 100644 --- a/src/testdir/test_plugin_vimball.vim +++ b/src/testdir/test_plugin_vimball.vim @@ -83,3 +83,17 @@ func Test_vimball_path_traversal() call assert_false(filereadable('../XVimball/Xtest.txt')) call s:teardown() endfunc + +func Test_vimball_path_traversal_drive_letter() + call s:Mkvimball() + call delete('XVimball', 'rf') + sp Xtest.vmb + " try to write to a Windows-style absolute path with a drive letter + 4s#XVimball#C:/&# + so % + call feedkeys("\", "it") + + let mess = execute(':mess')->split('\n')[-1] + call assert_match('(Vimball) Path Traversal Attack detected, aborting\.\.\.', mess) + call s:teardown() +endfunc diff --git a/src/version.c b/src/version.c index 6716d470b3..a836743b7a 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 358, /**/ 357, /**/ From 25e9fc44a87792a7de9a1f158ce23295480ef5e8 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Thu, 16 Apr 2026 20:12:08 +0000 Subject: [PATCH 058/545] patch 9.2.0359: wrong VertSplitNC highlighting on winbar Problem: wrong VertSplitNC highlighting on winbar (Maxim Kim, after v9.2.0349) Solution: Use VertSplit instead of VertSplitNC for winbar rows (Hirohito Higashi). vsep_row_is_curwin() and right_neighbor_at_row() used W_WINROW() which excludes winbar rows from the window's range. This caused the vertical separator at winbar rows to use VertSplitNC even when curwin is adjacent. Use w_winrow directly so winbar rows are included in the range check. fixes: #19985 closes: #19987 Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/screen.c | 6 ++-- .../dumps/Test_VertSplitNC_winbar_1.dump | 12 +++++++ .../dumps/Test_VertSplitNC_winbar_2.dump | 12 +++++++ src/testdir/test_highlight.vim | 32 +++++++++++++++++++ src/version.c | 2 ++ 5 files changed, 61 insertions(+), 3 deletions(-) create mode 100644 src/testdir/dumps/Test_VertSplitNC_winbar_1.dump create mode 100644 src/testdir/dumps/Test_VertSplitNC_winbar_2.dump diff --git a/src/screen.c b/src/screen.c index 8005b3cc33..eb7cc9cdd1 100644 --- a/src/screen.c +++ b/src/screen.c @@ -5129,9 +5129,9 @@ vsep_row_is_curwin(win_T *wp, int row) return true; // Check if curwin is immediately to the right of wp's separator and - // "row" is within curwin's row range. + // "row" is within curwin's row range (including the winbar). if (curwin->w_wincol == W_ENDCOL(wp) + wp->w_vsep_width - && row >= W_WINROW(curwin) + && row >= curwin->w_winrow && row < W_WINROW(curwin) + curwin->w_height) return true; @@ -5173,7 +5173,7 @@ right_neighbor_at_row(win_T *wp, int row) FOR_ALL_WINDOWS(rn) if (rn->w_wincol == rcol - && row >= W_WINROW(rn) + && row >= rn->w_winrow && row < W_WINROW(rn) + rn->w_height + rn->w_status_height) return rn; return NULL; diff --git a/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump b/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump new file mode 100644 index 0000000000..4fbee462da --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump @@ -0,0 +1,12 @@ +|w+0&#ffffff0|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&| +0#0000000#e0e0e08| +2#ffffff16#6c6c6c255|I|t|e|m| | +0#0000000#e0e0e08@29 +|w+0&#ffffff0|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&>w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p| +0#ffffff16#0000e05|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p +| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_winbar_2.dump b/src/testdir/dumps/Test_VertSplitNC_winbar_2.dump new file mode 100644 index 0000000000..765328356e --- /dev/null +++ b/src/testdir/dumps/Test_VertSplitNC_winbar_2.dump @@ -0,0 +1,12 @@ +>w+0&#ffffff0|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&| +0#0000000#e0e0e08| +2#ffffff16#6c6c6c255|I|t|e|m| | +0#0000000#e0e0e08@29 +|w+0&#ffffff0|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 +|[+0#ffffff16#0000e05|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p| |[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p +| +0#0000000#ffffff0@74 diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim index 6968e72b6a..ef593b8b35 100644 --- a/src/testdir/test_highlight.vim +++ b/src/testdir/test_highlight.vim @@ -1812,4 +1812,36 @@ func Test_VertSplitNC_winhighlight() call StopVimInTerminal(buf) endfunc +" Test that VertSplit (not VertSplitNC) is used for the separator rows +" adjacent to a window with a winbar. +func Test_VertSplitNC_winbar() + CheckScreendump + + let lines =<< trim END + hi StatusLine ctermfg=White ctermbg=DarkBlue cterm=NONE + hi StatusLineNC ctermfg=Black ctermbg=Gray cterm=NONE + hi VertSplit ctermfg=Green ctermbg=NONE cterm=NONE + hi VertSplitNC ctermfg=DarkGray ctermbg=NONE cterm=NONE + call setline(1, repeat(['winbar test'], 20)) + vsplit + wincmd w + nnoremenu 1.10 WinBar.Item :echo 'test' + END + call writefile(lines, 'Xtest_vertsplitNC_winbar', 'D') + + let buf = RunVimInTerminal('-S Xtest_vertsplitNC_winbar', {'rows': 12}) + call TermWait(buf) + + " Right window (with winbar) is current: the separator should use + " VertSplit for all rows including the winbar row. + call VerifyScreenDump(buf, 'Test_VertSplitNC_winbar_1', {}) + + " Move to left window: the separator should use VertSplitNC. + call term_sendkeys(buf, "\h") + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_VertSplitNC_winbar_2', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index a836743b7a..b015465ed5 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 359, /**/ 358, /**/ From 1c299f26316cde3f32ec95b7c440e868ada0cb20 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 16 Apr 2026 20:29:33 +0000 Subject: [PATCH 059/545] patch 9.2.0360: Cannot handle mouse-clicks in the tabpanel Problem: Cannot handle mouse-clicks in the tabpanel Solution: Add support using the %[FuncName] atom for the tabpanel (Yasuhiro Matsumoto) Extend the statusline/tabline click region mechanism to work with 'tabpanel'. The callback receives a dict with "area" set to "tabpanel" and a "tabnr" key indicating which tab page label was clicked. closes: #19960 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 18 ++--- runtime/doc/version9.txt | 6 +- src/globals.h | 4 ++ src/mouse.c | 57 +++++++++++---- src/screen.c | 9 +++ src/structs.h | 2 + src/tabpanel.c | 132 +++++++++++++++++++++++++++++++++- src/testdir/test_tabpanel.vim | 70 ++++++++++++++++++ src/version.c | 2 + 9 files changed, 273 insertions(+), 27 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 31a9629c0b..62d7ef8dfd 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 15 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 16 VIM REFERENCE MANUAL by Bram Moolenaar @@ -8689,7 +8689,7 @@ A jump table for the options with a short description can be found at |Q_op|. *stl-%[FuncName]* %[ defines clickable regions in the statusline. When the user clicks on a region with the mouse, the specified function is called. The - same syntax can also be used in 'tabline'. + same syntax can also be used in 'tabline' and 'tabpanel'. %[FuncName] Start of a clickable region. "FuncName" is the name of a Vim function to call when the region is clicked. @@ -8708,16 +8708,16 @@ A jump table for the options with a short description can be found at |Q_op|. "mods" Modifier keys: combination of "s" (shift), "c" (ctrl), "a" (alt). Empty string if no modifiers. "winid" |window-ID| of the window whose statusline was clicked, - or 0 when the click was in 'tabline'. - "area" "statusline" or "tabline". Indicates which option the - clicked region belongs to. Useful when a single - callback is shared between 'statusline' and 'tabline'. + or 0 when the click was in 'tabline' or 'tabpanel'. + "area" "statusline", "tabline", or "tabpanel". Indicates + which option the clicked region belongs to. + "tabnr" (tabpanel only) Tab page number of the clicked label. If the function returns non-zero, the statusline is redrawn. Dragging the statusline to resize the window still works even when - click handlers are defined. When used in 'tabline', clicks in - %[FuncName] regions are dispatched to the callback instead of the - default tab page selection behavior. + click handlers are defined. When used in 'tabline' or 'tabpanel', + clicks in %[FuncName] regions are dispatched to the callback + instead of the default tab-selection behavior. Example: > func! ClickFile(info) diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index ee24e29010..7fa53f87d8 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 15 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 16 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52613,8 +52613,8 @@ Other ~ pairs individually (e.g. 'listchars', 'fillchars', 'diffopt'). - |system()| and |systemlist()| functions accept a list as first argument, bypassing the shell completely. -- Allow mouse clickable regions in the |status-line| using the - |stl-%[FuncName]| atom. +- Allow mouse clickable regions in the 'statusline', 'tabline' and the + 'tabpanel' using the |stl-%[FuncName]| atom. - Enable reflow support in the |:terminal|. Platform specific ~ diff --git a/src/globals.h b/src/globals.h index 20450aa68c..9a6c285937 100644 --- a/src/globals.h +++ b/src/globals.h @@ -108,6 +108,10 @@ EXTERN short *TabPageIdxs INIT(= NULL); EXTERN stl_click_region_T *tabline_stl_click INIT(= NULL); EXTERN int tabline_stl_click_count INIT(= 0); +// Click regions for 'tabpanel' (%[FuncName]). +EXTERN stl_click_region_T *tabpanel_stl_click INIT(= NULL); +EXTERN int tabpanel_stl_click_count INIT(= 0); + #ifdef FEAT_PROP_POPUP // Array with size Rows x Columns containing zindex of popups. EXTERN short *popup_mask INIT(= NULL); diff --git a/src/mouse.c b/src/mouse.c index 1077c5ee72..45ffd4b513 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -20,9 +20,11 @@ static long mouse_hor_step = 6; static long mouse_vert_step = 3; static win_T *dragwin = NULL; // window being dragged -static int stl_click_handler(win_T *wp, int mcol, int which_button, int mods); +static int stl_click_handler(win_T *wp, int mrow, int mcol, int which_button, + int mods); static int stl_click_handler_regions(stl_click_region_T *regions, - int region_count, int winid, int mcol, + int region_count, int winid, + char_u *area_name, int mrow, int mcol, int which_button, int mods); void @@ -492,6 +494,17 @@ do_mouse( if (mouse_col < firstwin->w_wincol || mouse_col >= firstwin->w_wincol + topframe->fr_width) { + // Dispatch 'tabpanel' %[FuncName] click regions before falling through + // to tab-page selection. On drag events fall through to the normal + // tab-drag handling. + if (is_click && !is_drag + && stl_click_handler_regions(tabpanel_stl_click, + tabpanel_stl_click_count, + 0, (char_u *)"tabpanel", + mouse_row, mouse_col, + which_button, mod_mask)) + return FALSE; + tp_label.is_panel = true; tp_label.just_in = true; tp_label.nr = get_tabpagenr_on_tabpanel(); @@ -511,8 +524,9 @@ do_mouse( if (is_click && !is_drag && stl_click_handler_regions(tabline_stl_click, tabline_stl_click_count, - 0, mouse_col, which_button, - mod_mask)) + 0, (char_u *)"tabline", + mouse_row, mouse_col, + which_button, mod_mask)) return FALSE; tp_label.just_in = true; @@ -778,7 +792,7 @@ do_mouse( // Check for statusline click handler early, before visual mode or // other button-specific handling can interfere. if (in_status_line && is_click && !is_drag - && stl_click_handler(dragwin, mouse_col, + && stl_click_handler(dragwin, mouse_row, mouse_col, which_button, mod_mask)) { #ifdef FEAT_MOUSESHAPE @@ -1663,6 +1677,8 @@ stl_click_handler_regions( stl_click_region_T *regions, int region_count, int winid, + char_u *area_name, + int mrow, int mcol, int which_button, int mods) @@ -1682,10 +1698,12 @@ stl_click_handler_regions( if (regions == NULL || region_count == 0) return FALSE; - // Find the click region at the given column. + // Find the click region at the given row and column. for (n = 0; n < region_count; n++) { - if (col >= regions[n].col_start && col < regions[n].col_end) + if (regions[n].row == mrow + && col >= regions[n].col_start + && col < regions[n].col_end) break; } if (n >= region_count || regions[n].funcname == NULL) @@ -1728,10 +1746,13 @@ stl_click_handler_regions( dict_add_number(info, "winid", winid); // "area": which option the clicked region belongs to. Lets a shared - // dispatcher distinguish 'statusline' from 'tabline' (and future areas) - // without having to overload winid == 0. - dict_add_string(info, "area", - winid == 0 ? (char_u *)"tabline" : (char_u *)"statusline"); + // dispatcher distinguish 'statusline', 'tabline' and 'tabpanel' without + // having to overload winid == 0. + dict_add_string(info, "area", area_name); + + // Expose tab page number for 'tabpanel' regions. + if (regions[n].tabnr > 0) + dict_add_number(info, "tabnr", regions[n].tabnr); // Call the function with the info dict as argument. argvars[0].v_type = VAR_DICT; @@ -1755,9 +1776,14 @@ stl_click_handler_regions( { // Make sure the tabline gets redrawn too when the callback asks for // a redraw (redraw_statuslines() only redraws the tabline when - // redraw_tabline is set). + // redraw_tabline is set). For tabpanel the whole screen needs to be + // refreshed. if (winid == 0) redraw_tabline = TRUE; +# ifdef FEAT_TABPANEL + if (STRCMP(area_name, "tabpanel") == 0) + redraw_all_later(UPD_NOT_VALID); +# endif redraw_statuslines(); } @@ -1766,6 +1792,8 @@ stl_click_handler_regions( (void)regions; (void)region_count; (void)winid; + (void)area_name; + (void)mrow; (void)mcol; (void)which_button; (void)mods; @@ -1778,12 +1806,13 @@ stl_click_handler_regions( * Returns TRUE if the function was called and handled the click. */ static int -stl_click_handler(win_T *wp, int mcol, int which_button, int mods) +stl_click_handler(win_T *wp, int mrow, int mcol, int which_button, int mods) { if (wp == NULL) return FALSE; return stl_click_handler_regions(wp->w_stl_click, wp->w_stl_click_count, - wp->w_id, mcol, which_button, mods); + wp->w_id, (char_u *)"statusline", + mrow, mcol, which_button, mods); } // dragwin is declared near the top of the file diff --git a/src/screen.c b/src/screen.c index eb7cc9cdd1..b9bde1d574 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1492,8 +1492,13 @@ win_redr_custom( stl_click_region_T **out_regions; int *out_count; int base_col; + int base_row; int click_count = 0; + // clicktab reflects the last iteration of the draw loop above, so + // the regions belong to the last drawn row. + base_row = row + stlh_cnt - 1; + if (wp != NULL) { out_regions = &wp->w_stl_click; @@ -1547,11 +1552,13 @@ win_redr_custom( // Close previous region if there was one. if (cur_funcname != NULL) { + regions[rcount].row = base_row; regions[rcount].col_start = region_start; regions[rcount].col_end = base_col + len; regions[rcount].funcname = vim_strsave(cur_funcname); regions[rcount].minwid = cur_minwid; + regions[rcount].tabnr = 0; rcount++; } @@ -1563,11 +1570,13 @@ win_redr_custom( // Close final region if it extends to the end. if (cur_funcname != NULL) { + regions[rcount].row = base_row; regions[rcount].col_start = region_start; regions[rcount].col_end = base_col + maxwidth; regions[rcount].funcname = vim_strsave(cur_funcname); regions[rcount].minwid = cur_minwid; + regions[rcount].tabnr = 0; rcount++; } diff --git a/src/structs.h b/src/structs.h index 648ceec905..e76c651d2e 100644 --- a/src/structs.h +++ b/src/structs.h @@ -1451,10 +1451,12 @@ typedef struct { * Per-window resolved click regions (screen column based). */ typedef struct { + int row; // screen row where region lives int col_start; // screen column where region starts int col_end; // screen column where region ends char_u *funcname; // function name (allocated copy) int minwid; // minwid value + int tabnr; // tab page number (tabpanel only, 0 otherwise) } stl_click_region_T; diff --git a/src/tabpanel.c b/src/tabpanel.c index 51929a8b1c..4138f9760a 100644 --- a/src/tabpanel.c +++ b/src/tabpanel.c @@ -17,6 +17,9 @@ static void do_by_tplmode(int tplmode, int col_start, int col_end, int *pcurtab_row, int *ptabpagenr); +static void tabpanel_free_click_regions(void); +static void tabpanel_append_click_regions(stl_clickrec_T *clicktab, + char_u *buf, int row, int col_start, int col_end, int tabnr); // set pcurtab_row. don't redraw tabpanel. #define TPLMODE_GET_CURTAB_ROW 0 @@ -135,6 +138,108 @@ tabpanel_leftcol(void) return tpl_align == ALIGN_RIGHT ? 0 : tabpanel_width(); } +/* + * Free previously resolved 'tabpanel' click regions. + */ + static void +tabpanel_free_click_regions(void) +{ + int n; + + if (tabpanel_stl_click != NULL) + { + for (n = 0; n < tabpanel_stl_click_count; n++) + vim_free(tabpanel_stl_click[n].funcname); + VIM_CLEAR(tabpanel_stl_click); + } + tabpanel_stl_click_count = 0; +} + +/* + * Convert click records produced by build_stl_str_hl() for one line of + * 'tabpanel' into screen-column based regions and append them to the global + * tabpanel_stl_click array. The caller keeps ownership of the funcname + * strings inside "clicktab" — this function makes its own copies. + */ + static void +tabpanel_append_click_regions( + stl_clickrec_T *clicktab, + char_u *buf, + int row, + int col_start, + int col_end, + int tabnr) +{ + int count = 0; + int n; + int base_col; + int acc_width = 0; + int max_w = col_end - col_start; + char_u *p; + char_u *cur_funcname = NULL; + int cur_minwid = 0; + int region_start_col; + stl_click_region_T *new_arr; + int limit; + + if (clicktab == NULL) + return; + + for (n = 0; clicktab[n].start != NULL; n++) + count++; + if (count == 0) + return; + + base_col = (tpl_align == ALIGN_RIGHT ? topframe->fr_width : 0) + col_start; + region_start_col = base_col; + + // Grow the global array to make room for up to "count" more regions + // (one close for each record plus a possible trailing region). + new_arr = vim_realloc(tabpanel_stl_click, + sizeof(stl_click_region_T) * (tabpanel_stl_click_count + count + 1)); + if (new_arr == NULL) + return; + tabpanel_stl_click = new_arr; + + p = buf; + for (n = 0; clicktab[n].start != NULL; n++) + { + acc_width += vim_strnsize(p, (int)(clicktab[n].start - p)); + p = clicktab[n].start; + limit = acc_width < max_w ? acc_width : max_w; + + if (cur_funcname != NULL) + { + stl_click_region_T *r = + &tabpanel_stl_click[tabpanel_stl_click_count]; + r->row = row; + r->col_start = region_start_col; + r->col_end = base_col + limit; + r->funcname = vim_strsave(cur_funcname); + r->minwid = cur_minwid; + r->tabnr = tabnr; + tabpanel_stl_click_count++; + } + + cur_funcname = clicktab[n].funcname; + cur_minwid = clicktab[n].minwid; + region_start_col = base_col + limit; + } + + // Close the final region if it extends to the end. + if (cur_funcname != NULL) + { + stl_click_region_T *r = &tabpanel_stl_click[tabpanel_stl_click_count]; + r->row = row; + r->col_start = region_start_col; + r->col_end = base_col + max_w; + r->funcname = vim_strsave(cur_funcname); + r->minwid = cur_minwid; + r->tabnr = tabnr; + tabpanel_stl_click_count++; + } +} + /* * draw the tabpanel. */ @@ -150,7 +255,13 @@ draw_tabpanel(void) int is_right = tpl_align == ALIGN_RIGHT; if (maxwidth == 0) + { + tabpanel_free_click_regions(); return; + } + + // Discard old click regions — they'll be rebuilt during redraw below. + tabpanel_free_click_regions(); // Reset got_int to avoid build_stl_str_hl() isn't evaluated. got_int = FALSE; @@ -495,6 +606,7 @@ do_by_tplmode( char_u buf[IOSIZE]; stl_hlrec_T *hltab; stl_hlrec_T *tabtab; + stl_clickrec_T *clicktab = NULL; if (args.maxrow <= row - args.offsetrow) break; @@ -508,13 +620,31 @@ do_by_tplmode( (args.cwp, buf, sizeof(buf), &usefmt, opt_name, opt_scope, TPL_FILLCHAR, args.col_end - args.col_start, &hltab, &tabtab, - NULL); + tplmode == TPLMODE_REDRAW ? &clicktab : NULL); args.prow = &row; args.pcol = &col; draw_tabpanel_with_highlight(tplmode, buf, hltab, &args); + // Record any %[FuncName] click regions for this line once + // the text has been drawn. Only visible rows participate. + if (tplmode == TPLMODE_REDRAW && clicktab != NULL) + { + int screen_row = row - args.offsetrow; + int m; + + if (screen_row >= 0 && screen_row < args.maxrow) + tabpanel_append_click_regions(clicktab, buf, + screen_row, args.col_start, args.col_end, + (int)v.vval.v_number); + // We took ownership of the click records — free the + // function names (matches the non-NULL clicktab path in + // build_stl_str_hl()). + for (m = 0; clicktab[m].start != NULL; m++) + vim_free(clicktab[m].funcname); + } + // Move to next line for %@ if (*usefmt != NUL) { diff --git a/src/testdir/test_tabpanel.vim b/src/testdir/test_tabpanel.vim index 4bb7f39eb0..7803a0eb4d 100644 --- a/src/testdir/test_tabpanel.vim +++ b/src/testdir/test_tabpanel.vim @@ -249,6 +249,76 @@ function Test_tabpanel_mouse() let &showtabline = save_showtabline endfunc +func g:TplClickTestFunc(info) + let g:tpl_click_info = a:info + return 0 +endfunc + +function Test_tabpanel_click_handler() + let save_mouse = &mouse + let save_stal = &showtabline + let save_stpl = &showtabpanel + let save_tpl = &tabpanel + let save_tplo = &tabpanelopt + set mouse=a + set showtabline=0 + set showtabpanel=2 + set tabpanelopt=columns:16 + tabnew + tabnew + + " Place two adjacent %[FuncName] regions on every tab label. + set tabpanel=%1[TplClickTestFunc][A]%[]%2[TplClickTestFunc][B]%[] + redraw! + + " Click on [A] region in the first tab label (row 1). + call test_setmouse(1, 2) + call feedkeys("\\", 'xt') + call assert_true(exists('g:tpl_click_info')) + call assert_equal('l', g:tpl_click_info.button) + call assert_equal(1, g:tpl_click_info.nclicks) + call assert_equal(1, g:tpl_click_info.minwid) + call assert_equal(0, g:tpl_click_info.winid) + call assert_equal('tabpanel', g:tpl_click_info.area) + call assert_equal(1, g:tpl_click_info.tabnr) + unlet! g:tpl_click_info + + " Click on [B] region in the second tab label (row 2). + call test_setmouse(2, 5) + call feedkeys("\\", 'xt') + call assert_true(exists('g:tpl_click_info')) + call assert_equal(2, g:tpl_click_info.minwid) + call assert_equal(2, g:tpl_click_info.tabnr) + unlet! g:tpl_click_info + + " Middle click on [A] in tab 3. + call test_setmouse(3, 2) + call feedkeys("\\", 'xt') + call assert_true(exists('g:tpl_click_info')) + call assert_equal('m', g:tpl_click_info.button) + call assert_equal(1, g:tpl_click_info.minwid) + call assert_equal(3, g:tpl_click_info.tabnr) + unlet! g:tpl_click_info + + " A click outside any region (but still in the panel) must not fire the + " callback, and should fall through to the normal tab selection. + set tabpanel=xxx%1[TplClickTestFunc][Y]%[] + redraw! + tabfirst + call test_setmouse(2, 1) + call feedkeys("\\", 'xt') + call assert_false(exists('g:tpl_click_info')) + call assert_equal(2, tabpagenr()) + + tabonly! + call s:reset() + let &tabpanel = save_tpl + let &tabpanelopt = save_tplo + let &showtabpanel = save_stpl + let &showtabline = save_stal + let &mouse = save_mouse +endfunc + function Test_tabpanel_drawing() CheckScreendump diff --git a/src/version.c b/src/version.c index b015465ed5..2361deb7de 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 360, /**/ 359, /**/ From 450895d86b7b22fb730c27dbd3dfe089b66c7d5c Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 16 Apr 2026 21:25:43 +0000 Subject: [PATCH 060/545] runtime(make): fix wrong highlighting with $ inside double quotes fixes: #19986 Co-authored-by: Anton Khirnov Signed-off-by: Christian Brabandt --- runtime/syntax/make.vim | 3 ++- runtime/syntax/testdir/dumps/make_01_00.dump | 8 ++++---- runtime/syntax/testdir/input/make_01.mak | 3 +++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/runtime/syntax/make.vim b/runtime/syntax/make.vim index 0e973237a5..530b41e0e9 100644 --- a/runtime/syntax/make.vim +++ b/runtime/syntax/make.vim @@ -9,6 +9,7 @@ " 2025 Oct 25 by Vim project: update makeTargetinDefine highlighting (#18570) " 2025 Dec 23 by Vim project: fix too greedy match (#18938) " 2025 Dec 23 by Vim project: wrong highlight with paranthesis inside quotes (#18818) +" 2026 Apr 17 by Vim project: wrong highlight $ inside quotes (#19986) " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -41,7 +42,7 @@ endif syn match makeIdent "\$\$\w*" syn match makeIdent "\$\$\$\$\w*" containedin=makeDefine syn match makeIdent "\$[^({]" -syn match makeIdent "\$\$[^({]" containedin=makeDefine +syn match makeIdent "\$\$[^({\"']" containedin=makeDefine if get(b:, 'make_flavor', s:make_flavor) == 'microsoft' syn region makeIdent start="\$(" end=")" contains=makeStatement,makeIdent,makeDString,makeSString syn region makeIdent start="\${" end="}" contains=makeStatement,makeIdent,makeDString,makeSString diff --git a/runtime/syntax/testdir/dumps/make_01_00.dump b/runtime/syntax/testdir/dumps/make_01_00.dump index d476fcd2ac..cf31afb973 100644 --- a/runtime/syntax/testdir/dumps/make_01_00.dump +++ b/runtime/syntax/testdir/dumps/make_01_00.dump @@ -14,7 +14,7 @@ @75 |d+0#00e0e07&|e|f|a|u|l|t|:| +0#0000000&@66 | +0#e000002&@7|$+0#00e0e07&|(|c+0#af5f00255&|a|l@1| +0#00e0e07&|s|a|y|,|"+0#e000002&|H|e|l@1|o| |(|w|o|r|l|d|)|!|"|)+0#00e0e07&| +0#0000000&@38 -|~+0#4040ff13&| @73 -|~| @73 -|~| @73 -| +0#0000000&@56|1|,|1| @10|A|l@1| +@75 +|f+0#00e0e07&|o@1|:| +0#0000000&@70 +| +0#e000002&@7|e|c|h|o| |"|b|a|r|$+0#00e0e07&@1|"+0#e000002&| |b|a|z| +0#0000000&@50 +@57|1|,|1| @10|A|l@1| diff --git a/runtime/syntax/testdir/input/make_01.mak b/runtime/syntax/testdir/input/make_01.mak index e97c13c0aa..082972a59c 100644 --- a/runtime/syntax/testdir/input/make_01.mak +++ b/runtime/syntax/testdir/input/make_01.mak @@ -14,3 +14,6 @@ endef default: $(call say,"Hello (world)!") + +foo: + echo "bar$$" baz From 6b26afea3b95fc9f5af9b0c0c6700007f8a6ae5e Mon Sep 17 00:00:00 2001 From: tecis <67809811+tecis@users.noreply.github.com> Date: Fri, 17 Apr 2026 14:44:51 +0000 Subject: [PATCH 061/545] runtime(django): Add missing statements and filters. Added the fallowing. djangoStatement: - querystring: Added in version Django 5.2. - lorem: Added in version Django 1.8. - verbatim: Added in version Django 1.10. djangoFilter: - force_escape: Added in version Django 1.8. - iriencode: Added in version Django 1.8. - json_script: Added in version 2.1. - truncatechars_html: Added in version 1.7. > According to current documentation the added keywords are supported [Django version 6](https://docs.djangoproject.com/en/6.0/ref/templates/builtins). related: #19994 Signed-off-by: tecis <67809811+tecis@users.noreply.github.com> Signed-off-by: Christian Brabandt --- runtime/syntax/django.vim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/syntax/django.vim b/runtime/syntax/django.vim index 353221af08..6fdf3cdcb2 100644 --- a/runtime/syntax/django.vim +++ b/runtime/syntax/django.vim @@ -34,6 +34,7 @@ syn keyword djangoStatement contained endblocktrans get_available_languages syn keyword djangoStatement contained get_current_language_bidi plural syn keyword djangoStatement contained translate blocktranslate endblocktranslate syn keyword djangoStatement contained partialdef endpartialdef partial +syn keyword djangoStatement contained querystring lorem verbatim " Django templete built-in filters syn keyword djangoFilter contained add addslashes capfirst center cut date @@ -48,6 +49,7 @@ syn keyword djangoFilter contained safe safeseq stringformat striptags syn keyword djangoFilter contained time timesince timeuntil title truncatechars syn keyword djangoFilter contained truncatewords truncatewords_html unordered_list upper urlencode syn keyword djangoFilter contained urlize urlizetrunc wordcount wordwrap yesno +syn keyword djangoFilter contained force_escape iriencode json_script truncatechars_html " Keywords to highlight within comments syn keyword djangoTodo contained TODO FIXME XXX From 8bcae3ca5d26218b2f9f3b4b33faedc2b4026d1b Mon Sep 17 00:00:00 2001 From: tecis <67809811+tecis@users.noreply.github.com> Date: Fri, 17 Apr 2026 14:46:01 +0000 Subject: [PATCH 062/545] runtime(django): Add missing djangoStatement `get_language_info`. Source: [get_language_info](https://docs.djangoproject.com/en/6.0/topics/i18n/translation/#get-language-info) . related: #19994 Signed-off-by: tecis <67809811+tecis@users.noreply.github.com> Signed-off-by: Christian Brabandt --- runtime/syntax/django.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/syntax/django.vim b/runtime/syntax/django.vim index 6fdf3cdcb2..11787c409b 100644 --- a/runtime/syntax/django.vim +++ b/runtime/syntax/django.vim @@ -31,7 +31,7 @@ syn keyword djangoStatement contained openbrace closebrace opencomment syn keyword djangoStatement contained closecomment widthratio url with endwith syn keyword djangoStatement contained get_current_language trans noop blocktrans syn keyword djangoStatement contained endblocktrans get_available_languages -syn keyword djangoStatement contained get_current_language_bidi plural +syn keyword djangoStatement contained get_current_language_bidi get_language_info plural syn keyword djangoStatement contained translate blocktranslate endblocktranslate syn keyword djangoStatement contained partialdef endpartialdef partial syn keyword djangoStatement contained querystring lorem verbatim From 68d3129a05f0c97682126e51dcd7122fc6c2fbba Mon Sep 17 00:00:00 2001 From: tecis <67809811+tecis@users.noreply.github.com> Date: Fri, 17 Apr 2026 14:47:22 +0000 Subject: [PATCH 063/545] runtime(django): Removed unsupported template statements and filters by current LTS. Removed djangoStatement: - ifequal: Depricated version 4.0. - endifequal: Depricated version 4.0. - ifnotequal: Depricated version 4.0. - endifnotequal: Depricated version 4.0. - parsed - trans: Renamed to `translate` in version 4.0. - blocktrans: Renamed to `blocktranslate` in version 4.0. - endblocktrans: Renamed to `endblocktranslate` in version 4.0. Removed djangoFilter: - fix_ampersands: Removed in version 1.8. - length_is: Removed in version 5.1. sources: - Current LTS is version [5.2](https://www.djangoproject.com/download/#supported-versions). - Documentation template builtins [5.2](https://docs.djangoproject.com/en/5.2/ref/templates/builtins/#truncatechars-html). - Documentation template builtins [6](https://docs.djangoproject.com/en/6.0/ref/templates/builtins). - [Django Deprecation Timeline](https://docs.djangoproject.com/en/6.0/internals/deprecation) closes: #19994 Signed-off-by: tecis <67809811+tecis@users.noreply.github.com> Signed-off-by: Christian Brabandt --- runtime/syntax/django.vim | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/runtime/syntax/django.vim b/runtime/syntax/django.vim index 11787c409b..5b677063c1 100644 --- a/runtime/syntax/django.vim +++ b/runtime/syntax/django.vim @@ -3,6 +3,7 @@ " Maintainer: Dave Hodder " Last Change: 2021 Nov 29 " 2026 Feb 12 by Vim Project add partial support #19386 +" 2026 Apr 17 by Vim Project Update to Django 5.2 version #19994 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -22,15 +23,13 @@ syn keyword djangoStatement contained autoescape csrf_token empty syn keyword djangoStatement contained and as block endblock by cycle debug else elif syn keyword djangoStatement contained extends filter endfilter firstof for syn keyword djangoStatement contained endfor if endif ifchanged endifchanged -syn keyword djangoStatement contained ifequal endifequal ifnotequal -syn keyword djangoStatement contained endifnotequal in include load not now or -syn keyword djangoStatement contained parsed regroup reversed spaceless -syn keyword djangoStatement contained endspaceless ssi templatetag openblock +syn keyword djangoStatement contained in include load not now +syn keyword djangoStatement contained regroup reversed spaceless +syn keyword djangoStatement contained endspaceless templatetag openblock syn keyword djangoStatement contained closeblock openvariable closevariable -syn keyword djangoStatement contained openbrace closebrace opencomment +syn keyword djangoStatement contained openbrace closebrace opencomment or syn keyword djangoStatement contained closecomment widthratio url with endwith -syn keyword djangoStatement contained get_current_language trans noop blocktrans -syn keyword djangoStatement contained endblocktrans get_available_languages +syn keyword djangoStatement contained get_current_language noop get_available_languages syn keyword djangoStatement contained get_current_language_bidi get_language_info plural syn keyword djangoStatement contained translate blocktranslate endblocktranslate syn keyword djangoStatement contained partialdef endpartialdef partial @@ -40,7 +39,7 @@ syn keyword djangoStatement contained querystring lorem verbatim syn keyword djangoFilter contained add addslashes capfirst center cut date syn keyword djangoFilter contained default default_if_none dictsort syn keyword djangoFilter contained dictsortreversed divisibleby escape escapejs -syn keyword djangoFilter contained filesizeformat first fix_ampersands +syn keyword djangoFilter contained filesizeformat first syn keyword djangoFilter contained floatformat get_digit join last length length_is syn keyword djangoFilter contained linebreaks linebreaksbr linenumbers ljust syn keyword djangoFilter contained lower make_list phone2numeric pluralize From e666597622a4f75e36375a7a8ff500799d4b5fa9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 17 Apr 2026 14:51:53 +0000 Subject: [PATCH 064/545] runtime(doc): make window option description a bit less vague Say explicitly that ":setlocal" sets the local value, while ":set" also sets the global value. related: #19993 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 62d7ef8dfd..2670535fb1 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 16 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -324,12 +324,12 @@ that was last closed are used again. If this buffer has been edited in this window, the values from back then are used. Otherwise the values from the last closed window where the buffer was edited last are used. -It's possible to set a local window option specifically for a type of buffer. -When you edit another buffer in the same window, you don't want to keep -using these local window options. Therefore Vim keeps a global value of the -local window options, which is used when editing another buffer. Each window -has its own copy of these values. Thus these are local to the window, but -global to all buffers in the window. With this you can do: > +":setlocal" can be used to set a local window option specifically for a type +of buffer. When you edit another buffer in the same window, you don't want to +keep using these local window options. Meanwhile ":set" also sets a global +value of a local window option, which is used when editing another buffer. +Each window has its own copy of these global values, making them local to the +window, but global to all buffers in the window. With this you can do: > :e one :set list :e two From 30d42855eb7e938883b7bef48477fad65f49107f Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 17 Apr 2026 15:02:39 +0000 Subject: [PATCH 065/545] runtime(vim9): remove extra escaping in Open Before 71fd19d7ac9e83bf63d7bad337f43cd830a5b5bd this function went through a `:!` command on all platforms, so it needed special escaping for `#` and others. After that commit it doesn't go through that path on unix platforms. Then with 48581f2ba96550f5499cc322647b2ff1df5ad4ed this escaping was re-added on unix and it's needs since it goes through `sh -c`, but it should not have the extra escaping specific to `:!`. Specifically my original broken command is: ``` PATH=/usr/bin:/bin VIMRUNTIME=~/dev/vim/runtime ./src/vim -u NONE -c 'call dist#vim9#Open("https://github.com/keith/dotfiles/blob/7bce9f5c697df6a549cf97bf5606d8b639e5bf5a/vimrc#L19")' ``` Where the `#L19` ends up being opened as `%5C#L19`. But I verified this case still works as well: ``` PATH=/usr/bin:/bin VIMRUNTIME=~/dev/vim/runtime ./src/vim -u NONE -c 'call dist#vim9#Open("foo bar.txt")' ``` Which is what would otherwise break if we weren't doing any shell escaping on unix. closes: #19996 Signed-off-by: Keith Smiley Signed-off-by: Christian Brabandt --- runtime/autoload/dist/vim9.vim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/dist/vim9.vim b/runtime/autoload/dist/vim9.vim index d824673912..4d8785d4ae 100644 --- a/runtime/autoload/dist/vim9.vim +++ b/runtime/autoload/dist/vim9.vim @@ -3,7 +3,7 @@ vim9script # Vim runtime support library # # Maintainer: The Vim Project -# Last Change: 2026 Apr 06 +# Last Change: 2026 Apr 17 export def IsSafeExecutable(filetype: string, executable: string): bool if empty(exepath(executable)) @@ -139,7 +139,11 @@ export def Open(file: string) setlocal shell& defer setbufvar('%', '&shell', shell) endif - Launch($"{Viewer()} {shellescape(file, 1)}") + if has('unix') && !has('win32unix') && !exists('$WSL_DISTRO_NAME') + Launch($"{Viewer()} {shellescape(file)}") + else + Launch($"{Viewer()} {shellescape(file, 1)}") + endif enddef # Uncomment this line to check for compilation errors early From 01d9a1951fe4849559a87bced5a6d67813c57190 Mon Sep 17 00:00:00 2001 From: Zdenek Dohnal Date: Fri, 17 Apr 2026 15:55:53 +0000 Subject: [PATCH 066/545] patch 9.2.0361: tests: no tests for ch_listen() with IPs Problem: tests: no tests for ch_listen() with IPs Solution: Add tests that this is disallowed (Zdenek Dohnal) The functionality was removed, so the test is to make sure it fails as expected. closes: #19997 Signed-off-by: Zdenek Dohnal Signed-off-by: Christian Brabandt --- src/testdir/test_channel.vim | 11 +++++++++-- src/version.c | 2 ++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index 9110a141c7..23f8a0fc3e 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -2799,8 +2799,8 @@ func Test_listen() call assert_match('127.0.0.1:', g:server_received_addr) endfunc -func Test_listen_invalid_port() - call ch_log('Test_listen_invalid_port()') +func Test_listen_invalid_argument() + call ch_log('Test_listen_invalid_argument()') " missing port call assert_fails("call ch_listen('')", 'E475:') @@ -2816,6 +2816,13 @@ func Test_listen_invalid_port() " port number negative call assert_fails("call ch_listen('-1')", 'E475:') + + " make sure we don't accept hostname/IP address + call assert_fails("call ch_listen('127.0.0.1:4500')", 'E475:') + call assert_fails("call ch_listen('127.0.0.1')", 'E475:') + call assert_fails("call ch_listen('localhost:4500')", 'E475:') + call assert_fails("call ch_listen('localhost')", 'E475:') + call assert_fails("call ch_listen('[::1]')", 'E475:') endfunc func Test_listen_info_no_hostname() diff --git a/src/version.c b/src/version.c index 2361deb7de..4a4b69288b 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 361, /**/ 360, /**/ From 336533b3555aaa432a33507e445dfac42809006a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 16:16:15 +0000 Subject: [PATCH 067/545] CI: Bump the github-actions group across 2 directories with 4 updates Bumps the github-actions group with 3 updates in the / directory: [msys2/setup-msys2](https://github.com/msys2/setup-msys2), [actions/cache](https://github.com/actions/cache) and [github/codeql-action](https://github.com/github/codeql-action). Bumps the github-actions group with 1 update in the /.github/actions/test_artifacts directory: [actions/github-script](https://github.com/actions/github-script). Updates `msys2/setup-msys2` from 2 to 2.31.0 - [Release notes](https://github.com/msys2/setup-msys2/releases) - [Changelog](https://github.com/msys2/setup-msys2/blob/main/CHANGELOG.md) - [Commits](https://github.com/msys2/setup-msys2/compare/v2...v2.31.0) Updates `actions/cache` from 5 to 5.0.4 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v5...v5.0.4) Updates `github/codeql-action` from 4 to 4.35.1 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v4...v4.35.1) Updates `actions/github-script` from 8 to 9 - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v8...v9) --- updated-dependencies: - dependency-name: msys2/setup-msys2 dependency-version: 2.31.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: actions/cache dependency-version: 5.0.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action dependency-version: 4.35.1 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions - dependency-name: actions/github-script dependency-version: '9' dependency-type: direct:production update-type: version-update:semver-major dependency-group: github-actions ... closes: #20001 Signed-off-by: dependabot[bot] Signed-off-by: Christian Brabandt --- .github/actions/test_artifacts/action.yml | 2 +- .github/workflows/ci.yml | 4 ++-- .github/workflows/codeql-analysis.yml | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/actions/test_artifacts/action.yml b/.github/actions/test_artifacts/action.yml index eb4313de81..04b617579c 100644 --- a/.github/actions/test_artifacts/action.yml +++ b/.github/actions/test_artifacts/action.yml @@ -4,7 +4,7 @@ runs: using: "composite" steps: - name: Collect matrix properties for naming - uses: actions/github-script@v8 + uses: actions/github-script@v9 id: matrix-props env: MATRIX_PROPS: ${{ toJSON(matrix) }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2a9003a19..1f5f65430f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -435,7 +435,7 @@ jobs: echo "SODIUM_DIR=${SODIUM_DIR}" >> $GITHUB_ENV echo "GETTEXT_PATH=D:\gettext${{ matrix.arch == 'x64' && '64' || '32' }}" >> $GITHUB_ENV - - uses: msys2/setup-msys2@v2 + - uses: msys2/setup-msys2@v2.31.0 if: matrix.toolchain == 'mingw' with: update: true @@ -459,7 +459,7 @@ jobs: echo %GETTEXT64_URL%>> urls.txt - name: Cache downloaded files - uses: actions/cache@v5 + uses: actions/cache@v5.0.4 with: path: downloads key: ${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('urls.txt') }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3364adf340..ed78c8ff2c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -48,7 +48,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4 + uses: github/codeql-action/init@v4.35.1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -59,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v4 + uses: github/codeql-action/autobuild@v4.35.1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -73,4 +73,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4 + uses: github/codeql-action/analyze@v4.35.1 From 0e31fb024c846e36bb0d26d01ff179a0d1b3eae4 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Sun, 19 Apr 2026 18:32:55 +0000 Subject: [PATCH 068/545] patch 9.2.0362: division by zero with smoothscroll and small windows Problem: Resizing a smoothscrolled wrapped window to its textoff width with 'showbreak' can leave wrapped continuation lines with zero text width. win_lbr_chartabsize() still runs the partial max_head_vcol calculation in that state and divides by width2, crashing during redraw. Solution: Skip that partial head calculation when the wrapped continuation width is zero, matching the other width2 guards in charset.c (Jaehwang Jung) closes: #20012 AI-assisted: Codex Signed-off-by: Jaehwang Jung Signed-off-by: Christian Brabandt --- src/charset.c | 3 ++- src/testdir/test_scroll_opt.vim | 38 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/charset.c b/src/charset.c index 8ad768d354..bf7c59620f 100644 --- a/src/charset.c +++ b/src/charset.c @@ -1468,7 +1468,8 @@ win_lbr_chartabsize( if (max_head_vcol == 0 || vcol + size + added < max_head_vcol) head += cnt * head_mid; - else if (max_head_vcol > vcol + head_prev + prev_rem) + else if (width2 > 0 + && max_head_vcol > vcol + head_prev + prev_rem) head += (max_head_vcol - (vcol + head_prev + prev_rem) + width2 - 1) / width2 * head_mid; else if (max_head_vcol < 0) diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim index 390a8b59a2..6ece601f7a 100644 --- a/src/testdir/test_scroll_opt.vim +++ b/src/testdir/test_scroll_opt.vim @@ -2090,4 +2090,42 @@ func Test_scrolloffpad_with_folds() call StopVimInTerminal(buf) let &termwinsize = save_termwinsize endfunc + +" Resizing to "textoff" after 'smoothscroll' skips part of a wrapped line must +" not crash. +func Test_smoothscroll_textoff_showbreak() + CheckOption smoothscroll + + let donefile = 'XTest_crash_textoff_showbreak_done' + defer delete(donefile) + let lines =<< trim END + set noswapfile + + set scrolloff=0 + set lines=12 columns=40 + + call setline(1, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa') + set number wrap smoothscroll showbreak=> + vsplit + + let textoff = getwininfo(win_getid())[0].textoff + execute "normal! 0\" + redraw + execute 'vertical resize' textoff + redraw + call writefile(['done'], 'XTest_crash_textoff_showbreak_done') + END + call writefile(lines, 'XTest_crash_textoff_showbreak', 'D') + + let buf = 0 + let buf = term_start([GetVimProg(), '--clean'], #{term_rows: 24, term_cols: 80}) + call TermWait(buf, 200) + call term_sendkeys(buf, ":source XTest_crash_textoff_showbreak\") + call WaitForAssert({-> assert_true(filereadable(donefile))}) + let status = term_getstatus(buf) + call assert_equal('running', status) + call assert_true(filereadable(donefile)) + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 4a4b69288b..7d09dfddd1 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 362, /**/ 361, /**/ From f2e920321c2a8c41497e05b020f98feaa927151f Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Sun, 19 Apr 2026 18:39:46 +0000 Subject: [PATCH 069/545] patch 9.2.0363: Vim9: variable shadowed by script-local function Problem: Vim9: variable shadowed by script-local function (Mao-Yining) Solution: Set is_global flag to true in find_func() (Furkan Sahin) fixes: #20009 closes: #20011 Signed-off-by: Furkan Sahin Signed-off-by: Christian Brabandt --- src/testdir/test_vim9_script.vim | 86 ++++++++++++++++++++++++++++++++ src/version.c | 2 + src/vim9expr.c | 2 +- 3 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 30c2589ef2..262f47db2f 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -5847,4 +5847,90 @@ def Test_call_stack_string() g:StopVimInTerminal(buf) enddef +def Test_g_variable_not_shadowed_by_function() + var lines =<< trim END + vim9script + g:F = 1 + def F() + return 2 + enddef + def Check() + var x = g:F + assert_equal(1, x) + enddef + Check() + END + v9.CheckScriptSuccess(lines) +enddef + +" Test g: variable shadowed by script-local function in various expression contexts. +def Test_g_variable_shadow_multi_context() + var lines =<< trim END + vim9script + g:F = 1 + def F(): number + return 2 + enddef + + # 1. Assignment to local + def AssignCheck() + var x = g:F + assert_equal(1, x) + enddef + + # 2. Function argument + def ArgCheck(val: number): number + return val * 10 + enddef + def CallArg() + assert_equal(10, ArgCheck(g:F)) + enddef + + # 3. Return value + def ReturnCheck(): number + return g:F + enddef + + # 4. Binary operation + def BinaryCheck(): number + return g:F + 5 + enddef + + # 5. List literal element + def ListCheck(): list + return [g:F, 2, 3] + enddef + + # 6. Dict literal value + def DictCheck(): dict + return {val: g:F} + enddef + + # 7. Conditional expression + def CondCheck(): string + return g:F == 1 ? 'yes' : 'no' + enddef + + # 8. Loop condition (only evaluated once but still loads) + def LoopCheck(): number + var i = 0 + while i < g:F + i += 1 + endwhile + return i + enddef + + AssignCheck() + CallArg() + assert_equal(1, ReturnCheck()) + assert_equal(6, BinaryCheck()) + assert_equal([1, 2, 3], ListCheck()) + assert_equal({val: 1}, DictCheck()) + assert_equal('yes', CondCheck()) + assert_equal(1, LoopCheck()) + END + v9.CheckScriptSuccess(lines) + unlet g:F +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 7d09dfddd1..b85eb55b0c 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 363, /**/ 362, /**/ diff --git a/src/vim9expr.c b/src/vim9expr.c index 0344976c9d..a6c4f1ea10 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -946,7 +946,7 @@ compile_load( case 'g': if (vim_strchr(name, AUTOLOAD_CHAR) == NULL) { if (is_expr && ASCII_ISUPPER(*name) - && (find_func(name, FALSE) != NULL + && (find_func(name, TRUE) != NULL || gfatab.gfat_args.ga_len > 0)) res = generate_funcref(cctx, name, &gfatab, TRUE); From 618a327ce6f6c7a47bdbf2a4f5d1c329d97e6037 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 19 Apr 2026 19:58:56 +0000 Subject: [PATCH 070/545] patch 9.2.0364: tests: test_smoothscroll_textoff_showbreak() fails Problem: tests: test_smoothscroll_textoff_showbreak() fails (after v9.2.0363) Solution: Add missing CheckRunVimInTerminal related: #20011 Signed-off-by: Christian Brabandt --- src/testdir/test_scroll_opt.vim | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim index 6ece601f7a..4d0e44c2ea 100644 --- a/src/testdir/test_scroll_opt.vim +++ b/src/testdir/test_scroll_opt.vim @@ -2095,6 +2095,7 @@ endfunc " not crash. func Test_smoothscroll_textoff_showbreak() CheckOption smoothscroll + CheckRunVimInTerminal let donefile = 'XTest_crash_textoff_showbreak_done' defer delete(donefile) diff --git a/src/version.c b/src/version.c index b85eb55b0c..ebaf924cde 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 364, /**/ 363, /**/ From 1966a1c8963f59c00a9f25d129bec90366205e1b Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Sun, 19 Apr 2026 20:10:20 +0000 Subject: [PATCH 071/545] patch 9.2.0365: using int as bool Problem: using int as bool Solution: refactor: use bool type for internal flags in buf_T (Hirohito Higashi) Change the type of 23 internal state flag fields in buf_T from int to bool for improved type clarity and code readability. These fields are pure boolean flags that are never accessed via the option system's varp (which uses *(int *)varp = value), never compared with int fields holding non-0/1 values, and never use tristate values. Converted fields: - State flags: b_dev_valid, b_saving, b_mod_set, b_new_change, b_marks_read, b_modified_was_set, b_did_filetype, b_keep_filetype, b_au_did_filetype, b_u_synced, b_scanned, b_p_initialized - Characteristic flags: b_has_textprop, b_may_swap, b_did_warn, b_help, b_spell, b_shortname, b_has_sign_column, b_netbeans_file, b_was_netbeans_file, b_write_to_channel, b_diff_failed All TRUE/FALSE assignments to these fields have been updated to true/false accordingly. The type of temporary save variables (e.g. help_save in tag.c) has also been adjusted to bool. Option value fields (b_p_XXX) are kept as int because they are accessed via the option system and some use tristate (-1) semantics. Fields compared with int option values (b_start_eof, b_start_eol, b_start_bomb) are also kept as int to preserve comparison integrity. closes: #20020 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/autocmd.c | 6 +++--- src/buffer.c | 28 ++++++++++++++-------------- src/bufwrite.c | 10 +++++----- src/change.c | 8 ++++---- src/channel.c | 10 +++++----- src/diff.c | 2 +- src/drawscreen.c | 8 ++++---- src/ex_cmds.c | 2 +- src/ex_docmd.c | 2 +- src/fileio.c | 6 +++--- src/help.c | 2 +- src/insexpand.c | 4 ++-- src/memline.c | 18 +++++++++--------- src/netbeans.c | 12 ++++++------ src/option.c | 10 +++++----- src/optionstr.c | 2 +- src/popupwin.c | 2 +- src/quickfix.c | 4 ++-- src/sign.c | 2 +- src/spell.c | 2 +- src/structs.h | 46 +++++++++++++++++++++++----------------------- src/tag.c | 6 +++--- src/terminal.c | 2 +- src/textprop.c | 4 ++-- src/undo.c | 28 ++++++++++++++-------------- src/version.c | 2 ++ src/viminfo.c | 2 +- 27 files changed, 116 insertions(+), 114 deletions(-) diff --git a/src/autocmd.c b/src/autocmd.c index 8fe51dc92f..b27297f799 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -2366,7 +2366,7 @@ apply_autocmds_group( // Remember that FileType was triggered. Used for did_filetype(). if (event == EVENT_FILETYPE) - curbuf->b_did_filetype = TRUE; + curbuf->b_did_filetype = true; tail = gettail(fname); @@ -2475,7 +2475,7 @@ apply_autocmds_group( restore_search_patterns(); if (did_save_redobuff) restoreRedobuff(&save_redo); - curbuf->b_did_filetype = FALSE; + curbuf->b_did_filetype = false; while (au_pending_free_buf != NULL) { buf_T *b = au_pending_free_buf->b_next; @@ -2517,7 +2517,7 @@ apply_autocmds_group( aubuflocal_remove(buf); if (retval == OK && event == EVENT_FILETYPE) - curbuf->b_au_did_filetype = TRUE; + curbuf->b_au_did_filetype = true; return retval; } diff --git a/src/buffer.c b/src/buffer.c index 20f8dcc454..d51cf31a2a 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -259,7 +259,7 @@ open_buffer( // The autocommands in readfile() may change the buffer, but only AFTER // reading the file. set_bufref(&old_curbuf, curbuf); - curbuf->b_modified_was_set = FALSE; + curbuf->b_modified_was_set = false; // mark cursor position as being invalid curwin->w_valid = 0; @@ -278,7 +278,7 @@ open_buffer( { int old_msg_silent = msg_silent; #ifdef UNIX - int save_bin = curbuf->b_p_bin; + int save_bin = curbuf->b_p_bin; int perm; #endif #ifdef FEAT_NETBEANS_INTG @@ -824,7 +824,7 @@ close_buffer( buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED; // Init the options when loaded again. - buf->b_p_initialized = FALSE; + buf->b_p_initialized = false; } buf_clear_file(buf); if (del_buf) @@ -847,7 +847,7 @@ buf_clear_file(buf_T *buf) { buf->b_ml.ml_line_count = 1; unchanged(buf, TRUE, TRUE); - buf->b_shortname = FALSE; + buf->b_shortname = false; buf->b_p_eof = FALSE; buf->b_start_eof = FALSE; buf->b_p_eol = TRUE; @@ -2014,7 +2014,7 @@ enter_buffer(buf_T *buf) // ":ball" used in an autocommand. If there already is a filetype we // might prefer to keep it. if (*curbuf->b_p_ft == NUL) - curbuf->b_did_filetype = FALSE; + curbuf->b_did_filetype = false; open_buffer(FALSE, NULL, 0); } @@ -2294,7 +2294,7 @@ buflist_new( free_buffer_stuff(buf, FALSE); // delete local variables et al. // Init the options. - buf->b_p_initialized = FALSE; + buf->b_p_initialized = false; buf_copy_options(buf, BCO_ENTER); #ifdef FEAT_KEYMAP @@ -2374,15 +2374,15 @@ buflist_new( buf->b_fname = buf->b_sfname; #ifdef UNIX if (st.st_dev == (dev_T)-1) - buf->b_dev_valid = FALSE; + buf->b_dev_valid = false; else { - buf->b_dev_valid = TRUE; + buf->b_dev_valid = true; buf->b_dev = st.st_dev; buf->b_ino = st.st_ino; } #endif - buf->b_u_synced = TRUE; + buf->b_u_synced = true; buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED; if (flags & BLN_DUMMY) buf->b_flags |= BF_DUMMY; @@ -3671,16 +3671,16 @@ setfname( buf->b_fname = buf->b_sfname; #ifdef UNIX if (st.st_dev == (dev_T)-1) - buf->b_dev_valid = FALSE; + buf->b_dev_valid = false; else { - buf->b_dev_valid = TRUE; + buf->b_dev_valid = true; buf->b_dev = st.st_dev; buf->b_ino = st.st_ino; } #endif - buf->b_shortname = FALSE; + buf->b_shortname = false; buf_name_changed(buf); return OK; @@ -3894,12 +3894,12 @@ buf_setino(buf_T *buf) if (buf->b_fname != NULL && mch_stat((char *)buf->b_fname, &st) >= 0) { - buf->b_dev_valid = TRUE; + buf->b_dev_valid = true; buf->b_dev = st.st_dev; buf->b_ino = st.st_ino; } else - buf->b_dev_valid = FALSE; + buf->b_dev_valid = false; } /* diff --git a/src/bufwrite.c b/src/bufwrite.c index 4d00421d97..488ed94d92 100644 --- a/src/bufwrite.c +++ b/src/bufwrite.c @@ -1160,7 +1160,7 @@ buf_write( got_int = FALSE; // Mark the buffer as 'being saved' to prevent changed buffer warnings - buf->b_saving = TRUE; + buf->b_saving = true; // If we are not appending or filtering, the file exists, and the // 'writebackup', 'backup' or 'patchmode' option is set, need a backup. @@ -1407,13 +1407,13 @@ buf_write( // may try again with 'shortname' set if (!(buf->b_shortname || buf->b_p_sn)) { - buf->b_shortname = TRUE; + buf->b_shortname = true; did_set_shortname = TRUE; continue; } // setting shortname didn't help if (did_set_shortname) - buf->b_shortname = FALSE; + buf->b_shortname = false; break; } #endif @@ -2543,7 +2543,7 @@ buf_write( nofail: // Done saving, we accept changed buffer warnings again - buf->b_saving = FALSE; + buf->b_saving = false; vim_free(backup); if (buffer != smallbuf) @@ -2661,7 +2661,7 @@ buf_write( #ifdef FEAT_VIMINFO // Make sure marks will be written out to the viminfo file later, even when // the file is new. - curbuf->b_marks_read = TRUE; + curbuf->b_marks_read = true; #endif got_int |= prev_got_int; diff --git a/src/change.c b/src/change.c index 1fdee65445..c9e27c1949 100644 --- a/src/change.c +++ b/src/change.c @@ -61,7 +61,7 @@ change_warning(int col) out_flush(); ui_delay(1002L, TRUE); // give the user time to think about it } - curbuf->b_did_warn = TRUE; + curbuf->b_did_warn = true; redraw_cmdline = FALSE; // don't redraw and erase the message if (msg_row < Rows - 1) showmode(); @@ -702,7 +702,7 @@ changed_common( // This is the first of a new sequence of undo-able changes // and it's at some distance of the last change. Use a new // position in the changelist. - curbuf->b_new_change = FALSE; + curbuf->b_new_change = false; if (curbuf->b_changelistlen == JUMPLISTSIZE) { @@ -897,7 +897,7 @@ changedOneline(buf_T *buf, linenr_T lnum) else { // set the area that must be redisplayed to one line - buf->b_mod_set = TRUE; + buf->b_mod_set = true; buf->b_mod_top = lnum; buf->b_mod_bot = lnum + 1; buf->b_mod_xlines = 0; @@ -1035,7 +1035,7 @@ changed_lines_buf( else { // set the area that must be redisplayed - buf->b_mod_set = TRUE; + buf->b_mod_set = true; buf->b_mod_top = lnum; buf->b_mod_bot = lnume + xtra; buf->b_mod_xlines = xtra; diff --git a/src/channel.c b/src/channel.c index edf6d84c7a..d58c697081 100644 --- a/src/channel.c +++ b/src/channel.c @@ -1754,7 +1754,7 @@ channel_set_job(channel_T *channel, job_T *job, jobopt_T *options) { // Special mode: send last-but-one line when appending a line // to the buffer. - in_part->ch_bufref.br_buf->b_write_to_channel = TRUE; + in_part->ch_bufref.br_buf->b_write_to_channel = true; in_part->ch_buf_append = TRUE; in_part->ch_buf_top = in_part->ch_bufref.br_buf->b_ml.ml_line_count + 1; @@ -2054,7 +2054,7 @@ channel_write_new_lines(buf_T *buf) } } if (!found_one) - buf->b_write_to_channel = FALSE; + buf->b_write_to_channel = false; } /* @@ -3069,7 +3069,7 @@ append_to_buffer( { aco_save_T aco; linenr_T lnum = buffer->b_ml.ml_line_count; - int save_write_to = buffer->b_write_to_channel; + bool save_write_to = buffer->b_write_to_channel; chanpart_T *ch_part = &channel->ch_part[part]; int save_p_ma = buffer->b_p_ma; int empty = (buffer->b_ml.ml_flags & ML_EMPTY) ? 1 : 0; @@ -3089,7 +3089,7 @@ append_to_buffer( if (save_write_to) { --lnum; - buffer->b_write_to_channel = FALSE; + buffer->b_write_to_channel = false; } // Append to the buffer @@ -3168,7 +3168,7 @@ append_to_buffer( // Find channels reading from this buffer and adjust their // next-to-read line number. - buffer->b_write_to_channel = TRUE; + buffer->b_write_to_channel = true; FOR_ALL_CHANNELS(ch) { chanpart_T *in_part = &ch->ch_part[PART_IN]; diff --git a/src/diff.c b/src/diff.c index 9ac64d7f6a..2dff27ba28 100644 --- a/src/diff.c +++ b/src/diff.c @@ -820,7 +820,7 @@ diff_write_buffer(buf_T *buf, diffin_T *din, linenr_T start, linenr_T end) // Allocating memory failed. This can happen, because we try to read // the whole buffer text into memory. Set the failed flag, the diff // will be retried with external diff. The flag is never reset. - buf->b_diff_failed = TRUE; + buf->b_diff_failed = true; if (p_verbose > 0) { verbose_enter(); diff --git a/src/drawscreen.c b/src/drawscreen.c index 0a745cd885..d815c20da9 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -384,7 +384,7 @@ update_screen(int type_arg) // Reset b_mod_set flags. Going through all windows is probably faster // than going through all buffers (there could be many buffers). FOR_ALL_WINDOWS(wp) - wp->w_buffer->b_mod_set = FALSE; + wp->w_buffer->b_mod_set = false; #ifdef FEAT_PROP_POPUP // Display popup windows on top of the windows and command line. @@ -2846,13 +2846,13 @@ win_update(win_T *wp) if (wp->w_redr_type != 0) { // Don't update for changes in buffer again. - i = curbuf->b_mod_set; - curbuf->b_mod_set = FALSE; + bool b = curbuf->b_mod_set; + curbuf->b_mod_set = false; j = curbuf->b_mod_xlines; curbuf->b_mod_xlines = 0; curs_columns(TRUE); win_update(curwin); - curbuf->b_mod_set = i; + curbuf->b_mod_set = b; curbuf->b_mod_xlines = j; } // Other windows might have w_redr_type raised in update_topline(). diff --git a/src/ex_cmds.c b/src/ex_cmds.c index efb0c66dc0..18b156c9ce 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -3205,7 +3205,7 @@ do_ecmd( // Since we are starting to edit a file, consider the filetype to be // unset. Helps for when an autocommand changes files and expects syntax // highlighting to work in the other file. - curbuf->b_did_filetype = FALSE; + curbuf->b_did_filetype = false; /* * other_file oldbuf diff --git a/src/ex_docmd.c b/src/ex_docmd.c index ade93579dc..bd8b4991dc 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -10368,7 +10368,7 @@ ex_setfiletype(exarg_T *eap) set_option_value_give_err((char_u *)"filetype", 0L, arg, OPT_LOCAL); if (arg != eap->arg) - curbuf->b_did_filetype = FALSE; + curbuf->b_did_filetype = false; } static void diff --git a/src/fileio.c b/src/fileio.c index 975dc310e0..1906a4316a 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -236,7 +236,7 @@ readfile( #endif size_t fnamelen = 0; - curbuf->b_au_did_filetype = FALSE; // reset before triggering any autocommands + curbuf->b_au_did_filetype = false; // reset before triggering any autocommands curbuf->b_no_eol_lnum = 0; // in case it was set by the previous read @@ -4664,7 +4664,7 @@ buf_reload(buf_T *buf, int orig_mode, int reload_options) int old_msg_silent = msg_silent; curbuf->b_flags |= BF_CHECK_RO; // check for RO again - curbuf->b_keep_filetype = TRUE; // don't detect 'filetype' + curbuf->b_keep_filetype = true; // don't detect 'filetype' if (shortmess(SHM_FILEINFO)) msg_silent = 1; @@ -4721,7 +4721,7 @@ buf_reload(buf_T *buf, int orig_mode, int reload_options) curwin->w_cursor = old_cursor; check_cursor(); update_topline(); - curbuf->b_keep_filetype = FALSE; + curbuf->b_keep_filetype = false; #ifdef FEAT_FOLDING { win_T *wp; diff --git a/src/help.c b/src/help.c index 7bb6a0d7dc..c8155054d1 100644 --- a/src/help.c +++ b/src/help.c @@ -634,7 +634,7 @@ prepare_help_buffer(void) { char_u *p; - curbuf->b_help = TRUE; + curbuf->b_help = true; #ifdef FEAT_QUICKFIX set_string_option_direct((char_u *)"buftype", -1, (char_u *)"help", OPT_FREE|OPT_LOCAL, 0); diff --git a/src/insexpand.c b/src/insexpand.c index abdcafa7bf..2627e4bc3e 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -5402,7 +5402,7 @@ ins_compl_get_exp(pos_T *ini) buf_T *buf; FOR_ALL_BUFFERS(buf) - buf->b_scanned = 0; + buf->b_scanned = false; if (!st_cleared) { CLEAR_FIELD(st); @@ -5526,7 +5526,7 @@ ins_compl_get_exp(pos_T *ini) { // Mark a buffer scanned when it has been scanned completely if (buf_valid(st.ins_buf) && (type == 0 || type == CTRL_X_PATH_PATTERNS)) - st.ins_buf->b_scanned = TRUE; + st.ins_buf->b_scanned = true; compl_started = FALSE; } diff --git a/src/memline.c b/src/memline.c index c15946a6eb..8152a03a50 100644 --- a/src/memline.c +++ b/src/memline.c @@ -305,9 +305,9 @@ ml_open(buf_T *buf) * When 'updatecount' is non-zero swap file may be opened later. */ if (p_uc && buf->b_p_swf) - buf->b_may_swap = TRUE; + buf->b_may_swap = true; else - buf->b_may_swap = FALSE; + buf->b_may_swap = false; /* * Open the memfile. No swap file is created yet. @@ -793,7 +793,7 @@ ml_open_file(buf_T *buf) fname = vim_tempname('s', FALSE); if (fname != NULL) (void)mf_open_file(mfp, fname); // consumes fname! - buf->b_may_swap = FALSE; + buf->b_may_swap = false; return; } #endif @@ -851,7 +851,7 @@ ml_open_file(buf_T *buf) } // don't try to open a swap file again - buf->b_may_swap = FALSE; + buf->b_may_swap = false; } /* @@ -2442,9 +2442,9 @@ recov_file_names(char_u **names, char_u *path, int prepend_dot) char_u *p; int i; #ifndef MSWIN - int shortname = curbuf->b_shortname; + bool shortname = curbuf->b_shortname; - curbuf->b_shortname = FALSE; + curbuf->b_shortname = false; #endif string_T ret; @@ -2493,7 +2493,7 @@ recov_file_names(char_u **names, char_u *path, int prepend_dot) /* * Also try with 'shortname' set, in case the file is on a DOS filesystem. */ - curbuf->b_shortname = TRUE; + curbuf->b_shortname = true; # ifdef VMS names[num_names] = modname(path, (char_u *)"_sw%", FALSE); # else @@ -5108,7 +5108,7 @@ findswapname( vim_free(fname2); if (same) { - buf->b_shortname = TRUE; + buf->b_shortname = true; vim_free(fname); fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name); @@ -5178,7 +5178,7 @@ findswapname( fname[n - 1] = 'p'; if (r >= 0) // "file.swx" seems to exist { - buf->b_shortname = TRUE; + buf->b_shortname = true; vim_free(fname); fname = makeswapname(buf_fname, buf->b_ffname, buf, dir_name); diff --git a/src/netbeans.c b/src/netbeans.c index 993e52bf3d..c7cb786ae8 100644 --- a/src/netbeans.c +++ b/src/netbeans.c @@ -486,7 +486,7 @@ nb_parse_cmd(char_u *cmd) buf_T *buf; FOR_ALL_BUFFERS(buf) - buf->b_has_sign_column = FALSE; + buf->b_has_sign_column = false; // The IDE is breaking the connection. netbeans_close(); @@ -595,8 +595,8 @@ nb_free(void) vim_free(buf.signmap); if (buf.bufp != NULL && buf_valid(buf.bufp)) { - buf.bufp->b_netbeans_file = FALSE; - buf.bufp->b_was_netbeans_file = FALSE; + buf.bufp->b_netbeans_file = false; + buf.bufp->b_was_netbeans_file = false; } } VIM_CLEAR(buf_list); @@ -2211,11 +2211,11 @@ nb_do_cmd( } if (*args == 'T') { - buf->bufp->b_netbeans_file = TRUE; - buf->bufp->b_was_netbeans_file = TRUE; + buf->bufp->b_netbeans_file = true; + buf->bufp->b_was_netbeans_file = true; } else - buf->bufp->b_netbeans_file = FALSE; + buf->bufp->b_netbeans_file = false; // ===================================================================== } else if (streq((char *)cmd, "specialKeys")) diff --git a/src/option.c b/src/option.c index dc6f0966d5..5a580525ee 100644 --- a/src/option.c +++ b/src/option.c @@ -696,7 +696,7 @@ set_init_1(int clean_arg) set_option_value_give_err((char_u *)"bg", 0L, (char_u *)"dark", 0); #endif - curbuf->b_p_initialized = TRUE; + curbuf->b_p_initialized = true; curbuf->b_p_ac = -1; curbuf->b_p_ar = -1; // no local 'autoread' value #ifdef HAVE_FSYNC @@ -4177,7 +4177,7 @@ did_set_modified(optset_T *args) if (!args->os_newval.boolean) save_file_ff(curbuf); // Buffer is unchanged redraw_titles(); - curbuf->b_modified_was_set = args->os_newval.boolean; + curbuf->b_modified_was_set = !!args->os_newval.boolean; return NULL; } @@ -4481,7 +4481,7 @@ did_set_readonly(optset_T *args) // when 'readonly' is set may give W10 again if (curbuf->b_p_ro) - curbuf->b_did_warn = FALSE; + curbuf->b_did_warn = false; redraw_titles(); @@ -7998,7 +7998,7 @@ buf_copy_options(buf_T *buf, int flags) else buf->b_p_vts_array = NULL; #endif - buf->b_help = FALSE; + buf->b_help = false; if (buf->b_p_bt[0] == 'h') clear_string_option(&buf->b_p_bt); buf->b_p_ma = p_ma; @@ -8009,7 +8009,7 @@ buf_copy_options(buf_T *buf, int flags) // When the options should be copied (ignoring BCO_ALWAYS), set the // flag that indicates that the options have been initialized. if (should_copy) - buf->b_p_initialized = TRUE; + buf->b_p_initialized = true; } check_buf_options(buf); // make sure we don't have NULLs diff --git a/src/optionstr.c b/src/optionstr.c index e82c2b78cd..228e403d8a 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -5365,7 +5365,7 @@ do_filetype_autocmd(char_u **varp, int opt_flags, int value_changed) secure = 0; ++ft_recursive; - curbuf->b_did_filetype = TRUE; + curbuf->b_did_filetype = true; // Only pass TRUE for "force" when the value changed or not // used recursively, to avoid endless recurrence. apply_autocmds(EVENT_FILETYPE, curbuf->b_p_ft, curbuf->b_fname, diff --git a/src/popupwin.c b/src/popupwin.c index 319d882c55..20d7708c6b 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -2395,7 +2395,7 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type) buf->b_locked = TRUE; // prevent deleting the buffer // Avoid that 'buftype' is reset when this buffer is entered. - buf->b_p_initialized = TRUE; + buf->b_p_initialized = true; } wp->w_p_wrap = TRUE; // 'wrap' is default on wp->w_p_so = 0; // 'scrolloff' zero diff --git a/src/quickfix.c b/src/quickfix.c index 527760d669..46665e8ca3 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -5271,12 +5271,12 @@ qf_fill_buffer(qf_list_T *qfl, buf_T *buf, qfline_T *old_last, int qf_winid) 0L, (char_u *)"qf", OPT_LOCAL); curbuf->b_p_ma = FALSE; - curbuf->b_keep_filetype = TRUE; // don't detect 'filetype' + curbuf->b_keep_filetype = true; // don't detect 'filetype' apply_autocmds(EVENT_BUFREADPOST, (char_u *)"quickfix", NULL, FALSE, curbuf); apply_autocmds(EVENT_BUFWINENTER, (char_u *)"quickfix", NULL, FALSE, curbuf); - curbuf->b_keep_filetype = FALSE; + curbuf->b_keep_filetype = false; --curbuf_lock; // make sure it will be redrawn diff --git a/src/sign.c b/src/sign.c index c90856812a..6477af3072 100644 --- a/src/sign.c +++ b/src/sign.c @@ -254,7 +254,7 @@ insert_sign(buf_T *buf, // buffer to store sign in buf->b_signlist = newsign; # ifdef FEAT_NETBEANS_INTG if (netbeans_active()) - buf->b_has_sign_column = TRUE; + buf->b_has_sign_column = true; # endif } else diff --git a/src/spell.c b/src/spell.c index 01eb57e3a9..b48ee87b03 100644 --- a/src/spell.c +++ b/src/spell.c @@ -2589,7 +2589,7 @@ open_spellbuf(void) if (buf == NULL) return NULL; - buf->b_spell = TRUE; + buf->b_spell = true; buf->b_p_swf = TRUE; // may create a swap file #ifdef FEAT_CRYPT buf->b_p_key = empty_option; diff --git a/src/structs.h b/src/structs.h index e76c651d2e..21fcc0377b 100644 --- a/src/structs.h +++ b/src/structs.h @@ -3233,7 +3233,7 @@ struct file_buffer // b_sfname #ifdef UNIX - int b_dev_valid; // TRUE when b_dev has a valid number + bool b_dev_valid; // true when b_dev has a valid number dev_t b_dev; // device number ino_t b_ino; // inode number #endif @@ -3259,14 +3259,14 @@ struct file_buffer varnumber_T b_last_changedtick_pum; // b:changedtick for TextChangedP varnumber_T b_last_changedtick_i; // b:changedtick for TextChangedI - int b_saving; // Set to TRUE if we are in the middle of + bool b_saving; // Set to true if we are in the middle of // saving the buffer. /* * Changes to a buffer require updating of the display. To minimize the * work, remember changes made and update everything at once. */ - int b_mod_set; // TRUE when there are changes since the last + bool b_mod_set; // true when there are changes since the last // time the display was updated linenr_T b_mod_top; // topmost lnum that was changed linenr_T b_mod_bot; // lnum below last changed line, AFTER the @@ -3305,7 +3305,7 @@ struct file_buffer */ pos_T b_changelist[JUMPLISTSIZE]; int b_changelistlen; // number of active entries - int b_new_change; // set by u_savecommon() + bool b_new_change; // set in u_savecommon() /* * Character table, only used in charset.c for 'iskeyword' @@ -3327,12 +3327,12 @@ struct file_buffer pos_T b_op_end; #ifdef FEAT_VIMINFO - int b_marks_read; // Have we read viminfo marks yet? + bool b_marks_read; // Have we read viminfo marks yet? #endif - int b_modified_was_set; // did ":set modified" - int b_did_filetype; // FileType event found - int b_keep_filetype; // value for did_filetype when starting + bool b_modified_was_set; // did ":set modified" + bool b_did_filetype; // FileType event found + bool b_keep_filetype; // value for did_filetype when starting // to execute autocommands // Set by the apply_autocmds_group function if the given event is equal to @@ -3341,7 +3341,7 @@ struct file_buffer // // Relying on this value requires one to reset it prior calling // apply_autocmds_group(). - int b_au_did_filetype; + bool b_au_did_filetype; /* * The following only used in undo.c. @@ -3351,7 +3351,7 @@ struct file_buffer // if b_u_curhead is not NULL u_header_T *b_u_curhead; // pointer to current header int b_u_numhead; // current number of headers - int b_u_synced; // entry lists are synced + bool b_u_synced; // entry lists are synced long b_u_seq_last; // last used undo sequence number long b_u_save_nr_last; // counter for last file write long b_u_seq_cur; // uh_seq of header below which we are now @@ -3365,7 +3365,7 @@ struct file_buffer linenr_T b_u_line_lnum; // line number of line in u_line colnr_T b_u_line_colnr; // optional column number - int b_scanned; // ^N/^P have scanned this buffer + bool b_scanned; // ^N/^P have scanned this buffer // flags for use of ":lmap" and IM control long b_p_iminsert; // input mode for insert @@ -3388,7 +3388,7 @@ struct file_buffer * They are here because their value depends on the type of file * or contents of the file being edited. */ - int b_p_initialized; // set when options initialized + bool b_p_initialized; // set when options initialized #ifdef FEAT_EVAL sctx_T b_p_script_ctx[BV_COUNT]; // SCTXs for buffer-local options @@ -3611,7 +3611,7 @@ struct file_buffer list_T *b_recorded_changes; #endif #ifdef FEAT_PROP_POPUP - int b_has_textprop; // TRUE when text props were added + bool b_has_textprop; // true when text props were added hashtab_T *b_proptypes; // text property types local to buffer proptype_T **b_proparray; // entries of b_proptypes sorted on tp_id #endif @@ -3627,23 +3627,23 @@ struct file_buffer // When a buffer is created, it starts without a swap file. b_may_swap is // then set to indicate that a swap file may be opened later. It is reset // if a swap file could not be opened. - int b_may_swap; - int b_did_warn; // Set to 1 if user has been warned on first + bool b_may_swap; + bool b_did_warn; // Set to true if user has been warned on first // change of a read-only file // Two special kinds of buffers: // help buffer - used for help files, won't use a swap file. // spell buffer - used for spell info, never displayed and doesn't have a // file name. - int b_help; // TRUE for help file buffer (when set b_p_bt + bool b_help; // true for help file buffer (when set b_p_bt // is "help") #ifdef FEAT_SPELL - int b_spell; // TRUE for a spell file buffer, most fields + bool b_spell; // true for a spell file buffer, most fields // are not used! Use the B_SPELL macro to // access b_spell without #ifdef. #endif - int b_shortname; // this file has an 8.3 file name + bool b_shortname; // this file has an 8.3 file name #ifdef FEAT_JOB_CHANNEL char_u *b_prompt_text; // set by prompt_setprompt() @@ -3685,18 +3685,18 @@ struct file_buffer #ifdef FEAT_SIGNS sign_entry_T *b_signlist; // list of placed signs # ifdef FEAT_NETBEANS_INTG - int b_has_sign_column; // Flag that is set when a first sign is + bool b_has_sign_column; // Flag that is set when a first sign is // added and remains set until the end of // the netbeans session. # endif #endif #ifdef FEAT_NETBEANS_INTG - int b_netbeans_file; // TRUE when buffer is owned by NetBeans - int b_was_netbeans_file;// TRUE if b_netbeans_file was once set + bool b_netbeans_file; // true when buffer is owned by NetBeans + bool b_was_netbeans_file;// true if b_netbeans_file was once set #endif #ifdef FEAT_JOB_CHANNEL - int b_write_to_channel; // TRUE when appended lines are written to + bool b_write_to_channel; // true when appended lines are written to // a channel. #endif @@ -3711,7 +3711,7 @@ struct file_buffer // window. #endif #ifdef FEAT_DIFF - int b_diff_failed; // internal diff failed for this buffer + bool b_diff_failed; // internal diff failed for this buffer #endif }; // file_buffer diff --git a/src/tag.c b/src/tag.c index 0f12e384b5..2f887d9de1 100644 --- a/src/tag.c +++ b/src/tag.c @@ -3080,7 +3080,7 @@ find_tags( int save_emsg_off; - int help_save; + bool help_save; #ifdef FEAT_MULTI_LANG int i; char_u *saved_pat = NULL; // copy of pat[] @@ -3122,13 +3122,13 @@ find_tags( * Initialize a few variables */ if (st.help_only) // want tags from help file - curbuf->b_help = TRUE; // will be restored later + curbuf->b_help = true; // will be restored later #ifdef FEAT_CSCOPE else if (use_cscope) { // Make sure we don't mix help and cscope, confuses Coverity. st.help_only = FALSE; - curbuf->b_help = FALSE; + curbuf->b_help = false; } #endif diff --git a/src/terminal.c b/src/terminal.c index 6a9c286e29..92c889fc26 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -646,7 +646,7 @@ term_start( set_string_option_direct((char_u *)"buftype", -1, (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0); // Avoid that 'buftype' is reset when this buffer is entered. - curbuf->b_p_initialized = TRUE; + curbuf->b_p_initialized = true; // Mark the buffer as not modifiable. It can only be made modifiable after // the job finished. diff --git a/src/textprop.c b/src/textprop.c index 33165a8e43..78e9276e5d 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -965,7 +965,7 @@ f_prop_add_list(typval_T *argvars, typval_T *rettv UNUSED) // This must be done _before_ we start adding properties because property // changes trigger buffer (memline) reorganisation, which needs this flag // to be correctly set. - buf->b_has_textprop = TRUE; // this is never reset + buf->b_has_textprop = true; // this is never reset FOR_ALL_LIST_ITEMS(argvars[1].vval.v_list, li) { if (li->li_tv.v_type != VAR_LIST || li->li_tv.vval.v_list == NULL) @@ -1187,7 +1187,7 @@ prop_add_common( // This must be done _before_ we add the property because property changes // trigger buffer (memline) reorganisation, which needs this flag to be // correctly set. - buf->b_has_textprop = TRUE; // this is never reset + buf->b_has_textprop = true; // this is never reset prop_add_one(buf, type_name, id, text, text_padding_left, flags, start_lnum, end_lnum, start_col, end_col); diff --git a/src/undo.c b/src/undo.c index 4bfdc659f9..170eaac476 100644 --- a/src/undo.c +++ b/src/undo.c @@ -495,12 +495,12 @@ u_savecommon( size = bot - top - 1; /* - * If curbuf->b_u_synced == TRUE make a new header. + * If curbuf->b_u_synced is true make a new header. */ if (curbuf->b_u_synced) { // Need to create new entry in b_changelist. - curbuf->b_new_change = TRUE; + curbuf->b_new_change = true; if (get_undolevel() >= 0) { @@ -559,7 +559,7 @@ u_savecommon( { if (old_curhead != NULL) u_freebranch(curbuf, old_curhead, NULL); - curbuf->b_u_synced = FALSE; + curbuf->b_u_synced = false; return OK; } @@ -653,7 +653,7 @@ u_savecommon( // entry now. Following deleted/inserted lines go to // the re-used entry. u_getbot(); - curbuf->b_u_synced = FALSE; + curbuf->b_u_synced = false; // Move the found entry to become the last entry. The // order of undo/redo doesn't matter for the entries @@ -740,7 +740,7 @@ u_savecommon( uep->ue_array = NULL; uep->ue_next = curbuf->b_u_newhead->uh_entry; curbuf->b_u_newhead->uh_entry = uep; - curbuf->b_u_synced = FALSE; + curbuf->b_u_synced = false; undo_undoes = FALSE; #ifdef U_DEBUG @@ -2146,7 +2146,7 @@ u_read_undo(char_u *name, char_u *hash, char_u *orig_name UNUSED) curbuf->b_u_save_nr_last = last_save_nr; curbuf->b_u_save_nr_cur = last_save_nr; - curbuf->b_u_synced = TRUE; + curbuf->b_u_synced = true; vim_free(uhp_table); # ifdef U_DEBUG @@ -2199,7 +2199,7 @@ u_undo(int count) * original vi. If this happens twice in one macro the result will not * be compatible. */ - if (curbuf->b_u_synced == FALSE) + if (!curbuf->b_u_synced) { u_sync(TRUE); count = 1; @@ -2333,7 +2333,7 @@ undo_time( } // First make sure the current undoable change is synced. - if (curbuf->b_u_synced == FALSE) + if (!curbuf->b_u_synced) u_sync(TRUE); u_newcount = 0; @@ -3078,7 +3078,7 @@ u_sync( return; // XIM is busy, don't break an undo sequence #endif if (get_undolevel() < 0) - curbuf->b_u_synced = TRUE; // no entries, nothing to do + curbuf->b_u_synced = true; // no entries, nothing to do else { u_getbot(); // compute ue_bot of previous u_save @@ -3215,7 +3215,7 @@ ex_undojoin(exarg_T *eap UNUSED) return; // no entries, nothing to do else // Append next change to the last entry - curbuf->b_u_synced = FALSE; + curbuf->b_u_synced = false; } /* @@ -3226,7 +3226,7 @@ ex_undojoin(exarg_T *eap UNUSED) u_unchanged(buf_T *buf) { u_unch_branch(buf->b_u_oldhead); - buf->b_did_warn = FALSE; + buf->b_did_warn = false; } /* @@ -3319,7 +3319,7 @@ u_get_headentry(void) /* * u_getbot(): compute the line number of the previous u_save - * It is called only when b_u_synced is FALSE. + * It is called only when b_u_synced is false. */ static void u_getbot(void) @@ -3353,7 +3353,7 @@ u_getbot(void) curbuf->b_u_newhead->uh_getbot_entry = NULL; } - curbuf->b_u_synced = TRUE; + curbuf->b_u_synced = true; } /* @@ -3480,7 +3480,7 @@ u_freeentry(u_entry_T *uep, long n) u_clearall(buf_T *buf) { buf->b_u_newhead = buf->b_u_oldhead = buf->b_u_curhead = NULL; - buf->b_u_synced = TRUE; + buf->b_u_synced = true; buf->b_u_numhead = 0; buf->b_u_line_ptr.ul_line = NULL; buf->b_u_line_ptr.ul_len = 0; diff --git a/src/version.c b/src/version.c index ebaf924cde..861b8e4428 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 365, /**/ 364, /**/ diff --git a/src/viminfo.c b/src/viminfo.c index 8b6aa3e70a..d05900544a 100644 --- a/src/viminfo.c +++ b/src/viminfo.c @@ -2533,7 +2533,7 @@ check_marks_read(void) // Always set b_marks_read; needed when 'viminfo' is changed to include // the ' parameter after opening a buffer. - curbuf->b_marks_read = TRUE; + curbuf->b_marks_read = true; } static int From 237d77e07ca79f29d724a4ad74bdfd5eca830394 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 19 Apr 2026 20:21:37 +0000 Subject: [PATCH 072/545] patch 9.2.0366: pum: flicker when updating pum in place Problem: pum: flicker when updating pum in place Solution: Skip update_screen() when the popup menu is redrawn at the same position (Yasuhiro Matsumoto). closes: #20015 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/insexpand.c | 5 +++-- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/insexpand.c b/src/insexpand.c index 2627e4bc3e..eb9420a198 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -1828,8 +1828,9 @@ ins_compl_show_pum(void) if (!pum_wanted() || !pum_enough_matches()) return; - // Update the screen later, before drawing the popup menu over it. - pum_call_update_screen(); + // Avoid redrawing the screen under a pum that stays in place. + if (!pum_redraw_in_same_position()) + pum_call_update_screen(); if (compl_match_array == NULL) // Need to build the popup menu list. diff --git a/src/version.c b/src/version.c index 861b8e4428..4c2be4b468 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 366, /**/ 365, /**/ From 723c0acf2535c87a5b7be0284e7379e071a1d610 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 19 Apr 2026 20:38:35 +0000 Subject: [PATCH 073/545] patch 9.2.0367: runtime(netrw): ~ note expanded on MS Windows Problem: runtime(netrw): ~ note expanded on MS Windows (Tom Vamvanij) Solution: Expand ~ on MS Windows (Yasuhiro Matsumoto) On Windows, ":Explore ~" did nothing because the tilde expansion was gated to Unix/Cygwin only. Additionally, substitute() interprets backslashes in the replacement string specially (e.g. \U as a case modifier), which would corrupt $HOME values like C:\Users\name even if the branch were taken. Include has("win32") in the guard, anchor the pattern to the start of the string, and escape backslashes, ampersands and tildes in $HOME before substituting. fixes: #20003 closes: #20014 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/pack/dist/opt/netrw/autoload/netrw.vim | 5 +++-- src/testdir/test_plugin_netrw.vim | 6 ++++++ src/version.c | 2 ++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index 0e6fe6cea6..e3028cb28e 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -25,6 +25,7 @@ " 2026 Apr 01 by Vim Project use fnameescape() with netrw#FileUrlEdit() " 2026 Apr 05 by Vim Project Fix netrw#RFC2396() #19913 " 2026 Apr 15 by Vim Project Add missing escape() +" 2026 Apr 19 by Vim Project expand ~ on Windows #20003 " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -530,8 +531,8 @@ function netrw#Explore(indx,dosplit,style,...) NetrwKeepj norm! 0 if a:0 > 0 - if a:1 =~ '^\~' && (has("unix") || g:netrw_cygwin) - let dirname= simplify(substitute(a:1,'\~',expand("$HOME"),'')) + if a:1 =~ '^\~' && (has("unix") || has("win32") || g:netrw_cygwin) + let dirname= simplify(substitute(a:1,'^\~',escape(expand("$HOME"),'\&~'),'')) elseif a:1 == '.' let dirname= simplify(exists("b:netrw_curdir")? b:netrw_curdir : getcwd()) if dirname !~ '/$' diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim index b234670928..6ee731d88f 100644 --- a/src/testdir/test_plugin_netrw.vim +++ b/src/testdir/test_plugin_netrw.vim @@ -609,4 +609,10 @@ func Test_netrw_RFC2396() call assert_equal('a b', netrw#RFC2396(fname)) endfunc +func Test_netrw_Home_tilde() + Explore ~ + call assert_match('Netrw Directory Listing', getline(2)) + bw! +endfunc + " vim:ts=8 sts=2 sw=2 et diff --git a/src/version.c b/src/version.c index 4c2be4b468..20e593ac87 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 367, /**/ 366, /**/ From c13232699db413e735f30b5649c78a7f38a9a069 Mon Sep 17 00:00:00 2001 From: John Marriott Date: Sun, 19 Apr 2026 20:58:33 +0000 Subject: [PATCH 074/545] patch 9.2.0368: too many strlen() calls when adding strings to dicts Problem: too many strlen() calls when adding strings to dicts Solution: Refactor code to use string_T, use dict_add_string_len() instead of dict_add_string() (John Marriott) Additionally: - In textprop.c, in function prop_fill_dict() use a string_T to store local variable text_align. - In popupwin.c, use a string_T to store struct member pp_name in struct poppos_entry_T. - In mark.c, refactor function add_mark() to pass in the length of argument mname. - In insexpand.c: ->Use a string_T to store the elements of static array ctrl_x_mode_names. ->Refactor function trigger_complete_done_event(): ->->change type of argument char_u *word to string_T *word. ->->make one access of array ctrl_x_mode_names instead of two. ->Refactor function ins_compl_mode() to accept a string_T to return the resulting string. - In fileio.c: ->Refactor function getftypewfd() to accept a string_T to return the resulting string. ->In function create_readdirex_item() use a string_T to store local variable q. - In cmdexpand.c, store global cmdline_orig as a string_T. - In autocmd.c, in function f_autocmd_get() use a string_T to store local variables event_name and group_name. Measure their lengths once when they are assigned so they are not remeasured on each call to dict_add_string() in the subsequent for loop. - In channel.c, in function channel_part_info() drop local variable status and use s instead. Make s a string_T. closes: #19999 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/autocmd.c | 24 ++++++---- src/channel.c | 96 ++++++++++++++++++++++++++++++--------- src/cmdexpand.c | 15 ++++--- src/evalfunc.c | 34 +++++++++----- src/fileio.c | 116 +++++++++++++++++++++++++++++++++++------------- src/insexpand.c | 106 +++++++++++++++++++++++++------------------ src/mark.c | 28 ++++++------ src/match.c | 8 ++-- src/memline.c | 10 +++-- src/menu.c | 27 ++++++----- src/mouse.c | 4 +- src/popupwin.c | 31 ++++++++----- src/quickfix.c | 14 +++--- src/register.c | 33 +++++++++----- src/sign.c | 31 ++++++++----- src/term.c | 3 +- src/terminal.c | 32 ++++++------- src/textprop.c | 26 ++++++++--- src/version.c | 2 + 19 files changed, 427 insertions(+), 213 deletions(-) diff --git a/src/autocmd.c b/src/autocmd.c index b27297f799..99d6b3c924 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -3343,7 +3343,7 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) AutoCmd *ac; list_T *event_list; dict_T *event_dict; - char_u *event_name = NULL; + string_T event_name = {NULL, 0}; char_u *pat = NULL; char_u *name = NULL; int group = AUGROUP_ALL; @@ -3425,12 +3425,13 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) if (event_arg != NUM_EVENTS && event != event_arg) continue; - event_name = event_nr2name(event); + event_name.string = event_nr2name(event); + event_name.length = STRLEN(event_name.string); // iterate through all the patterns for this autocmd event FOR_ALL_AUTOCMD_PATTERNS(event, ap) { - char_u *group_name; + string_T group_name; if (ap->pat == NULL) // pattern has been removed continue; @@ -3441,7 +3442,14 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) if (pat != NULL && STRCMP(pat, ap->pat) != 0) continue; - group_name = get_augroup_name(NULL, ap->group); + group_name.string = get_augroup_name(NULL, ap->group); + if (group_name.string == NULL) + { + group_name.string = (char_u *)""; + group_name.length = 0; + } + else + group_name.length = STRLEN(group_name.string); // iterate through all the commands for this pattern and add one // item for each cmd. @@ -3455,10 +3463,10 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) return; } - if (dict_add_string(event_dict, "event", event_name) == FAIL - || dict_add_string(event_dict, "group", - group_name == NULL ? (char_u *)"" - : group_name) == FAIL + if (dict_add_string_len(event_dict, "event", + event_name.string, (int)event_name.length) == FAIL + || dict_add_string_len(event_dict, "group", + group_name.string, (int)group_name.length) == FAIL || (ap->buflocal_nr != 0 && (dict_add_number(event_dict, "bufnr", ap->buflocal_nr) == FAIL)) diff --git a/src/channel.c b/src/channel.c index d58c697081..56878458ea 100644 --- a/src/channel.c +++ b/src/channel.c @@ -3668,46 +3668,98 @@ channel_part_info(channel_T *channel, dict_T *dict, char *name, ch_part_T part) chanpart_T *chanpart = &channel->ch_part[part]; char namebuf[20]; // longest is "sock_timeout" size_t tail; - char *status; - char *s = ""; + string_T s; vim_strncpy((char_u *)namebuf, (char_u *)name, 4); - STRCAT(namebuf, "_"); tail = STRLEN(namebuf); + STRCPY(namebuf + tail, "_"); + tail += STRLEN_LITERAL("_"); STRCPY(namebuf + tail, "status"); if (chanpart->ch_fd != INVALID_FD) - status = "open"; + { + s.string = (char_u *)"open"; + s.length = STRLEN_LITERAL("open"); + } else if (channel_has_readahead(channel, part)) - status = "buffered"; + { + s.string = (char_u *)"buffered"; + s.length = STRLEN_LITERAL("buffered"); + } else - status = "closed"; - dict_add_string(dict, namebuf, (char_u *)status); + { + s.string = (char_u *)"closed"; + s.length = STRLEN_LITERAL("closed"); + } + dict_add_string_len(dict, namebuf, s.string, (int)s.length); STRCPY(namebuf + tail, "mode"); switch (chanpart->ch_mode) { - case CH_MODE_NL: s = "NL"; break; - case CH_MODE_RAW: s = "RAW"; break; - case CH_MODE_JSON: s = "JSON"; break; - case CH_MODE_JS: s = "JS"; break; - case CH_MODE_LSP: s = "LSP"; break; - case CH_MODE_DAP: s = "DAP"; break; + case CH_MODE_NL: + s.string = (char_u *)"NL"; + s.length = STRLEN_LITERAL("NL"); + break; + case CH_MODE_RAW: + s.string = (char_u *)"RAW"; + s.length = STRLEN_LITERAL("RAW"); + break; + case CH_MODE_JSON: + s.string = (char_u *)"JSON"; + s.length = STRLEN_LITERAL("JSON"); + break; + case CH_MODE_JS: + s.string = (char_u *)"JS"; + s.length = STRLEN_LITERAL("JS"); + break; + case CH_MODE_LSP: + s.string = (char_u *)"LSP"; + s.length = STRLEN_LITERAL("LSP"); + break; + case CH_MODE_DAP: + s.string = (char_u *)"DAP"; + s.length = STRLEN_LITERAL("DAP"); + break; + default: + s.string = (char_u *)""; + s.length = 0; + break; } - dict_add_string(dict, namebuf, (char_u *)s); + dict_add_string_len(dict, namebuf, s.string, (int)s.length); STRCPY(namebuf + tail, "io"); if (part == PART_SOCK) - s = "socket"; + { + s.string = (char_u *)"socket"; + s.length = STRLEN_LITERAL("socket"); + } else switch (chanpart->ch_io) { - case JIO_NULL: s = "null"; break; - case JIO_PIPE: s = "pipe"; break; - case JIO_FILE: s = "file"; break; - case JIO_BUFFER: s = "buffer"; break; - case JIO_OUT: s = "out"; break; + case JIO_NULL: + s.string = (char_u *)"null"; + s.length = STRLEN_LITERAL("null"); + break; + case JIO_PIPE: + s.string = (char_u *)"pipe"; + s.length = STRLEN_LITERAL("pipe"); + break; + case JIO_FILE: + s.string = (char_u *)"file"; + s.length = STRLEN_LITERAL("file"); + break; + case JIO_BUFFER: + s.string = (char_u *)"buffer"; + s.length = STRLEN_LITERAL("buffer"); + break; + case JIO_OUT: + s.string = (char_u *)"out"; + s.length = STRLEN_LITERAL("out"); + break; + default: + s.string = (char_u *)""; + s.length = 0; } - dict_add_string(dict, namebuf, (char_u *)s); + dict_add_string_len(dict, namebuf, s.string, (int)s.length); STRCPY(namebuf + tail, "timeout"); dict_add_number(dict, namebuf, chanpart->ch_timeout); @@ -5066,7 +5118,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval) id = di->di_tv.vval.v_number; } if (ch_mode == CH_MODE_LSP && !dict_has_key(d, "jsonrpc")) - dict_add_string(d, "jsonrpc", (char_u *)"2.0"); + dict_add_string_len(d, "jsonrpc", (char_u *)"2.0", STRLEN_LITERAL("2.0")); text = json_encode_lsp_msg(&argvars[1]); } else diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 63757179a5..befc18c79c 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -36,7 +36,7 @@ static int compl_match_arraysize; static int compl_startcol; static int compl_selected; // cmdline before expansion -static char_u *cmdline_orig = NULL; +static string_T cmdline_orig = {NULL, 0}; #define SHOW_MATCH(m) (showtail ? showmatches_gettail(matches[m]) : matches[m]) @@ -338,8 +338,12 @@ nextwild( // Save cmdline before inserting selected item if (!wild_navigate && ccline->cmdbuff != NULL) { - vim_free(cmdline_orig); - cmdline_orig = vim_strnsave(ccline->cmdbuff, ccline->cmdlen); + vim_free(cmdline_orig.string); + cmdline_orig.string = vim_strnsave(ccline->cmdbuff, ccline->cmdlen); + if (cmdline_orig.string == NULL) + cmdline_orig.length = 0; + else + cmdline_orig.length = ccline->cmdlen; } if (p != NULL && !got_int && !(options & WILD_NOSELECT)) @@ -1188,7 +1192,7 @@ ExpandCleanup(expand_T *xp) void clear_cmdline_orig(void) { - VIM_CLEAR(cmdline_orig); + VIM_CLEAR_STRING(cmdline_orig); } /* @@ -4715,7 +4719,8 @@ f_cmdcomplete_info(typval_T *argvars UNUSED, typval_T *rettv) || ccline->xpc == NULL || ccline->xpc->xp_files == NULL) return; retdict = rettv->vval.v_dict; - ret = dict_add_string(retdict, "cmdline_orig", cmdline_orig); + ret = dict_add_string_len(retdict, "cmdline_orig", + cmdline_orig.string, (int)cmdline_orig.length); if (ret == OK) ret = dict_add_number(retdict, "pum_visible", pum_visible()); if (ret == OK) diff --git a/src/evalfunc.c b/src/evalfunc.c index 04774128dd..e03dc9b201 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -10416,6 +10416,7 @@ f_getreginfo(typval_T *argvars, typval_T *rettv) { int regname; char_u buf[NUMBUFLEN + 2]; + size_t buflen; long reglen = 0; dict_T *dict; list_T *list; @@ -10439,23 +10440,34 @@ f_getreginfo(typval_T *argvars, typval_T *rettv) return; (void)dict_add_list(dict, "regcontents", list); - buf[0] = NUL; - buf[1] = NUL; switch (get_reg_type(regname, ®len)) { - case MLINE: buf[0] = 'V'; break; - case MCHAR: buf[0] = 'v'; break; + case MLINE: + buf[0] = 'V'; + buf[1] = NUL; + buflen = 1; + break; + case MCHAR: + buf[0] = 'v'; + buf[1] = NUL; + buflen = 1; + break; case MBLOCK: - vim_snprintf((char *)buf, sizeof(buf), "%c%ld", Ctrl_V, - reglen + 1); - break; + buflen = vim_snprintf_safelen((char *)buf, sizeof(buf), + "%c%ld", Ctrl_V, reglen + 1); + break; + default: + buf[0] = NUL; + buflen = 0; + break; } - (void)dict_add_string(dict, (char *)"regtype", buf); + (void)dict_add_string_len(dict, (char *)"regtype", buf, (int)buflen); buf[0] = get_register_name(get_unname_register()); buf[1] = NUL; + buflen = (buf[0] == NUL) ? 0 : 1; if (regname == '"') - (void)dict_add_string(dict, (char *)"points_to", buf); + (void)dict_add_string_len(dict, (char *)"points_to", buf, (int)buflen); else { dictitem_T *item = dictitem_alloc((char_u *)"isunnamed"); @@ -10473,11 +10485,11 @@ f_getreginfo(typval_T *argvars, typval_T *rettv) static void return_register(int regname, typval_T *rettv) { - char_u buf[2] = {0, 0}; + char_u buf[2] = {NUL, NUL}; buf[0] = (char_u)regname; rettv->v_type = VAR_STRING; - rettv->vval.v_string = vim_strsave(buf); + rettv->vval.v_string = vim_strnsave(buf, (buf[0] == NUL) ? 0 : 1); } /* diff --git a/src/fileio.c b/src/fileio.c index 1906a4316a..b023e5a0d1 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4811,8 +4811,8 @@ getfpermwfd(WIN32_FIND_DATAW *wfd, char_u *perm) return getfpermst(&st, perm); } - static char_u * -getftypewfd(WIN32_FIND_DATAW *wfd) + static void +getftypewfd(WIN32_FIND_DATAW *wfd, string_T *ret) { DWORD flag = wfd->dwFileAttributes; DWORD tag = wfd->dwReserved0; @@ -4820,20 +4820,40 @@ getftypewfd(WIN32_FIND_DATAW *wfd) if (flag & FILE_ATTRIBUTE_REPARSE_POINT) { if (tag == IO_REPARSE_TAG_MOUNT_POINT) - return (char_u*)"junction"; + { + ret->string = (char_u *)"junction"; + ret->length = STRLEN_LITERAL("junction"); + return; + } if (tag == IO_REPARSE_TAG_SYMLINK) { if (flag & FILE_ATTRIBUTE_DIRECTORY) - return (char_u*)"linkd"; + { + ret->string = (char_u *)"linkd"; + ret->length = STRLEN_LITERAL("linkd"); + return; + } - return (char_u*)"link"; + ret->string = (char_u *)"link"; + ret->length = STRLEN_LITERAL("link"); + return; } - return (char_u*)"reparse"; // unknown reparse point type + + // unknown reparse point type + ret->string = (char_u *)"reparse"; + ret->length = STRLEN_LITERAL("reparse"); + return; } + if (flag & FILE_ATTRIBUTE_DIRECTORY) - return (char_u*)"dir"; + { + ret->string = (char_u *)"dir"; + ret->length = STRLEN_LITERAL("dir"); + return; + } - return (char_u*)"file"; + ret->string = (char_u *)"file"; + ret->length = STRLEN_LITERAL("file"); } static dict_T * @@ -4843,6 +4863,7 @@ create_readdirex_item(WIN32_FIND_DATAW *wfd) char_u *p; varnumber_T size, time; char_u permbuf[] = "---------"; + string_T s; item = dict_alloc(); if (item == NULL) @@ -4870,14 +4891,15 @@ create_readdirex_item(WIN32_FIND_DATAW *wfd) if (dict_add_number(item, "time", time) == FAIL) goto theend; - if (dict_add_string(item, "type", getftypewfd(wfd)) == FAIL) + getftypewfd(wfd, &s); + if (dict_add_string_len(item, "type", s.string, (int)s.length) == FAIL) goto theend; if (dict_add_string(item, "perm", getfpermwfd(wfd, permbuf)) == FAIL) goto theend; - if (dict_add_string(item, "user", (char_u*)"") == FAIL) + if (dict_add_string_len(item, "user", (char_u *)"", 0) == FAIL) goto theend; - if (dict_add_string(item, "group", (char_u*)"") == FAIL) + if (dict_add_string_len(item, "group", (char_u *)"", 0) == FAIL) goto theend; return item; @@ -4893,11 +4915,12 @@ create_readdirex_item(char_u *path, char_u *name) dict_T *item; char *p; size_t pathlen, len; + size_t namelen; stat_T st; int ret, link = FALSE; varnumber_T size; char_u permbuf[] = "---------"; - char_u *q = NULL; + string_T q = {NULL, 0}; struct passwd *pw; struct group *gr; @@ -4907,7 +4930,8 @@ create_readdirex_item(char_u *path, char_u *name) item->dv_refcount++; pathlen = STRLEN(path); - len = pathlen + 1 + STRLEN(name) + 1; + namelen = STRLEN(name); + len = pathlen + 1 + namelen + 1; p = alloc(len); if (p == NULL) goto theend; @@ -4921,11 +4945,14 @@ create_readdirex_item(char_u *path, char_u *name) link = TRUE; ret = mch_stat(p, &st); if (ret < 0) - q = (char_u*)"link"; + { + q.string = (char_u *)"link"; + q.length = STRLEN_LITERAL("link"); + } } vim_free(p); - if (dict_add_string(item, "name", name) == FAIL) + if (dict_add_string_len(item, "name", name, (int)namelen) == FAIL) goto theend; if (ret >= 0) @@ -4944,32 +4971,53 @@ create_readdirex_item(char_u *path, char_u *name) if (link) { if (S_ISDIR(st.st_mode)) - q = (char_u*)"linkd"; + { + q.string = (char_u *)"linkd"; + q.length = STRLEN_LITERAL("linkd"); + } else - q = (char_u*)"link"; + { + q.string = (char_u *)"link"; + q.length = STRLEN_LITERAL("link"); + } } else - q = getftypest(&st); - if (dict_add_string(item, "type", q) == FAIL) + { + q.string = getftypest(&st); + q.length = STRLEN(q.string); + } + if (dict_add_string_len(item, "type", q.string, (int)q.length) == FAIL) goto theend; if (dict_add_string(item, "perm", getfpermst(&st, permbuf)) == FAIL) goto theend; pw = getpwuid(st.st_uid); if (pw == NULL) - q = (char_u*)""; + { + q.string = (char_u *)""; + q.length = 0; + } else - q = (char_u*)pw->pw_name; - if (dict_add_string(item, "user", q) == FAIL) + { + q.string = (char_u *)pw->pw_name; + q.length = STRLEN(q.string); + } + if (dict_add_string_len(item, "user", q.string, (int)q.length) == FAIL) goto theend; # if !defined(VMS) || (defined(VMS) && defined(HAVE_XOS_R_H)) gr = getgrgid(st.st_gid); if (gr == NULL) - q = (char_u*)""; + { + q.string = (char_u *)""; + q.length = 0; + } else - q = (char_u*)gr->gr_name; + { + q.string = (char_u *)gr->gr_name; + q.length = STRLEN(q.string); + } # endif - if (dict_add_string(item, "group", q) == FAIL) + if (dict_add_string_len(item, "group", q.string, (int)q.length) == FAIL) goto theend; } else @@ -4978,13 +5026,21 @@ create_readdirex_item(char_u *path, char_u *name) goto theend; if (dict_add_number(item, "time", -1) == FAIL) goto theend; - if (dict_add_string(item, "type", q == NULL ? (char_u*)"" : q) == FAIL) - goto theend; - if (dict_add_string(item, "perm", (char_u*)"") == FAIL) + if (q.string == NULL) + { + if (dict_add_string_len(item, "type", (char_u *)"", 0) == FAIL) + goto theend; + } + else + { + if (dict_add_string_len(item, "type", q.string, (int)q.length) == FAIL) + goto theend; + } + if (dict_add_string_len(item, "perm", (char_u *)"", 0) == FAIL) goto theend; - if (dict_add_string(item, "user", (char_u*)"") == FAIL) + if (dict_add_string_len(item, "user", (char_u *)"", 0) == FAIL) goto theend; - if (dict_add_string(item, "group", (char_u*)"") == FAIL) + if (dict_add_string_len(item, "group", (char_u *)"", 0) == FAIL) goto theend; } return item; diff --git a/src/insexpand.c b/src/insexpand.c index eb9420a198..72c3237776 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -67,27 +67,30 @@ static char *ctrl_x_msgs[] = }; #if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) -static char *ctrl_x_mode_names[] = { - "keyword", - "ctrl_x", - "scroll", - "whole_line", - "files", - "tags", - "path_patterns", - "path_defines", - "unknown", // CTRL_X_FINISHED - "dictionary", - "thesaurus", - "cmdline", - "function", - "omni", - "spell", - NULL, // CTRL_X_LOCAL_MSG only used in "ctrl_x_msgs" - "eval", - "cmdline", - "register", +# define STRING_INIT(s) \ + {(char_u *)(s), STRLEN_LITERAL(s)} +static string_T ctrl_x_mode_names[] = { + STRING_INIT("keyword"), + STRING_INIT("ctrl_x"), + STRING_INIT("scroll"), + STRING_INIT("whole_line"), + STRING_INIT("files"), + STRING_INIT("tags"), + STRING_INIT("path_patterns"), + STRING_INIT("path_defines"), + STRING_INIT("unknown"), // CTRL_X_FINISHED + STRING_INIT("dictionary"), + STRING_INIT("thesaurus"), + STRING_INIT("cmdline"), + STRING_INIT("function"), + STRING_INIT("omni"), + STRING_INIT("spell"), + {NULL, 0}, // CTRL_X_LOCAL_MSG is only used in "ctrl_x_msgs" + STRING_INIT("eval"), + STRING_INIT("cmdline"), + STRING_INIT("register"), }; +# undef STRING_INIT #endif /* @@ -1386,13 +1389,13 @@ ins_compl_dict_alloc(compl_T *match) if (dict == NULL) return NULL; - dict_add_string(dict, "word", match->cp_str.string); + dict_add_string_len(dict, "word", match->cp_str.string, (int)match->cp_str.length); dict_add_string(dict, "abbr", match->cp_text[CPT_ABBR]); dict_add_string(dict, "menu", match->cp_text[CPT_MENU]); dict_add_string(dict, "kind", match->cp_text[CPT_KIND]); dict_add_string(dict, "info", match->cp_text[CPT_INFO]); if (match->cp_user_data.v_type == VAR_UNKNOWN) - dict_add_string(dict, "user_data", (char_u *)""); + dict_add_string_len(dict, "user_data", (char_u *)"", 0); else dict_add_tv(dict, "user_data", &match->cp_user_data); @@ -2888,21 +2891,21 @@ set_ctrl_x_mode(int c) * Trigger CompleteDone event and adds relevant information to v:event */ static void -trigger_complete_done_event(int mode UNUSED, char_u *word UNUSED) +trigger_complete_done_event(int mode UNUSED, string_T *word UNUSED) { #if defined(FEAT_EVAL) save_v_event_T save_v_event; dict_T *v_event = get_v_event(&save_v_event); - char_u *mode_str = NULL; + string_T *mode_str; - mode = mode & ~CTRL_X_WANT_IDENT; - if (ctrl_x_mode_names[mode]) - mode_str = (char_u *)ctrl_x_mode_names[mode]; + if (word == NULL || word->string == NULL) + (void)dict_add_string_len(v_event, "complete_word", (char_u *)"", 0); + else + (void)dict_add_string_len(v_event, "complete_word", word->string, (int)word->length); - (void)dict_add_string(v_event, "complete_word", - word == NULL ? (char_u *)"" : word); - (void)dict_add_string(v_event, "complete_type", - mode_str != NULL ? mode_str : (char_u *)""); + mode_str = &ctrl_x_mode_names[mode & ~CTRL_X_WANT_IDENT]; + (void)dict_add_string_len(v_event, "complete_type", + (mode_str->string == NULL) ? (char_u *)"" : mode_str->string, (int)mode_str->length); dict_set_items_ro(v_event); #endif @@ -2920,7 +2923,7 @@ trigger_complete_done_event(int mode UNUSED, char_u *word UNUSED) ins_compl_stop(int c, int prev_mode, int retval) { int want_cindent; - char_u *word = NULL; + string_T word = {NULL, 0}; // Remove pre-inserted text when present. if (ins_compl_preinsert_effect() && ins_compl_win_active(curwin)) @@ -2979,7 +2982,12 @@ ins_compl_stop(int c, int prev_mode, int retval) && (c == CAR || c == K_KENTER || c == NL))) && pum_visible()) { - word = vim_strsave(compl_shown_match->cp_str.string); + word.string = vim_strnsave(compl_shown_match->cp_str.string, + compl_shown_match->cp_str.length); + if (word.string == NULL) + word.length = 0; + else + word.length = compl_shown_match->cp_str.length; retval = TRUE; } @@ -3045,8 +3053,8 @@ ins_compl_stop(int c, int prev_mode, int retval) do_c_expr_indent(); // Trigger the CompleteDone event to give scripts a chance to act // upon the end of completion. - trigger_complete_done_event(prev_mode, word); - vim_free(word); + trigger_complete_done_event(prev_mode, &word); + vim_free(word.string); return retval; } @@ -3956,13 +3964,22 @@ f_complete_check(typval_T *argvars UNUSED, typval_T *rettv) /* * Return Insert completion mode name string */ - static char_u * -ins_compl_mode(void) + static void +ins_compl_mode(string_T *ret) { if (ctrl_x_mode_not_defined_yet() || ctrl_x_mode_scroll() || compl_started) - return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; + { + string_T *mode; - return (char_u *)""; + mode = &ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; + ret->string = (mode->string == NULL) ? (char_u *)"" : mode->string; + ret->length = mode->length; + } + else + { + ret->string = (char_u *)""; + ret->length = 0; + } } /* @@ -4025,7 +4042,7 @@ ins_compl_update_sequence_numbers(void) static void fill_complete_info_dict(dict_T *di, compl_T *match, int add_match) { - dict_add_string(di, "word", match->cp_str.string); + dict_add_string_len(di, "word", match->cp_str.string, (int)match->cp_str.length); dict_add_string(di, "abbr", match->cp_text[CPT_ABBR]); dict_add_string(di, "menu", match->cp_text[CPT_MENU]); dict_add_string(di, "kind", match->cp_text[CPT_KIND]); @@ -4034,7 +4051,7 @@ fill_complete_info_dict(dict_T *di, compl_T *match, int add_match) dict_add_bool(di, "match", match->cp_in_match_array); if (match->cp_user_data.v_type == VAR_UNKNOWN) // Add an empty string for backwards compatibility - dict_add_string(di, "user_data", (char_u *)""); + dict_add_string_len(di, "user_data", (char_u *)"", 0); else dict_add_tv(di, "user_data", &match->cp_user_data); } @@ -4085,7 +4102,12 @@ get_complete_info(list_T *what_list, dict_T *retdict) } if (ret == OK && (what_flag & CI_WHAT_MODE)) - ret = dict_add_string(retdict, "mode", ins_compl_mode()); + { + string_T mode; + + ins_compl_mode(&mode); + ret = dict_add_string_len(retdict, "mode", mode.string, (int)mode.length); + } if (ret == OK && (what_flag & CI_WHAT_PUM_VISIBLE)) ret = dict_add_number(retdict, "pum_visible", pum_visible()); diff --git a/src/mark.c b/src/mark.c index 7cd4f31bcb..eb77d8af9f 100644 --- a/src/mark.c +++ b/src/mark.c @@ -1454,7 +1454,7 @@ get_namedfm(void) * Add information about mark 'mname' to list 'l' */ static int -add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname) +add_mark(list_T *l, char_u *mname, size_t mnamelen, pos_T *pos, int bufnr, char_u *fname) { dict_T *d; list_T *lpos; @@ -1481,7 +1481,7 @@ add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname) list_append_number(lpos, pos->col < MAXCOL ? pos->col + 1 : MAXCOL); list_append_number(lpos, pos->coladd); - if (dict_add_string(d, "mark", mname) == FAIL + if (dict_add_string_len(d, "mark", mname, (int)mnamelen) == FAIL || dict_add_list(d, "pos", lpos) == FAIL || (fname != NULL && dict_add_string(d, "file", fname) == FAIL)) { @@ -1500,25 +1500,26 @@ add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname) get_buf_local_marks(buf_T *buf, list_T *l) { char_u mname[3] = "' "; + size_t mnamelen = STRLEN_LITERAL("' "); int i; // Marks 'a' to 'z' for (i = 0; i < NMARKS; ++i) { mname[1] = 'a' + i; - add_mark(l, mname, &buf->b_namedm[i], buf->b_fnum, NULL); + add_mark(l, mname, mnamelen, &buf->b_namedm[i], buf->b_fnum, NULL); } // Mark '' is a window local mark and not a buffer local mark - add_mark(l, (char_u *)"''", &curwin->w_pcmark, curbuf->b_fnum, NULL); - - add_mark(l, (char_u *)"'\"", &buf->b_last_cursor, buf->b_fnum, NULL); - add_mark(l, (char_u *)"'[", &buf->b_op_start, buf->b_fnum, NULL); - add_mark(l, (char_u *)"']", &buf->b_op_end, buf->b_fnum, NULL); - add_mark(l, (char_u *)"'^", &buf->b_last_insert, buf->b_fnum, NULL); - add_mark(l, (char_u *)"'.", &buf->b_last_change, buf->b_fnum, NULL); - add_mark(l, (char_u *)"'<", &buf->b_visual.vi_start, buf->b_fnum, NULL); - add_mark(l, (char_u *)"'>", &buf->b_visual.vi_end, buf->b_fnum, NULL); + add_mark(l, (char_u *)"''", STRLEN_LITERAL("''"), &curwin->w_pcmark, curbuf->b_fnum, NULL); + + add_mark(l, (char_u *)"'\"", STRLEN_LITERAL("'\""), &buf->b_last_cursor, buf->b_fnum, NULL); + add_mark(l, (char_u *)"'[", STRLEN_LITERAL("'["), &buf->b_op_start, buf->b_fnum, NULL); + add_mark(l, (char_u *)"']", STRLEN_LITERAL("']"), &buf->b_op_end, buf->b_fnum, NULL); + add_mark(l, (char_u *)"'^", STRLEN_LITERAL("'^"), &buf->b_last_insert, buf->b_fnum, NULL); + add_mark(l, (char_u *)"'.", STRLEN_LITERAL("'."), &buf->b_last_change, buf->b_fnum, NULL); + add_mark(l, (char_u *)"'<", STRLEN_LITERAL("'<"), &buf->b_visual.vi_start, buf->b_fnum, NULL); + add_mark(l, (char_u *)"'>", STRLEN_LITERAL("'>"), &buf->b_visual.vi_end, buf->b_fnum, NULL); } /* @@ -1528,6 +1529,7 @@ get_buf_local_marks(buf_T *buf, list_T *l) get_global_marks(list_T *l) { char_u mname[3] = "' "; + size_t mnamelen = STRLEN_LITERAL("' "); int i; char_u *name; @@ -1541,7 +1543,7 @@ get_global_marks(list_T *l) if (name != NULL) { mname[1] = i >= NMARKS ? i - NMARKS + '0' : i + 'A'; - add_mark(l, mname, &namedfm[i].fmark.mark, + add_mark(l, mname, mnamelen, &namedfm[i].fmark.mark, namedfm[i].fmark.fnum, name); if (namedfm[i].fmark.fnum != 0) vim_free(name); diff --git a/src/match.c b/src/match.c index 912abfd74b..709ac30495 100644 --- a/src/match.c +++ b/src/match.c @@ -1048,10 +1048,12 @@ f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED) # if defined(FEAT_CONCEAL) if (cur->mit_conceal_char) { - char_u buf[MB_MAXBYTES + 1]; + char_u buf[MB_MAXBYTES + 1]; + int buflen; - buf[(*mb_char2bytes)(cur->mit_conceal_char, buf)] = NUL; - dict_add_string(dict, "conceal", (char_u *)&buf); + buflen = (*mb_char2bytes)(cur->mit_conceal_char, buf); + buf[buflen] = NUL; + dict_add_string_len(dict, "conceal", (char_u *)&buf, buflen); } # endif list_append_dict(rettv->vval.v_list, dict); diff --git a/src/memline.c b/src/memline.c index 8152a03a50..b374971e26 100644 --- a/src/memline.c +++ b/src/memline.c @@ -2225,9 +2225,11 @@ get_b0_dict(char_u *fname, dict_T *d) if (read_eintr(fd, &b0, sizeof(b0)) == sizeof(b0)) { if (ml_check_b0_id(&b0) == FAIL) - dict_add_string(d, "error", (char_u *)"Not a swap file"); + dict_add_string_len(d, "error", + (char_u *)"Not a swap file", STRLEN_LITERAL("Not a swap file")); else if (b0_magic_wrong(&b0)) - dict_add_string(d, "error", (char_u *)"Magic number mismatch"); + dict_add_string_len(d, "error", + (char_u *)"Magic number mismatch", STRLEN_LITERAL("Magic number mismatch")); else { // we have swap information @@ -2245,11 +2247,11 @@ get_b0_dict(char_u *fname, dict_T *d) } } else - dict_add_string(d, "error", (char_u *)"Cannot read file"); + dict_add_string_len(d, "error", (char_u *)"Cannot read file", STRLEN_LITERAL("Cannot read file")); close(fd); } else - dict_add_string(d, "error", (char_u *)"Cannot open file"); + dict_add_string_len(d, "error", (char_u *)"Cannot open file", STRLEN_LITERAL("Cannot open file")); } #endif diff --git a/src/menu.c b/src/menu.c index 3c0dfca677..861d787112 100644 --- a/src/menu.c +++ b/src/menu.c @@ -2892,15 +2892,17 @@ menuitem_getinfo(char_u *menu_name, vimmenu_T *menu, int modes, dict_T *dict) if (status == OK) { char_u buf[NUMBUFLEN]; + int buflen; if (has_mbyte) - buf[utf_char2bytes(menu->mnemonic, buf)] = NUL; + buflen = utf_char2bytes(menu->mnemonic, buf); else { buf[0] = (char_u)menu->mnemonic; - buf[1] = NUL; + buflen = (buf[0] == NUL) ? 0 : 1; } - status = dict_add_string(dict, "shortcut", buf); + buf[buflen] = NUL; + status = dict_add_string_len(dict, "shortcut", buf, buflen); } if (status == OK && menu->children == NULL) { @@ -2913,14 +2915,17 @@ menuitem_getinfo(char_u *menu_name, vimmenu_T *menu, int modes, dict_T *dict) { if (menu->strings[bit] != NULL) { - char_u *tofree = NULL; - - status = dict_add_string(dict, "rhs", - *menu->strings[bit] == NUL - ? (char_u *)"" - : (tofree = str2special_save( - menu->strings[bit], FALSE, FALSE))); - vim_free(tofree); + if (*menu->strings[bit] == NUL) + status = dict_add_string_len(dict, "rhs", + (char_u *)"", STRLEN_LITERAL("")); + else + { + char_u *tofree = NULL; + + status = dict_add_string(dict, "rhs", + tofree = str2special_save(menu->strings[bit], FALSE, FALSE)); + vim_free(tofree); + } } if (status == OK) status = dict_add_bool(dict, "noremenu", diff --git a/src/mouse.c b/src/mouse.c index 45ffd4b513..04a420e4bb 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -1731,7 +1731,7 @@ stl_click_handler_regions( else button_str[0] = 'm'; button_str[1] = NUL; - dict_add_string(info, "button", button_str); + dict_add_string_len(info, "button", button_str, 1); // Modifiers. if (mods & MOD_MASK_SHIFT) @@ -1741,7 +1741,7 @@ stl_click_handler_regions( if (mods & MOD_MASK_ALT) mods_str[mi++] = 'a'; mods_str[mi] = NUL; - dict_add_string(info, "mods", mods_str); + dict_add_string_len(info, "mods", mods_str, mi); dict_add_number(info, "winid", winid); diff --git a/src/popupwin.c b/src/popupwin.c index 20d7708c6b..35bcd4c80f 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -16,17 +16,20 @@ #if defined(FEAT_PROP_POPUP) typedef struct { - char *pp_name; + string_T pp_name; poppos_T pp_val; } poppos_entry_T; +#define STRING_INIT(s) \ + {(char_u *)(s), STRLEN_LITERAL(s)} static poppos_entry_T poppos_entries[] = { - {"botleft", POPPOS_BOTLEFT}, - {"topleft", POPPOS_TOPLEFT}, - {"botright", POPPOS_BOTRIGHT}, - {"topright", POPPOS_TOPRIGHT}, - {"center", POPPOS_CENTER} + {STRING_INIT("botleft"), POPPOS_BOTLEFT}, + {STRING_INIT("topleft"), POPPOS_TOPLEFT}, + {STRING_INIT("botright"), POPPOS_BOTRIGHT}, + {STRING_INIT("topright"), POPPOS_TOPRIGHT}, + {STRING_INIT("center"), POPPOS_CENTER} }; +#undef STRING_INIT #ifdef HAS_MESSAGE_WINDOW // Window used for ":echowindow" @@ -474,7 +477,7 @@ get_pos_entry(dict_T *d, int give_error) return POPPOS_NONE; for (nr = 0; nr < (int)ARRAY_LENGTH(poppos_entries); ++nr) - if (STRCMP(str, poppos_entries[nr].pp_name) == 0) + if (STRCMP(str, poppos_entries[nr].pp_name.string) == 0) return poppos_entries[nr].pp_val; if (give_error) @@ -3887,14 +3890,18 @@ f_popup_getoptions(typval_T *argvars, typval_T *rettv) for (i = 0; i < (int)ARRAY_LENGTH(poppos_entries); ++i) if (wp->w_popup_pos == poppos_entries[i].pp_val) { - dict_add_string(dict, "pos", - (char_u *)poppos_entries[i].pp_name); + dict_add_string_len(dict, "pos", + poppos_entries[i].pp_name.string, + (int)poppos_entries[i].pp_name.length); break; } - dict_add_string(dict, "close", (char_u *)( - wp->w_popup_close == POPCLOSE_BUTTON ? "button" - : wp->w_popup_close == POPCLOSE_CLICK ? "click" : "none")); + if (wp->w_popup_close == POPCLOSE_BUTTON) + dict_add_string_len(dict, "close", (char_u *)"button", STRLEN_LITERAL("button")); + else if (wp->w_popup_close == POPCLOSE_CLICK) + dict_add_string_len(dict, "close", (char_u *)"click", STRLEN_LITERAL("click")); + else + dict_add_string_len(dict, "close", (char_u *)"none", STRLEN_LITERAL("none")); #if defined(FEAT_TIMERS) dict_add_number(dict, "time", wp->w_popup_timer != NULL diff --git a/src/quickfix.c b/src/quickfix.c index 46665e8ca3..28f3655e1d 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -7176,6 +7176,7 @@ get_qfline_items(qfline_T *qfp, list_T *list) int bufnum; dict_T *dict; char_u buf[2]; + size_t buflen; // Handle entries with a non-existing buffer number. bufnum = qfp->qf_fnum; @@ -7189,6 +7190,7 @@ get_qfline_items(qfline_T *qfp, list_T *list) buf[0] = qfp->qf_type; buf[1] = NUL; + buflen = (buf[0] == NUL) ? 0 : 1; if (dict_add_number(dict, "bufnr", (long)bufnum) == FAIL || dict_add_number(dict, "lnum", (long)qfp->qf_lnum) == FAIL || dict_add_number(dict, "end_lnum", (long)qfp->qf_end_lnum) == FAIL @@ -7199,7 +7201,7 @@ get_qfline_items(qfline_T *qfp, list_T *list) || dict_add_string(dict, "module", qfp->qf_module) == FAIL || dict_add_string(dict, "pattern", qfp->qf_pattern) == FAIL || dict_add_string(dict, "text", qfp->qf_text) == FAIL - || dict_add_string(dict, "type", buf) == FAIL + || dict_add_string_len(dict, "type", buf, (int)buflen) == FAIL || (qfp->qf_user_data.v_type != VAR_UNKNOWN && dict_add_tv(dict, "user_data", &qfp->qf_user_data) == FAIL ) || dict_add_number(dict, "valid", (long)qfp->qf_valid) == FAIL) @@ -7484,7 +7486,7 @@ qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *retdict) int status = OK; if (flags & QF_GETLIST_TITLE) - status = dict_add_string(retdict, "title", (char_u *)""); + status = dict_add_string_len(retdict, "title", (char_u *)"", 0); if ((status == OK) && (flags & QF_GETLIST_ITEMS)) { list_T *l = list_alloc(); @@ -7498,7 +7500,7 @@ qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *retdict) if ((status == OK) && (flags & QF_GETLIST_WINID)) status = dict_add_number(retdict, "winid", qf_winid(qi)); if ((status == OK) && (flags & QF_GETLIST_CONTEXT)) - status = dict_add_string(retdict, "context", (char_u *)""); + status = dict_add_string_len(retdict, "context", (char_u *)"", 0); if ((status == OK) && (flags & QF_GETLIST_ID)) status = dict_add_number(retdict, "id", 0); if ((status == OK) && (flags & QF_GETLIST_IDX)) @@ -7512,7 +7514,7 @@ qf_getprop_defaults(qf_info_T *qi, int flags, int locstack, dict_T *retdict) if ((status == OK) && (flags & QF_GETLIST_QFBUFNR)) status = qf_getprop_qfbufnr(qi, retdict); if ((status == OK) && (flags & QF_GETLIST_QFTF)) - status = dict_add_string(retdict, "quickfixtextfunc", (char_u *)""); + status = dict_add_string_len(retdict, "quickfixtextfunc", (char_u *)"", 0); return status; } @@ -7589,7 +7591,7 @@ qf_getprop_ctx(qf_list_T *qfl, dict_T *retdict) status = FAIL; } else - status = dict_add_string(retdict, "context", (char_u *)""); + status = dict_add_string_len(retdict, "context", (char_u *)"", 0); return status; } @@ -7628,7 +7630,7 @@ qf_getprop_qftf(qf_list_T *qfl, dict_T *retdict) clear_tv(&tv); } else - status = dict_add_string(retdict, "quickfixtextfunc", (char_u *)""); + status = dict_add_string_len(retdict, "quickfixtextfunc", (char_u *)"", 0); return status; } diff --git a/src/register.c b/src/register.c index 0215f0153d..f7a02e8d95 100644 --- a/src/register.c +++ b/src/register.c @@ -1093,6 +1093,7 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg) list_T *list; int n; char_u buf[NUMBUFLEN + 2]; + size_t buflen; long reglen = 0; save_v_event_T save_v_event; @@ -1114,7 +1115,8 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg) // register name or empty string for unnamed operation buf[0] = (char_u)oap->regname; buf[1] = NUL; - (void)dict_add_string(v_event, "regname", buf); + buflen = (buf[0] == NUL) ? 0 : 1; + (void)dict_add_string_len(v_event, "regname", buf, (int)buflen); // motion type: inclusive or exclusive (void)dict_add_bool(v_event, "inclusive", oap->inclusive); @@ -1123,21 +1125,32 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg) buf[0] = get_op_char(oap->op_type); buf[1] = get_extra_op_char(oap->op_type); buf[2] = NUL; - (void)dict_add_string(v_event, "operator", buf); + buflen = (buf[0] == NUL) ? 0 : (buf[1] == NUL) ? 1 : 2; + (void)dict_add_string_len(v_event, "operator", buf, (int)buflen); // register type - buf[0] = NUL; - buf[1] = NUL; switch (get_reg_type(oap->regname, ®len)) { - case MLINE: buf[0] = 'V'; break; - case MCHAR: buf[0] = 'v'; break; + case MLINE: + buf[0] = 'V'; + buf[1] = NUL; + buflen = 1; + break; + case MCHAR: + buf[0] = 'v'; + buf[1] = NUL; + buflen = 1; + break; case MBLOCK: - vim_snprintf((char *)buf, sizeof(buf), "%c%ld", Ctrl_V, - reglen + 1); - break; + buflen = vim_snprintf_safelen((char *)buf, sizeof(buf), + "%c%ld", Ctrl_V, reglen + 1); + break; + default: + buf[0] = NUL; + buflen = 0; + break; } - (void)dict_add_string(v_event, "regtype", buf); + (void)dict_add_string_len(v_event, "regtype", buf, (int)buflen); // selection type - visual or not (void)dict_add_bool(v_event, "visual", oap->is_VIsual); diff --git a/src/sign.c b/src/sign.c index 6477af3072..a8dfd05eee 100644 --- a/src/sign.c +++ b/src/sign.c @@ -322,9 +322,10 @@ sign_get_info(sign_entry_T *sign) return NULL; dict_add_number(d, "id", sign->se_id); - dict_add_string(d, "group", - (sign->se_group == NULL) ? (char_u *)"" - : sign->se_group->sg_name); + if (sign->se_group == NULL) + dict_add_string_len(d, "group", (char_u *)"", 0); + else + dict_add_string(d, "group", sign->se_group->sg_name); dict_add_number(d, "lnum", sign->se_lnum); dict_add_string(d, "name", sign_typenr2name(sign->se_typenr)); dict_add_number(d, "priority", sign->se_priority); @@ -1777,32 +1778,40 @@ sign_getinfo(sign_T *sp, dict_T *retdict) { char_u *p = get_highlight_name_ext(NULL, sp->sn_line_hl - 1, FALSE); if (p == NULL) - p = (char_u *)"NONE"; - dict_add_string(retdict, "linehl", p); + dict_add_string_len(retdict, "linehl", + (char_u *)"NONE", STRLEN_LITERAL("NONE")); + else + dict_add_string(retdict, "linehl", p); } if (sp->sn_text_hl > 0) { char_u *p = get_highlight_name_ext(NULL, sp->sn_text_hl - 1, FALSE); if (p == NULL) - p = (char_u *)"NONE"; - dict_add_string(retdict, "texthl", p); + dict_add_string_len(retdict, "texthl", + (char_u *)"NONE", STRLEN_LITERAL("NONE")); + else + dict_add_string(retdict, "texthl", p); } if (sp->sn_cul_hl > 0) { char_u *p = get_highlight_name_ext(NULL, sp->sn_cul_hl - 1, FALSE); if (p == NULL) - p = (char_u *)"NONE"; - dict_add_string(retdict, "culhl", p); + dict_add_string_len(retdict, "culhl", + (char_u *)"NONE", STRLEN_LITERAL("NONE")); + else + dict_add_string(retdict, "culhl", p); } if (sp->sn_num_hl > 0) { char_u *p = get_highlight_name_ext(NULL, sp->sn_num_hl - 1, FALSE); if (p == NULL) - p = (char_u *)"NONE"; - dict_add_string(retdict, "numhl", p); + dict_add_string_len(retdict, "numhl", + (char_u *)"NONE", STRLEN_LITERAL("NONE")); + else + dict_add_string(retdict, "numhl", p); } } diff --git a/src/term.c b/src/term.c index 468a7eb488..882df66551 100644 --- a/src/term.c +++ b/src/term.c @@ -1587,7 +1587,8 @@ f_terminalprops(typval_T *argvars UNUSED, typval_T *rettv) value[0] = term_props[i].tpr_status; value[1] = NUL; - dict_add_string(rettv->vval.v_dict, term_props[i].tpr_name, value); + dict_add_string_len(rettv->vval.v_dict, term_props[i].tpr_name, + value, (value[0] == NUL) ? 0 : 1); } # endif } diff --git a/src/terminal.c b/src/terminal.c index 92c889fc26..7e812fc972 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -6784,14 +6784,14 @@ f_term_scrape(typval_T *argvars, typval_T *rettv) VTermScreenCellAttrs attrs; VTermColor fg, bg; char_u rgb[8]; + size_t rgblen; char_u mbs[MB_MAXBYTES * VTERM_MAX_CHARS_PER_CELL + 1]; - int off = 0; + size_t mbslen; int i; if (screen == NULL) { cellattr_T *cellattr; - int len; // vterm has finished, get the cell from scrollback if (pos.col >= line->sb_cols) @@ -6801,10 +6801,10 @@ f_term_scrape(typval_T *argvars, typval_T *rettv) attrs = cellattr->attrs; fg = cellattr->fg; bg = cellattr->bg; - len = mb_ptr2len(p); - mch_memmove(mbs, p, len); - mbs[len] = NUL; - p += len; + mbslen = mb_ptr2len(p); + mch_memmove(mbs, p, mbslen); + mbs[mbslen] = NUL; + p += mbslen; } else { @@ -6812,13 +6812,15 @@ f_term_scrape(typval_T *argvars, typval_T *rettv) if (vterm_screen_get_cell(screen, pos, &cell) == 0) break; + + mbslen = 0; for (i = 0; i < VTERM_MAX_CHARS_PER_CELL; ++i) { if (cell.chars[i] == 0) break; - off += (*utf_char2bytes)((int)cell.chars[i], mbs + off); + mbslen += (*utf_char2bytes)((int)cell.chars[i], mbs + mbslen); } - mbs[off] = NUL; + mbs[mbslen] = NUL; width = cell.width; attrs = cell.attrs; fg = cell.fg; @@ -6829,14 +6831,14 @@ f_term_scrape(typval_T *argvars, typval_T *rettv) break; list_append_dict(l, dcell); - dict_add_string(dcell, "chars", mbs); + dict_add_string_len(dcell, "chars", mbs, (int)mbslen); - vim_snprintf((char *)rgb, 8, "#%02x%02x%02x", - fg.red, fg.green, fg.blue); - dict_add_string(dcell, "fg", rgb); - vim_snprintf((char *)rgb, 8, "#%02x%02x%02x", - bg.red, bg.green, bg.blue); - dict_add_string(dcell, "bg", rgb); + rgblen = vim_snprintf_safelen((char *)rgb, sizeof(rgb), + "#%02x%02x%02x", fg.red, fg.green, fg.blue); + dict_add_string_len(dcell, "fg", rgb, (int)rgblen); + rgblen = vim_snprintf_safelen((char *)rgb, sizeof(rgb), + "#%02x%02x%02x", bg.red, bg.green, bg.blue); + dict_add_string_len(dcell, "bg", rgb, (int)rgblen); dict_add_number(dcell, "attr", cell2attr(term, NULL, &attrs, &fg, &bg)); diff --git a/src/textprop.c b/src/textprop.c index 78e9276e5d..782701aff1 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -1727,23 +1727,35 @@ prop_fill_dict(dict_T *dict, textprop_T *prop, buf_T *buf) dict_add_number(dict, "type_bufnr", 0); if (virtualtext_prop) { + string_T text_align = {NULL, 0}; + // virtual text property - u.tp_text must be set by caller dict_add_string(dict, "text", prop->u.tp_text); // text_align - char_u *text_align = NULL; if (prop->tp_flags & TP_FLAG_ALIGN_RIGHT) - text_align = (char_u *)"right"; + { + text_align.string = (char_u *)"right"; + text_align.length = STRLEN_LITERAL("right"); + } else if (prop->tp_flags & TP_FLAG_ALIGN_ABOVE) - text_align = (char_u *)"above"; + { + text_align.string = (char_u *)"above"; + text_align.length = STRLEN_LITERAL("above"); + } else if (prop->tp_flags & TP_FLAG_ALIGN_BELOW) - text_align = (char_u *)"below"; - if (text_align != NULL) - dict_add_string(dict, "text_align", text_align); + { + text_align.string = (char_u *)"below"; + text_align.length = STRLEN_LITERAL("below"); + } + if (text_align.string != NULL) + dict_add_string_len(dict, "text_align", + text_align.string, (int)text_align.length); // text_wrap if (prop->tp_flags & TP_FLAG_WRAP) - dict_add_string(dict, "text_wrap", (char_u *)"wrap"); + dict_add_string_len(dict, "text_wrap", + (char_u *)"wrap", STRLEN_LITERAL("wrap")); if (prop->tp_padleft != 0) dict_add_number(dict, "text_padding_left", prop->tp_padleft); } diff --git a/src/version.c b/src/version.c index 20e593ac87..9c634f7daa 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 368, /**/ 367, /**/ From 6ecff78129307676f74c68bd08648465f980c6fd Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Sun, 19 Apr 2026 21:51:51 +0000 Subject: [PATCH 075/545] patch 9.2.0369: multiple definitions of STRING_INIT macro Problem: multiple definitions of STRING_INIT macro Solution: Refactor use of STRING_INIT and use a single STR_LITERAL_INIT() macro instead (Hirohito Higashi) Consolidate the ad-hoc STRING_INIT() macros that were defined and used locally in multiple source files. Define a single STR_LITERAL_INIT() macro in macros.h and replace all previous STRING_INIT() usages with it. No functional change. related: #19999 closes: #20023 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/clientserver.c | 7 +- src/insexpand.c | 39 +++-- src/macros.h | 3 + src/misc2.c | 348 ++++++++++++++++++++++----------------------- src/os_mswin.c | 53 ++++--- src/os_unix.c | 7 +- src/popupwin.c | 13 +- src/version.c | 2 + 8 files changed, 228 insertions(+), 244 deletions(-) diff --git a/src/clientserver.c b/src/clientserver.c index ae5130dd3e..9186f4db16 100644 --- a/src/clientserver.c +++ b/src/clientserver.c @@ -677,13 +677,10 @@ build_drop_cmd( string_T cdp; char_u *cwd; // reset wildignore temporarily -# define STRING_INIT(s) \ - {(char_u *)(s), STRLEN_LITERAL(s)} const string_T wig[] = { - STRING_INIT(":let g:_wig=&wig|set wig="), - STRING_INIT(":let &wig=g:_wig|unlet g:_wig") + STR_LITERAL_INIT(":let g:_wig=&wig|set wig="), + STR_LITERAL_INIT(":let &wig=g:_wig|unlet g:_wig") }; -# undef STRING_INIT if (filec > 0 && filev[0][0] == '+') { diff --git a/src/insexpand.c b/src/insexpand.c index 72c3237776..7545e9b4ec 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -67,30 +67,27 @@ static char *ctrl_x_msgs[] = }; #if defined(FEAT_COMPL_FUNC) || defined(FEAT_EVAL) -# define STRING_INIT(s) \ - {(char_u *)(s), STRLEN_LITERAL(s)} static string_T ctrl_x_mode_names[] = { - STRING_INIT("keyword"), - STRING_INIT("ctrl_x"), - STRING_INIT("scroll"), - STRING_INIT("whole_line"), - STRING_INIT("files"), - STRING_INIT("tags"), - STRING_INIT("path_patterns"), - STRING_INIT("path_defines"), - STRING_INIT("unknown"), // CTRL_X_FINISHED - STRING_INIT("dictionary"), - STRING_INIT("thesaurus"), - STRING_INIT("cmdline"), - STRING_INIT("function"), - STRING_INIT("omni"), - STRING_INIT("spell"), + STR_LITERAL_INIT("keyword"), + STR_LITERAL_INIT("ctrl_x"), + STR_LITERAL_INIT("scroll"), + STR_LITERAL_INIT("whole_line"), + STR_LITERAL_INIT("files"), + STR_LITERAL_INIT("tags"), + STR_LITERAL_INIT("path_patterns"), + STR_LITERAL_INIT("path_defines"), + STR_LITERAL_INIT("unknown"), // CTRL_X_FINISHED + STR_LITERAL_INIT("dictionary"), + STR_LITERAL_INIT("thesaurus"), + STR_LITERAL_INIT("cmdline"), + STR_LITERAL_INIT("function"), + STR_LITERAL_INIT("omni"), + STR_LITERAL_INIT("spell"), {NULL, 0}, // CTRL_X_LOCAL_MSG is only used in "ctrl_x_msgs" - STRING_INIT("eval"), - STRING_INIT("cmdline"), - STRING_INIT("register"), + STR_LITERAL_INIT("eval"), + STR_LITERAL_INIT("cmdline"), + STR_LITERAL_INIT("register"), }; -# undef STRING_INIT #endif /* diff --git a/src/macros.h b/src/macros.h index 9007b5b37a..5fdcfdb2a8 100644 --- a/src/macros.h +++ b/src/macros.h @@ -373,6 +373,9 @@ s.length = 0; \ } while (0) +#define STR_LITERAL_INIT(s) \ + {(char_u *)(s), STRLEN_LITERAL(s)} + // Whether a command index indicates a user command. #define IS_USER_CMDIDX(idx) ((int)(idx) < 0) diff --git a/src/misc2.c b/src/misc2.c index 1d3a35b062..aca4d3c93b 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -915,8 +915,6 @@ static char_u modifier_keys_table[] = NUL }; -#define STRING_INIT(s) \ - {(char_u *)(s), STRLEN_LITERAL(s)} static struct key_name_entry { int enabled; // is this entry available (TRUE/FALSE)? @@ -926,200 +924,199 @@ static struct key_name_entry } key_names_table[] = // Must be sorted by the 'name.string' field in ascending order because it is used by bsearch()! { - {TRUE, K_BS, STRING_INIT("BackSpace"), TRUE}, - {TRUE, '|', STRING_INIT("Bar"), FALSE}, - {TRUE, K_BS, STRING_INIT("BS"), FALSE}, - {TRUE, '\\', STRING_INIT("Bslash"), FALSE}, - {TRUE, K_COMMAND, STRING_INIT("Cmd"), FALSE}, - {TRUE, CAR, STRING_INIT("CR"), FALSE}, - {TRUE, CSI, STRING_INIT("CSI"), FALSE}, - {TRUE, K_CURSORHOLD, STRING_INIT("CursorHold"), FALSE}, + {TRUE, K_BS, STR_LITERAL_INIT("BackSpace"), TRUE}, + {TRUE, '|', STR_LITERAL_INIT("Bar"), FALSE}, + {TRUE, K_BS, STR_LITERAL_INIT("BS"), FALSE}, + {TRUE, '\\', STR_LITERAL_INIT("Bslash"), FALSE}, + {TRUE, K_COMMAND, STR_LITERAL_INIT("Cmd"), FALSE}, + {TRUE, CAR, STR_LITERAL_INIT("CR"), FALSE}, + {TRUE, CSI, STR_LITERAL_INIT("CSI"), FALSE}, + {TRUE, K_CURSORHOLD, STR_LITERAL_INIT("CursorHold"), FALSE}, { #ifdef FEAT_MOUSE_DEC TRUE, #else FALSE, #endif - K_DEC_MOUSE, STRING_INIT("DecMouse"), FALSE}, - {TRUE, K_DEL, STRING_INIT("Del"), FALSE}, - {TRUE, K_DEL, STRING_INIT("Delete"), TRUE}, - {TRUE, K_DOWN, STRING_INIT("Down"), FALSE}, - {TRUE, K_DROP, STRING_INIT("Drop"), FALSE}, - {TRUE, K_END, STRING_INIT("End"), FALSE}, - {TRUE, CAR, STRING_INIT("Enter"), TRUE}, - {TRUE, ESC, STRING_INIT("Esc"), FALSE}, - - {TRUE, K_F1, STRING_INIT("F1"), FALSE}, - {TRUE, K_F10, STRING_INIT("F10"), FALSE}, - {TRUE, K_F11, STRING_INIT("F11"), FALSE}, - {TRUE, K_F12, STRING_INIT("F12"), FALSE}, - {TRUE, K_F13, STRING_INIT("F13"), FALSE}, - {TRUE, K_F14, STRING_INIT("F14"), FALSE}, - {TRUE, K_F15, STRING_INIT("F15"), FALSE}, - {TRUE, K_F16, STRING_INIT("F16"), FALSE}, - {TRUE, K_F17, STRING_INIT("F17"), FALSE}, - {TRUE, K_F18, STRING_INIT("F18"), FALSE}, - {TRUE, K_F19, STRING_INIT("F19"), FALSE}, - - {TRUE, K_F2, STRING_INIT("F2"), FALSE}, - {TRUE, K_F20, STRING_INIT("F20"), FALSE}, - {TRUE, K_F21, STRING_INIT("F21"), FALSE}, - {TRUE, K_F22, STRING_INIT("F22"), FALSE}, - {TRUE, K_F23, STRING_INIT("F23"), FALSE}, - {TRUE, K_F24, STRING_INIT("F24"), FALSE}, - {TRUE, K_F25, STRING_INIT("F25"), FALSE}, - {TRUE, K_F26, STRING_INIT("F26"), FALSE}, - {TRUE, K_F27, STRING_INIT("F27"), FALSE}, - {TRUE, K_F28, STRING_INIT("F28"), FALSE}, - {TRUE, K_F29, STRING_INIT("F29"), FALSE}, - - {TRUE, K_F3, STRING_INIT("F3"), FALSE}, - {TRUE, K_F30, STRING_INIT("F30"), FALSE}, - {TRUE, K_F31, STRING_INIT("F31"), FALSE}, - {TRUE, K_F32, STRING_INIT("F32"), FALSE}, - {TRUE, K_F33, STRING_INIT("F33"), FALSE}, - {TRUE, K_F34, STRING_INIT("F34"), FALSE}, - {TRUE, K_F35, STRING_INIT("F35"), FALSE}, - {TRUE, K_F36, STRING_INIT("F36"), FALSE}, - {TRUE, K_F37, STRING_INIT("F37"), FALSE}, - - {TRUE, K_F4, STRING_INIT("F4"), FALSE}, - {TRUE, K_F5, STRING_INIT("F5"), FALSE}, - {TRUE, K_F6, STRING_INIT("F6"), FALSE}, - {TRUE, K_F7, STRING_INIT("F7"), FALSE}, - {TRUE, K_F8, STRING_INIT("F8"), FALSE}, - {TRUE, K_F9, STRING_INIT("F9"), FALSE}, - - {TRUE, K_FOCUSGAINED, STRING_INIT("FocusGained"), FALSE}, - {TRUE, K_FOCUSLOST, STRING_INIT("FocusLost"), FALSE}, - {TRUE, K_HELP, STRING_INIT("Help"), FALSE}, - {TRUE, K_HOME, STRING_INIT("Home"), FALSE}, - {TRUE, K_IGNORE, STRING_INIT("Ignore"), FALSE}, - {TRUE, K_INS, STRING_INIT("Ins"), TRUE}, - {TRUE, K_INS, STRING_INIT("Insert"), FALSE}, + K_DEC_MOUSE, STR_LITERAL_INIT("DecMouse"), FALSE}, + {TRUE, K_DEL, STR_LITERAL_INIT("Del"), FALSE}, + {TRUE, K_DEL, STR_LITERAL_INIT("Delete"), TRUE}, + {TRUE, K_DOWN, STR_LITERAL_INIT("Down"), FALSE}, + {TRUE, K_DROP, STR_LITERAL_INIT("Drop"), FALSE}, + {TRUE, K_END, STR_LITERAL_INIT("End"), FALSE}, + {TRUE, CAR, STR_LITERAL_INIT("Enter"), TRUE}, + {TRUE, ESC, STR_LITERAL_INIT("Esc"), FALSE}, + + {TRUE, K_F1, STR_LITERAL_INIT("F1"), FALSE}, + {TRUE, K_F10, STR_LITERAL_INIT("F10"), FALSE}, + {TRUE, K_F11, STR_LITERAL_INIT("F11"), FALSE}, + {TRUE, K_F12, STR_LITERAL_INIT("F12"), FALSE}, + {TRUE, K_F13, STR_LITERAL_INIT("F13"), FALSE}, + {TRUE, K_F14, STR_LITERAL_INIT("F14"), FALSE}, + {TRUE, K_F15, STR_LITERAL_INIT("F15"), FALSE}, + {TRUE, K_F16, STR_LITERAL_INIT("F16"), FALSE}, + {TRUE, K_F17, STR_LITERAL_INIT("F17"), FALSE}, + {TRUE, K_F18, STR_LITERAL_INIT("F18"), FALSE}, + {TRUE, K_F19, STR_LITERAL_INIT("F19"), FALSE}, + + {TRUE, K_F2, STR_LITERAL_INIT("F2"), FALSE}, + {TRUE, K_F20, STR_LITERAL_INIT("F20"), FALSE}, + {TRUE, K_F21, STR_LITERAL_INIT("F21"), FALSE}, + {TRUE, K_F22, STR_LITERAL_INIT("F22"), FALSE}, + {TRUE, K_F23, STR_LITERAL_INIT("F23"), FALSE}, + {TRUE, K_F24, STR_LITERAL_INIT("F24"), FALSE}, + {TRUE, K_F25, STR_LITERAL_INIT("F25"), FALSE}, + {TRUE, K_F26, STR_LITERAL_INIT("F26"), FALSE}, + {TRUE, K_F27, STR_LITERAL_INIT("F27"), FALSE}, + {TRUE, K_F28, STR_LITERAL_INIT("F28"), FALSE}, + {TRUE, K_F29, STR_LITERAL_INIT("F29"), FALSE}, + + {TRUE, K_F3, STR_LITERAL_INIT("F3"), FALSE}, + {TRUE, K_F30, STR_LITERAL_INIT("F30"), FALSE}, + {TRUE, K_F31, STR_LITERAL_INIT("F31"), FALSE}, + {TRUE, K_F32, STR_LITERAL_INIT("F32"), FALSE}, + {TRUE, K_F33, STR_LITERAL_INIT("F33"), FALSE}, + {TRUE, K_F34, STR_LITERAL_INIT("F34"), FALSE}, + {TRUE, K_F35, STR_LITERAL_INIT("F35"), FALSE}, + {TRUE, K_F36, STR_LITERAL_INIT("F36"), FALSE}, + {TRUE, K_F37, STR_LITERAL_INIT("F37"), FALSE}, + + {TRUE, K_F4, STR_LITERAL_INIT("F4"), FALSE}, + {TRUE, K_F5, STR_LITERAL_INIT("F5"), FALSE}, + {TRUE, K_F6, STR_LITERAL_INIT("F6"), FALSE}, + {TRUE, K_F7, STR_LITERAL_INIT("F7"), FALSE}, + {TRUE, K_F8, STR_LITERAL_INIT("F8"), FALSE}, + {TRUE, K_F9, STR_LITERAL_INIT("F9"), FALSE}, + + {TRUE, K_FOCUSGAINED, STR_LITERAL_INIT("FocusGained"), FALSE}, + {TRUE, K_FOCUSLOST, STR_LITERAL_INIT("FocusLost"), FALSE}, + {TRUE, K_HELP, STR_LITERAL_INIT("Help"), FALSE}, + {TRUE, K_HOME, STR_LITERAL_INIT("Home"), FALSE}, + {TRUE, K_IGNORE, STR_LITERAL_INIT("Ignore"), FALSE}, + {TRUE, K_INS, STR_LITERAL_INIT("Ins"), TRUE}, + {TRUE, K_INS, STR_LITERAL_INIT("Insert"), FALSE}, { #ifdef FEAT_MOUSE_JSB TRUE, #else FALSE, #endif - K_JSBTERM_MOUSE, STRING_INIT("JsbMouse"), FALSE}, - {TRUE, K_K0, STRING_INIT("k0"), FALSE}, - {TRUE, K_K1, STRING_INIT("k1"), FALSE}, - {TRUE, K_K2, STRING_INIT("k2"), FALSE}, - {TRUE, K_K3, STRING_INIT("k3"), FALSE}, - {TRUE, K_K4, STRING_INIT("k4"), FALSE}, - {TRUE, K_K5, STRING_INIT("k5"), FALSE}, - {TRUE, K_K6, STRING_INIT("k6"), FALSE}, - {TRUE, K_K7, STRING_INIT("k7"), FALSE}, - {TRUE, K_K8, STRING_INIT("k8"), FALSE}, - {TRUE, K_K9, STRING_INIT("k9"), FALSE}, - - {TRUE, K_KDEL, STRING_INIT("kDel"), FALSE}, - {TRUE, K_KDIVIDE, STRING_INIT("kDivide"), FALSE}, - {TRUE, K_KEND, STRING_INIT("kEnd"), FALSE}, - {TRUE, K_KENTER, STRING_INIT("kEnter"), FALSE}, - {TRUE, K_KHOME, STRING_INIT("kHome"), FALSE}, - {TRUE, K_KINS, STRING_INIT("kInsert"), FALSE}, - {TRUE, K_KMINUS, STRING_INIT("kMinus"), FALSE}, - {TRUE, K_KMULTIPLY, STRING_INIT("kMultiply"), FALSE}, - {TRUE, K_KPAGEDOWN, STRING_INIT("kPageDown"), FALSE}, - {TRUE, K_KPAGEUP, STRING_INIT("kPageUp"), FALSE}, - {TRUE, K_KPLUS, STRING_INIT("kPlus"), FALSE}, - {TRUE, K_KPOINT, STRING_INIT("kPoint"), FALSE}, - {TRUE, K_LEFT, STRING_INIT("Left"), FALSE}, - {TRUE, K_LEFTDRAG, STRING_INIT("LeftDrag"), FALSE}, - {TRUE, K_LEFTMOUSE, STRING_INIT("LeftMouse"), FALSE}, - {TRUE, K_LEFTMOUSE_NM, STRING_INIT("LeftMouseNM"), FALSE}, - {TRUE, K_LEFTRELEASE, STRING_INIT("LeftRelease"), FALSE}, - {TRUE, K_LEFTRELEASE_NM, STRING_INIT("LeftReleaseNM"), FALSE}, - {TRUE, NL, STRING_INIT("LF"), TRUE}, - {TRUE, NL, STRING_INIT("LineFeed"), TRUE}, - {TRUE, '<', STRING_INIT("lt"), FALSE}, - {TRUE, K_MIDDLEDRAG, STRING_INIT("MiddleDrag"), FALSE}, - {TRUE, K_MIDDLEMOUSE, STRING_INIT("MiddleMouse"), FALSE}, - {TRUE, K_MIDDLERELEASE, STRING_INIT("MiddleRelease"), FALSE}, - {TRUE, K_MOUSE, STRING_INIT("Mouse"), FALSE}, - {TRUE, K_MOUSEDOWN, STRING_INIT("MouseDown"), TRUE}, - {TRUE, K_MOUSEMOVE, STRING_INIT("MouseMove"), FALSE}, - {TRUE, K_MOUSEUP, STRING_INIT("MouseUp"), TRUE}, + K_JSBTERM_MOUSE, STR_LITERAL_INIT("JsbMouse"), FALSE}, + {TRUE, K_K0, STR_LITERAL_INIT("k0"), FALSE}, + {TRUE, K_K1, STR_LITERAL_INIT("k1"), FALSE}, + {TRUE, K_K2, STR_LITERAL_INIT("k2"), FALSE}, + {TRUE, K_K3, STR_LITERAL_INIT("k3"), FALSE}, + {TRUE, K_K4, STR_LITERAL_INIT("k4"), FALSE}, + {TRUE, K_K5, STR_LITERAL_INIT("k5"), FALSE}, + {TRUE, K_K6, STR_LITERAL_INIT("k6"), FALSE}, + {TRUE, K_K7, STR_LITERAL_INIT("k7"), FALSE}, + {TRUE, K_K8, STR_LITERAL_INIT("k8"), FALSE}, + {TRUE, K_K9, STR_LITERAL_INIT("k9"), FALSE}, + + {TRUE, K_KDEL, STR_LITERAL_INIT("kDel"), FALSE}, + {TRUE, K_KDIVIDE, STR_LITERAL_INIT("kDivide"), FALSE}, + {TRUE, K_KEND, STR_LITERAL_INIT("kEnd"), FALSE}, + {TRUE, K_KENTER, STR_LITERAL_INIT("kEnter"), FALSE}, + {TRUE, K_KHOME, STR_LITERAL_INIT("kHome"), FALSE}, + {TRUE, K_KINS, STR_LITERAL_INIT("kInsert"), FALSE}, + {TRUE, K_KMINUS, STR_LITERAL_INIT("kMinus"), FALSE}, + {TRUE, K_KMULTIPLY, STR_LITERAL_INIT("kMultiply"), FALSE}, + {TRUE, K_KPAGEDOWN, STR_LITERAL_INIT("kPageDown"), FALSE}, + {TRUE, K_KPAGEUP, STR_LITERAL_INIT("kPageUp"), FALSE}, + {TRUE, K_KPLUS, STR_LITERAL_INIT("kPlus"), FALSE}, + {TRUE, K_KPOINT, STR_LITERAL_INIT("kPoint"), FALSE}, + {TRUE, K_LEFT, STR_LITERAL_INIT("Left"), FALSE}, + {TRUE, K_LEFTDRAG, STR_LITERAL_INIT("LeftDrag"), FALSE}, + {TRUE, K_LEFTMOUSE, STR_LITERAL_INIT("LeftMouse"), FALSE}, + {TRUE, K_LEFTMOUSE_NM, STR_LITERAL_INIT("LeftMouseNM"), FALSE}, + {TRUE, K_LEFTRELEASE, STR_LITERAL_INIT("LeftRelease"), FALSE}, + {TRUE, K_LEFTRELEASE_NM, STR_LITERAL_INIT("LeftReleaseNM"), FALSE}, + {TRUE, NL, STR_LITERAL_INIT("LF"), TRUE}, + {TRUE, NL, STR_LITERAL_INIT("LineFeed"), TRUE}, + {TRUE, '<', STR_LITERAL_INIT("lt"), FALSE}, + {TRUE, K_MIDDLEDRAG, STR_LITERAL_INIT("MiddleDrag"), FALSE}, + {TRUE, K_MIDDLEMOUSE, STR_LITERAL_INIT("MiddleMouse"), FALSE}, + {TRUE, K_MIDDLERELEASE, STR_LITERAL_INIT("MiddleRelease"), FALSE}, + {TRUE, K_MOUSE, STR_LITERAL_INIT("Mouse"), FALSE}, + {TRUE, K_MOUSEDOWN, STR_LITERAL_INIT("MouseDown"), TRUE}, + {TRUE, K_MOUSEMOVE, STR_LITERAL_INIT("MouseMove"), FALSE}, + {TRUE, K_MOUSEUP, STR_LITERAL_INIT("MouseUp"), TRUE}, { #ifdef FEAT_MOUSE_NET TRUE, #else FALSE, #endif - K_NETTERM_MOUSE, STRING_INIT("NetMouse"), FALSE}, - {TRUE, NL, STRING_INIT("NewLine"), TRUE}, - {TRUE, NL, STRING_INIT("NL"), FALSE}, - {TRUE, K_ZERO, STRING_INIT("Nul"), FALSE}, - {TRUE, OSC, STRING_INIT("OSC"), FALSE}, - {TRUE, K_PAGEDOWN, STRING_INIT("PageDown"), FALSE}, - {TRUE, K_PAGEUP, STRING_INIT("PageUp"), FALSE}, - {TRUE, K_PE, STRING_INIT("PasteEnd"), FALSE}, - {TRUE, K_PS, STRING_INIT("PasteStart"), FALSE}, - {TRUE, K_PLUG, STRING_INIT("Plug"), FALSE}, + K_NETTERM_MOUSE, STR_LITERAL_INIT("NetMouse"), FALSE}, + {TRUE, NL, STR_LITERAL_INIT("NewLine"), TRUE}, + {TRUE, NL, STR_LITERAL_INIT("NL"), FALSE}, + {TRUE, K_ZERO, STR_LITERAL_INIT("Nul"), FALSE}, + {TRUE, OSC, STR_LITERAL_INIT("OSC"), FALSE}, + {TRUE, K_PAGEDOWN, STR_LITERAL_INIT("PageDown"), FALSE}, + {TRUE, K_PAGEUP, STR_LITERAL_INIT("PageUp"), FALSE}, + {TRUE, K_PE, STR_LITERAL_INIT("PasteEnd"), FALSE}, + {TRUE, K_PS, STR_LITERAL_INIT("PasteStart"), FALSE}, + {TRUE, K_PLUG, STR_LITERAL_INIT("Plug"), FALSE}, { #ifdef FEAT_MOUSE_PTERM TRUE, #else FALSE, #endif - K_PTERM_MOUSE, STRING_INIT("PtermMouse"), FALSE}, - {TRUE, CAR, STRING_INIT("Return"), TRUE}, - {TRUE, K_RIGHT, STRING_INIT("Right"), FALSE}, - {TRUE, K_RIGHTDRAG, STRING_INIT("RightDrag"), FALSE}, - {TRUE, K_RIGHTMOUSE, STRING_INIT("RightMouse"), FALSE}, - {TRUE, K_RIGHTRELEASE, STRING_INIT("RightRelease"), FALSE}, - {TRUE, K_SCRIPT_COMMAND, STRING_INIT("ScriptCmd"), FALSE}, - {TRUE, K_MOUSEUP, STRING_INIT("ScrollWheelDown"), FALSE}, - {TRUE, K_MOUSERIGHT, STRING_INIT("ScrollWheelLeft"), FALSE}, - {TRUE, K_MOUSELEFT, STRING_INIT("ScrollWheelRight"), FALSE}, - {TRUE, K_MOUSEDOWN, STRING_INIT("ScrollWheelUp"), FALSE}, - {TRUE, K_SGR_MOUSE, STRING_INIT("SgrMouse"), FALSE}, - {TRUE, K_SGR_MOUSERELEASE, STRING_INIT("SgrMouseRelease"), FALSE}, + K_PTERM_MOUSE, STR_LITERAL_INIT("PtermMouse"), FALSE}, + {TRUE, CAR, STR_LITERAL_INIT("Return"), TRUE}, + {TRUE, K_RIGHT, STR_LITERAL_INIT("Right"), FALSE}, + {TRUE, K_RIGHTDRAG, STR_LITERAL_INIT("RightDrag"), FALSE}, + {TRUE, K_RIGHTMOUSE, STR_LITERAL_INIT("RightMouse"), FALSE}, + {TRUE, K_RIGHTRELEASE, STR_LITERAL_INIT("RightRelease"), FALSE}, + {TRUE, K_SCRIPT_COMMAND, STR_LITERAL_INIT("ScriptCmd"), FALSE}, + {TRUE, K_MOUSEUP, STR_LITERAL_INIT("ScrollWheelDown"), FALSE}, + {TRUE, K_MOUSERIGHT, STR_LITERAL_INIT("ScrollWheelLeft"), FALSE}, + {TRUE, K_MOUSELEFT, STR_LITERAL_INIT("ScrollWheelRight"), FALSE}, + {TRUE, K_MOUSEDOWN, STR_LITERAL_INIT("ScrollWheelUp"), FALSE}, + {TRUE, K_SGR_MOUSE, STR_LITERAL_INIT("SgrMouse"), FALSE}, + {TRUE, K_SGR_MOUSERELEASE, STR_LITERAL_INIT("SgrMouseRelease"), FALSE}, { #ifdef FEAT_EVAL TRUE, #else FALSE, #endif - K_SNR, STRING_INIT("SNR"), FALSE}, - {TRUE, ' ', STRING_INIT("Space"), FALSE}, - {TRUE, TAB, STRING_INIT("Tab"), FALSE}, - {TRUE, K_TAB, STRING_INIT("Tab"), FALSE}, - {TRUE, K_UNDO, STRING_INIT("Undo"), FALSE}, - {TRUE, K_UP, STRING_INIT("Up"), FALSE}, + K_SNR, STR_LITERAL_INIT("SNR"), FALSE}, + {TRUE, ' ', STR_LITERAL_INIT("Space"), FALSE}, + {TRUE, TAB, STR_LITERAL_INIT("Tab"), FALSE}, + {TRUE, K_TAB, STR_LITERAL_INIT("Tab"), FALSE}, + {TRUE, K_UNDO, STR_LITERAL_INIT("Undo"), FALSE}, + {TRUE, K_UP, STR_LITERAL_INIT("Up"), FALSE}, { #ifdef FEAT_MOUSE_URXVT TRUE, #else FALSE, #endif - K_URXVT_MOUSE, STRING_INIT("UrxvtMouse"), FALSE}, - {TRUE, K_X1DRAG, STRING_INIT("X1Drag"), FALSE}, - {TRUE, K_X1MOUSE, STRING_INIT("X1Mouse"), FALSE}, - {TRUE, K_X1RELEASE, STRING_INIT("X1Release"), FALSE}, - {TRUE, K_X2DRAG, STRING_INIT("X2Drag"), FALSE}, - {TRUE, K_X2MOUSE, STRING_INIT("X2Mouse"), FALSE}, - {TRUE, K_X2RELEASE, STRING_INIT("X2Release"), FALSE}, - {TRUE, K_CSI, STRING_INIT("xCSI"), FALSE}, - {TRUE, K_XDOWN, STRING_INIT("xDown"), FALSE}, - {TRUE, K_XEND, STRING_INIT("xEnd"), FALSE}, - {TRUE, K_XF1, STRING_INIT("xF1"), FALSE}, - {TRUE, K_XF2, STRING_INIT("xF2"), FALSE}, - {TRUE, K_XF3, STRING_INIT("xF3"), FALSE}, - {TRUE, K_XF4, STRING_INIT("xF4"), FALSE}, - {TRUE, K_XHOME, STRING_INIT("xHome"), FALSE}, - {TRUE, K_XLEFT, STRING_INIT("xLeft"), FALSE}, - {TRUE, K_OSC, STRING_INIT("xOSC"), FALSE}, - {TRUE, K_XRIGHT, STRING_INIT("xRight"), FALSE}, - {TRUE, K_XUP, STRING_INIT("xUp"), FALSE}, - {TRUE, K_ZEND, STRING_INIT("zEnd"), FALSE}, - {TRUE, K_ZHOME, STRING_INIT("zHome"), FALSE} + K_URXVT_MOUSE, STR_LITERAL_INIT("UrxvtMouse"), FALSE}, + {TRUE, K_X1DRAG, STR_LITERAL_INIT("X1Drag"), FALSE}, + {TRUE, K_X1MOUSE, STR_LITERAL_INIT("X1Mouse"), FALSE}, + {TRUE, K_X1RELEASE, STR_LITERAL_INIT("X1Release"), FALSE}, + {TRUE, K_X2DRAG, STR_LITERAL_INIT("X2Drag"), FALSE}, + {TRUE, K_X2MOUSE, STR_LITERAL_INIT("X2Mouse"), FALSE}, + {TRUE, K_X2RELEASE, STR_LITERAL_INIT("X2Release"), FALSE}, + {TRUE, K_CSI, STR_LITERAL_INIT("xCSI"), FALSE}, + {TRUE, K_XDOWN, STR_LITERAL_INIT("xDown"), FALSE}, + {TRUE, K_XEND, STR_LITERAL_INIT("xEnd"), FALSE}, + {TRUE, K_XF1, STR_LITERAL_INIT("xF1"), FALSE}, + {TRUE, K_XF2, STR_LITERAL_INIT("xF2"), FALSE}, + {TRUE, K_XF3, STR_LITERAL_INIT("xF3"), FALSE}, + {TRUE, K_XF4, STR_LITERAL_INIT("xF4"), FALSE}, + {TRUE, K_XHOME, STR_LITERAL_INIT("xHome"), FALSE}, + {TRUE, K_XLEFT, STR_LITERAL_INIT("xLeft"), FALSE}, + {TRUE, K_OSC, STR_LITERAL_INIT("xOSC"), FALSE}, + {TRUE, K_XRIGHT, STR_LITERAL_INIT("xRight"), FALSE}, + {TRUE, K_XUP, STR_LITERAL_INIT("xUp"), FALSE}, + {TRUE, K_ZEND, STR_LITERAL_INIT("zEnd"), FALSE}, + {TRUE, K_ZHOME, STR_LITERAL_INIT("zHome"), FALSE} // NOTE: When adding a long name update MAX_KEY_NAME_LEN. }; -#undef STRING_INIT /* * Return the modifier mask bit (MOD_MASK_*) which corresponds to the given @@ -2148,29 +2145,26 @@ cursorentry_T shape_table[SHAPE_IDX_COUNT] = * Table with names for mouse shapes. Keep in sync with all the tables for * mch_set_mouse_shape()!. */ -# define STRING_INIT(s) \ - {(char_u *)(s), STRLEN_LITERAL(s)} static string_T mshape_names[] = { - STRING_INIT("arrow"), // default, must be the first one - STRING_INIT("blank"), // hidden - STRING_INIT("beam"), - STRING_INIT("updown"), - STRING_INIT("udsizing"), - STRING_INIT("leftright"), - STRING_INIT("lrsizing"), - STRING_INIT("busy"), - STRING_INIT("no"), - STRING_INIT("crosshair"), - STRING_INIT("hand1"), - STRING_INIT("hand2"), - STRING_INIT("pencil"), - STRING_INIT("question"), - STRING_INIT("rightup-arrow"), - STRING_INIT("up-arrow"), + STR_LITERAL_INIT("arrow"), // default, must be the first one + STR_LITERAL_INIT("blank"), // hidden + STR_LITERAL_INIT("beam"), + STR_LITERAL_INIT("updown"), + STR_LITERAL_INIT("udsizing"), + STR_LITERAL_INIT("leftright"), + STR_LITERAL_INIT("lrsizing"), + STR_LITERAL_INIT("busy"), + STR_LITERAL_INIT("no"), + STR_LITERAL_INIT("crosshair"), + STR_LITERAL_INIT("hand1"), + STR_LITERAL_INIT("hand2"), + STR_LITERAL_INIT("pencil"), + STR_LITERAL_INIT("question"), + STR_LITERAL_INIT("rightup-arrow"), + STR_LITERAL_INIT("up-arrow"), {NULL, 0} }; -# undef STRING_INIT # define MSHAPE_NAMES_COUNT (ARRAY_LENGTH(mshape_names) - 1) # endif diff --git a/src/os_mswin.c b/src/os_mswin.c index 6eaf84f6ce..bfd0f1a386 100644 --- a/src/os_mswin.c +++ b/src/os_mswin.c @@ -2658,32 +2658,30 @@ struct charset_pair BYTE charset; }; -# define STRING_INIT(s) \ - {(char_u *)(s), STRLEN_LITERAL(s)} static struct charset_pair charset_pairs[] = { - {STRING_INIT("ANSI"), ANSI_CHARSET}, - {STRING_INIT("CHINESEBIG5"), CHINESEBIG5_CHARSET}, - {STRING_INIT("DEFAULT"), DEFAULT_CHARSET}, - {STRING_INIT("HANGEUL"), HANGEUL_CHARSET}, - {STRING_INIT("OEM"), OEM_CHARSET}, - {STRING_INIT("SHIFTJIS"), SHIFTJIS_CHARSET}, - {STRING_INIT("SYMBOL"), SYMBOL_CHARSET}, - {STRING_INIT("ARABIC"), ARABIC_CHARSET}, - {STRING_INIT("BALTIC"), BALTIC_CHARSET}, - {STRING_INIT("EASTEUROPE"), EASTEUROPE_CHARSET}, - {STRING_INIT("GB2312"), GB2312_CHARSET}, - {STRING_INIT("GREEK"), GREEK_CHARSET}, - {STRING_INIT("HEBREW"), HEBREW_CHARSET}, - {STRING_INIT("JOHAB"), JOHAB_CHARSET}, - {STRING_INIT("MAC"), MAC_CHARSET}, - {STRING_INIT("RUSSIAN"), RUSSIAN_CHARSET}, - {STRING_INIT("THAI"), THAI_CHARSET}, - {STRING_INIT("TURKISH"), TURKISH_CHARSET} + {STR_LITERAL_INIT("ANSI"), ANSI_CHARSET}, + {STR_LITERAL_INIT("CHINESEBIG5"), CHINESEBIG5_CHARSET}, + {STR_LITERAL_INIT("DEFAULT"), DEFAULT_CHARSET}, + {STR_LITERAL_INIT("HANGEUL"), HANGEUL_CHARSET}, + {STR_LITERAL_INIT("OEM"), OEM_CHARSET}, + {STR_LITERAL_INIT("SHIFTJIS"), SHIFTJIS_CHARSET}, + {STR_LITERAL_INIT("SYMBOL"), SYMBOL_CHARSET}, + {STR_LITERAL_INIT("ARABIC"), ARABIC_CHARSET}, + {STR_LITERAL_INIT("BALTIC"), BALTIC_CHARSET}, + {STR_LITERAL_INIT("EASTEUROPE"), EASTEUROPE_CHARSET}, + {STR_LITERAL_INIT("GB2312"), GB2312_CHARSET}, + {STR_LITERAL_INIT("GREEK"), GREEK_CHARSET}, + {STR_LITERAL_INIT("HEBREW"), HEBREW_CHARSET}, + {STR_LITERAL_INIT("JOHAB"), JOHAB_CHARSET}, + {STR_LITERAL_INIT("MAC"), MAC_CHARSET}, + {STR_LITERAL_INIT("RUSSIAN"), RUSSIAN_CHARSET}, + {STR_LITERAL_INIT("THAI"), THAI_CHARSET}, + {STR_LITERAL_INIT("TURKISH"), TURKISH_CHARSET} # ifdef VIETNAMESE_CHARSET , - {STRING_INIT("VIETNAMESE"), VIETNAMESE_CHARSET} + {STR_LITERAL_INIT("VIETNAMESE"), VIETNAMESE_CHARSET} # endif }; @@ -2696,23 +2694,22 @@ struct quality_pair static struct quality_pair quality_pairs[] = { # ifdef CLEARTYPE_QUALITY - {STRING_INIT("CLEARTYPE"), CLEARTYPE_QUALITY}, + {STR_LITERAL_INIT("CLEARTYPE"), CLEARTYPE_QUALITY}, # endif # ifdef ANTIALIASED_QUALITY - {STRING_INIT("ANTIALIASED"), ANTIALIASED_QUALITY}, + {STR_LITERAL_INIT("ANTIALIASED"), ANTIALIASED_QUALITY}, # endif # ifdef NONANTIALIASED_QUALITY - {STRING_INIT("NONANTIALIASED"), NONANTIALIASED_QUALITY}, + {STR_LITERAL_INIT("NONANTIALIASED"), NONANTIALIASED_QUALITY}, # endif # ifdef PROOF_QUALITY - {STRING_INIT("PROOF"), PROOF_QUALITY}, + {STR_LITERAL_INIT("PROOF"), PROOF_QUALITY}, # endif # ifdef DRAFT_QUALITY - {STRING_INIT("DRAFT"), DRAFT_QUALITY}, + {STR_LITERAL_INIT("DRAFT"), DRAFT_QUALITY}, # endif - {STRING_INIT("DEFAULT"), DEFAULT_QUALITY} + {STR_LITERAL_INIT("DEFAULT"), DEFAULT_QUALITY} }; -# undef STRING_INIT /* * Convert a charset ID to a name. diff --git a/src/os_unix.c b/src/os_unix.c index 1d382f7f33..12285b8e48 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -7260,13 +7260,10 @@ mch_expand_wildcards( int check_spaces; static int did_find_nul = FALSE; int ampersand = FALSE; -# define STRING_INIT(s) \ - {(char_u *)(s), STRLEN_LITERAL(s)} // vimglob() function to define for Posix shell - static string_T sh_vimglob_func = STRING_INIT("vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >"); + static string_T sh_vimglob_func = STR_LITERAL_INIT("vimglob() { while [ $# -ge 1 ]; do echo \"$1\"; shift; done }; vimglob >"); // vimglob() function with globstar setting enabled, only for bash >= 4.X - static string_T sh_globstar_opt = STRING_INIT("[[ ${BASH_VERSINFO[0]} -ge 4 ]] && shopt -s globstar; "); -# undef STRING_INIT + static string_T sh_globstar_opt = STR_LITERAL_INIT("[[ ${BASH_VERSINFO[0]} -ge 4 ]] && shopt -s globstar; "); *num_file = 0; // default: no files found diff --git a/src/popupwin.c b/src/popupwin.c index 35bcd4c80f..c1d614d03e 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -20,16 +20,13 @@ typedef struct { poppos_T pp_val; } poppos_entry_T; -#define STRING_INIT(s) \ - {(char_u *)(s), STRLEN_LITERAL(s)} static poppos_entry_T poppos_entries[] = { - {STRING_INIT("botleft"), POPPOS_BOTLEFT}, - {STRING_INIT("topleft"), POPPOS_TOPLEFT}, - {STRING_INIT("botright"), POPPOS_BOTRIGHT}, - {STRING_INIT("topright"), POPPOS_TOPRIGHT}, - {STRING_INIT("center"), POPPOS_CENTER} + {STR_LITERAL_INIT("botleft"), POPPOS_BOTLEFT}, + {STR_LITERAL_INIT("topleft"), POPPOS_TOPLEFT}, + {STR_LITERAL_INIT("botright"), POPPOS_BOTRIGHT}, + {STR_LITERAL_INIT("topright"), POPPOS_TOPRIGHT}, + {STR_LITERAL_INIT("center"), POPPOS_CENTER} }; -#undef STRING_INIT #ifdef HAS_MESSAGE_WINDOW // Window used for ":echowindow" diff --git a/src/version.c b/src/version.c index 9c634f7daa..681858f70d 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 369, /**/ 368, /**/ From 2c436be6f7b4436dc2de4b4dd2a8d1ce0f948748 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Mon, 20 Apr 2026 14:43:52 +0000 Subject: [PATCH 076/545] patch 9.2.0370: duplicate code with literal string_T assignment Problem: Duplicate code with literal string_T assignment Solution: Add STR_LITERAL_SET() macro for string_T literal assignment (Hirohito Higashi). Previously, assigning a string literal to a string_T variable required two lines that repeated the literal: s.string = (char_u *)"open"; s.length = STRLEN_LITERAL("open"); Writing the literal twice is error-prone -- a typo in one of them leaves the pointer and the cached length out of sync. Add a STR_LITERAL_SET() macro in macros.h so that the assignment can be written in one statement with the literal appearing only once: STR_LITERAL_SET(s, "open"); Replace all occurrences of the two-line pattern across the codebase with the new macro. No functional change. related: #19999 related: #20023 closes: #20025 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/autocmd.c | 5 +---- src/channel.c | 36 ++++++++++-------------------------- src/eval.c | 10 ++-------- src/fileio.c | 25 +++++-------------------- src/macros.h | 6 ++++++ src/os_win32.c | 15 +++------------ src/scriptfile.c | 6 ++---- src/strings.c | 5 +---- src/textprop.c | 15 +++------------ src/version.c | 2 ++ src/vim9type.c | 10 ++-------- 11 files changed, 37 insertions(+), 98 deletions(-) diff --git a/src/autocmd.c b/src/autocmd.c index 99d6b3c924..78965529db 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -3444,10 +3444,7 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) group_name.string = get_augroup_name(NULL, ap->group); if (group_name.string == NULL) - { - group_name.string = (char_u *)""; - group_name.length = 0; - } + STR_LITERAL_SET(group_name, ""); else group_name.length = STRLEN(group_name.string); diff --git a/src/channel.c b/src/channel.c index 56878458ea..dc8645b060 100644 --- a/src/channel.c +++ b/src/channel.c @@ -3677,52 +3677,36 @@ channel_part_info(channel_T *channel, dict_T *dict, char *name, ch_part_T part) STRCPY(namebuf + tail, "status"); if (chanpart->ch_fd != INVALID_FD) - { - s.string = (char_u *)"open"; - s.length = STRLEN_LITERAL("open"); - } + STR_LITERAL_SET(s, "open"); else if (channel_has_readahead(channel, part)) - { - s.string = (char_u *)"buffered"; - s.length = STRLEN_LITERAL("buffered"); - } + STR_LITERAL_SET(s, "buffered"); else - { - s.string = (char_u *)"closed"; - s.length = STRLEN_LITERAL("closed"); - } + STR_LITERAL_SET(s, "closed"); dict_add_string_len(dict, namebuf, s.string, (int)s.length); STRCPY(namebuf + tail, "mode"); switch (chanpart->ch_mode) { case CH_MODE_NL: - s.string = (char_u *)"NL"; - s.length = STRLEN_LITERAL("NL"); + STR_LITERAL_SET(s, "NL"); break; case CH_MODE_RAW: - s.string = (char_u *)"RAW"; - s.length = STRLEN_LITERAL("RAW"); + STR_LITERAL_SET(s, "RAW"); break; case CH_MODE_JSON: - s.string = (char_u *)"JSON"; - s.length = STRLEN_LITERAL("JSON"); + STR_LITERAL_SET(s, "JSON"); break; case CH_MODE_JS: - s.string = (char_u *)"JS"; - s.length = STRLEN_LITERAL("JS"); + STR_LITERAL_SET(s, "JS"); break; case CH_MODE_LSP: - s.string = (char_u *)"LSP"; - s.length = STRLEN_LITERAL("LSP"); + STR_LITERAL_SET(s, "LSP"); break; case CH_MODE_DAP: - s.string = (char_u *)"DAP"; - s.length = STRLEN_LITERAL("DAP"); + STR_LITERAL_SET(s, "DAP"); break; default: - s.string = (char_u *)""; - s.length = 0; + STR_LITERAL_SET(s, ""); break; } dict_add_string_len(dict, namebuf, s.string, (int)s.length); diff --git a/src/eval.c b/src/eval.c index 6f7c9960a2..7d212b5b30 100644 --- a/src/eval.c +++ b/src/eval.c @@ -6579,15 +6579,9 @@ class_tv2string(typval_T *tv, char_u **tofree) class_name.string = cl->class_name.string; class_name.length = cl->class_name.length; if (IS_INTERFACE(cl)) - { - s.string = (char_u *)"interface"; - s.length = 9; - } + STR_LITERAL_SET(s, "interface"); else if (IS_ENUM(cl)) - { - s.string = (char_u *)"enum"; - s.length = 4; - } + STR_LITERAL_SET(s, "enum"); } rsize = s.length + 1 + class_name.length + 1; diff --git a/src/fileio.c b/src/fileio.c index b023e5a0d1..1a7ee9b94d 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4945,10 +4945,7 @@ create_readdirex_item(char_u *path, char_u *name) link = TRUE; ret = mch_stat(p, &st); if (ret < 0) - { - q.string = (char_u *)"link"; - q.length = STRLEN_LITERAL("link"); - } + STR_LITERAL_SET(q, "link"); } vim_free(p); @@ -4971,15 +4968,9 @@ create_readdirex_item(char_u *path, char_u *name) if (link) { if (S_ISDIR(st.st_mode)) - { - q.string = (char_u *)"linkd"; - q.length = STRLEN_LITERAL("linkd"); - } + STR_LITERAL_SET(q, "linkd"); else - { - q.string = (char_u *)"link"; - q.length = STRLEN_LITERAL("link"); - } + STR_LITERAL_SET(q, "link"); } else { @@ -4993,10 +4984,7 @@ create_readdirex_item(char_u *path, char_u *name) pw = getpwuid(st.st_uid); if (pw == NULL) - { - q.string = (char_u *)""; - q.length = 0; - } + STR_LITERAL_SET(q, ""); else { q.string = (char_u *)pw->pw_name; @@ -5007,10 +4995,7 @@ create_readdirex_item(char_u *path, char_u *name) # if !defined(VMS) || (defined(VMS) && defined(HAVE_XOS_R_H)) gr = getgrgid(st.st_gid); if (gr == NULL) - { - q.string = (char_u *)""; - q.length = 0; - } + STR_LITERAL_SET(q, ""); else { q.string = (char_u *)gr->gr_name; diff --git a/src/macros.h b/src/macros.h index 5fdcfdb2a8..3b246b2200 100644 --- a/src/macros.h +++ b/src/macros.h @@ -376,6 +376,12 @@ #define STR_LITERAL_INIT(s) \ {(char_u *)(s), STRLEN_LITERAL(s)} +#define STR_LITERAL_SET(str, s) \ + do { \ + (str).string = (char_u *)(s); \ + (str).length = STRLEN_LITERAL(s); \ + } while (0) + // Whether a command index indicates a user command. #define IS_USER_CMDIDX(idx) ((int)(idx) < 0) diff --git a/src/os_win32.c b/src/os_win32.c index e7216c1220..e8f8162f1b 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -2730,10 +2730,7 @@ executable_exists( { pathext.string = mch_getenv("PATHEXT"); if (pathext.string == NULL) - { - pathext.string = (char_u *)".com;.exe;.bat;.cmd"; - pathext.length = 19; - } + STR_LITERAL_SET(pathext, ".com;.exe;.bat;.cmd"); else pathext.length = STRLEN(pathext.string); @@ -2774,10 +2771,7 @@ executable_exists( // Prepend single "." to pathext, it means no extension added. if (pathext.string == NULL) - { - pathext.string = (char_u *)"."; - pathext.length = 1; - } + STR_LITERAL_SET(pathext, "."); else if (noext == TRUE) { char_u *tmp; @@ -2828,10 +2822,7 @@ executable_exists( * is an executable file. */ if (pathbuf.string == NULL) - { - pathbuf.string = (char_u *)"."; - pathbuf.length = 1; - } + STR_LITERAL_SET(pathbuf, "."); p = pathbuf.string; while (*p) { diff --git a/src/scriptfile.c b/src/scriptfile.c index df90fe7711..d0735ffe44 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -199,12 +199,10 @@ estack_sfile(estack_arg_T which UNUSED) switch (entry->es_type) { case ETYPE_SCRIPT: - type_name.string = (char_u *)"script "; - type_name.length = 7; + STR_LITERAL_SET(type_name, "script "); break; case ETYPE_UFUNC: - type_name.string = (char_u *)"function "; - type_name.length = 9; + STR_LITERAL_SET(type_name, "function "); break; default: break; diff --git a/src/strings.c b/src/strings.c index 82ebdb644d..ec81ee469a 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1601,10 +1601,7 @@ f_str2blob(typval_T *argvars, typval_T *rettv) string_T str = {li->li_tv.vval.v_string, 0}; if (str.string == NULL) - { - str.string = (char_u *)""; - str.length = 0; - } + STR_LITERAL_SET(str, ""); else str.length = STRLEN(str.string); diff --git a/src/textprop.c b/src/textprop.c index 782701aff1..b4aedf98d7 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -1734,20 +1734,11 @@ prop_fill_dict(dict_T *dict, textprop_T *prop, buf_T *buf) // text_align if (prop->tp_flags & TP_FLAG_ALIGN_RIGHT) - { - text_align.string = (char_u *)"right"; - text_align.length = STRLEN_LITERAL("right"); - } + STR_LITERAL_SET(text_align, "right"); else if (prop->tp_flags & TP_FLAG_ALIGN_ABOVE) - { - text_align.string = (char_u *)"above"; - text_align.length = STRLEN_LITERAL("above"); - } + STR_LITERAL_SET(text_align, "above"); else if (prop->tp_flags & TP_FLAG_ALIGN_BELOW) - { - text_align.string = (char_u *)"below"; - text_align.length = STRLEN_LITERAL("below"); - } + STR_LITERAL_SET(text_align, "below"); if (text_align.string != NULL) dict_add_string_len(dict, "text_align", text_align.string, (int)text_align.length); diff --git a/src/version.c b/src/version.c index 681858f70d..489ec89dee 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 370, /**/ 369, /**/ diff --git a/src/vim9type.c b/src/vim9type.c index 3d9282f601..1bd94a60cb 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -2658,10 +2658,7 @@ type_name_class_or_obj(char *name, type_T *type, char **tofree) name = "enum"; } else - { - class_name.string = (char_u *)"any"; - class_name.length = 3; - } + STR_LITERAL_SET(class_name, "any"); size_t len = STRLEN(name) + class_name.length + 3; *tofree = alloc(len); @@ -2695,10 +2692,7 @@ type_name_func(type_T *type, char **tofree) string_T arg_type; if (type->tt_args == NULL) - { - arg_type.string = (char_u *)"[unknown]"; - arg_type.length = 9; - } + STR_LITERAL_SET(arg_type, "[unknown]"); else { arg_type.string = (char_u *)type_name(type->tt_args[i], &arg_free); From b30803b231beea13e2efa42fefc04be71a4e9bb5 Mon Sep 17 00:00:00 2001 From: Bez Hermoso Date: Mon, 20 Apr 2026 14:47:58 +0000 Subject: [PATCH 077/545] patch 9.2.0371: filetype: ghostty config files are not recognized Problem: filetype: ghostty config files are not recognized Solution: Detect ghostty configuration files as ghostty filetype, include a simple ghostty filetype plugin (Bez Hermoso) closes: #20002 Co-authored-by: Christian Brabandt Signed-off-by: Bez Hermoso Signed-off-by: Christian Brabandt --- runtime/autoload/dist/ft.vim | 6 ++++-- runtime/filetype.vim | 6 +++++- runtime/ftplugin/ghostty.vim | 10 ++++++++++ src/testdir/test_filetype.vim | 3 +++ src/version.c | 2 ++ 5 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 runtime/ftplugin/ghostty.vim diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index f0330c205c..7b7fb2954d 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -3,7 +3,7 @@ vim9script # Vim functions for file type detection # # Maintainer: The Vim Project -# Last Change: 2026 Apr 15 +# Last Change: 2026 Apr 20 # Former Maintainer: Bram Moolenaar # These functions are moved here from runtime/filetype.vim to make startup @@ -3113,7 +3113,9 @@ const ft_from_ext = { "bp": "bp", # Tiltfile "Tiltfile": "tiltfile", - "tiltfile": "tiltfile" + "tiltfile": "tiltfile", + # Ghostty + "ghostty": "ghostty", } # Key: file name (the final path component, excluding the drive and root) # Value: filetype diff --git a/runtime/filetype.vim b/runtime/filetype.vim index 0c76a60a52..da632acc54 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: 2026 Apr 08 +" Last Change: 2026 Apr 20 " Former Maintainer: Bram Moolenaar " If the filetype can be detected from extension or file name(the final path component), @@ -1617,6 +1617,10 @@ au BufNewFile,BufRead *.txt \| setf text \| endif +" Ghostty configuration +au BufNewFile,BufRead */ghostty/config setf ghostty +au BufNewFile,BufRead */ghostty/themes/* call s:StarSetf('ghostty') +au BufNewFile,BufRead */com.mitchellh.ghostty/config setf ghostty " Generic log file diff --git a/runtime/ftplugin/ghostty.vim b/runtime/ftplugin/ghostty.vim new file mode 100644 index 0000000000..479fd610bd --- /dev/null +++ b/runtime/ftplugin/ghostty.vim @@ -0,0 +1,10 @@ +" Vim filetype plugin file +" Language: ghostty + +" Quit if a ftplugin file was already loaded +if exists("b:did_ftplugin") + finish +endif + +" Use the cfg plugin, it's similar enough. +runtime! ftplugin/cfg.vim diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 892cd32388..583dad4d1c 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -327,6 +327,9 @@ def s:GetFilenameChecks(): dict> gedcom: ['file.ged', 'lltxxxxx.txt', '/tmp/lltmp', '/tmp/lltmp-file', 'any/tmp/lltmp', 'any/tmp/lltmp-file'], gel: ['file.gel'], gemtext: ['file.gmi', 'file.gemini'], + ghostty: ['ghostty/config', 'ghostty/keymaps.ghostty', '/.config/ghostty/config', '/.config/ghostty/keymaps.ghostty', + '~/Library/Application Support/com.mitchellh.ghostty/config.ghostty', '~/Library/Application Support/com.mitchellh.ghostty/config', + '~/.config/ghostty/themes/Custom Theme', '/usr/share/ghostty/themes/Builtin Theme'], gift: ['file.gift'], gitattributes: ['file.git/info/attributes', '.gitattributes', '/.config/git/attributes', '/etc/gitattributes', '/usr/local/etc/gitattributes', 'some.git/info/attributes'] + WhenConfigHome('$XDG_CONFIG_HOME/git/attributes'), gitcommit: ['COMMIT_EDITMSG', 'MERGE_MSG', 'TAG_EDITMSG', 'NOTES_EDITMSG', 'EDIT_DESCRIPTION'], diff --git a/src/version.c b/src/version.c index 489ec89dee..4cdbfdc057 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 371, /**/ 370, /**/ From 1c88aee1fa720aea07e133f32645914dcddd3435 Mon Sep 17 00:00:00 2001 From: Eisuke Kawashima Date: Mon, 20 Apr 2026 15:14:31 +0000 Subject: [PATCH 078/545] runtime: Remove wrong syn oneline keyword from a few syntax files Also: - drop a few trailing whitespaces - mark the oneline keyword for :syn keyword as error in the Vim syntax script, add tests for it. closes: #20018 Signed-off-by: Eisuke Kawashima Signed-off-by: Christian Brabandt --- runtime/syntax/autohotkey.vim | 5 +- runtime/syntax/cabal.vim | 14 ++--- runtime/syntax/generator/vim.vim.base | 4 +- runtime/syntax/monk.vim | 59 ++++++++++--------- runtime/syntax/shared/typescriptcommon.vim | 8 +-- .../testdir/dumps/vim_ex_syntax_06.dump | 2 +- .../testdir/dumps/vim_ex_syntax_07.dump | 2 +- .../testdir/dumps/vim_ex_syntax_08.dump | 2 +- .../testdir/dumps/vim_ex_syntax_09.dump | 2 +- .../testdir/dumps/vim_ex_syntax_10.dump | 2 +- .../testdir/dumps/vim_ex_syntax_11.dump | 2 +- .../testdir/dumps/vim_ex_syntax_12.dump | 2 +- .../testdir/dumps/vim_ex_syntax_13.dump | 2 +- .../testdir/dumps/vim_ex_syntax_14.dump | 2 +- .../testdir/dumps/vim_ex_syntax_15.dump | 2 +- .../testdir/dumps/vim_ex_syntax_16.dump | 2 +- .../testdir/dumps/vim_ex_syntax_17.dump | 2 +- .../testdir/dumps/vim_ex_syntax_18.dump | 2 +- .../testdir/dumps/vim_ex_syntax_19.dump | 2 +- .../testdir/dumps/vim_ex_syntax_20.dump | 2 +- .../testdir/dumps/vim_ex_syntax_21.dump | 14 ++--- .../testdir/dumps/vim_ex_syntax_22.dump | 20 +++++++ .../syntax/testdir/input/vim_ex_syntax.vim | 12 ++++ runtime/syntax/vim.vim | 6 +- 24 files changed, 103 insertions(+), 69 deletions(-) create mode 100644 runtime/syntax/testdir/dumps/vim_ex_syntax_22.dump diff --git a/runtime/syntax/autohotkey.vim b/runtime/syntax/autohotkey.vim index a888394923..db3c26ec0a 100644 --- a/runtime/syntax/autohotkey.vim +++ b/runtime/syntax/autohotkey.vim @@ -3,6 +3,8 @@ " Maintainer: Michael Wong " https://github.com/mmikeww/autohotkey.vim " Latest Revision: 2022-07-25 +" Last Change: +" 2026 Apr 20 by Vim project: remove wrong oneline keyword #20018 " Previous Maintainers: SungHyun Nam " Nikolai Weibull @@ -35,7 +37,6 @@ syn region autohotkeyString syn match autohotkeyVariable \ display - \ oneline \ contains=autohotkeyBuiltinVariable \ keepend \ '%\S\{-}%' @@ -123,7 +124,7 @@ syn keyword autohotkeyCommand syn keyword autohotkeyFunction \ InStr RegExMatch RegExReplace StrLen SubStr Asc Chr Func - \ DllCall VarSetCapacity WinActive WinExist IsLabel OnMessage + \ DllCall VarSetCapacity WinActive WinExist IsLabel OnMessage \ Abs Ceil Exp Floor Log Ln Mod Round Sqrt Sin Cos Tan ASin ACos ATan \ FileExist GetKeyState NumGet NumPut StrGet StrPut RegisterCallback \ IsFunc Trim LTrim RTrim IsObject Object Array FileOpen diff --git a/runtime/syntax/cabal.vim b/runtime/syntax/cabal.vim index 74cda51266..a1db25034a 100644 --- a/runtime/syntax/cabal.vim +++ b/runtime/syntax/cabal.vim @@ -5,8 +5,10 @@ " Previous Maintainer: Vincent Berthoux " File Types: .cabal " Last Change: 22 Oct 2022 +" 2026 Apr 20 by Vim project: remove wrong oneline keyword #20018 +" " v1.6: Added support for foreign-libraries -" Added highlighting for various fields +" Added highlighting for various fields " v1.5: Incorporated changes from " https://github.com/sdiehl/haskell-vim-proto/blob/master/vim/syntax/cabal.vim " Use `syn keyword` instead of `syn match`. @@ -25,7 +27,7 @@ " Cabal known compiler are highlighted too. " " V1.2: Added cpp-options which was missing. Feature implemented -" by GHC, found with a GHC warning, but undocumented. +" by GHC, found with a GHC warning, but undocumented. " Whatever... " " v1.1: Fixed operator problems and added ftdetect file @@ -186,7 +188,7 @@ syn match cabalVersionRegionA \ contains=cabalVersionOperator,cabalVersion \ keepend \ /\%(==\|\^\?>=\|<=\|<\|>\)\s*\d\+\%(\.\d\+\)*\%(\.\*\)\?\>/ -" version inside `version: ...` +" version inside `version: ...` syn match cabalVersionRegionB \ contains=cabalStatementRegion,cabalVersionOperator,cabalVersion \ /^\s*\%(cabal-\)\?version\s*:.*$/ @@ -198,7 +200,6 @@ syn match cabalName contained /:\@<=.*/ syn match cabalNameRegion \ contains=cabalStatementRegion,cabalName \ nextgroup=cabalStatementRegion - \ oneline \ /^\c\s*name\s*:.*$/ " author region @@ -206,7 +207,6 @@ syn match cabalAuthor contained /:\@<=.*/ syn match cabalAuthorRegion \ contains=cabalStatementRegion,cabalStatement,cabalAuthor \ nextgroup=cabalStatementRegion - \ oneline \ /^\c\s*author\s*:.*$/ " maintainer region @@ -214,7 +214,6 @@ syn match cabalMaintainer contained /:\@<=.*/ syn match cabalMaintainerRegion \ contains=cabalStatementRegion,cabalStatement,cabalMaintainer \ nextgroup=cabalStatementRegion - \ oneline \ /^\c\s*maintainer\s*:.*$/ " license region @@ -222,7 +221,6 @@ syn match cabalLicense contained /:\@<=.*/ syn match cabalLicenseRegion \ contains=cabalStatementRegion,cabalStatement,cabalLicense \ nextgroup=cabalStatementRegion - \ oneline \ /^\c\s*license\s*:.*$/ " license-file region @@ -230,7 +228,6 @@ syn match cabalLicenseFile contained /:\@<=.*/ syn match cabalLicenseFileRegion \ contains=cabalStatementRegion,cabalStatement,cabalLicenseFile \ nextgroup=cabalStatementRegion - \ oneline \ /^\c\s*license-file\s*:.*$/ " tested-with region with compilers and versions @@ -238,7 +235,6 @@ syn keyword cabalCompiler contained ghc nhc yhc hugs hbc helium jhc lhc syn match cabalTestedWithRegion \ contains=cabalStatementRegion,cabalStatement,cabalCompiler,cabalVersionRegionA \ nextgroup=cabalStatementRegion - \ oneline \ /^\c\s*tested-with\s*:.*$/ " build type keywords diff --git a/runtime/syntax/generator/vim.vim.base b/runtime/syntax/generator/vim.vim.base index d73688927a..24a51167ac 100644 --- a/runtime/syntax/generator/vim.vim.base +++ b/runtime/syntax/generator/vim.vim.base @@ -1860,10 +1860,11 @@ syn keyword vimSynType contained include skipwhite nextgroup=vimSynIncludeClust syn match vimSynIncludeCluster contained "@[_a-zA-Z0-9]\+\>" " Syntax: keyword {{{2 -syn cluster vimSynKeyGroup contains=@vimContinue,vimSynCchar,vimSynNextgroup,vimSynKeyOpt,vimSynContainedin +syn cluster vimSynKeyGroup contains=@vimContinue,vimSynCchar,vimSynNextgroup,vimSynKeyOpt,vimSynContainedin,vimSynKeyError syn keyword vimSynType contained keyword skipwhite nextgroup=vimSynKeyRegion syn region vimSynKeyRegion contained keepend matchgroup=vimGroupName start="\h\w*\>" skip=+\\\\\|\\|\|\n\s*\%(\\\|"\\ \)+ matchgroup=vimCmdSep end="|\|$" contains=@vimSynKeyGroup syn match vimSynKeyOpt contained "\%#=1\<\%(conceal\|contained\|transparent\|skipempty\|skipwhite\|skipnl\)\>" +syn match vimSynKeyError contained "\" " Syntax: match {{{2 syn cluster vimSynMtchGroup contains=@vimContinue,vimSynCchar,vimSynContains,vimSynContainedin,vimSynError,vimSynMtchOpt,vimSynNextgroup,vimSynRegPat,vimNotation,vimMtchComment @@ -2431,6 +2432,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimSyncError vimError hi def link vimSynConcealError vimError hi def link vimSynError vimError + hi def link vimSynKeyError vimError hi def link vimSynFoldlevelError vimError hi def link vimSynIskeywordError vimError hi def link vimSynSpellError vimError diff --git a/runtime/syntax/monk.vim b/runtime/syntax/monk.vim index 3af810173a..22c91417a9 100644 --- a/runtime/syntax/monk.vim +++ b/runtime/syntax/monk.vim @@ -2,6 +2,7 @@ " Language: Monk (See-Beyond Technologies) " Maintainer: Mike Litherland " Last Change: 2012 Feb 03 by Thilo Six +" 2026 Apr 20 by Vim project: remove wrong oneline keyword #20018 " This syntax file is good enough for my needs, but others " may desire more features. Suggestions and bug reports @@ -33,8 +34,8 @@ syn case ignore " Fascist highlighting: everything that doesn't fit the rules is an error... -syn match monkError oneline ![^ \t()";]*! -syn match monkError oneline ")" +syn match monkError ![^ \t()";]*! +syn match monkError ")" " Quoted and backquoted stuff @@ -131,51 +132,51 @@ syn keyword monkFunc valid-integer? verify-type " using variables is a day's work for a trained secretary... " This is a useful lax approximation: -syn match monkNumber oneline "[-#+0-9.][-#+/0-9a-f@i.boxesfdl]*" -syn match monkError oneline ![-#+0-9.][-#+/0-9a-f@i.boxesfdl]*[^-#+/0-9a-f@i.boxesfdl \t()";][^ \t()";]*! +syn match monkNumber "[-#+0-9.][-#+/0-9a-f@i.boxesfdl]*" +syn match monkError ![-#+0-9.][-#+/0-9a-f@i.boxesfdl]*[^-#+/0-9a-f@i.boxesfdl \t()";][^ \t()";]*! -syn match monkOther oneline ![+-][ \t()";]!me=e-1 -syn match monkOther oneline ![+-]$! +syn match monkOther ![+-][ \t()";]!me=e-1 +syn match monkOther ![+-]$! " ... so that a single + or -, inside a quoted context, would not be " interpreted as a number (outside such contexts, it's a monkFunc) -syn match monkDelimiter oneline !\.[ \t()";]!me=e-1 -syn match monkDelimiter oneline !\.$! +syn match monkDelimiter !\.[ \t()";]!me=e-1 +syn match monkDelimiter !\.$! " ... and a single dot is not a number but a delimiter " Simple literals: -syn match monkBoolean oneline "#[tf]" -syn match monkError oneline !#[tf][^ \t()";]\+! +syn match monkBoolean "#[tf]" +syn match monkError !#[tf][^ \t()";]\+! -syn match monkChar oneline "#\\" -syn match monkChar oneline "#\\." -syn match monkError oneline !#\\.[^ \t()";]\+! -syn match monkChar oneline "#\\space" -syn match monkError oneline !#\\space[^ \t()";]\+! -syn match monkChar oneline "#\\newline" -syn match monkError oneline !#\\newline[^ \t()";]\+! +syn match monkChar "#\\" +syn match monkChar "#\\." +syn match monkError !#\\.[^ \t()";]\+! +syn match monkChar "#\\space" +syn match monkError !#\\space[^ \t()";]\+! +syn match monkChar "#\\newline" +syn match monkError !#\\newline[^ \t()";]\+! " This keeps all other stuff unhighlighted, except *stuff* and : -syn match monkOther oneline ,[a-z!$%&*/:<=>?^_~][-a-z!$%&*/:<=>?^_~0-9+.@]*, -syn match monkError oneline ,[a-z!$%&*/:<=>?^_~][-a-z!$%&*/:<=>?^_~0-9+.@]*[^-a-z!$%&*/:<=>?^_~0-9+.@ \t()";]\+[^ \t()";]*, +syn match monkOther ,[a-z!$%&*/:<=>?^_~][-a-z!$%&*/:<=>?^_~0-9+.@]*, +syn match monkError ,[a-z!$%&*/:<=>?^_~][-a-z!$%&*/:<=>?^_~0-9+.@]*[^-a-z!$%&*/:<=>?^_~0-9+.@ \t()";]\+[^ \t()";]*, -syn match monkOther oneline "\.\.\." -syn match monkError oneline !\.\.\.[^ \t()";]\+! +syn match monkOther "\.\.\." +syn match monkError !\.\.\.[^ \t()";]\+! " ... a special identifier -syn match monkConstant oneline ,\*[-a-z!$%&*/:<=>?^_~0-9+.@]*\*[ \t()";],me=e-1 -syn match monkConstant oneline ,\*[-a-z!$%&*/:<=>?^_~0-9+.@]*\*$, -syn match monkError oneline ,\*[-a-z!$%&*/:<=>?^_~0-9+.@]*\*[^-a-z!$%&*/:<=>?^_~0-9+.@ \t()";]\+[^ \t()";]*, +syn match monkConstant ,\*[-a-z!$%&*/:<=>?^_~0-9+.@]*\*[ \t()";],me=e-1 +syn match monkConstant ,\*[-a-z!$%&*/:<=>?^_~0-9+.@]*\*$, +syn match monkError ,\*[-a-z!$%&*/:<=>?^_~0-9+.@]*\*[^-a-z!$%&*/:<=>?^_~0-9+.@ \t()";]\+[^ \t()";]*, -syn match monkConstant oneline ,<[-a-z!$%&*/:<=>?^_~0-9+.@]*>[ \t()";],me=e-1 -syn match monkConstant oneline ,<[-a-z!$%&*/:<=>?^_~0-9+.@]*>$, -syn match monkError oneline ,<[-a-z!$%&*/:<=>?^_~0-9+.@]*>[^-a-z!$%&*/:<=>?^_~0-9+.@ \t()";]\+[^ \t()";]*, +syn match monkConstant ,<[-a-z!$%&*/:<=>?^_~0-9+.@]*>[ \t()";],me=e-1 +syn match monkConstant ,<[-a-z!$%&*/:<=>?^_~0-9+.@]*>$, +syn match monkError ,<[-a-z!$%&*/:<=>?^_~0-9+.@]*>[^-a-z!$%&*/:<=>?^_~0-9+.@ \t()";]\+[^ \t()";]*, " Monk input and output structures -syn match monkSyntax oneline "\(\~input\|\[I\]->\)[^ \t]*" -syn match monkFunc oneline "\(\~output\|\[O\]->\)[^ \t]*" +syn match monkSyntax "\(\~input\|\[I\]->\)[^ \t]*" +syn match monkFunc "\(\~output\|\[O\]->\)[^ \t]*" " Non-quoted lists, and strings: diff --git a/runtime/syntax/shared/typescriptcommon.vim b/runtime/syntax/shared/typescriptcommon.vim index 9a909c6755..1cb1740f8e 100644 --- a/runtime/syntax/shared/typescriptcommon.vim +++ b/runtime/syntax/shared/typescriptcommon.vim @@ -306,13 +306,13 @@ syntax keyword typescriptRepeat do while for nextgroup=typescript syntax keyword typescriptRepeat for nextgroup=typescriptLoopParen,typescriptAsyncFor skipwhite skipempty syntax keyword typescriptBranch break continue containedin=typescriptBlock syntax keyword typescriptCase case nextgroup=@typescriptPrimitive skipwhite containedin=typescriptBlock -syntax keyword typescriptDefault default containedin=typescriptBlock nextgroup=@typescriptValue,typescriptClassKeyword,typescriptInterfaceKeyword skipwhite oneline +syntax keyword typescriptDefault default containedin=typescriptBlock nextgroup=@typescriptValue,typescriptClassKeyword,typescriptInterfaceKeyword skipwhite syntax keyword typescriptStatementKeyword with syntax keyword typescriptStatementKeyword yield skipwhite nextgroup=@typescriptValue containedin=typescriptBlock syntax keyword typescriptTry try syntax keyword typescriptExceptions throw finally -syntax keyword typescriptExceptions catch nextgroup=typescriptCall skipwhite skipempty oneline +syntax keyword typescriptExceptions catch nextgroup=typescriptCall skipwhite skipempty syntax keyword typescriptDebugger debugger syntax keyword typescriptAsyncFor await nextgroup=typescriptLoopParen skipwhite skipempty contained @@ -1766,9 +1766,9 @@ endif " patch " patch for generated code syntax keyword typescriptGlobal Promise - \ nextgroup=typescriptGlobalPromiseDot,typescriptFuncCallArg,typescriptTypeArguments oneline + \ nextgroup=typescriptGlobalPromiseDot,typescriptFuncCallArg,typescriptTypeArguments syntax keyword typescriptGlobal Map WeakMap - \ nextgroup=typescriptGlobalPromiseDot,typescriptFuncCallArg,typescriptTypeArguments oneline + \ nextgroup=typescriptGlobalPromiseDot,typescriptFuncCallArg,typescriptTypeArguments syntax keyword typescriptConstructor contained constructor \ nextgroup=@typescriptCallSignature diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_06.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_06.dump index 67ba95814c..3bcb86e4e3 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_06.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_06.dump @@ -17,4 +17,4 @@ @6|\+0#e000e06&| +0#0000000&|c+0#e000e06&|o|n|t|a|i|n|e|d| +0#0000000&@57 @6|"+0#0000e05&|\| |c|o|n|t|a|i|n|e|d|i|n| |o|p|t|i|o|n| +0#0000000&@47 @6|\+0#e000e06&| +0#0000000&|c+0#e000e06&|o|n|t|a|i|n|e|d|i|n|=|t+0#0000001#ffff4012|e|s|t|C|o|n|t|a|i|n|e|r| +0#0000000#ffffff0@41 -@57|1|0|5|,|0|-|1| @6|2|7|%| +@57|1|0|5|,|0|-|1| @6|2|6|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_07.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_07.dump index 696f88cb07..a4be8bb7d6 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_07.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_07.dump @@ -17,4 +17,4 @@ @6|"+0#0000e05&|\| |k|e|y|w|o|r|d| |3| +0#0000000&@56 @6|\+0#e000e06&| +0#0000000&|k|e|y|w|o|r|d|3| @58 @75 -@57|1|2@1|,|7| @8|3|2|%| +@57|1|2@1|,|7| @8|3|1|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_08.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_08.dump index 156ff01e59..744f23d907 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_08.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_08.dump @@ -17,4 +17,4 @@ @6|"+0#0000e05&|\| |c@1|h|a|r| |o|p|t|i|o|n| +0#0000000&@53 @6|\+0#e000e06&| +0#0000000&|c+0#e000e06&@1|h|a|r|=|&+0#e000002&| +0#0000000&@59 @6|"+0#0000e05&|\| |c|o|n|t|a|i|n|e|d| |o|p|t|i|o|n| +0#0000000&@49 -@57|1|4|0|,|7| @8|3|7|%| +@57|1|4|0|,|7| @8|3|6|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_09.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_09.dump index a7ed5c811e..e42e016cc4 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_09.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_09.dump @@ -17,4 +17,4 @@ @6|"+0#0000e05&|\| +0#0000000&|f+0#0000e05&|o|l|d| +0#0000000&|o+0#0000e05&|p|t|i|o|n| +0#0000000&@54 @6|\+0#e000e06&| +0#0000000&|f+0#e000e06&|o|l|d| +0#0000000&@62 @6|"+0#0000e05&|\| |d|i|s|p|l|a|y| |o|p|t|i|o|n| +0#0000000&@51 -@57|1|5|8|,|7| @8|4|2|%| +@57|1|5|8|,|7| @8|4|1|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_10.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_10.dump index 7031132d74..983f1d5faf 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_10.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_10.dump @@ -17,4 +17,4 @@ @6|\+0#e000e06&| +0#0000000&|e+0#00e0003&|n|d|=|"+0#e000002&|e|n|d|-|p|a|t@1|e|r|n|"| +0#0000000&@49 @6|"+0#0000e05&|\| |c|o|n|c|e|a|l| |o|p|t|i|o|n| +0#0000000&@51 @6|\+0#e000e06&| +0#0000000&|c+0#e000e06&|o|n|c|e|a|l| +0#0000000&@59 -@57|1|7|6|,|7| @8|4|7|%| +@57|1|7|6|,|7| @8|4|5|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_11.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_11.dump index c14f06636a..1e7ba63640 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_11.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_11.dump @@ -17,4 +17,4 @@ @6|\+0#e000e06&| +0#0000000&|s+0#e000e06&|k|i|p|n|l| +0#0000000&@60 @6|"+0#0000e05&|\| |c|o|n|t|a|i|n|s| |o|p|t|i|o|n| +0#0000000&@50 @6|\+0#e000e06&| +0#0000000&|c+0#e000e06&|o|n|t|a|i|n|s|=|t+0#0000001#ffff4012|e|s|t|C|o|n|t|a|i|n|e|d|1|,|t|e|s|t|C|o|n|t|a|i|n|e|d|2| +0#0000000#ffffff0@28 -@57|1|9|4|,|7| @8|5|2|%| +@57|1|9|4|,|7| @8|5|0|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_12.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_12.dump index 28a905a8f8..a7489b5653 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_12.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_12.dump @@ -17,4 +17,4 @@ |s+0#af5f00255&|y|n| +0#0000000&|c+0#00e0003&|l|u|s|t|e|r| +0#0000000&|t|e|s|t|C|l|u|s|t|e|r| @51 @6|"+0#0000e05&|\| |O|P|T|I|O|N|S| +0#0000000&@58 @6|"+0#0000e05&|\| |c|o|n|t|a|i|n|s| |o|p|t|i|o|n| +0#0000000&@50 -@57|2|1|2|,|7| @8|5|7|%| +@57|2|1|2|,|7| @8|5@1|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_13.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_13.dump index e828336806..c9068bdb5f 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_13.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_13.dump @@ -17,4 +17,4 @@ |s+0#af5f00255&|y|n| +0#0000000&|k+0#00e0003&|e|y|w|o|r|d| +0#0000000&|t|e|s|t|N|e|x|t|3| |k|e|y|w|o|r|d| @45 |s+0#af5f00255&|y|n| +0#0000000&|k+0#00e0003&|e|y|w|o|r|d| +0#0000000&|t|e|s|t|N|e|x|t|4| |k|e|y|w|o|r|d| @45 |s+0#af5f00255&|y|n| +0#0000000&|k+0#00e0003&|e|y|w|o|r|d| +0#0000000&|t|e|s|t|N|e|x|t|5| |k|e|y|w|o|r|d| @45 -@57|2|3|0|,|7| @8|6|2|%| +@57|2|3|0|,|7| @8|6|0|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_14.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_14.dump index f648fee758..13cf00d224 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_14.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_14.dump @@ -17,4 +17,4 @@ @6|\+0#e000e06&| +0#0000000&|k|e|y|w|o|r|d|4| @58 @6|\+0#e000e06&| +0#0000000&|k|e|y|w|o|r|d|5| @58 @6|\+0#e000e06&| +0#0000000&|k|e|y|w|o|r|d|6| @58 -@57|2|4|8|,|0|-|1| @6|6|7|%| +@57|2|4|8|,|0|-|1| @6|6|5|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_15.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_15.dump index 6945ec2c62..4bec90ecb3 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_15.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_15.dump @@ -17,4 +17,4 @@ @6|\+0#e000e06&| +0#0000000&|t+0#0000001#ffff4012|e|x|t|N|e|x|t|6|,+0#0000000#ffffff0| @56 @6|"+0#0000e05&|\| +0#0000000&|c+0#0000e05&|o|m@1|e|n|t| +0#0000000&@58 @6|\+0#e000e06&| +0#0000000&|t+0#0000001#ffff4012|e|x|t|N|e|x|t|7| +0#0000000#ffffff0|,| @55 -@57|2|6@1|,|7| @8|7|2|%| +@57|2|6@1|,|7| @8|7|0|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_16.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_16.dump index 3d1a002d69..8d668d55a1 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_16.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_16.dump @@ -17,4 +17,4 @@ @6|\+0#e000e06&| +0#0000000&|,| |t+0#0000001#ffff4012|e|x|t|N|e|x|t|1|7| +0#0000000#ffffff0|,| |t+0#0000001#ffff4012|e|x|t|N|e|x|t|1|8| +0#0000000#ffffff0|,| @39 @6|"+0#0000e05&|\| +0#0000000&|c+0#0000e05&|o|m@1|e|n|t| +0#0000000&@58 @6|\+0#e000e06&| +0#0000000&|t+0#0000001#ffff4012|e|x|t|N|e|x|t|1|9|,+0#0000000#ffffff0| |@+0#0000001#ffff4012|t|e|s|t|C|l|u|s|t|e|r| +0#0000000#ffffff0@42 -@57|2|8|4|,|7| @8|7@1|%| +@57|2|8|4|,|7| @8|7|5|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_17.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_17.dump index c923c00d45..056116d2a6 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_17.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_17.dump @@ -17,4 +17,4 @@ |f+0#af5f00255&|u|n|c|t|i|o|n|!| +0#0000000&|s+0#e000e06&|:|C+0#0000000&|o|n|t|a|i|n|e|d|G|r|o|u|p|(+0#e000e06&|)| +0#0000000&@46 @2|"+0#0000e05&| |.@2| +0#0000000&@67 @2|f+0#af5f00255&|o|r| +0#0000000&|c+0#00e0e07&|l|u|s|t|e|r| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|[+0#e000e06&|'+0#e000002&|m|a|r|k|d|o|w|n|H|i|g|h|l|i|g|h|t|_|z|s|h|'|,+0#0000000&| |'+0#e000002&|z|s|h|'|]+0#e000e06&| +0#0000000&@25 -@57|3|0|2|,|7| @8|8|2|%| +@57|3|0|2|,|7| @8|8|0|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_18.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_18.dump index 605dc0cba1..e96cf04d38 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_18.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_18.dump @@ -17,4 +17,4 @@ |"+0#0000e05&| |I|s@1|u|e| |#|1|8|4|9|1| |(|T|w|o| |"+0#e000002&|)|"|s+0#0000e05&| |a|r|e| |i|n|c|o|r@1|e|c|t|l|y| |c|o|l|o|r|e|d| |'|v|i|m|O|p|e|r|E|r@1|o|r|'| |i|n| |s|y|n|t|a|x|/|m |a|i|l|.|v|i|m|)| +0#0000000&@66 @75 -@57|3|2|0|,|0|-|1| @6|8|7|%| +@57|3|2|0|,|0|-|1| @6|8|4|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_19.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_19.dump index 3dbd5826bf..34b80d9e2e 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_19.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_19.dump @@ -17,4 +17,4 @@ @75 |"+0#0000e05&| |C|o|d|e| |>| |K|e|y|w|o|r|d|s| |{@2|2| +0#0000000&@52 |s+0#af5f00255&|y|n|t|a|x| +0#0000000&|c+0#00e0003&|l|u|s|t|e|r| +0#0000000&|t|y|p|s|t|C|o|d|e|K|e|y|w|o|r|d|s| @42 -@57|3@1|6|,|1| @8|9|2|%| +@57|3@1|6|,|1| @8|8|9|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_20.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_20.dump index e05dc99127..ed355d795c 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_20.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_20.dump @@ -17,4 +17,4 @@ @4|\+0#e000e06&| +0#0000000&|c+0#e000e06&|o|n|t|a|i|n|s|=| +0#0000000&@59 @12|"+0#0000e05&|\| +0#0000000&|c+0#0000e05&|o|m@1|e|n|t| +0#0000000&@52 @12|\+0#e000e06&| +0#0000000&@1|t+0#0000001#ffff4012|y|p|s|t|C|o|d|e|C|o|n|d|i|t|i|o|n|a|l| +0#0000000#ffffff0@39 -@57|3|5|4|,|0|-|1| @6|9|7|%| +@57|3|5|4|,|0|-|1| @6|9|4|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_21.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_21.dump index e0746ea9d6..20477577ec 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_syntax_21.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_21.dump @@ -11,10 +11,10 @@ |s+0#af5f00255&|y|n| +0#0000000&|r+0#00e0003&|e|g|i|o|n| +0#0000000&@1|s|l|r|n|r|c|C|m|d|L|i|n|e| @6|m+0#e000e06&|a|t|c|h|g|r|o|u|p|=|s+0#0000000&|l|r|n|r|c|C|m|d| |s+0#00e0003&|t|a|r|t|=|"+0#e000002&|\|<|\+0#e000e06&|(|a+0#e000002&|u|t|o|b|a|u|d|\+0#e000e06&|||.+0#e000002& @2|\+0#e000e06&|||v+0#e000002&|i|s|i|b|l|e|_|h|e|a|d|e|r|s|\+0#e000e06&|)|\+0#e000002&|>|"| +0#0000000&|e+0#00e0003&|n|d|=|"+0#e000002&|$|"| +0#0000000&|o+0#e000e06&|n|e|l|i|n|e| +0#0000000&|c+0#e000e06&|o|n|t|a|i|n|s|=|s+0#0000001#ffff4012|l|r|n|r|c|\|(|S|t|r|i|n|g|\|||C|o|m@1|e|n|t|\|) | +0#0000000#ffffff0@74 -|~+0#4040ff13&| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -| +0#0000000&@56|3|7|2|,|0|-|1| @6|B|o|t| +|"+0#0000e05&| |:|s|y|n|t|a|x| |k|e|y|w|o|r|d| |—| |o|n|e|l|i|n|e| |a|c@1|e|p|t|e|d| |b|u|t| |m|e|a|n|i|n|g|l|e|s@1| +0#0000000&@22 +|s+0#af5f00255&|y|n|t|a|x| +0#0000000&|k+0#00e0003&|e|y|w|o|r|d| +0#0000000&|T|e|s|t|K|w| |f|o@1| |b|a|r| |o+0#ffffff16#ff404010|n|e|l|i|n|e| +0#0000000#ffffff0@37 +|s+0#af5f00255&|y|n|t|a|x| +0#0000000&|k+0#00e0003&|e|y|w|o|r|d| +0#0000000&|T|e|s|t|K|w|2| |o+0#ffffff16#ff404010|n|e|l|i|n|e| +0#0000000#ffffff0|b|a|z| |q|u@1|x| @35 +@75 +|"+0#0000e05&| |:|s|y|n|t|a|x| |m|a|t|c|h| |—| |o|n|e|l|i|n|e| |a|c@1|e|p|t|e|d| |b|u|t| |m|e|a|n|i|n|g|l|e|s@1| +0#0000000&@24 +|s+0#af5f00255&|y|n|t|a|x| +0#0000000&|m+0#00e0003&|a|t|c|h| +0#0000000&|T|e|s|t|M|a|t|c|h| |/+0#e000002&|\|<|f|o@1|\|>|/| +0#0000000&|o+0#ffffff16#ff404010|n|e|l|i|n|e| +0#0000000#ffffff0@34 +@57|3|7|2|,|0|-|1| @6|9|8|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_syntax_22.dump b/runtime/syntax/testdir/dumps/vim_ex_syntax_22.dump new file mode 100644 index 0000000000..7c1765f636 --- /dev/null +++ b/runtime/syntax/testdir/dumps/vim_ex_syntax_22.dump @@ -0,0 +1,20 @@ +|s+0#af5f00255#ffffff0|y|n|t|a|x| +0#0000000&|m+0#00e0003&|a|t|c|h| +0#0000000&|T|e|s|t|M|a|t|c|h| |/+0#e000002&|\|<|f|o@1|\|>|/| +0#0000000&|o+0#ffffff16#ff404010|n|e|l|i|n|e| +0#0000000#ffffff0@34 +|s+0#af5f00255&|y|n|t|a|x| +0#0000000&|m+0#00e0003&|a|t|c|h| +0#0000000&|T|e|s|t|M|a|t|c|h|2| |o+0#ffffff16#ff404010|n|e|l|i|n|e| +0#0000000#ffffff0|/+0#e000002&|\|<|b|a|r|\|>|/| +0#0000000&@33 +@75 +|"+0#0000e05&| |:|s|y|n|t|a|x| |r|e|g|i|o|n| |—| |o|n|e|l|i|n|e| |i|s| |m|e|a|n|i|n|g|f|u|l| |h|e|r|e| +0#0000000&@29 +|s+0#af5f00255&|y|n|t|a|x| +0#0000000&|r+0#00e0003&|e|g|i|o|n| +0#0000000&|T|e|s|t|R|e|g|i|o|n| |s+0#00e0003&|t|a|r|t|=|/+0#e000002&|{|/| +0#0000000&|e+0#00e0003&|n|d|=|/+0#e000002&|}|/| +0#0000000&|o+0#e000e06&|n|e|l|i|n|e| +0#0000000&@24 +>s+0#af5f00255&|y|n|t|a|x| +0#0000000&|r+0#00e0003&|e|g|i|o|n| +0#0000000&|T|e|s|t|R|e|g|i|o|n|2| |o+0#e000e06&|n|e|l|i|n|e| +0#0000000&|s+0#00e0003&|t|a|r|t|=|/+0#e000002&|"|/| +0#0000000&|e+0#00e0003&|n|d|=|/+0#e000002&|"|/| +0#0000000&@23 +|s+0#af5f00255&|y|n|t|a|x| +0#0000000&|r+0#00e0003&|e|g|i|o|n| +0#0000000&|T|e|s|t|R|e|g|i|o|n|3| |s+0#00e0003&|t|a|r|t|=|/+0#e000002&|\|/|\|*|/| +0#0000000&|e+0#00e0003&|n|d|=|/+0#e000002&|\|*|\|/@1| +0#0000000&|o+0#e000e06&|n|e|l|i|n|e| +0#0000000&|c+0#e000e06&|o|n|t|a|i|n|e|d|i|n|=|A|L@1| +0#0000000&@1 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|3|8@1|,|1| @8|B|o|t| diff --git a/runtime/syntax/testdir/input/vim_ex_syntax.vim b/runtime/syntax/testdir/input/vim_ex_syntax.vim index 851931206c..f8412446ba 100644 --- a/runtime/syntax/testdir/input/vim_ex_syntax.vim +++ b/runtime/syntax/testdir/input/vim_ex_syntax.vim @@ -375,3 +375,15 @@ syntax cluster typstCodeKeywords syn match slrnrcColorInit contained "^\s*color\s\+\S\+" skipwhite nextgroup=slrnrcColorVal\(Str\)\= contains=slrnrcColor\(Obj\|ObjStr\)\= syn region slrnrcCmdLine matchgroup=slrnrcCmd start="\<\(autobaud\|...\|visible_headers\)\>" end="$" oneline contains=slrnrc\(String\|Comment\) +" :syntax keyword — oneline accepted but meaningless +syntax keyword TestKw foo bar oneline +syntax keyword TestKw2 oneline baz quux + +" :syntax match — oneline accepted but meaningless +syntax match TestMatch /\/ oneline +syntax match TestMatch2 oneline /\/ + +" :syntax region — oneline is meaningful here +syntax region TestRegion start=/{/ end=/}/ oneline +syntax region TestRegion2 oneline start=/"/ end=/"/ +syntax region TestRegion3 start=/\/\*/ end=/\*\// oneline containedin=ALL diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index a390df78ed..0780f8695e 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -2,7 +2,7 @@ " Language: Vim script " Maintainer: Hirohito Higashi " Doug Kearns -" Last Change: 2026 Apr 15 +" Last Change: 2026 Apr 20 " Former Maintainer: Charles E. Campbell " DO NOT CHANGE DIRECTLY. @@ -1923,10 +1923,11 @@ syn keyword vimSynType contained include skipwhite nextgroup=vimSynIncludeClust syn match vimSynIncludeCluster contained "@[_a-zA-Z0-9]\+\>" " Syntax: keyword {{{2 -syn cluster vimSynKeyGroup contains=@vimContinue,vimSynCchar,vimSynNextgroup,vimSynKeyOpt,vimSynContainedin +syn cluster vimSynKeyGroup contains=@vimContinue,vimSynCchar,vimSynNextgroup,vimSynKeyOpt,vimSynContainedin,vimSynKeyError syn keyword vimSynType contained keyword skipwhite nextgroup=vimSynKeyRegion syn region vimSynKeyRegion contained keepend matchgroup=vimGroupName start="\h\w*\>" skip=+\\\\\|\\|\|\n\s*\%(\\\|"\\ \)+ matchgroup=vimCmdSep end="|\|$" contains=@vimSynKeyGroup syn match vimSynKeyOpt contained "\%#=1\<\%(conceal\|contained\|transparent\|skipempty\|skipwhite\|skipnl\)\>" +syn match vimSynKeyError contained "\" " Syntax: match {{{2 syn cluster vimSynMtchGroup contains=@vimContinue,vimSynCchar,vimSynContains,vimSynContainedin,vimSynError,vimSynMtchOpt,vimSynNextgroup,vimSynRegPat,vimNotation,vimMtchComment @@ -2494,6 +2495,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimSyncError vimError hi def link vimSynConcealError vimError hi def link vimSynError vimError + hi def link vimSynKeyError vimError hi def link vimSynFoldlevelError vimError hi def link vimSynIskeywordError vimError hi def link vimSynSpellError vimError From 73ba353c9db6f3344610a5a05e9084c72d6b2e0f Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 20 Apr 2026 15:35:39 +0000 Subject: [PATCH 079/545] patch 9.2.0372: pum: rendering issues with multibyte text and opacity Problem: pum: rendering issues with multibyte text and opacity Solution: Fix trailing-cell handling near popup text boundary, use popup attrs on opaque popup text, preserve right border when bg wide char spills, blend popup text bg with underlying bg, fix wide background char corruption (Yasuhiro Matsumoto) closes: #20017 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/highlight.c | 28 +++-- src/screen.c | 76 ++++++++++++- .../dumps/Test_pumopt_opacity_text_attrs.dump | 20 ++++ .../dumps/Test_pumopt_opacity_wide_bg.dump | 20 ++++ .../Test_pumopt_opacity_wide_bg_shifted.dump | 20 ++++ src/testdir/test_popup.vim | 100 ++++++++++++++++++ src/version.c | 2 + 7 files changed, 256 insertions(+), 10 deletions(-) create mode 100644 src/testdir/dumps/Test_pumopt_opacity_text_attrs.dump create mode 100644 src/testdir/dumps/Test_pumopt_opacity_wide_bg.dump create mode 100644 src/testdir/dumps/Test_pumopt_opacity_wide_bg_shifted.dump diff --git a/src/highlight.c b/src/highlight.c index eb7707c81f..67b551c67e 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -3250,9 +3250,13 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED) base_fg, popup_aep->ae_u.gui.bg_color, blend); } } - else if (popup_aep->ae_u.gui.fg_color != INVALCOLOR) + else { - // blend_fg=FALSE: use popup foreground + // blend_fg=FALSE: popup text is opaque. Replace the + // underlying cell's attribute flags and fg with the + // popup's, so the underlying syntax highlighting does + // not bleed through. + new_en.ae_attr = popup_aep->ae_attr; new_en.ae_u.gui.fg_color = popup_aep->ae_u.gui.fg_color; } // Blend background color: blend popup bg toward underlying bg @@ -3294,8 +3298,17 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED) popup_aep = syn_cterm_attr2entry(popup_attr); if (popup_aep != NULL) { - // Blend foreground color - if (popup_aep->ae_u.cterm.fg_color > 0) + if (!blend_fg) + { + // blend_fg=FALSE: popup text is opaque. Replace the + // underlying cell's attribute flags and fg with the + // popup's, so the underlying syntax highlighting does + // not bleed through. + new_en.ae_attr = popup_aep->ae_attr; + new_en.ae_u.cterm.fg_color = popup_aep->ae_u.cterm.fg_color; + } + else if (popup_aep->ae_u.cterm.fg_color > 0) + // Blend foreground color new_en.ae_u.cterm.fg_color = popup_aep->ae_u.cterm.fg_color; // Use popup background color (cterm colors don't support blending) if (popup_aep->ae_u.cterm.bg_color > 0) @@ -3330,8 +3343,11 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED) base_fg, popup_bg, blend); } } - else if (popup_fg != INVALCOLOR) - // blend_fg=FALSE: use popup foreground + else + // blend_fg=FALSE: popup text is opaque. Replace fg + // with popup's (even INVALCOLOR) so the underlying + // syntax highlighting fg does not bleed. ae_attr + // was already set above for this branch. new_en.ae_u.cterm.fg_rgb = popup_fg; if (popup_bg != INVALCOLOR) { diff --git a/src/screen.c b/src/screen.c index b9bde1d574..dd9aa8d29a 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1814,6 +1814,21 @@ screen_puts_len( force_redraw_this = force_redraw_next; force_redraw_next = FALSE; + // When drawing pum text with opacity, blend the popup bg with the + // saved underlying bg so text cells match the padding cells that + // use hl_pum_blend_attr. Without this, popup text appears on a + // solid bg while padding shows a blended bg, creating a visible + // seam. + int cell_attr = attr; + if (screen_pum_blend > 0 && pum_bg_attrs != NULL + && row >= pum_bg_top && row < pum_bg_bot + && col < pum_bg_cols) + { + int soff = (row - pum_bg_top) * pum_bg_cols + col; + cell_attr = hl_blend_attr(pum_bg_attrs[soff], attr, + screen_pum_blend, FALSE); + } + need_redraw = ScreenLines[off] != c || (mbyte_cells == 2 && ScreenLines[off + 1] != (enc_dbcs ? ptr[1] : 0)) @@ -1825,7 +1840,7 @@ screen_puts_len( (u8char_T)(c < 0x80 && u8cc[0] == 0 ? 0 : u8c) || (ScreenLinesUC[off] != 0 && screen_comp_differs(off, u8cc)))) - || ScreenAttrs[off] != attr + || ScreenAttrs[off] != cell_attr || exmode_active; if ((need_redraw || force_redraw_this) && !skip_for_popup(row, col)) @@ -1886,7 +1901,7 @@ screen_puts_len( && (*mb_off2cells)(off + 1, max_off) > 1))) ScreenLines[off + mbyte_blen] = 0; ScreenLines[off] = c; - ScreenAttrs[off] = attr; + ScreenAttrs[off] = cell_attr; ScreenCols[off] = -1; if (enc_utf8) { @@ -1907,7 +1922,10 @@ screen_puts_len( if (mbyte_cells == 2) { ScreenLines[off + 1] = 0; - ScreenAttrs[off + 1] = attr; + ScreenLinesUC[off + 1] = 0; + for (int ci = 0; ci < Screen_mco; ++ci) + ScreenLinesC[ci][off + 1] = 0; + ScreenAttrs[off + 1] = cell_attr; ScreenCols[off + 1] = -1; } screen_char(off, row, col); @@ -1915,7 +1933,7 @@ screen_puts_len( else if (mbyte_cells == 2) { ScreenLines[off + 1] = ptr[1]; - ScreenAttrs[off + 1] = attr; + ScreenAttrs[off + 1] = cell_attr; ScreenCols[off + 1] = -1; screen_char_2(off, row, col); } @@ -2793,8 +2811,58 @@ screen_fill( && col < pum_bg_cols) { int soff = (row - pum_bg_top) * pum_bg_cols + col; + + // Skip the trailing cell of a wide background char: its + // leading cell already emitted the full wide glyph via + // screen_char(); drawing here would clobber the right half. + // Only applies when the previous cell was actually processed + // in this fill range -- if the fill starts here (col == + // start_col), the "wide lead" is outside our range (e.g., + // popup text wrote a narrow cell there), so we fall through + // to the regular blend path which renders a blended space. + if (enc_utf8 && pum_bg_linesUC != NULL + && col > start_col + && pum_bg_linesUC[soff] == 0 + && pum_bg_linesUC[soff - 1] != 0 + && utf_char2cells(pum_bg_linesUC[soff - 1]) == 2) + { + ScreenLines[off] = 0; + if (ScreenLinesUC != NULL) + ScreenLinesUC[off] = 0; + ScreenAttrs[off] = ScreenAttrs[off - 1]; + goto next_col; + } + int underlying_attr = pum_bg_attrs[soff]; + // If restoring the leading cell of a wide background char + // would extend past the end of the fill range (e.g. into a + // popup border cell on the right edge), render a blended + // space instead so the border at col+1 is preserved. + // Likewise, if this cell is the trailing half of a wide + // background char whose leading cell is outside the fill + // range (e.g., popup text wrote a narrow cell there), + // restoring the trailing alone would emit a stray NUL. + if (enc_utf8 && pum_bg_linesUC != NULL + && ((pum_bg_linesUC[soff] != 0 + && utf_char2cells(pum_bg_linesUC[soff]) == 2 + && col + 1 >= end_col) + || (col == start_col + && pum_bg_linesUC[soff] == 0 + && col > 0 + && pum_bg_linesUC[soff - 1] != 0 + && utf_char2cells(pum_bg_linesUC[soff - 1]) + == 2))) + { + ScreenLines[off] = ' '; + if (ScreenLinesUC != NULL) + ScreenLinesUC[off] = 0; + ScreenAttrs[off] = hl_pum_blend_attr(underlying_attr, + attr, screen_pum_blend); + screen_char(off, row, col); + goto next_col; + } + // Restore underlying character so text shows through. ScreenLines[off] = pum_bg_lines[soff]; if (enc_utf8 && pum_bg_linesUC != NULL diff --git a/src/testdir/dumps/Test_pumopt_opacity_text_attrs.dump b/src/testdir/dumps/Test_pumopt_opacity_text_attrs.dump new file mode 100644 index 0000000000..93ea3a5a4a --- /dev/null +++ b/src/testdir/dumps/Test_pumopt_opacity_text_attrs.dump @@ -0,0 +1,20 @@ +|ほ*0&#ffffff0|げ> +&@70 +|ほ*0#0000001#ffff4012|げ|ほ|げ|ほ|げ|漢*4&&| +&| +4#e000e06#ffffff0|テ*&|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ふ*0#ffffff16#0000e05|が|漢|字|ほ|げ|漢*4&&| +&| +4#e000e06#ffffff0|テ*&|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|カ*0#ffffff16#0000e05|タ|カ|ナ|候|補|漢*4&&| +&| +4#e000e06#ffffff0|テ*&|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@44|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_pumopt_opacity_wide_bg.dump b/src/testdir/dumps/Test_pumopt_opacity_wide_bg.dump new file mode 100644 index 0000000000..ed52522f01 --- /dev/null +++ b/src/testdir/dumps/Test_pumopt_opacity_wide_bg.dump @@ -0,0 +1,20 @@ +|ほ*0&#ffffff0|げ> +&@70 +|╭+0#0000001#ffd7ff255|─@15|╮|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|│+0#0000001#ffd7ff255| +0&#e0e0e08|ほ*&|げ@1|ほ|げ|漢|字| +&|│+0&#ffd7ff255|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|│+0#0000001#ffd7ff255| |ふ*&|が|漢|字|げ|漢|字| +&|│|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|│+0#0000001#ffd7ff255| |カ*&|タ|カ|ナ|候|補|字| +&|│|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|╰+0#0000001#ffd7ff255|─@15|╯|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@44|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_pumopt_opacity_wide_bg_shifted.dump b/src/testdir/dumps/Test_pumopt_opacity_wide_bg_shifted.dump new file mode 100644 index 0000000000..c145bf0800 --- /dev/null +++ b/src/testdir/dumps/Test_pumopt_opacity_wide_bg_shifted.dump @@ -0,0 +1,20 @@ +|ほ*0&#ffffff0|げ> +&@70 +|╭+0#0000001#ffd7ff255|─@15|╮|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|│+0#0000001#ffd7ff255| +0&#e0e0e08|ほ*&|げ| +&|げ*&|ほ|げ|漢|字|│+0&#ffd7ff255| +0#0000000#ffffff0|ス*&|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|│+0#0000001#ffd7ff255| |ふ*&|が|漢|字|げ|漢|字| +&|│|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|│+0#0000001#ffd7ff255| |カ*&|タ|カ|ナ|候|補| +&|字*&|│+&| +0#0000000#ffffff0|ス*&|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|╰+0#0000001#ffd7ff255|─@15|╯|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|a|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|a|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|a|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|a|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|a|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|a|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|a|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@44|1|,|1| @10|T|o|p| diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim index 0ea6ae48e3..3362cd14f0 100644 --- a/src/testdir/test_popup.vim +++ b/src/testdir/test_popup.vim @@ -2443,6 +2443,106 @@ func Test_pumopt_opacity_screendump() call StopVimInTerminal(buf) endfunc +" Test pumopt opacity with multibyte background text and multibyte popup +" items. Exercises the wide-character alignment in the blend path: +" - a wide background character whose trailing cell falls inside the pum +" fill range must not be re-emitted after screen_char() has already drawn +" the full glyph (would clobber the right half); +" - a wide background character at the right edge of the fill range must +" not spill into the adjacent border cell. +func Test_pumopt_opacity_wide_bg() + CheckScreendump + let lines =<< trim END + set pumopt=opacity:50,border:round + set completeopt=menu + call setline(1, '') + for i in range(20) + call append(line('$'), 'ほげほげほげ漢字テストあいうえおカタカナ') + endfor + normal gg + inoremap call complete(col('.'), + \ ['ほげ', 'ふが漢字', 'カタカナ候補']) + END + call writefile(lines, 'Xpumoptopacitywide', 'D') + let buf = RunVimInTerminal('-S Xpumoptopacitywide', {}) + call TermWait(buf) + call term_sendkeys(buf, "i\") + call TermWait(buf, 100) + call VerifyScreenDump(buf, 'Test_pumopt_opacity_wide_bg', {}) + call term_sendkeys(buf, "\\u") + call TermWait(buf) + call StopVimInTerminal(buf) +endfunc + +" Test pumopt opacity when every other background line is shifted by one +" narrow cell, so the background's wide-character boundaries do not align +" with the popup's wide-character grid. Exercises the blend path when: +" - pum_bg has the trailing half of a wide character at col == start_col +" (its leading half was overwritten by the popup text's clear_next_cell +" narrow space), where restoring the trailing alone would emit a stray +" NUL; a blended space must be rendered instead. +" - clearing of ScreenLinesUC[off + 1] when screen_puts_len writes a wide +" character on a cell whose trailing previously held a different wide +" char's leading codepoint. +func Test_pumopt_opacity_wide_bg_shifted() + CheckScreendump + let lines =<< trim END + set pumopt=opacity:50,border:round + set completeopt=menu + call setline(1, '') + let base = 'ほげほげほげ漢字テストあいうえおカタカナ' + for i in range(20) + call append(line('$'), (i % 2 == 0 ? '' : 'a') .. base) + endfor + normal gg + inoremap call complete(col('.'), + \ ['ほげ', 'ふが漢字', 'カタカナ候補']) + END + call writefile(lines, 'Xpumoptopacityshifted', 'D') + let buf = RunVimInTerminal('-S Xpumoptopacityshifted', {}) + call TermWait(buf) + call term_sendkeys(buf, "i\") + call TermWait(buf, 100) + call VerifyScreenDump(buf, 'Test_pumopt_opacity_wide_bg_shifted', {}) + call term_sendkeys(buf, "\\u") + call TermWait(buf) + call StopVimInTerminal(buf) +endfunc + +" Test that opaque popup text uses the popup's own attributes (fg and flags +" like italic) rather than inheriting them from the syntax-highlighted +" multibyte background cell underneath. Without this guard, popup text +" picks up the fg/italic of whatever buffer text happened to sit under +" each cell. +func Test_pumopt_opacity_text_attrs() + CheckScreendump + let lines =<< trim END + set pumopt=opacity:50 + set completeopt=menu + hi clear + hi Pmenu guifg=#ffffff guibg=#004488 ctermfg=white ctermbg=darkblue + hi PmenuSel guifg=#000000 guibg=#ffcc00 ctermfg=black ctermbg=yellow + hi Special guifg=#ff66cc cterm=italic gui=italic + call setline(1, '') + for i in range(20) + call append(line('$'), 'ほげほげほげ漢字テストあいうえおカタカナ') + endfor + call matchadd('Special', '漢字\|テスト') + normal gg + inoremap call complete(col('.'), + \ ['ほげ', 'ふが漢字', 'カタカナ候補']) + END + call writefile(lines, 'Xpumoptopacityattrs', 'D') + let buf = RunVimInTerminal('-S Xpumoptopacityattrs', {}) + call TermWait(buf) + call term_sendkeys(buf, "i\") + call TermWait(buf, 100) + call VerifyScreenDump(buf, 'Test_pumopt_opacity_text_attrs', {}) + call term_sendkeys(buf, "\\u") + call TermWait(buf) + call StopVimInTerminal(buf) +endfunc + " Test pumopt opacity:100 (fully opaque, same as default) func Test_pumopt_opacity_100() CheckScreendump diff --git a/src/version.c b/src/version.c index 4cdbfdc057..54ea03e260 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 372, /**/ 371, /**/ From 49e8630a2859e6ed497fb5702663c276f8ab6597 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 20 Apr 2026 15:43:56 +0000 Subject: [PATCH 080/545] patch 9.2.0373: Ctrl-R mapping not triggered during completion Problem: Ctrl-R mapping not triggered during completion. Solution: Move Ctrl-R check out of vim_is_ctrl_x_key() (zeertzjq). fixes: #20004 closes: #20006 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/insexpand.c | 8 ++------ src/testdir/test_ins_complete.vim | 20 ++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/insexpand.c b/src/insexpand.c index 7545e9b4ec..019557cab7 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -494,10 +494,6 @@ has_compl_option(int dict_opt) int vim_is_ctrl_x_key(int c) { - // Always allow ^R - let its results then be checked - if (c == Ctrl_R && ctrl_x_mode != CTRL_X_REGISTER) - return TRUE; - // Accept and if the popup menu is visible. if (ins_compl_pum_key(c)) return TRUE; @@ -3155,7 +3151,7 @@ ins_compl_prep(int c) else if (ctrl_x_mode_not_default()) { // We're already in CTRL-X mode, do we stay in it? - if (!vim_is_ctrl_x_key(c)) + if (c != Ctrl_R && !vim_is_ctrl_x_key(c)) { ctrl_x_mode = ctrl_x_mode_scroll() ? CTRL_X_NORMAL : CTRL_X_FINISHED; edit_submode = NULL; @@ -3183,7 +3179,7 @@ ins_compl_prep(int c) // reset continue_* if we left expansion-mode, if we stay they'll be // (re)set properly in ins_complete() - if (!vim_is_ctrl_x_key(c)) + if (c != Ctrl_R && !vim_is_ctrl_x_key(c)) { compl_cont_status = 0; compl_cont_mode = 0; diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index a32bc45cca..ac1b5678ce 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -6305,4 +6305,24 @@ func Test_autocomplete_with_auto_format() call test_override("char_avail", 0) endfunc +func Test_completion_with_mapped_ctrl_r() + new + let b:n = 0 + let @a = 'AABBCCDDEE' + " Ctrl-R mapping is triggered + inoremap let b:n += 1 + inoremap call complete(col('.'), []) + call feedkeys("i\\<*C-R>abcde\", 'tx') + call assert_equal(1, b:n) + call assert_equal('abcde', getline('.')) + + " Ctrl-X Ctrl-R still works with Ctrl-R mapped + call feedkeys("ccAAB\<*C-X>\<*C-R>\<*C-Y>\", 'tx') + call assert_equal(1, b:n) + call assert_equal('AABBCCDDEE', getline('.')) + + let @a = '' + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab nofoldenable diff --git a/src/version.c b/src/version.c index 54ea03e260..1328b4db38 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 373, /**/ 372, /**/ From c62342e5cfc339a87c1eb40ef34b2b31070d72a6 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Mon, 20 Apr 2026 16:05:43 +0000 Subject: [PATCH 081/545] patch 9.2.0374: c_CTRL-{G,T} does not handle offset Problem: c_CTRL-{G,T} does not handle offset, when cycling between matches Solution: Refactor parsing logic into parse_search_pattern_offset() and handle offsets, note: highlighting does not handle offsets yet (Barrett Ruth). fixes: #19991 closes: #19998 Signed-off-by: Barrett Ruth Signed-off-by: Christian Brabandt --- runtime/doc/cmdline.txt | 9 +- src/ex_getln.c | 85 ++++++++++-------- src/proto/search.pro | 1 + src/search.c | 167 +++++++++++++++++++++++------------- src/testdir/test_search.vim | 21 +++++ src/version.c | 2 + 6 files changed, 183 insertions(+), 102 deletions(-) diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 4a0729d0ed..bf47c51999 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -1,4 +1,4 @@ -*cmdline.txt* For Vim version 9.2. Last change: 2026 Mar 17 +*cmdline.txt* For Vim version 9.2. Last change: 2026 Apr 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -466,14 +466,15 @@ CTRL-L A match is done on the pattern in front of the cursor. If *c_CTRL-G* */_CTRL-G* CTRL-G When 'incsearch' is set, entering a search pattern for "/" or "?" and the current match is displayed then CTRL-G will move - to the next match (does not take |search-offset| into account) + to the next match. The |search-offset| is applied when + is pressed, but does not affect the match highlighting. Use CTRL-T to move to the previous match. Hint: on a regular keyboard G is below T. *c_CTRL-T* */_CTRL-T* CTRL-T When 'incsearch' is set, entering a search pattern for "/" or "?" and the current match is displayed then CTRL-T will move - to the previous match (does not take |search-offset| into - account). + to the previous match. The |search-offset| is applied when + is pressed, but does not affect the match highlighting. Use CTRL-G to move to the next match. Hint: on a regular keyboard T is above G. diff --git a/src/ex_getln.c b/src/ex_getln.c index f89bcecc50..0f69d0db23 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -614,14 +614,18 @@ may_adjust_incsearch_highlighting( incsearch_state_T *is_state, int c) { - int skiplen, patlen; - pos_T t; - char_u *pat; - int search_flags = SEARCH_NOOF; - int i; - int save; - int bslsh = FALSE; - int search_delim; + int skiplen, patlen; + pos_T t; + char_u *pat; + char_u *dircp = NULL; + char_u *searchstr; + char_u *strcopy = NULL; + size_t searchstrlen; + size_t patlen_s; + soffset_T offset; + int search_flags = SEARCH_NOOF; + int i; + int search_delim; // Parsing range may already set the last search pattern. // NOTE: must call restore_last_search_pattern() before returning! @@ -639,31 +643,16 @@ may_adjust_incsearch_highlighting( return FAIL; } - if (search_delim == ccline.cmdbuff[skiplen]) - { - pat = last_search_pattern(); - if (pat == NULL) - { - restore_last_search_pattern(); - return FAIL; - } - skiplen = 0; - patlen = (int)last_search_pattern_len(); - } - else - pat = ccline.cmdbuff + skiplen; + pat = ccline.cmdbuff + skiplen; + searchstr = pat; + searchstrlen = (size_t)patlen; + patlen_s = (size_t)(ccline.cmdlen - skiplen); // do not search for the search end delimiter, // unless it is part of the pattern - if (patlen > 2 && firstc == pat[patlen - 1]) - { - patlen--; - if (pat[patlen - 1] == '\\') - { - pat[patlen - 1] = firstc; - bslsh = TRUE; - } - } + (void)parse_search_pattern_offset(&pat, &patlen_s, search_delim, + SEARCH_OPT, &strcopy, &searchstr, + &searchstrlen, &dircp, &offset); cursor_off(); out_flush(); @@ -681,18 +670,39 @@ may_adjust_incsearch_highlighting( if (!p_hls) search_flags += SEARCH_KEEP; ++emsg_off; - save = pat[patlen]; - pat[patlen] = NUL; i = searchit(curwin, curbuf, &t, NULL, c == Ctrl_G ? FORWARD : BACKWARD, - pat, patlen, count, search_flags, RE_SEARCH, NULL); + searchstr, searchstrlen, count, search_flags, RE_SEARCH, NULL); --emsg_off; - pat[patlen] = save; - if (bslsh) - pat[patlen - 1] = '\\'; + if (dircp != NULL) + *dircp = search_delim; if (i) { - is_state->search_start = is_state->match_start; + pos_T match_start = is_state->match_start; + pos_T match_end = is_state->match_end; + long off = offset.off; + + is_state->search_start = match_start; + if (!offset.line && (offset.end || off != 0)) + { + if (offset.end) + { + is_state->search_start = match_end; + (void)decl(&is_state->search_start); + } + while (off > 0) + { + if (incl(&is_state->search_start) == -1) + break; + --off; + } + while (off < 0) + { + if (decl(&is_state->search_start) == -1) + break; + ++off; + } + } is_state->match_end = t; is_state->match_start = t; if (c == Ctrl_T && firstc != '?') @@ -733,6 +743,7 @@ may_adjust_incsearch_highlighting( } else vim_beep(BO_ERROR); + vim_free(strcopy); restore_last_search_pattern(); return FAIL; } diff --git a/src/proto/search.pro b/src/proto/search.pro index 445817762d..3e700a6d8e 100644 --- a/src/proto/search.pro +++ b/src/proto/search.pro @@ -24,6 +24,7 @@ void set_last_search_pat(char_u *s, int idx, int magic, int setlast); void last_pat_prog(regmmatch_T *regmatch); int searchit(win_T *win, buf_T *buf, pos_T *pos, pos_T *end_pos, int dir, char_u *pat, size_t patlen, long count, int options, int pat_use, searchit_arg_T *extra_arg); void set_search_direction(int cdir); +int parse_search_pattern_offset(char_u **pat, size_t *patlen, int search_delim, int options, char_u **strcopy, char_u **searchstr, size_t *searchstrlen, char_u **dircp, soffset_T *offset); int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, size_t patlen, long count, int options, searchit_arg_T *sia); int search_for_exact_line(buf_T *buf, pos_T *pos, int dir, char_u *pat); int searchc(cmdarg_T *cap, int t_cmd); diff --git a/src/search.c b/src/search.c index 9b51ef94a9..03e66aaec8 100644 --- a/src/search.c +++ b/src/search.c @@ -1215,6 +1215,109 @@ first_submatch(regmmatch_T *rp) } #endif +/* + * Parse a search pattern followed by an optional offset (e.g. "pat/e+1"). + * On entry "*pat" points at the start of the pattern and "*patlen" is its + * length. Updates the in/out parameters: + * *pat / *patlen - moved past the pattern and offset + * *strcopy - allocated copy if "\?" or "\/" was unescaped + * (caller must vim_free() it) + * *searchstr and *searchstrlen - pointer/length of the search pattern only + * *dircp - location of the trailing delimiter that was + * replaced with NUL (or NULL); caller may restore + * it + * *offset - parsed offset (line/end/off) + * + * Returns the length of the parsed pattern + offset (used by get_address() + * to know how much of the command line was consumed). + */ + int +parse_search_pattern_offset( + char_u **pat, + size_t *patlen, + int search_delim, + int options, + char_u **strcopy, + char_u **searchstr, + size_t *searchstrlen, + char_u **dircp, + soffset_T *offset) +{ + int cmdlen = 0; + char_u *p; + char_u *ps; + + if (*pat == NULL || **pat == NUL) + return 0; + + ps = *strcopy; + *searchstr = *pat; + *searchstrlen = *patlen; + *dircp = NULL; + + /* + * Find end of regular expression. + * If there is a matching '/' or '?', toss it. + */ + p = skip_regexp_ex(*pat, search_delim, magic_isset(), + strcopy, NULL, NULL); + if (*strcopy != ps) + { + size_t len = STRLEN(*strcopy); + // made a copy of "pat" to change "\?" to "?" + cmdlen += (int)(*patlen - len); + *pat = *strcopy; + *patlen = len; + *searchstr = *strcopy; + *searchstrlen = len; + } + if (*p == search_delim) + { + *searchstrlen = p - *pat; + *dircp = p; // remember where we put the NUL + *p++ = NUL; + } + + offset->line = FALSE; + offset->end = FALSE; + offset->off = 0; + /* + * Check for a line offset or a character offset. + * For get_address (echo off) we don't check for a character + * offset, because it is meaningless and the 's' could be a + * substitute command. + */ + if (*p == '+' || *p == '-' || VIM_ISDIGIT(*p)) + offset->line = TRUE; + else if ((options & SEARCH_OPT) + && (*p == 'e' || *p == 's' || *p == 'b')) + { + if (*p == 'e') // end + offset->end = SEARCH_END; + ++p; + } + if (VIM_ISDIGIT(*p) || *p == '+' || *p == '-') // got an offset + { + // 'nr' or '+nr' or '-nr' + if (VIM_ISDIGIT(*p) || VIM_ISDIGIT(*(p + 1))) + offset->off = atol((char *)p); + else if (*p == '-') // single '-' + offset->off = -1; + else // single '+' + offset->off = 1; + ++p; + while (VIM_ISDIGIT(*p)) // skip number + ++p; + } + + // compute length of search command for get_address() + cmdlen += (int)(p - *pat); + *patlen -= p - *pat; + *pat = p; // put pat after search command + + return cmdlen; +} + /* * Highest level string search function. * Search for the 'count'th occurrence of pattern 'pat' in direction 'dirc' @@ -1257,7 +1360,6 @@ do_search( long c; char_u *dircp; char_u *strcopy = NULL; - char_u *ps; int show_search_stats; char_u *msgbuf = NULL; size_t msgbuflen = 0; @@ -1369,66 +1471,9 @@ do_search( if (pat != NULL && *pat != NUL) // look for (new) offset { - /* - * Find end of regular expression. - * If there is a matching '/' or '?', toss it. - */ - ps = strcopy; - p = skip_regexp_ex(pat, search_delim, magic_isset(), - &strcopy, NULL, NULL); - if (strcopy != ps) - { - size_t len = STRLEN(strcopy); - // made a copy of "pat" to change "\?" to "?" - searchcmdlen += (int)(patlen - len); - pat = strcopy; - patlen = len; - searchstr = strcopy; - searchstrlen = len; - } - if (*p == search_delim) - { - searchstrlen = p - pat; - dircp = p; // remember where we put the NUL - *p++ = NUL; - } - spats[0].off.line = FALSE; - spats[0].off.end = FALSE; - spats[0].off.off = 0; - /* - * Check for a line offset or a character offset. - * For get_address (echo off) we don't check for a character - * offset, because it is meaningless and the 's' could be a - * substitute command. - */ - if (*p == '+' || *p == '-' || VIM_ISDIGIT(*p)) - spats[0].off.line = TRUE; - else if ((options & SEARCH_OPT) - && (*p == 'e' || *p == 's' || *p == 'b')) - { - if (*p == 'e') // end - spats[0].off.end = SEARCH_END; - ++p; - } - if (VIM_ISDIGIT(*p) || *p == '+' || *p == '-') // got an offset - { - // 'nr' or '+nr' or '-nr' - if (VIM_ISDIGIT(*p) || VIM_ISDIGIT(*(p + 1))) - spats[0].off.off = atol((char *)p); - else if (*p == '-') // single '-' - spats[0].off.off = -1; - else // single '+' - spats[0].off.off = 1; - ++p; - while (VIM_ISDIGIT(*p)) // skip number - ++p; - } - - // compute length of search command for get_address() - searchcmdlen += (int)(p - pat); - - patlen -= p - pat; - pat = p; // put pat after search command + searchcmdlen += parse_search_pattern_offset(&pat, &patlen, + search_delim, options, &strcopy, &searchstr, + &searchstrlen, &dircp, &spats[0].off); } show_search_stats = FALSE; diff --git a/src/testdir/test_search.vim b/src/testdir/test_search.vim index 725d08b59a..0a50681751 100644 --- a/src/testdir/test_search.vim +++ b/src/testdir/test_search.vim @@ -700,6 +700,27 @@ func Test_search_cmdline7() call feedkeys("//e\\", 'tx') call assert_equal('1 bbvimb', getline('.')) call assert_equal(4, col('.')) + call cursor(1, 1) + call feedkeys("//+1\\", 'tx') + call assert_equal(' 2 bbvimb', getline('.')) + call assert_equal([0, 2, 1, 0], getpos('.')) + call setline(1, 'blah blah blah') + call feedkeys("gg0/blah/e\\", 'tx') + call assert_equal([0, 1, 9, 0], getpos('.')) + call feedkeys("gg0/blah/e\\\", 'tx') + call assert_equal([0, 1, 14, 0], getpos('.')) + call feedkeys("gg0/blah/e\\\\", 'tx') + call assert_equal([0, 1, 9, 0], getpos('.')) + call cursor(1, col('$')) + call feedkeys("?blah?e\\", 'tx') + call assert_equal([0, 1, 9, 0], getpos('.')) + call feedkeys("gg0/blah/e+1\\", 'tx') + call assert_equal([0, 1, 10, 0], getpos('.')) + call feedkeys("gg0/blah/e-2\\", 'tx') + call assert_equal([0, 1, 7, 0], getpos('.')) + call setline(1, 'a/b a/b a/b') + call feedkeys("gg0/a\\/b/e\\", 'tx') + call assert_equal([0, 1, 7, 0], getpos('.')) set noincsearch call test_override("char_avail", 0) diff --git a/src/version.c b/src/version.c index 1328b4db38..f36742a33a 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 374, /**/ 373, /**/ From c9d4c1dc43c457173bf342f16f21c4cf9a0d9d2d Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Mon, 20 Apr 2026 16:12:54 +0000 Subject: [PATCH 082/545] patch 9.2.0375: prop_find() does not find a virt text in starting line Problem: prop_find() does not find a virt text in the starting line (@rickhowe, after v9.2.0320) Solution: Do not skip virtual text properties with tp_col == MAXCOL on the starting line (Furkan Sahin) The column matching logic incorrectly skipped virtual text properties with tp_col == MAXCOL on the starting line. Exclude such properties from the column range check so they are always found. fixes: #20013 closes: #20019 Signed-off-by: Furkan Sahin Signed-off-by: Christian Brabandt --- src/testdir/test_textprop.vim | 34 ++++++++++++++++++++++++++++++++++ src/textprop.c | 8 ++++++-- src/version.c | 2 ++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index f94acfc97c..a57493bc81 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -4907,4 +4907,38 @@ func Test_textprop_materialize_list() call assert_equal([], prop_list(1, #{ids: 3->range()})) endfunc +func Test_prop_find_floating_vtext() + new + call setline(1, ['111', '222', '333']) + let tn = 'test' + call prop_type_add(tn, {'highlight': 'Search'}) + for ln in range(1, 3) + call prop_add(ln, 0, {'type': tn, 'text': '-----', 'text_align': 'above'}) + endfor + " forward search must find the virtual text on the starting line + let found = prop_find({'type': tn, 'lnum': 1, 'col': 1}) + call assert_equal(1, found.lnum) + call assert_equal('-----', found.text) + " backward search must also find the virtual text on the starting line + let found = prop_find({'type': tn, 'lnum': 1, 'col': 1}, 'b') + call assert_equal(1, found.lnum) + call assert_equal('-----', found.text) + bwipe! + call prop_type_delete(tn) + " Also cover 'below' and 'right' aligned virtual text (also tp_col==MAXCOL) + for align in ['below', 'right'] + new + call setline(1, ['aaa', 'bbb']) + call prop_type_add(tn, {'highlight': 'Search'}) + call prop_add(1, 0, {'type': tn, 'text': 'VT', 'text_align': align}) + let found = prop_find({'type': tn, 'lnum': 1, 'col': 1}) + call assert_equal(1, found.lnum, 'forward, align=' .. align) + call assert_equal('VT', found.text, 'forward, align=' .. align) + let found = prop_find({'type': tn, 'lnum': 1, 'col': 1}, 'b') + call assert_equal(1, found.lnum, 'backward, align=' .. align) + call assert_equal('VT', found.text, 'backward, align=' .. align) + bwipe! + call prop_type_delete(tn) + endfor +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/textprop.c b/src/textprop.c index b4aedf98d7..48cbad4b6a 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -1967,12 +1967,16 @@ f_prop_find(typval_T *argvars, typval_T *rettv) // after `col`, depending on the search direction. if (lnum == lnum_start) { + bool is_floating_vtext = prop.tp_id < 0 + && prop.tp_col == MAXCOL; if (dir == BACKWARD) { - if (prop.tp_col > col) + // Virtual text with MAXCOL always matches any column + if (!is_floating_vtext && prop.tp_col > col) continue; } - else if (prop.tp_col + prop.tp_len - (prop.tp_len != 0) < col) + else if (!is_floating_vtext + && prop.tp_col + prop.tp_len - (prop.tp_len != 0) < col) continue; } if (both ? prop.tp_id == id && prop.tp_type == type_id diff --git a/src/version.c b/src/version.c index f36742a33a..47ea1bac76 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 375, /**/ 374, /**/ From f74a41620ba9a69edfe3ce0b88b42024cb6361bb Mon Sep 17 00:00:00 2001 From: Furkan Sahin Date: Mon, 20 Apr 2026 16:22:50 +0000 Subject: [PATCH 083/545] patch 9.2.0376: Vim9: elseif condition compiled in dead branch Problem: When an `if` condition is constant true, the `else` block is skipped during compilation. However, any `elseif` condition within that skipped block was still compiled. This caused errors when the condition referenced variables only declared in the skipped block or when it checked for missing features (like `has('clipboard')`) (Coacher) Solution: In compile_elseif(), when scope->se_skip_save is already SKIP_YES, skip compiling the elseif condition expression using skip_expr_cctx() (Furkan Sahin) fixes: #19160 closes: #20021 Signed-off-by: Furkan Sahin Signed-off-by: Christian Brabandt --- src/testdir/test_vim9_script.vim | 61 ++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9cmds.c | 7 ++++ 3 files changed, 70 insertions(+) diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 262f47db2f..fcb585f026 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -5933,4 +5933,65 @@ def Test_g_variable_shadow_multi_context() unlet g:F enddef +" Test that dead elseif conditions are skipped without errors inside a :def function +def Test_skip_elseif_dead_branch() + var lines =<< trim END + vim9script + def CompileTest() + if true + echo 'ok' + else + var x = 0 + if x > 0 + echo 'pos' + elseif x < 0 + echo 'neg' + endif + endif + enddef + CompileTest() + END + v9.CheckScriptSuccess(lines) +enddef + +" Another variation: using has() in a dead branch that would fail if compiled +def Test_skip_elseif_with_has_in_dead_branch() + var lines =<< trim END + vim9script + def HasTest() + if true + echo 'ok' + else + var x = 0 + if x > 0 + echo 'pos' + elseif has('clipboard') + echo 'neg' + endif + endif + enddef + HasTest() + END + v9.CheckScriptSuccess(lines) +enddef + +def Test_if_false_elseif_true_still_takes_elseif() + var lines =<< trim END + vim9script + var result = '' + def F() + if false + result = 'A' + elseif true + result = 'B' + else + result = 'C' + endif + enddef + F() + assert_equal('B', result) + END + v9.CheckScriptSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 47ea1bac76..272d7dda07 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 376, /**/ 375, /**/ diff --git a/src/vim9cmds.c b/src/vim9cmds.c index db81229eed..5005cf898a 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -623,6 +623,13 @@ compile_elseif(char_u *arg, cctx_T *cctx) return p; } + if (scope->se_skip_save == SKIP_YES) + { + // Enclosing outer block is dead, skip this elseif + skip_expr_cctx(&p, cctx); + return p; + } + if (cctx->ctx_skip == SKIP_UNKNOWN) { int moved_cmdmod = FALSE; From 3c3050e64808cf41b6633b9a5ffe1bc90ec91d1d Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Mon, 20 Apr 2026 16:33:57 +0000 Subject: [PATCH 084/545] patch 9.2.0377: Using int as bool type in gui_T struct Problem: Several gui_T fields are declared as "int" or "char" but are used strictly as boolean flags with TRUE/FALSE values. The integer types obscure the boolean intent and are wider than needed. Solution: Change the following gui_T members to bool (stdbool.h) and update their assignments from TRUE/FALSE to true/false accordingly (Hirohito Higashi) The following conversions have been done: - int -> bool (11 members): in_focus, in_use, starting, dying, dofork, dospawn, pointer_hidden, force_redraw, directx_enabled, font_can_bold, which_scrollbars[3] - char -> bool (2 members): cursor_is_valid, menu_is_active No existing code compares these members against TRUE/FALSE explicitly (e.g. "== TRUE"), so only plain assignments are affected. gui_init() used counter-style "--gui.starting" / "++gui.starting" to temporarily clear the flag across a call to gui_mch_enable_menu(). With gui.starting now bool this triggers -Werror=bool-operation, so replace it with an explicit save/restore. X11 Bool members (rsrc_rev_video, color_approx) are intentionally left unchanged: rsrc_rev_video is registered as an X Toolkit resource with XtRBool / sizeof(Bool) and must keep the int-sized X11 Bool type. closes: #20005 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/gui.c | 60 ++++++++++++++++++++++++----------------------- src/gui.h | 26 ++++++++++---------- src/gui_gtk_x11.c | 12 +++++----- src/gui_motif.c | 2 +- src/gui_w32.c | 2 +- src/gui_x11.c | 4 ++-- src/main.c | 36 ++++++++++++++-------------- src/misc1.c | 2 +- src/version.c | 2 ++ 9 files changed, 75 insertions(+), 71 deletions(-) diff --git a/src/gui.c b/src/gui.c index f62bf697e9..bf277e2b93 100644 --- a/src/gui.c +++ b/src/gui.c @@ -198,7 +198,7 @@ gui_attempt_start(void) static int recursive = 0; ++recursive; - gui.starting = TRUE; + gui.starting = true; #ifdef FEAT_GUI_GTK gui.event_time = GDK_CURRENT_TIME; @@ -388,8 +388,8 @@ gui_read_child_pipe(int fd) void gui_prepare(int *argc, char **argv) { - gui.in_use = FALSE; // No GUI yet (maybe later) - gui.starting = FALSE; // No GUI yet (maybe later) + gui.in_use = false; // No GUI yet (maybe later) + gui.starting = false; // No GUI yet (maybe later) gui_mch_prepare(argc, argv); } @@ -412,17 +412,17 @@ gui_init_check(void) } gui.shell_created = false; - gui.dying = FALSE; - gui.in_focus = TRUE; // so the guicursor setting works + gui.dying = false; + gui.in_focus = true; // so the guicursor setting works gui.dragged_sb = SBAR_NONE; gui.dragged_wp = NULL; - gui.pointer_hidden = FALSE; + gui.pointer_hidden = false; gui.col = 0; gui.row = 0; gui.num_cols = Columns; gui.num_rows = Rows; - gui.cursor_is_valid = FALSE; + gui.cursor_is_valid = false; gui.scroll_region_top = 0; gui.scroll_region_bot = Rows - 1; gui.scroll_region_left = 0; @@ -457,7 +457,7 @@ gui_init_check(void) gui.menu_font = NOFONT; # endif # endif - gui.menu_is_active = TRUE; // default: include menu + gui.menu_is_active = true; // default: include menu # ifndef FEAT_GUI_GTK gui.menu_height = MENU_DEFAULT_HEIGHT; gui.menu_width = 0; @@ -674,7 +674,7 @@ gui_init(void) /* * Create the GUI shell. */ - gui.in_use = TRUE; // Must be set after menus have been set up + gui.in_use = true; // Must be set after menus have been set up if (gui_mch_init() == FAIL) goto error; @@ -780,9 +780,11 @@ gui_init(void) // It was still there to make F10 work. if (vim_strchr(p_go, GO_MENUS) == NULL) { - --gui.starting; + bool save_starting = gui.starting; + + gui.starting = false; gui_mch_enable_menu(FALSE); - ++gui.starting; + gui.starting = save_starting; gui_mch_update(); } # endif @@ -855,7 +857,7 @@ gui_init(void) #endif error: - gui.in_use = FALSE; + gui.in_use = false; clip_init(FALSE); } @@ -866,7 +868,7 @@ gui_exit(int rc) // don't free the fonts, it leads to a BUS error // richard@whitequeen.com Jul 99 free_highlight_fonts(); - gui.in_use = FALSE; + gui.in_use = false; gui_mch_exit(rc); } @@ -1163,7 +1165,7 @@ gui_check_pos(void) if (gui.col >= screen_Columns) gui.col = screen_Columns - 1; if (gui.cursor_row >= screen_Rows || gui.cursor_col >= screen_Columns) - gui.cursor_is_valid = FALSE; + gui.cursor_is_valid = false; } /* @@ -1229,7 +1231,7 @@ gui_update_cursor( if (gui.row >= screen_Rows || gui.col >= screen_Columns) return; - gui.cursor_is_valid = TRUE; + gui.cursor_is_valid = true; /* * How the cursor is drawn depends on the current mode. @@ -1850,7 +1852,7 @@ gui_clear_block( // Invalidate cursor if it was in this block if ( gui.cursor_row >= row1 && gui.cursor_row <= row2 && gui.cursor_col >= col1 && gui.cursor_col <= col2) - gui.cursor_is_valid = FALSE; + gui.cursor_is_valid = false; } /* @@ -1922,7 +1924,7 @@ gui_write( case 'C': // Clear screen clip_scroll_selection(9999); gui_mch_clear_all(); - gui.cursor_is_valid = FALSE; + gui.cursor_is_valid = false; force_scrollbar = TRUE; break; case 'M': // Move cursor @@ -2691,7 +2693,7 @@ gui_outstr_nowrap( if (gui.cursor_row == gui.row && gui.cursor_col >= col && gui.cursor_col < col + len) - gui.cursor_is_valid = FALSE; + gui.cursor_is_valid = false; } #ifdef FEAT_SIGN_ICONS @@ -2731,7 +2733,7 @@ gui_undraw_cursor(void) // Cursor_is_valid is reset when the cursor is undrawn, also reset it // here in case it wasn't needed to undraw it. - gui.cursor_is_valid = FALSE; + gui.cursor_is_valid = false; } void @@ -2943,7 +2945,7 @@ gui_delete_lines(int row, int count) && gui.cursor_col <= gui.scroll_region_right) { if (gui.cursor_row < row + count) - gui.cursor_is_valid = FALSE; + gui.cursor_is_valid = false; else if (gui.cursor_row <= gui.scroll_region_bot) gui.cursor_row -= count; } @@ -2971,7 +2973,7 @@ gui_insert_lines(int row, int count) if (gui.cursor_row <= gui.scroll_region_bot - count) gui.cursor_row += count; else if (gui.cursor_row <= gui.scroll_region_bot) - gui.cursor_is_valid = FALSE; + gui.cursor_is_valid = false; } } } @@ -3545,30 +3547,30 @@ gui_init_which_components(char_u *oldval UNUSED) p_go = temp; } } - gui.menu_is_active = FALSE; + gui.menu_is_active = false; #endif for (i = 0; i < 3; i++) - gui.which_scrollbars[i] = FALSE; + gui.which_scrollbars[i] = false; for (p = p_go; *p; p++) switch (*p) { case GO_LEFT: - gui.which_scrollbars[SBAR_LEFT] = TRUE; + gui.which_scrollbars[SBAR_LEFT] = true; break; case GO_RIGHT: - gui.which_scrollbars[SBAR_RIGHT] = TRUE; + gui.which_scrollbars[SBAR_RIGHT] = true; break; case GO_VLEFT: if (win_hasvertsplit()) - gui.which_scrollbars[SBAR_LEFT] = TRUE; + gui.which_scrollbars[SBAR_LEFT] = true; break; case GO_VRIGHT: if (win_hasvertsplit()) - gui.which_scrollbars[SBAR_RIGHT] = TRUE; + gui.which_scrollbars[SBAR_RIGHT] = true; break; case GO_BOT: - gui.which_scrollbars[SBAR_BOTTOM] = TRUE; + gui.which_scrollbars[SBAR_BOTTOM] = true; break; #ifdef FEAT_GUI_DARKTHEME case GO_DARKTHEME: @@ -3577,7 +3579,7 @@ gui_init_which_components(char_u *oldval UNUSED) #endif #ifdef FEAT_MENU case GO_MENUS: - gui.menu_is_active = TRUE; + gui.menu_is_active = true; break; #endif case GO_GREY: diff --git a/src/gui.h b/src/gui.h index 76d5882221..8b8b74020c 100644 --- a/src/gui.h +++ b/src/gui.h @@ -231,23 +231,23 @@ typedef long guicolor_T; // handle for a GUI color; for X11 this should typedef struct Gui { - int in_focus; // Vim has input focus - int in_use; // Is the GUI being used? - int starting; // GUI will start in a little while + bool in_focus; // Vim has input focus + bool in_use; // Is the GUI being used? + bool starting; // GUI will start in a little while bool shell_created; // Has the shell been created yet? - int dying; // Is vim dying? Then output to terminal - int dofork; // Use fork() when GUI is starting + bool dying; // Is vim dying? Then output to terminal + bool dofork; // Use fork() when GUI is starting #ifdef GUI_MAY_SPAWN - int dospawn; // Use spawn() when GUI is starting + bool dospawn; // Use spawn() when GUI is starting #endif int dragged_sb; // Which scrollbar being dragged, if any? win_T *dragged_wp; // Which WIN's sb being dragged, if any? - int pointer_hidden; // Is the mouse pointer hidden? + bool pointer_hidden; // Is the mouse pointer hidden? int col; // Current cursor column in GUI display int row; // Current cursor row in GUI display int cursor_col; // Physical cursor column in GUI display int cursor_row; // Physical cursor row in GUI display - char cursor_is_valid; // There is a cursor at cursor_row/col + bool cursor_is_valid; // There is a cursor at cursor_row/col int num_cols; // Number of columns int num_rows; // Number of rows int scroll_region_top; // Top (first) line of scroll region @@ -259,9 +259,9 @@ typedef struct Gui int scrollbar_height; // Height of horizontal scrollbar int left_sbar_x; // Calculated x coord for left scrollbar int right_sbar_x; // Calculated x coord for right scrollbar - int force_redraw; // Force a redraw even e.g. not resized + bool force_redraw; // Force a redraw even e.g. not resized #ifdef FEAT_DIRECTX - int directx_enabled; // DirectX (DirectWrite) rendering active + bool directx_enabled; // DirectX (DirectWrite) rendering active #endif #ifdef FEAT_MENU @@ -269,11 +269,11 @@ typedef struct Gui int menu_height; // Height of the menu bar int menu_width; // Width of the menu bar # endif - char menu_is_active; // TRUE if menu is present + bool menu_is_active; // true if menu is present #endif scrollbar_T bottom_sbar; // Bottom scrollbar - int which_scrollbars[3];// Which scrollbar boxes are active? + bool which_scrollbars[3];// Which scrollbar boxes are active? int prev_wrap; // For updating the horizontal scrollbar int char_width; // Width of char cell in pixels int char_height; // Height of char cell in pixels, includes @@ -288,7 +288,7 @@ typedef struct Gui GuiFont ital_font; // Italic font GuiFont boldital_font; // Bold-Italic font #else - int font_can_bold; // Whether norm_font supports bold weight. + bool font_can_bold; // Whether norm_font supports bold weight. // The styled font variants are not used. #endif diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index 5a9645a562..2dab629394 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -675,7 +675,7 @@ gui_mch_prepare(int *argc, char **argv) break; #ifdef FEAT_NETBEANS_INTG case ARG_NETBEANS: - gui.dofork = FALSE; // don't fork() when starting GUI + gui.dofork = false; // don't fork() when starting GUI netbeansArg = argv[i]; break; #endif @@ -689,7 +689,7 @@ gui_mch_prepare(int *argc, char **argv) // Only when the GUI can start. if ((option->flags & ARG_NEEDS_GUI) && gui_mch_early_init_check(FALSE) == OK) - gui.starting = TRUE; + gui.starting = true; if (option->flags & ARG_KEEP) ++i; @@ -1710,7 +1710,7 @@ gui_mch_early_init_check(int give_message) q = mch_getenv((char_u *)"WAYLAND_DISPLAY"); if ((p == NULL || *p == NUL) && (q == NULL || *q == NUL)) { - gui.dying = TRUE; + gui.dying = true; if (give_message) emsg(_((char *)e_cannot_open_display)); return FAIL; @@ -1751,7 +1751,7 @@ gui_mch_init_check(void) // Don't use gtk_init() or gnome_init(), it exits on failure. if (!gtk_init_check(&gui_argc, &gui_argv)) { - gui.dying = TRUE; + gui.dying = true; emsg(_((char *)e_cannot_open_display)); return FAIL; } @@ -4224,7 +4224,7 @@ gui_mch_init(void) G_CALLBACK(scroll_event), NULL); // Pretend we don't have input focus, we will get an event if we do. - gui.in_focus = FALSE; + gui.in_focus = false; // Handle changes to the "Xft/DPI" setting. { @@ -5299,7 +5299,7 @@ get_styled_font_variants(void) PangoFont *plain_font; PangoFont *bold_font; - gui.font_can_bold = FALSE; + gui.font_can_bold = false; plain_font = pango_context_load_font(gui.text_context, gui.norm_font); diff --git a/src/gui_motif.c b/src/gui_motif.c index 9f4a5856d0..e7e8a5340b 100644 --- a/src/gui_motif.c +++ b/src/gui_motif.c @@ -578,7 +578,7 @@ gui_x11_create_widgets(void) gui_x11_callbacks(textArea, vimForm); // Pretend we don't have input focus, we will get an event if we do. - gui.in_focus = FALSE; + gui.in_focus = false; } /* diff --git a/src/gui_w32.c b/src/gui_w32.c index 093c84176e..724266aebb 100644 --- a/src/gui_w32.c +++ b/src/gui_w32.c @@ -3731,7 +3731,7 @@ gui_mch_delete_lines( // scrolling such that the cursor ends up in the top-left character on // the screen... But why? (Webb) // It's probably fixed by disabling drawing the cursor while scrolling. - // gui.cursor_is_valid = FALSE; + // gui.cursor_is_valid = false; gui_clear_block(gui.scroll_region_bot - num_lines + 1, gui.scroll_region_left, diff --git a/src/gui_x11.c b/src/gui_x11.c index a61d47a077..9cd8f5f72e 100644 --- a/src/gui_x11.c +++ b/src/gui_x11.c @@ -1146,7 +1146,7 @@ gui_mch_prepare(int *argc, char **argv) #ifdef FEAT_NETBEANS_INTG if (strncmp("-nb", argv[arg], 3) == 0) { - gui.dofork = FALSE; // don't fork() when starting GUI + gui.dofork = false; // don't fork() when starting GUI netbeansArg = argv[arg]; mch_memmove(&argv[arg], &argv[arg + 1], (--*argc - arg) * sizeof(char *)); @@ -1197,7 +1197,7 @@ gui_mch_init_check(void) #endif if (app_context == NULL || gui.dpy == NULL) { - gui.dying = TRUE; + gui.dying = true; emsg(_(e_cannot_open_display)); return FAIL; } diff --git a/src/main.c b/src/main.c index d4019ff51e..bfabcbbeda 100644 --- a/src/main.c +++ b/src/main.c @@ -198,7 +198,7 @@ main // Check if the current executable file is for the GUI subsystem. gui.starting = mch_is_gui_executable(); # elif defined(FEAT_GUI_MSWIN) - gui.starting = TRUE; + gui.starting = true; # endif # ifdef FEAT_CLIENTSERVER @@ -228,7 +228,7 @@ main * :gui. */ # ifdef ALWAYS_USE_GUI - gui.starting = TRUE; + gui.starting = true; # else # if defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) /* @@ -239,7 +239,7 @@ main { if (gui_init_check() == FAIL) { - gui.starting = FALSE; + gui.starting = false; // When running "evim" or "gvim -y" we need the menus, exit if we // don't have them. @@ -1038,7 +1038,7 @@ common_init_2(mparm_T *paramp) #endif #ifdef FEAT_GUI - gui.dofork = TRUE; // default is to use fork() + gui.dofork = true; // default is to use fork() #endif /* @@ -1910,7 +1910,7 @@ early_arg_scan(mparm_T *parmp UNUSED) # ifdef FEAT_GUI if (strstr(argv[i], "-wait") != 0) // don't fork() when starting the GUI to edit files ourself - gui.dofork = FALSE; + gui.dofork = false; # endif } # if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER) @@ -2027,7 +2027,7 @@ parse_command_name(mparm_T *parmp) || TOLOWER_ASC(initstr[1]) == 'g')) { # ifdef FEAT_GUI - gui.starting = TRUE; + gui.starting = true; # endif parmp->evim_mode = TRUE; ++initstr; @@ -2041,12 +2041,12 @@ parse_command_name(mparm_T *parmp) ++initstr; # endif # ifdef GUI_MAY_SPAWN - gui.dospawn = FALSE; // No need to spawn a new process. + gui.dospawn = false; // No need to spawn a new process. # endif } # ifdef GUI_MAY_SPAWN else - gui.dospawn = TRUE; // Not "gvim". Need to spawn gvim.exe. + gui.dospawn = true; // Not "gvim". Need to spawn gvim.exe. # endif @@ -2189,7 +2189,7 @@ command_line_scan(mparm_T *parmp) cmdline_width = Columns = 80; // need to init Columns info_message = TRUE; // use mch_msg(), not mch_errmsg() # if defined(FEAT_GUI) && !defined(ALWAYS_USE_GUI) && !defined(VIMDLL) - gui.starting = FALSE; // not starting GUI, will exit + gui.starting = false; // not starting GUI, will exit # endif list_version(); msg_putchar('\n'); @@ -2215,7 +2215,7 @@ command_line_scan(mparm_T *parmp) else if (STRNICMP(argv[0] + argv_idx, "nofork", 6) == 0) { # ifdef FEAT_GUI - gui.dofork = FALSE; // don't fork() when starting GUI + gui.dofork = false; // don't fork() when starting GUI # endif } else if (STRNICMP(argv[0] + argv_idx, "noplugin", 8) == 0) @@ -2327,7 +2327,7 @@ command_line_scan(mparm_T *parmp) case 'f': // "-f" GUI: run in foreground. Amiga: open // window directly, not with newcli # ifdef FEAT_GUI - gui.dofork = FALSE; // don't fork() when starting GUI + gui.dofork = false; // don't fork() when starting GUI # endif break; @@ -2344,7 +2344,7 @@ command_line_scan(mparm_T *parmp) case 'h': // "-h" give help message # ifdef FEAT_GUI_GNOME // Tell usage() to exit for "gvim". - gui.starting = FALSE; + gui.starting = false; # endif usage(); break; @@ -2374,7 +2374,7 @@ command_line_scan(mparm_T *parmp) case 'y': // "-y" easy mode # ifdef FEAT_GUI - gui.starting = TRUE; // start GUI a bit later + gui.starting = true; // start GUI a bit later # endif parmp->evim_mode = TRUE; break; @@ -2504,7 +2504,7 @@ command_line_scan(mparm_T *parmp) case 'v': // "-v" Vi-mode (as if called "vi") exmode_active = 0; # if defined(FEAT_GUI) && !defined(VIMDLL) - gui.starting = FALSE; // don't start GUI + gui.starting = false; // don't start GUI # endif break; @@ -2694,7 +2694,7 @@ command_line_scan(mparm_T *parmp) */ # ifdef FEAT_GUI if (term_is_gui((char_u *)argv[0])) - gui.starting = TRUE; // start GUI a bit later + gui.starting = true; // start GUI a bit later else # endif parmp->term = (char_u *)argv[0]; @@ -3508,7 +3508,7 @@ source_startup_scripts(mparm_T *parmp) main_start_gui(void) { # ifdef FEAT_GUI - gui.starting = TRUE; // start GUI a bit later + gui.starting = true; // start GUI a bit later # else mch_errmsg(_(e_gui_cannot_be_used_not_enabled_at_compile_time)); mch_errmsg("\n"); @@ -3595,7 +3595,7 @@ mainerr( gui.in_use = mch_is_gui_executable(); #endif #ifdef FEAT_GUI_MSWIN - gui.starting = FALSE; // Needed to show as error. + gui.starting = false; // Needed to show as error. #endif init_longVersion(); @@ -3822,7 +3822,7 @@ usage(void) if (gui.starting) { mch_msg("\n"); - gui.dofork = FALSE; + gui.dofork = false; } else # endif diff --git a/src/misc1.c b/src/misc1.c index 1cf73a004d..067db99499 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -2284,7 +2284,7 @@ prepare_to_exit(void) #ifdef FEAT_GUI if (gui.in_use) { - gui.dying = TRUE; + gui.dying = true; out_trash(); // trash any pending output } else diff --git a/src/version.c b/src/version.c index 272d7dda07..c65aa73da9 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 377, /**/ 376, /**/ From 146d5da0d16b7ea9c767a978cf02384d0831eb92 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Mon, 20 Apr 2026 17:12:29 +0000 Subject: [PATCH 085/545] patch 9.2.0378: Using int as bool type in win_T struct Problem: Several win_T fields are declared as "int" or "char" but are used strictly as boolean flags with TRUE/FALSE values. The integer types obscure the boolean intent and are wider than needed. Solution: Change the following win_T members to bool (stdbool.h) and update their assignments from TRUE/FALSE to true/false accordingly. The following conversions have been done: - int -> bool (10 members): w_set_curswant, w_botfill, w_old_botfill, w_do_win_fix_cursor, w_popup_fixed, w_border_highlight_isset, w_cline_folded, w_redr_status, w_arg_idx_invalid, w_has_scrollbar - char -> bool (4 members): w_topline_was_set, w_ru_empty, w_fold_manual, w_foldinvalid No existing code compares these members against TRUE/FALSE explicitly or uses ++/-- / bitwise ops on them, so only plain assignments are affected. Excluded: - w_locked (recursion counter with ++/--), - w_want_scrollbar (may hold -1 from dict_get_bool), - w_winbar_height (used in arithmetic and exposed as number via getwininfo()). related: #20005 closes: #20008 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/arglist.c | 4 +-- src/buffer.c | 12 ++++---- src/diff.c | 4 +-- src/drawline.c | 2 +- src/drawscreen.c | 20 +++++++------- src/edit.c | 24 ++++++++-------- src/evalfunc.c | 6 ++-- src/evalwindow.c | 4 +-- src/ex_cmds.c | 2 +- src/ex_docmd.c | 2 +- src/ex_getln.c | 4 +-- src/fold.c | 18 ++++++------ src/if_lua.c | 2 +- src/if_mzsch.c | 2 +- src/if_ruby.c | 2 +- src/if_tcl.c | 2 +- src/indent.c | 2 +- src/memline.c | 2 +- src/misc1.c | 2 +- src/misc2.c | 2 +- src/mouse.c | 8 +++--- src/move.c | 8 +++--- src/normal.c | 72 ++++++++++++++++++++++++------------------------ src/ops.c | 8 +++--- src/option.c | 4 +-- src/optionstr.c | 4 +-- src/popupmenu.c | 4 +-- src/popupwin.c | 12 ++++---- src/quickfix.c | 4 +-- src/register.c | 2 +- src/screen.c | 8 +++--- src/search.c | 4 +-- src/structs.h | 28 +++++++++---------- src/tag.c | 4 +-- src/terminal.c | 4 +-- src/version.c | 2 ++ src/window.c | 18 ++++++------ 37 files changed, 157 insertions(+), 155 deletions(-) diff --git a/src/arglist.c b/src/arglist.c index 6fccf69c26..84de912f01 100644 --- a/src/arglist.c +++ b/src/arglist.c @@ -541,7 +541,7 @@ check_arg_idx(win_T *win) { // We are not editing the current entry in the argument list. // Set "arg_had_last" if we are editing the last one. - win->w_arg_idx_invalid = TRUE; + win->w_arg_idx_invalid = true; if (win->w_arg_idx != WARGCOUNT(win) - 1 && arg_had_last == FALSE && ALIST(win) == &global_alist @@ -557,7 +557,7 @@ check_arg_idx(win_T *win) { // We are editing the current entry in the argument list. // Set "arg_had_last" if it's also the last one - win->w_arg_idx_invalid = FALSE; + win->w_arg_idx_invalid = false; if (win->w_arg_idx == WARGCOUNT(win) - 1 && win->w_alist == &global_alist) arg_had_last = TRUE; diff --git a/src/buffer.c b/src/buffer.c index d51cf31a2a..7ea9e93511 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -2001,8 +2001,8 @@ enter_buffer(buf_T *buf) curwin->w_cursor.lnum = 1; curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; - curwin->w_topline_was_set = FALSE; + curwin->w_set_curswant = true; + curwin->w_topline_was_set = false; // mark cursor position as being invalid curwin->w_valid = 0; @@ -2625,7 +2625,7 @@ buflist_getfile( curwin->w_cursor.col = col; check_cursor_col(); curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } retval = OK; } @@ -2655,7 +2655,7 @@ buflist_getfpos(void) curwin->w_cursor.col = fpos->col; check_cursor_col(); curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } } @@ -3344,7 +3344,7 @@ get_winopts(buf_T *buf) #endif #ifdef FEAT_FOLDING curwin->w_fold_manual = wp->w_fold_manual; - curwin->w_foldinvalid = TRUE; + curwin->w_foldinvalid = true; cloneFoldGrowArray(&wp->w_folds, &curwin->w_folds); #endif } @@ -3354,7 +3354,7 @@ get_winopts(buf_T *buf) copy_winopt(&wip->wi_opt, &curwin->w_onebuf_opt); #ifdef FEAT_FOLDING curwin->w_fold_manual = wip->wi_fold_manual; - curwin->w_foldinvalid = TRUE; + curwin->w_foldinvalid = true; cloneFoldGrowArray(&wip->wi_folds, &curwin->w_folds); #endif } diff --git a/src/diff.c b/src/diff.c index 2dff27ba28..a29c434110 100644 --- a/src/diff.c +++ b/src/diff.c @@ -2747,11 +2747,11 @@ diff_set_topline(win_T *fromwin, win_T *towin) } // safety check (if diff info gets outdated strange things may happen) - towin->w_botfill = FALSE; + towin->w_botfill = false; if (towin->w_topline > towin->w_buffer->b_ml.ml_line_count) { towin->w_topline = towin->w_buffer->b_ml.ml_line_count; - towin->w_botfill = TRUE; + towin->w_botfill = true; } if (towin->w_topline < 1) { diff --git a/src/drawline.c b/src/drawline.c index 419412bd1f..93561e689f 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -4034,7 +4034,7 @@ win_line( curwin->w_cline_row = startrow; curwin->w_cline_height = wlv.row - startrow; #ifdef FEAT_FOLDING - curwin->w_cline_folded = FALSE; + curwin->w_cline_folded = false; #endif curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW); } diff --git a/src/drawscreen.c b/src/drawscreen.c index d815c20da9..2f36f4f5a0 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -212,7 +212,7 @@ update_screen(int type_arg) wp->w_redr_type = UPD_NOT_VALID; if (W_WINROW(wp) + wp->w_height + wp->w_status_height <= msg_scrolled) - wp->w_redr_status = TRUE; + wp->w_redr_status = true; } } } @@ -489,7 +489,7 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED) row = statusline_row(wp); - wp->w_redr_status = FALSE; + wp->w_redr_status = false; if (wp->w_status_height == 0) { // no status line, can only be last window @@ -501,7 +501,7 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED) || (!ignore_pum && pum_visible())) { // Don't redraw right now, do it later. - wp->w_redr_status = TRUE; + wp->w_redr_status = true; } #ifdef FEAT_STL_OPT else if (*p_stl != NUL || *wp->w_p_stl != NUL) @@ -653,7 +653,7 @@ showruler(int always) if (pum_visible()) { // Don't redraw right now, do it later. - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; return; } #if defined(FEAT_STL_OPT) @@ -1442,7 +1442,7 @@ fold_line( { curwin->w_cline_row = row; curwin->w_cline_height = 1; - curwin->w_cline_folded = TRUE; + curwin->w_cline_folded = true; curwin->w_valid |= (VALID_CHEIGHT|VALID_CROW); } @@ -1563,7 +1563,7 @@ win_update(win_T *wp) if (type == UPD_NOT_VALID) { - wp->w_redr_status = TRUE; + wp->w_redr_status = true; wp->w_lines_valid = 0; } @@ -3370,7 +3370,7 @@ redraw_buf_and_status_later(buf_T *buf, int type) if (wp->w_buffer == buf) { redraw_win_later(wp, type); - wp->w_redr_status = TRUE; + wp->w_redr_status = true; } } } @@ -3387,7 +3387,7 @@ status_redraw_all(void) FOR_ALL_WINDOWS(wp) if (wp->w_status_height) { - wp->w_redr_status = TRUE; + wp->w_redr_status = true; redraw_later(UPD_VALID); } } @@ -3403,7 +3403,7 @@ status_redraw_curbuf(void) FOR_ALL_WINDOWS(wp) if (wp->w_status_height != 0 && wp->w_buffer == curbuf) { - wp->w_redr_status = TRUE; + wp->w_redr_status = true; redraw_later(UPD_VALID); } } @@ -3447,7 +3447,7 @@ redraw_statuslines(void) win_redraw_last_status(frame_T *frp) { if (frp->fr_layout == FR_LEAF) - frp->fr_win->w_redr_status = TRUE; + frp->fr_win->w_redr_status = true; else if (frp->fr_layout == FR_ROW) { FOR_ALL_FRAMES(frp, frp->fr_child) diff --git a/src/edit.c b/src/edit.c index 79a4177bfc..60cae0e15c 100644 --- a/src/edit.c +++ b/src/edit.c @@ -468,7 +468,7 @@ edit( // set curwin->w_curswant for next K_DOWN or K_UP if (!arrow_used) - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; // If there is no typeahead may check for timestamps (e.g., for when a // menu invoked a shell command). @@ -2740,7 +2740,7 @@ beginline(int flags) && !((flags & BL_FIX) && ptr[1] == NUL); ++ptr) ++curwin->w_cursor.col; } - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } adjust_skipcol(); } @@ -2768,7 +2768,7 @@ oneright(void) coladvance(getviscol() + ((*ptr != TAB && vim_isprintc((*mb_ptr2char)(ptr))) ? ptr2cells(ptr) : 1)); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; // Return OK if the cursor moved, FAIL otherwise (at window edge). return (prevpos.col != curwin->w_cursor.col || prevpos.coladd != curwin->w_cursor.coladd) ? OK : FAIL; @@ -2789,7 +2789,7 @@ oneright(void) return FAIL; curwin->w_cursor.col += l; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; adjust_skipcol(); return OK; } @@ -2836,7 +2836,7 @@ oneleft(void) curwin->w_cursor.coladd = 0; } - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; adjust_skipcol(); return OK; } @@ -2844,7 +2844,7 @@ oneleft(void) if (curwin->w_cursor.col == 0) return FAIL; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; --curwin->w_cursor.col; // if the character on the left of the current cursor is a multi-byte @@ -3791,7 +3791,7 @@ ins_esc( // When an autoindent was removed, curswant stays after the // indent if (restart_edit == NUL && (colnr_T)temp == curwin->w_cursor.col) - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; // Remember the last Insert position in the '^ mark. if ((cmdmod.cmod_flags & CMOD_KEEPJUMPS) == 0) @@ -4740,7 +4740,7 @@ ins_left(void) start_arrow(&tpos); --(curwin->w_cursor.lnum); coladvance((colnr_T)MAXCOL); - curwin->w_set_curswant = TRUE; // so we stay at the end + curwin->w_set_curswant = true; // so we stay at the end } else vim_beep(BO_CRSR); @@ -4800,7 +4800,7 @@ ins_s_left(void) if (!end_change) AppendCharToRedobuff(K_S_LEFT); (void)bck_word(1L, FALSE, FALSE); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } else vim_beep(BO_CRSR); @@ -4822,7 +4822,7 @@ ins_right(void) start_arrow_with_change(&curwin->w_cursor, end_change); if (!end_change) AppendCharToRedobuff(K_RIGHT); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if (virtual_active()) oneright(); else @@ -4845,7 +4845,7 @@ ins_right(void) && curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { start_arrow(&curwin->w_cursor); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; ++curwin->w_cursor.lnum; curwin->w_cursor.col = 0; } @@ -4870,7 +4870,7 @@ ins_s_right(void) if (!end_change) AppendCharToRedobuff(K_S_RIGHT); (void)fwd_word(1L, FALSE, 0); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } else vim_beep(BO_CRSR); diff --git a/src/evalfunc.c b/src/evalfunc.c index e03dc9b201..f4f6a802ca 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -10863,7 +10863,7 @@ search_cmn(typval_T *argvars, pos_T *match_pos, int *flagsp) if (flags & SP_NOMOVE) curwin->w_cursor = save_cursor; else - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; theend: p_ws = save_p_ws; @@ -11455,7 +11455,7 @@ set_position(typval_T *argvars, typval_T *rettv, int charpos) if (curswant >= 0) { curwin->w_curswant = curswant - 1; - curwin->w_set_curswant = FALSE; + curwin->w_set_curswant = false; } check_cursor(); rettv->vval.v_number = 0; @@ -12006,7 +12006,7 @@ f_spellbadword(typval_T *argvars UNUSED, typval_T *rettv) if (len != 0) { word = ml_get_cursor(); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } } else if (*curbuf->b_s.b_p_spl != NUL) diff --git a/src/evalwindow.c b/src/evalwindow.c index 53116c7a1d..c93979e08c 100644 --- a/src/evalwindow.c +++ b/src/evalwindow.c @@ -794,7 +794,7 @@ f_win_execute(typval_T *argvars, typval_T *rettv) // Update the status line if the cursor moved. if (win_valid(wp) && !EQUAL_POS(curpos, wp->w_cursor)) - wp->w_redr_status = TRUE; + wp->w_redr_status = true; // In case the command moved the cursor or changed the Visual area, // check it is valid. @@ -1256,7 +1256,7 @@ f_winrestview(typval_T *argvars, typval_T *rettv UNUSED) if (dict_has_key(dict, "curswant")) { curwin->w_curswant = (colnr_T)dict_get_number(dict, "curswant"); - curwin->w_set_curswant = FALSE; + curwin->w_set_curswant = false; } if (dict_has_key(dict, "topline")) diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 18b156c9ce..7cf63adaf9 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -3430,7 +3430,7 @@ do_ecmd( curwin->w_cursor.col = solcol; check_cursor_col(); curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } else beginline(BL_SOL | BL_FIX); diff --git a/src/ex_docmd.c b/src/ex_docmd.c index bd8b4991dc..74e32a74ed 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -7917,7 +7917,7 @@ ex_syncbind(exarg_T *eap UNUSED) curwin->w_scbind_pos = topline; redraw_later(UPD_VALID); cursor_correct(); - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; } } curwin = save_curwin; diff --git a/src/ex_getln.c b/src/ex_getln.c index 0f69d0db23..3cec79f809 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -584,7 +584,7 @@ may_do_incsearch_highlighting( // May redraw the status line to show the cursor position. if (p_ru && curwin->w_status_height > 0) - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; update_screen(UPD_SOME_VALID); highlight_match = FALSE; @@ -1840,7 +1840,7 @@ getcmdline_int( FOR_ALL_WINDOWS(wp) if (*p_stl != NUL || *wp->w_p_stl != NUL) { - wp->w_redr_status = TRUE; + wp->w_redr_status = true; found_one = TRUE; } diff --git a/src/fold.c b/src/fold.c index 9618c21c62..b9b54d8fc9 100644 --- a/src/fold.c +++ b/src/fold.c @@ -501,7 +501,7 @@ newFoldLevelWin(win_T *wp) fp = (fold_T *)wp->w_folds.ga_data; for (i = 0; i < wp->w_folds.ga_len; ++i) fp[i].fd_flags = FD_LEVEL; - wp->w_fold_manual = FALSE; + wp->w_fold_manual = false; } changed_window_setting_win(wp); } @@ -692,7 +692,7 @@ foldCreate(linenr_T start, linenr_T end) if (use_level && !closed && level < curwin->w_p_fdl) closeFold(start, 1L); if (!use_level) - curwin->w_fold_manual = TRUE; + curwin->w_fold_manual = true; fp->fd_flags = FD_CLOSED; fp->fd_small = MAYBE; @@ -805,7 +805,7 @@ deleteFold( clearFolding(win_T *win) { deleteFoldRecurse(&win->w_folds); - win->w_foldinvalid = FALSE; + win->w_foldinvalid = false; } // foldUpdate() {{{2 @@ -875,7 +875,7 @@ foldUpdate(win_T *wp, linenr_T top, linenr_T bot) void foldUpdateAll(win_T *win) { - win->w_foldinvalid = TRUE; + win->w_foldinvalid = true; redraw_win_later(win, UPD_NOT_VALID); } @@ -1219,7 +1219,7 @@ checkupdate(win_T *wp) return; foldUpdate(wp, (linenr_T)1, (linenr_T)MAXLNUM); // will update all - wp->w_foldinvalid = FALSE; + wp->w_foldinvalid = false; } // setFoldRepeat() {{{2 @@ -1387,7 +1387,7 @@ setManualFoldWin( found->fd_flags = FD_CLOSED; done |= DONE_ACTION; } - wp->w_fold_manual = TRUE; + wp->w_fold_manual = true; if (done & DONE_ACTION) changed_window_setting_win(wp); done |= DONE_FOLD; @@ -2177,7 +2177,7 @@ foldUpdateIEMS(win_T *wp, linenr_T top, linenr_T bot) // Need to update all folds. top = 1; bot = wp->w_buffer->b_ml.ml_line_count; - wp->w_foldinvalid = FALSE; + wp->w_foldinvalid = false; // Mark all folds as maybe-small. setSmallMaybe(&wp->w_folds); @@ -2679,14 +2679,14 @@ foldUpdateIEMSRecurse( // The first fold depends on the containing fold. if (topflags == FD_OPEN) { - flp->wp->w_fold_manual = TRUE; + flp->wp->w_fold_manual = true; fp->fd_flags = FD_OPEN; } else if (i <= 0) { fp->fd_flags = topflags; if (topflags != FD_LEVEL) - flp->wp->w_fold_manual = TRUE; + flp->wp->w_fold_manual = true; } else fp->fd_flags = (fp - 1)->fd_flags; diff --git a/src/if_lua.c b/src/if_lua.c index fc2e392f2d..3a60e920e0 100644 --- a/src/if_lua.c +++ b/src/if_lua.c @@ -1687,7 +1687,7 @@ luaV_window_newindex(lua_State *L) luaV_checksandbox(L); # endif w->w_cursor.col = v - 1; - w->w_set_curswant = TRUE; + w->w_set_curswant = true; update_screen(UPD_VALID); } else if (strncmp(s, "width", 5) == 0) diff --git a/src/if_mzsch.c b/src/if_mzsch.c index a6043e1d82..c7a40909a5 100644 --- a/src/if_mzsch.c +++ b/src/if_mzsch.c @@ -2128,7 +2128,7 @@ set_cursor(void *data, int argc, Scheme_Object **argv) win->win->w_cursor.lnum = lnum; win->win->w_cursor.col = col; - win->win->w_set_curswant = TRUE; + win->win->w_set_curswant = true; update_screen(UPD_VALID); raise_if_error(); diff --git a/src/if_ruby.c b/src/if_ruby.c index b5b0079ece..5c867afc14 100644 --- a/src/if_ruby.c +++ b/src/if_ruby.c @@ -1692,7 +1692,7 @@ window_set_cursor(VALUE self, VALUE pos) col = RARRAY_PTR(pos)[1]; win->w_cursor.lnum = NUM2LONG(lnum); win->w_cursor.col = NUM2UINT(col); - win->w_set_curswant = TRUE; + win->w_set_curswant = true; check_cursor(); // put cursor on an existing line update_screen(UPD_NOT_VALID); return Qnil; diff --git a/src/if_tcl.c b/src/if_tcl.c index 3aebcbd0fd..8c70b6eb10 100644 --- a/src/if_tcl.c +++ b/src/if_tcl.c @@ -1103,7 +1103,7 @@ winselfcmd( // TODO: should check column win->w_cursor.lnum = val1; win->w_cursor.col = col2vim(val2); - win->w_set_curswant = TRUE; + win->w_set_curswant = true; flags |= FL_UPDATE_SCREEN; break; diff --git a/src/indent.c b/src/indent.c index 8fe4177c30..9f19f6eb6e 100644 --- a/src/indent.c +++ b/src/indent.c @@ -1454,7 +1454,7 @@ change_indent( curwin->w_cursor.col = 0; else curwin->w_cursor.col = (colnr_T)new_cursor_col; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; changed_cline_bef_curs(); // May have to adjust the start of the insert. diff --git a/src/memline.c b/src/memline.c index b374971e26..2887ebbf9d 100644 --- a/src/memline.c +++ b/src/memline.c @@ -6236,7 +6236,7 @@ goto_byte(long cnt) curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = (colnr_T)boff; curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } check_cursor(); diff --git a/src/misc1.c b/src/misc1.c index 067db99499..a0f9f799e2 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -621,7 +621,7 @@ check_status(buf_T *buf) FOR_ALL_WINDOWS(wp) if (wp->w_buffer == buf && wp->w_status_height) { - wp->w_redr_status = TRUE; + wp->w_redr_status = true; set_must_redraw(UPD_VALID); } } diff --git a/src/misc2.c b/src/misc2.c index aca4d3c93b..076593d6e0 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -719,7 +719,7 @@ set_leftcol(colnr_T leftcol) } if (retval) - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; redraw_later(UPD_NOT_VALID); return retval; } diff --git a/src/mouse.c b/src/mouse.c index 04a420e4bb..f6ab770040 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -1123,7 +1123,7 @@ do_mouse( find_end_of_word(&curwin->w_cursor); } } - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } if (is_click) redraw_curbuf_later(UPD_INVERTED); // update the inversion @@ -1329,7 +1329,7 @@ ins_mousescroll(int dir) int did_scroll = (orig_topline != curwin->w_topline || orig_leftcol != curwin->w_leftcol); - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; curwin = old_curwin; curbuf = curwin->w_buffer; @@ -2346,7 +2346,7 @@ jump_to_mouse( } curwin->w_curswant = col; - curwin->w_set_curswant = FALSE; // May still have been TRUE + curwin->w_set_curswant = false; // May still have been TRUE if (coladvance(col) == FAIL) // Mouse click beyond end of line { if (inclusive != NULL) @@ -2436,7 +2436,7 @@ nv_mousescroll(cmdarg_T *cap) // Call the common mouse scroll function shared with other modes. do_mousescroll(cap); - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; curwin = old_curwin; curbuf = curwin->w_buffer; } diff --git a/src/move.c b/src/move.c index 1e35101303..7e3be27264 100644 --- a/src/move.c +++ b/src/move.c @@ -650,7 +650,7 @@ update_curswant_force(void) - curwin->w_virtcol_first_char #endif ; - curwin->w_set_curswant = FALSE; + curwin->w_set_curswant = false; } /* @@ -766,7 +766,7 @@ set_topline(win_T *wp, linenr_T lnum) if (wp->w_botline > wp->w_buffer->b_ml.ml_line_count + 1) wp->w_botline = wp->w_buffer->b_ml.ml_line_count + 1; wp->w_topline = lnum; - wp->w_topline_was_set = TRUE; + wp->w_topline_was_set = true; #ifdef FEAT_DIFF if (lnum != prev_topline) // Keep the filler lines when the topline didn't change. @@ -3044,7 +3044,7 @@ scroll_cursor_halfway(int atend, int prefer_above) #ifdef FEAT_DIFF curwin->w_topfill = topfill; if (old_topline > curwin->w_topline + curwin->w_height) - curwin->w_botfill = FALSE; + curwin->w_botfill = false; check_topfill(curwin, FALSE); #endif curwin->w_valid &= ~(VALID_WROW|VALID_CROW|VALID_BOTLINE|VALID_BOTLINE_AP); @@ -3458,7 +3458,7 @@ do_check_cursorbind(void) // Only scroll when 'scrollbind' hasn't done this. if (!curwin->w_p_scb) update_topline(); - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; } } diff --git a/src/normal.c b/src/normal.c index c29fb6c25a..2c46bf31fa 100644 --- a/src/normal.c +++ b/src/normal.c @@ -1845,7 +1845,7 @@ display_showcmd(void) if (*p_sloc == 's') { if (showcmd_is_clear) - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; else win_redr_status(curwin, FALSE); } @@ -1998,7 +1998,7 @@ check_scrollbind(linenr_T topline_diff, long leftcol_diff) redraw_later(UPD_VALID); cursor_correct(); - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; } // do the horizontal scroll @@ -2314,7 +2314,7 @@ find_decl( } else { - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; // "n" searches forward now reset_search_dir(); } @@ -2923,14 +2923,14 @@ nv_zet(cmdarg_T *cap) // "zx": re-apply 'foldlevel' and open folds at the cursor case 'x': curwin->w_p_fen = TRUE; - curwin->w_foldinvalid = TRUE; // recompute folds + curwin->w_foldinvalid = true; // recompute folds newFoldLevel(); // update right now foldOpenCursor(); break; // "zX": undo manual opens/closes, re-apply 'foldlevel' case 'X': curwin->w_p_fen = TRUE; - curwin->w_foldinvalid = TRUE; // recompute folds + curwin->w_foldinvalid = true; // recompute folds old_fdl = -1; // force an update break; @@ -3875,7 +3875,7 @@ nv_right(cmdarg_T *cap) ++curwin->w_cursor.lnum; curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; cap->oap->inclusive = FALSE; } continue; @@ -3895,7 +3895,7 @@ nv_right(cmdarg_T *cap) } else if (past_line) { - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if (virtual_active()) oneright(); else @@ -3953,7 +3953,7 @@ nv_left(cmdarg_T *cap) { --(curwin->w_cursor.lnum); coladvance((colnr_T)MAXCOL); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; // When the NL before the first char has to be deleted we // put the cursor on the NUL after the previous line. @@ -4222,7 +4222,7 @@ normal_search( cap->oap->motion_type = MCHAR; cap->oap->inclusive = FALSE; cap->oap->use_reg_one = TRUE; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; CLEAR_FIELD(sia); i = do_search(cap->oap, dir, dir, pat, patlen, cap->count1, @@ -4289,7 +4289,7 @@ nv_csearch(cmdarg_T *cap) return; } - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; // Include a Tab for "tx" and for "dfx". if (gchar_cursor() == TAB && virtual_active() && cap->arg == FORWARD && (t_cmd || cap->oap->op_type != OP_NOP)) @@ -4431,7 +4431,7 @@ nv_bracket_block(cmdarg_T *cap, pos_T *old_pos) { setpcmark(); curwin->w_cursor = *pos; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; #ifdef FEAT_FOLDING if ((fdo_flags & FDO_BLOCK) && KeyTyped && cap->oap->op_type == OP_NOP) @@ -4495,7 +4495,7 @@ nv_brackets(cmdarg_T *cap) (linenr_T)MAXLNUM, FALSE, FALSE); vim_free(ptr); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } } else @@ -4520,7 +4520,7 @@ nv_brackets(cmdarg_T *cap) else flag = '}'; // "][" or "[]" - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; // Imitate strange Vi behaviour: When using "]]" with an operator // we also stop at '}'. if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, flag, @@ -4605,7 +4605,7 @@ nv_brackets(cmdarg_T *cap) break; } else - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; # ifdef FEAT_FOLDING if (cap->oap->op_type == OP_NOP && (fdo_flags & FDO_SEARCH) && KeyTyped) foldOpenCursor(); @@ -4665,7 +4665,7 @@ nv_percent(cmdarg_T *cap) { setpcmark(); curwin->w_cursor = *pos; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; curwin->w_cursor.coladd = 0; adjust_for_sel(cap); } @@ -4690,7 +4690,7 @@ nv_brace(cmdarg_T *cap) cap->oap->use_reg_one = TRUE; // The motion used to be inclusive for "(", but that is not what Vi does. cap->oap->inclusive = FALSE; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if (findsent(cap->arg, cap->count1) == FAIL) { @@ -4730,7 +4730,7 @@ nv_findpar(cmdarg_T *cap) cap->oap->motion_type = MCHAR; cap->oap->inclusive = FALSE; cap->oap->use_reg_one = TRUE; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if (!findpar(&cap->oap->inclusive, cap->arg, cap->count1, NUL, FALSE)) { clearopbeep(cap->oap); @@ -4778,7 +4778,7 @@ nv_kundo(cmdarg_T *cap) } #endif u_undo((int)cap->count1); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } /* @@ -4983,7 +4983,7 @@ nv_replace(cmdarg_T *cap) if (has_mbyte) mb_adjust_cursor(); curbuf->b_op_end = curwin->w_cursor; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; set_last_insert(cap->nchar); } } @@ -5033,7 +5033,7 @@ v_swap_corners(int cmdchar) old_cursor = curwin->w_cursor; curwin->w_cursor = VIsual; VIsual = old_cursor; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } } @@ -5187,7 +5187,7 @@ n_swapchar(cmdarg_T *cap) check_cursor(); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if (did_change) { changed_lines(startpos.lnum, startpos.col, curwin->w_cursor.lnum + 1, @@ -5224,7 +5224,7 @@ nv_cursormark(cmdarg_T *cap, int flag, pos_T *pos) if (cap->cmdchar == '`') cap->oap->use_reg_one = TRUE; cap->oap->inclusive = FALSE; // ignored if not MCHAR - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } /* @@ -5411,7 +5411,7 @@ nv_pcmark(cmdarg_T *cap) pos = movemark((int)cap->count1); if (pos == (pos_T *)-1) // jump to other file { - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; check_cursor(); } else if (pos != NULL) // can jump @@ -5551,7 +5551,7 @@ nv_visual(cmdarg_T *cap) coladvance(curwin->w_curswant); } else - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; redraw_curbuf_later(UPD_INVERTED); // show the inversion } else @@ -5813,7 +5813,7 @@ nv_g_home_m_cmd(cmdarg_T *cap) while (VIM_ISWHITE(i) && oneright() == OK); curwin->w_valid &= ~VALID_WCOL; } - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; #ifdef FEAT_FOLDING if (hasAnyFolding(curwin)) { @@ -5854,7 +5854,7 @@ nv_g_underscore_cmd(cmdarg_T *cap) while (curwin->w_cursor.col > 0 && VIM_ISWHITE(ptr[curwin->w_cursor.col])) --curwin->w_cursor.col; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; adjust_for_sel(cap); } @@ -6103,7 +6103,7 @@ nv_g_cmd(cmdarg_T *cap) coladvance((colnr_T)(i * cap->count0 / 100)); else coladvance((colnr_T)(i / 2)); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } break; @@ -6135,7 +6135,7 @@ nv_g_cmd(cmdarg_T *cap) case 'e': case 'E': oap->motion_type = MCHAR; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; oap->inclusive = TRUE; if (bckend_word(cap->count1, cap->nchar == 'E', FALSE) == FAIL) clearopbeep(oap); @@ -6409,7 +6409,7 @@ nv_redo_or_register(cmdarg_T *cap) return; u_redo((int)cap->count1); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } /* @@ -6432,7 +6432,7 @@ nv_Undo(cmdarg_T *cap) return; u_undoline(); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } /* @@ -6569,7 +6569,7 @@ nv_pipe(cmdarg_T *cap) curwin->w_curswant = 0; // keep curswant at the column where we wanted to go, not where // we ended; differs if line is too short - curwin->w_set_curswant = FALSE; + curwin->w_set_curswant = false; } /* @@ -6581,7 +6581,7 @@ nv_bck_word(cmdarg_T *cap) { cap->oap->motion_type = MCHAR; cap->oap->inclusive = FALSE; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if (bck_word(cap->count1, cap->arg, FALSE) == FAIL) clearopbeep(cap->oap); #ifdef FEAT_FOLDING @@ -6651,7 +6651,7 @@ nv_wordcmd(cmdarg_T *cap) } cap->oap->motion_type = MCHAR; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if (word_end) n = end_word(cap->count1, cap->arg, flag, FALSE); else @@ -6931,7 +6931,7 @@ nv_esc(cmdarg_T *cap) { end_visual_mode(); // stop Visual check_cursor_col(); // make sure cursor is not beyond EOL - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; redraw_curbuf_later(UPD_INVERTED); } else if (no_reason) @@ -6957,7 +6957,7 @@ nv_esc(cmdarg_T *cap) void set_cursor_for_append_to_line(void) { - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if (get_ve_flags() == VE_ALL) { int save_State = State; @@ -7242,7 +7242,7 @@ nv_object( if (flag == FAIL) clearopbeep(cap->oap); adjust_cursor_col(); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } /* diff --git a/src/ops.c b/src/ops.c index 715897331e..0ebdb381d9 100644 --- a/src/ops.c +++ b/src/ops.c @@ -1719,7 +1719,7 @@ op_insert(oparg_T *oap, long count1) if (oap->block_mode && curwin->w_cursor.coladd == 0) { // Move the cursor to the character right of the block. - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; while (*ml_get_cursor() != NUL && (curwin->w_cursor.col < bd.textcol + bd.textlen)) ++curwin->w_cursor.col; @@ -2390,7 +2390,7 @@ do_join( check_cursor_col(); curwin->w_cursor.coladd = 0; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; theend: vim_free(spaces); @@ -3273,7 +3273,7 @@ do_addsub( if (visual) curwin->w_cursor = save_cursor; else if (did_change) - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; else if (virtual_active()) curwin->w_cursor.coladd = save_coladd; @@ -4270,7 +4270,7 @@ do_pending_operator(cmdarg_T *cap, int old_col, int gui_yank) if (l > 1) oap->end.col += l - 1; } - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; // oap->empty is set when start and end are the same. The inclusive // flag affects this too, unless yanking and the end is on a NUL. diff --git a/src/option.c b/src/option.c index 5a580525ee..a1138a0e17 100644 --- a/src/option.c +++ b/src/option.c @@ -5283,7 +5283,7 @@ set_bool_option( if (curwin->w_curswant != MAXCOL && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0 && (options[opt_idx].flags & P_HLONLY) == 0) - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if ((opt_flags & OPT_NO_REDRAW) == 0) check_redraw(options[opt_idx].flags); @@ -5523,7 +5523,7 @@ set_num_option( if (curwin->w_curswant != MAXCOL && (options[opt_idx].flags & (P_CURSWANT | P_RALL)) != 0 && (options[opt_idx].flags & P_HLONLY) == 0) - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if ((opt_flags & OPT_NO_REDRAW) == 0) check_redraw(options[opt_idx].flags); diff --git a/src/optionstr.c b/src/optionstr.c index 228e403d8a..ab7c5ffff1 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -1416,7 +1416,7 @@ did_set_buftype(optset_T *args UNUSED) if (curwin->w_status_height) { - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; redraw_later(UPD_VALID); } curbuf->b_help = (curbuf->b_p_bt[0] == 'h'); @@ -5553,7 +5553,7 @@ did_set_string_option( if (curwin->w_curswant != MAXCOL && (get_option_flags(opt_idx) & (P_CURSWANT | P_RALL)) != 0 && (get_option_flags(opt_idx) & P_HLONLY) == 0) - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; if ((opt_flags & OPT_NO_REDRAW) == 0) { diff --git a/src/popupmenu.c b/src/popupmenu.c index a50faaa05b..cadcda7f4d 100644 --- a/src/popupmenu.c +++ b/src/popupmenu.c @@ -1483,7 +1483,7 @@ pum_set_selected(int n, int repeat UNUSED) // window is not resized, skip the preview window's // status line redrawing. if (ins_compl_active() && !resized) - curwin->w_redr_status = FALSE; + curwin->w_redr_status = false; // Return cursor to where we were validate_cursor(); @@ -1513,7 +1513,7 @@ pum_set_selected(int n, int repeat UNUSED) // StatusLineNC for a moment and cause flicker. pum_will_redraw = !resized; save_redr_status = curwin_save->w_redr_status; - curwin_save->w_redr_status = FALSE; + curwin_save->w_redr_status = false; update_screen(0); pum_pretend_not_visible = FALSE; pum_will_redraw = FALSE; diff --git a/src/popupwin.c b/src/popupwin.c index c1d614d03e..7016605115 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -880,7 +880,7 @@ apply_general_options(win_T *wp, dict_T *dict) int i; CHECK_LIST_MATERIALIZE(list); - wp->w_border_highlight_isset = TRUE; + wp->w_border_highlight_isset = true; // Clear all highlights if list is empty if (list->lv_len == 0) { @@ -1566,7 +1566,7 @@ popup_adjust_position(win_T *wp) if (wp->w_buffer->b_term != NULL && !term_is_finished(wp->w_buffer)) // Terminal window with running job never has a scrollbar, adjusts to // window height. - wp->w_has_scrollbar = FALSE; + wp->w_has_scrollbar = false; #endif maxwidth_no_scrollbar = maxwidth; if (wp->w_has_scrollbar) @@ -1736,7 +1736,7 @@ popup_adjust_position(win_T *wp) if (wp->w_buffer->b_term == NULL || term_is_finished(wp->w_buffer)) #endif { - wp->w_has_scrollbar = TRUE; + wp->w_has_scrollbar = true; if (width_with_scrollbar > 0) wp->w_width = width_with_scrollbar; } @@ -3385,7 +3385,7 @@ redraw_under_popup_area(int winrow, int wincol, int height, int width, int lefto redrawWinline(twp, lnum); } else if (line_cp == twp->w_height) - twp->w_redr_status = TRUE; + twp->w_redr_status = true; } } } @@ -4423,7 +4423,7 @@ redraw_win_under_opacity_popup(win_T *wp) else if (line_cp == twp->w_height) // Status bar line: mark for redraw to prevent // opacity blend accumulation. - twp->w_redr_status = TRUE; + twp->w_redr_status = true; } } } @@ -4707,7 +4707,7 @@ may_update_popup_mask(int type) if (line_cp >= wp->w_height) // In (or below) status line - wp->w_redr_status = TRUE; + wp->w_redr_status = true; else { // compute the position in the buffer line diff --git a/src/quickfix.c b/src/quickfix.c index 28f3655e1d..902c4e6f66 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -3649,7 +3649,7 @@ qf_jump_goto_line( coladvance(qf_col - 1); else curwin->w_cursor.col = qf_col - 1; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; check_cursor(); } else @@ -4768,7 +4768,7 @@ qf_win_goto(win_T *win, linenr_T lnum) curwin->w_curswant = 0; update_topline(); // scroll to show the line redraw_later(UPD_VALID); - curwin->w_redr_status = TRUE; // update ruler + curwin->w_redr_status = true; // update ruler curwin = old_curwin; curbuf = curwin->w_buffer; } diff --git a/src/register.c b/src/register.c index f7a02e8d95..9eeb88f442 100644 --- a/src/register.c +++ b/src/register.c @@ -2388,7 +2388,7 @@ do_put( } msgmore(nr_lines); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; // Make sure the cursor is not after the NUL. int len = ml_get_curline_len(); diff --git a/src/screen.c b/src/screen.c index dd9aa8d29a..3798de88fd 100644 --- a/src/screen.c +++ b/src/screen.c @@ -3938,7 +3938,7 @@ win_ins_lines( */ if (!did_delete) { - wp->w_redr_status = TRUE; + wp->w_redr_status = true; redraw_cmdline = TRUE; nextrow = W_WINROW(wp) + wp->w_height + wp->w_status_height; lastrow = nextrow + line_count; @@ -3954,7 +3954,7 @@ win_ins_lines( // deletion will have messed up other windows if (did_delete) { - wp->w_redr_status = TRUE; + wp->w_redr_status = true; win_rest_invalid(W_NEXT(wp)); } return FAIL; @@ -4004,7 +4004,7 @@ win_del_lines( if (screen_ins_lines(0, W_WINROW(wp) + wp->w_height - line_count, line_count, (int)Rows, clear_attr, NULL) == FAIL) { - wp->w_redr_status = TRUE; + wp->w_redr_status = true; win_rest_invalid(wp->w_next); } } @@ -4120,7 +4120,7 @@ win_rest_invalid(win_T *wp) while (wp != NULL) { redraw_win_later(wp, UPD_NOT_VALID); - wp->w_redr_status = TRUE; + wp->w_redr_status = true; wp = wp->w_next; } redraw_cmdline = TRUE; diff --git a/src/search.c b/src/search.c index 03e66aaec8..bb85873dc0 100644 --- a/src/search.c +++ b/src/search.c @@ -1758,7 +1758,7 @@ do_search( if (options & SEARCH_MARK) setpcmark(); curwin->w_cursor = pos; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; end_do_search: if ((options & SEARCH_KEEP) || (cmdmod.cmod_flags & CMOD_KEEPPATTERNS)) @@ -4003,7 +4003,7 @@ find_pattern_in_path( if (action != ACTION_SHOW) { curwin->w_cursor.col = (colnr_T)(startp - line); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; } # if defined(FEAT_QUICKFIX) diff --git a/src/structs.h b/src/structs.h index 21fcc0377b..14978739f5 100644 --- a/src/structs.h +++ b/src/structs.h @@ -4062,7 +4062,7 @@ struct window_S // used to try to stay in the same column // for up/down cursor motions. - int w_set_curswant; // If set, then update w_curswant the next + bool w_set_curswant; // If set, then update w_curswant the next // time through cursupdate() to the // current virtual column @@ -4093,7 +4093,7 @@ struct window_S */ linenr_T w_topline; // buffer line number of the line at the // top of the window - char w_topline_was_set; // flag set to TRUE when topline is set, + bool w_topline_was_set; // flag set to true when topline is set, // e.g. by winrestview() linenr_T w_botline; // number of the line below the bottom of @@ -4102,9 +4102,9 @@ struct window_S #ifdef FEAT_DIFF int w_topfill; // number of filler lines above w_topline int w_old_topfill; // w_topfill at last redraw - int w_botfill; // TRUE when filler lines are actually + bool w_botfill; // true when filler lines are actually // below w_topline (at end of file) - int w_old_botfill; // w_botfill at last redraw + bool w_old_botfill; // w_botfill at last redraw #endif colnr_T w_leftcol; // screen column number of the left most // character in the window; used when @@ -4151,7 +4151,7 @@ struct window_S int w_vsep_width; // Number of separator columns (0 or 1). pos_save_T w_save_cursor; // backup of cursor pos and topline - int w_do_win_fix_cursor;// if TRUE cursor may be invalid + bool w_do_win_fix_cursor;// if true cursor may be invalid #ifdef FEAT_PROP_POPUP int w_popup_flags; // POPF_ values @@ -4159,7 +4159,7 @@ struct window_S int w_popup_handled; // POPUP_HANDLE[0-9] flags char_u *w_popup_title; poppos_T w_popup_pos; - int w_popup_fixed; // do not shift popup to fit on screen + bool w_popup_fixed; // do not shift popup to fit on screen int w_popup_prop_type; // when not zero: textprop type ID win_T *w_popup_prop_win; // window to search for textprop int w_popup_prop_id; // when not zero: textprop ID @@ -4173,13 +4173,13 @@ struct window_S int w_wantcol; // "col" for popup window int w_firstline; // "firstline" for popup window int w_want_scrollbar; // when zero don't use a scrollbar - int w_has_scrollbar; // 1 if scrollbar displayed, 0 otherwise + bool w_has_scrollbar; // true if scrollbar displayed char_u *w_scrollbar_highlight; // "scrollbarhighlight" char_u *w_thumb_highlight; // "thumbhighlight" int w_popup_padding[4]; // popup padding top/right/bot/left int w_popup_border[4]; // popup border top/right/bot/left char_u *w_border_highlight[4]; // popup border highlight - int w_border_highlight_isset; // borderhighlight was explicitly set + bool w_border_highlight_isset; // borderhighlight was explicitly set int w_border_char[8]; // popup border characters int w_popup_shadow; // popup shadow (right and bottom edges) @@ -4246,7 +4246,7 @@ struct window_S */ int w_cline_height; // current size of cursor line #ifdef FEAT_FOLDING - int w_cline_folded; // cursor line is folded + bool w_cline_folded; // cursor line is folded #endif int w_cline_row; // starting row of the cursor line @@ -4285,9 +4285,9 @@ struct window_S #ifdef FEAT_FOLDING garray_T w_folds; // array of nested folds - char w_fold_manual; // when TRUE: some folds are opened/closed + bool w_fold_manual; // when true: some folds are opened/closed // manually - char w_foldinvalid; // when TRUE: folding needs to be + bool w_foldinvalid; // when true: folding needs to be // recomputed #endif #ifdef FEAT_LINEBREAK @@ -4307,7 +4307,7 @@ struct window_S // w_redr_type is UPD_REDRAW_TOP linenr_T w_redraw_top; // when != 0: first line needing redraw linenr_T w_redraw_bot; // when != 0: last line needing redraw - int w_redr_status; // if TRUE status line must be redrawn + bool w_redr_status; // if true status line must be redrawn // remember what is shown in the ruler for this window (if 'ruler' set) pos_T w_ru_cursor; // cursor position shown in ruler @@ -4317,14 +4317,14 @@ struct window_S #ifdef FEAT_DIFF int w_ru_topfill; // topfill shown in ruler #endif - char w_ru_empty; // TRUE if ruler shows 0-1 (empty line) + bool w_ru_empty; // true if ruler shows 0-1 (empty line) int w_alt_fnum; // alternate file (for # and CTRL-^) alist_T *w_alist; // pointer to arglist for this window int w_arg_idx; // current index in argument list (can be // out of range!) - int w_arg_idx_invalid; // editing another file than w_arg_idx + bool w_arg_idx_invalid; // editing another file than w_arg_idx char_u *w_localdir; // absolute path of local directory or // NULL diff --git a/src/tag.c b/src/tag.c index 2f887d9de1..e10a837601 100644 --- a/src/tag.c +++ b/src/tag.c @@ -476,7 +476,7 @@ do_tag( curwin->w_cursor.lnum = saved_fmark.mark.lnum; } curwin->w_cursor.col = saved_fmark.mark.col; - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; check_cursor(); #ifdef FEAT_FOLDING if ((fdo_flags & FDO_TAG) && old_KeyTyped) @@ -3898,7 +3898,7 @@ jumpto_tag( if (GETFILE_SUCCESS(getfile_result)) // got to the right file { - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; postponed_split = 0; save_magic_overruled = magic_overruled; diff --git a/src/terminal.c b/src/terminal.c index 7e812fc972..b843f22da3 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -2464,7 +2464,7 @@ term_enter_normal_mode(void) check_cursor(); if (coladvance(col) == FAIL) coladvance(MAXCOL); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; curwin->w_topline = term->tl_buffer_scrolled + 1; } @@ -3562,7 +3562,7 @@ handle_settermprop( if (term == curbuf->b_term) { maketitle(); - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; } break; diff --git a/src/version.c b/src/version.c index c65aa73da9..d544375b0f 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 378, /**/ 377, /**/ diff --git a/src/window.c b/src/window.c index 26d923d9c0..478ec03ae1 100644 --- a/src/window.c +++ b/src/window.c @@ -724,7 +724,7 @@ do_window( Prenum1, ACTION_SPLIT, (linenr_T)1, (linenr_T)MAXLNUM, FALSE, FALSE); vim_free(ptr); - curwin->w_set_curswant = TRUE; + curwin->w_set_curswant = true; break; #endif @@ -5777,7 +5777,7 @@ win_enter_ext(win_T *wp, int flags) if (curwin_invalid == 0) { prevwin = curwin; // remember for CTRL-W p - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; } curwin = wp; curbuf = wp->w_buffer; @@ -5818,7 +5818,7 @@ win_enter_ext(win_T *wp, int flags) } maketitle(); - curwin->w_redr_status = TRUE; + curwin->w_redr_status = true; #ifdef FEAT_TERMINAL if (bt_terminal(curwin->w_buffer)) // terminal is likely in another mode @@ -5831,7 +5831,7 @@ win_enter_ext(win_T *wp, int flags) { win_T *ww; FOR_ALL_WINDOWS(ww) - ww->w_redr_status = TRUE; + ww->w_redr_status = true; } #if defined(FEAT_TABPANEL) redraw_tabpanel = TRUE; @@ -6513,7 +6513,7 @@ frame_comp_pos(frame_T *topfrp, int *row, int *col) wp->w_winrow = *row; wp->w_wincol = *col; redraw_win_later(wp, UPD_NOT_VALID); - wp->w_redr_status = TRUE; + wp->w_redr_status = true; } // WinBar will not show if the window height is zero h = VISIBLE_HEIGHT(wp) + wp->w_status_height; @@ -7257,7 +7257,7 @@ win_fix_scroll(int resize) { // Cursor position in this window may now be invalid. It is kept // potentially invalid until the window is made the current window. - wp->w_do_win_fix_cursor = TRUE; + wp->w_do_win_fix_cursor = true; // If window has moved update botline to keep the same screenlines. if (*p_spk == 's' && wp->w_winrow != wp->w_prev_winrow @@ -7315,7 +7315,7 @@ win_fix_cursor(int normal) || wp->w_buffer->b_ml.ml_line_count < wp->w_height) return; - wp->w_do_win_fix_cursor = FALSE; + wp->w_do_win_fix_cursor = false; // Determine valid cursor range. long so = MIN(wp->w_height / 2, get_scrolloff_value()); linenr_T lnum = wp->w_cursor.lnum; @@ -7385,7 +7385,7 @@ win_new_height(win_T *wp, int height) } wp->w_height = height; - wp->w_redr_status = TRUE; + wp->w_redr_status = true; win_comp_scroll(wp); // There is no point in adjusting the scroll position when exiting. Some @@ -7531,7 +7531,7 @@ win_new_width(win_T *wp, int width) curs_columns(TRUE); // validate w_wrow redraw_win_later(wp, UPD_NOT_VALID); - wp->w_redr_status = TRUE; + wp->w_redr_status = true; } void From fe3c27ce0b7b889a2ae29833cae55ca92ae86518 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Mon, 20 Apr 2026 17:18:53 +0000 Subject: [PATCH 086/545] patch 9.2.0379: gui.color_approx is never used Problem: gui.color_approx in gui_T has not been assigned anywhere since patch 7.4.2094 ("The color allocation in X11 is overly complicated", 2016), which dropped the single "gui.color_approx = TRUE;" site. Because the member is zero-initialized and never written, the check "if (gui.color_approx)" in gui_mch_init() is always false and the "E458: Cannot allocate colormap entry, ..." warning can never be emitted. Solution: Remove the struct member and the unreachable branch. The E458 error definition is removed. Update the example error code "E458" in the ex_eval.c comment to "E457" accordingly. closes: #20007 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/errors.h | 5 +---- src/ex_eval.c | 2 +- src/gui.h | 1 - src/gui_x11.c | 3 --- src/po/vim.pot | 5 +---- src/version.c | 2 ++ 6 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/errors.h b/src/errors.h index 53e5b1dda6..8ac627af68 100644 --- a/src/errors.h +++ b/src/errors.h @@ -1138,10 +1138,7 @@ EXTERN char e_cant_find_postscript_resource_file_str_ps[] EXTERN char e_cant_read_postscript_resource_file_str[] INIT(= N_("E457: Can't read PostScript resource file \"%s\"")); #endif -#ifdef FEAT_GUI_X11 -EXTERN char e_cannot_allocate_colormap_entry_some_colors_may_be_incorrect[] - INIT(= N_("E458: Cannot allocate colormap entry, some colors may be incorrect")); -#endif +// E458 unused #if defined(UNIX) || defined(FEAT_SESSION) EXTERN char e_cannot_go_back_to_previous_directory[] INIT(= N_("E459: Cannot go back to previous directory")); diff --git a/src/ex_eval.c b/src/ex_eval.c index 00e4d5fcf3..966c4f78a2 100644 --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -275,7 +275,7 @@ cause_errthrow( { char *tmsg; - // Skip the extra "Vim " prefix for message "E458". + // Skip the extra "Vim " prefix for message "E457". tmsg = elem->msg; if (STRNCMP(tmsg, "Vim E", 5) == 0 && VIM_ISDIGIT(tmsg[5]) diff --git a/src/gui.h b/src/gui.h index 8b8b74020c..4639b3a129 100644 --- a/src/gui.h +++ b/src/gui.h @@ -341,7 +341,6 @@ typedef struct Gui Bool rsrc_rev_video; // Use reverse video? char_u *geom; // Geometry, eg "80x24" - Bool color_approx; // Some color was approximated #endif #ifdef FEAT_GUI_GTK diff --git a/src/gui_x11.c b/src/gui_x11.c index 9cd8f5f72e..dffa10cb64 100644 --- a/src/gui_x11.c +++ b/src/gui_x11.c @@ -1419,9 +1419,6 @@ gui_mch_init(void) #endif } - if (gui.color_approx) - emsg(_(e_cannot_allocate_colormap_entry_some_colors_may_be_incorrect)); - #ifdef FEAT_BEVAL_GUI gui_init_tooltip_font(); #endif diff --git a/src/po/vim.pot b/src/po/vim.pot index 936961c92f..4cc00d1143 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-04-15 19:10+0000\n" +"POT-Creation-Date: 2026-04-20 17:22+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -5222,9 +5222,6 @@ msgstr "" msgid "E457: Can't read PostScript resource file \"%s\"" msgstr "" -msgid "E458: Cannot allocate colormap entry, some colors may be incorrect" -msgstr "" - msgid "E459: Cannot go back to previous directory" msgstr "" diff --git a/src/version.c b/src/version.c index d544375b0f..d2d4b141ca 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 379, /**/ 378, /**/ From b328686d6a1eae1c519a0cdc0420c0d87b6d1fd1 Mon Sep 17 00:00:00 2001 From: glepnir Date: Mon, 20 Apr 2026 17:36:56 +0000 Subject: [PATCH 087/545] patch 9.2.0380: completion: a few issues in completion code Problem: ins_compl_stop() sets compl_best_matches = 0, but that's a pointer, should reset compl_num_bests instead, find_common_prefix() reads cpt_sources_array[cur_source] without checking cur_source != -1 which causes an OOB for -1, find_next_completion_match(): second `if` in the pending loop should be `else if`. Forward paging only moves one step per call. Solution: Reset compl_num_bests instead, add a check for cur_source not equal -1, change if to else if (glepnir) closes: #20000 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- src/insexpand.c | 7 ++++--- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/insexpand.c b/src/insexpand.c index 019557cab7..0019c7eb45 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -3034,7 +3034,7 @@ ins_compl_stop(int c, int prev_mode, int retval) } compl_autocomplete = FALSE; compl_from_nonkeyword = FALSE; - compl_best_matches = 0; + compl_num_bests = 0; compl_ins_end_col = 0; if (c == Ctrl_C && cmdwin_type != 0) @@ -5795,7 +5795,8 @@ find_common_prefix(size_t *prefix_len, int curbuf_only) } if (!match_limit_exceeded && (!curbuf_only - || cpt_sources_array[cur_source].cs_flag == '.')) + || (cur_source != -1 + && cpt_sources_array[cur_source].cs_flag == '.'))) { if (first == NULL && STRNCMP(ins_compl_leader(), compl->cp_str.string, ins_compl_leader_len()) == 0) @@ -6076,7 +6077,7 @@ find_next_completion_match( compl_shown_match = compl_shown_match->cp_next; --compl_pending; } - if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) + else if (compl_pending < 0 && compl_shown_match->cp_prev != NULL) { compl_shown_match = compl_shown_match->cp_prev; ++compl_pending; diff --git a/src/version.c b/src/version.c index d2d4b141ca..ddac9aa6da 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 380, /**/ 379, /**/ From ec3f79e037349735466135cded2b7362521370cf Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 20 Apr 2026 17:44:45 +0000 Subject: [PATCH 088/545] patch 9.2.0381: Vim9: Missing check_secure() in exec_instructions() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: Vim9: Missing check_secure() when executing ISN_STOREENV instruction (Andrej Tomči) Solution: Add check_secure(), add test. closes: #19992 Signed-off-by: Christian Brabandt --- src/testdir/test_restricted.vim | 45 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim9execute.c | 2 +- 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_restricted.vim b/src/testdir/test_restricted.vim index 2d8c8ce5f7..d9810826a0 100644 --- a/src/testdir/test_restricted.vim +++ b/src/testdir/test_restricted.vim @@ -220,4 +220,49 @@ func Test_restricted_cscope() call delete('XResult_cscope') endfunc +func Test_vim9_storeenv_sandbox() + let lines =<< trim END + vim9script + + function g:LegacySetEnv() + let $VIM_SANDBOX_TEST = 'legacy' + endfunc + + def Vim9SetEnv() + $VIM_SANDBOX_TEST = 'vim9_bypass' + enddef + + # Legacy path should be blocked by check_secure() + var legacy_blocked = false + try + legacy sandbox call LegacySetEnv() + catch /E48/ + legacy_blocked = true + endtry + assert_true(legacy_blocked, 'legacy $ENV assignment should be blocked in sandbox') + assert_false(exists('$VIM_SANDBOX_TEST')) + + # Vim9 path should also be blocked by check_secure() + var vim9_blocked = false + try + sandbox Vim9SetEnv() + catch /E48/ + vim9_blocked = true + endtry + assert_true(vim9_blocked, 'Vim9 ISN_STOREENV should be blocked in sandbox') + assert_false(exists('$VIM_SANDBOX_TEST')) + writefile([ + legacy_blocked, + vim9_blocked, + string(v:errors)], 'XResult_storeenv') + qa + END + call writefile(lines, 'Xtest_storeenv_sandbox.vim', 'D') + let expected = ['true', 'true', '[]'] + if RunVim([], [], '-u NONE -N -i NONE --not-a-term -S Xtest_storeenv_sandbox.vim') + call assert_equal(expected, readfile('XResult_storeenv')) + endif + call delete('XResult_storeenv') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index ddac9aa6da..eaaa793d8b 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 381, /**/ 380, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index 1bc25ed98e..d83b7968b2 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -4472,7 +4472,7 @@ exec_instructions(ectx_T *ectx) // store $ENV case ISN_STOREENV: - if (check_restricted()) + if (check_secure() || check_restricted()) goto theend; --ectx->ec_stack.ga_len; tv = STACK_TV_BOT(0); From d0b6ee320b5156c9d880cc4aef8e5231fdae2728 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Mon, 20 Apr 2026 17:59:56 +0000 Subject: [PATCH 089/545] patch 9.2.0382: Wayland: focus-stealing is non-working Problem: Wayland: focus-stealing is non-working Solution: Remove it, the feature could be re-implemented using clipboard-providers feature, see :h wayland-primary-selection (Foxe Chen). closes: #19984 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 4 +- runtime/doc/options.txt | 12 +- runtime/doc/tags | 3 - runtime/doc/various.txt | 5 +- runtime/doc/version9.txt | 7 +- runtime/doc/wayland.txt | 101 +- runtime/optwin.vim | 4 - runtime/syntax/vim.vim | 14 +- src/Makefile | 14 +- src/auto/configure | 38 - src/auto/wayland/Makefile | 22 +- .../primary-selection-unstable-v1.xml | 225 --- src/auto/wayland/protocols/xdg-shell.xml | 1415 ----------------- src/clipboard.c | 378 +---- src/config.h.in | 3 - src/configure.ac | 20 - src/evalfunc.c | 7 - src/feature.h | 7 - src/option.h | 3 - src/optiondefs.h | 7 +- src/po/vim.pot | 6 +- src/proto/wayland.pro | 1 - src/testdir/test_wayland.vim | 76 - src/version.c | 7 +- src/wayland.c | 279 +--- src/wayland.h | 71 - 26 files changed, 94 insertions(+), 2635 deletions(-) delete mode 100644 src/auto/wayland/protocols/primary-selection-unstable-v1.xml delete mode 100644 src/auto/wayland/protocols/xdg-shell.xml diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index f929b9e4c6..0fe02ac944 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 14 +*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -13354,8 +13354,6 @@ vtp Compiled for vcon support |+vtp| (check vcon to find out if it works in the current console). wayland Compiled with Wayland protocol support. wayland_clipboard Compiled with support for Wayland clipboard. -wayland_focus_steal Compiled with support for Wayland clipboard focus - stealing. wildignore Compiled with 'wildignore' option. wildmenu Compiled with 'wildmenu' option. win16 old version for MS-Windows 3.1 (always false) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 2670535fb1..c02d40eaa8 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 17 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -10669,11 +10669,10 @@ A jump table for the options with a short description can be found at |Q_op|. *'wlsteal'* *'wst'* *'nowlsteal'* *'nowst'* 'wlsteal' 'wst' boolean (default off) global - {only when the |+wayland_focus_steal| feature is - included} + DEPRECATED: This option is no longer used; changing it has no effect. + When enabled, then allow Vim to steal focus by creating a temporary - surface, in order to access the clipboard. For more information see - |wayland-focus-steal|. + surface, in order to access the clipboard. *'wltimeoutlen'* *'wtm'* 'wltimeoutlen' 'wtm' number (default 500) @@ -10686,9 +10685,6 @@ A jump table for the options with a short description can be found at |Q_op|. some cases. On the other hand, it may also mean you receive errors when the compositor takes more time to respond than usual. - Additionally, this option is also used as the maximum timeout when - waiting for a surface to gain focus, see |wayland-focus-steal|. - *'wrap'* *'nowrap'* 'wrap' boolean (default on) local to window diff --git a/runtime/doc/tags b/runtime/doc/tags index 7effaf824f..720eb330f7 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -1562,7 +1562,6 @@ $quote eval.txt /*$quote* +vtp various.txt /*+vtp* +wayland various.txt /*+wayland* +wayland_clipboard various.txt /*+wayland_clipboard* -+wayland_focus_steal various.txt /*+wayland_focus_steal* +wildignore various.txt /*+wildignore* +wildmenu various.txt /*+wildmenu* +windows various.txt /*+windows* @@ -11805,8 +11804,6 @@ waittime channel.txt /*waittime* warningmsg-variable eval.txt /*warningmsg-variable* wayland wayland.txt /*wayland* wayland-and-x11 wayland.txt /*wayland-and-x11* -wayland-focus-steal wayland.txt /*wayland-focus-steal* -wayland-gnome wayland.txt /*wayland-gnome* wayland-gui wayland.txt /*wayland-gui* wayland-persist wayland.txt /*wayland-persist* wayland-primary-selection wayland.txt /*wayland-primary-selection* diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 880feb634a..1a625b3503 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 9.2. Last change: 2026 Apr 06 +*various.txt* For Vim version 9.2. Last change: 2026 Apr 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -529,9 +529,6 @@ T *+vreplace* |gR| and |gr| *+vtp* on MS-Windows console: support for 'termguicolors' N *+wayland* Unix only: support for the Wayland protocol. N *+wayland_clipboard* Unix only: support for Wayland selections/clipboard. -N *+wayland_focus_steal* - Unix only: support for Wayland clipboard on - compositors without a data control protocol T *+wildignore* 'wildignore' Always enabled since 9.0.0278 T *+wildmenu* 'wildmenu' Always enabled since 9.0.0279 T *+windows* more than one window; Always enabled since 8.0.1118. diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 7fa53f87d8..12d46cffb8 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 16 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -41954,7 +41954,7 @@ Vim Arguments: ~ Configure Switches: ~ --with-wayland Enable the |wayland| feature. ---enable-wayland-focus-steal Enable the |wayland-focus-steal| feature. +--enable-wayland-focus-steal Enable the wayland-focus-steal feature. --enable-socketserver Enable the |socketserver-clientserver| feature. @@ -52636,6 +52636,9 @@ Changed ~ are moved to the resulting joined line instead of being dropped. - Only a few whitelisted options are allowed to be set by a modeline to improve overall security, see for details: 'modelinestrict' +- Removed the Wayland focus steal feature 'wlsteal', since it causes too many + issues and can now be re-implemted using the |clipboard-providers| feature + if needed, see |wayland-primary-selection| for an example. *added-9.3* Added ~ diff --git a/runtime/doc/wayland.txt b/runtime/doc/wayland.txt index 8d352bde94..af491064d2 100644 --- a/runtime/doc/wayland.txt +++ b/runtime/doc/wayland.txt @@ -1,4 +1,4 @@ -*wayland.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*wayland.txt* For Vim version 9.2. Last change: 2026 Apr 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -54,15 +54,12 @@ try connecting again. 2. Wayland Selections *wayland-selections* Vim supports the wlr-data-control-unstable-v1 and ext-data-control-v1 -protocols, for accessing the current Wayland selection. These are the best -case scenario protocols, see |wayland-focus-steal|. Selection in this case +protocols, for accessing the current Wayland selection. Selection in this case essentially means the "clipboard." You can check if your Wayland compositor supports either of these protocols by running the wayland-info command, which should be bundled with libwayland on your system: > wayland-info | grep -E '(ext_data_control|zwlr_data_control)' + wayland-info | grep -E '(ext_data_control|zwlr_data_control)' +If the "version:" entry for "zwlr_data_control_manager_v1" is "2" or greater, +then primary selection is supported. If you also get +"ext_data_control_manager_v1", then Vim will use that protocol instead, which +has primary selection support builtin into it. + +If your Wayland compositor does not support the wlr-data-control-v1 or the +ext-data-control-v1 protocol, Vim cannot access the clipboard directly through +the Wayland protocol. External tools such as wl-clipboard can be used instead +via a user-defined |clipboard-providers|. + +Example: define a provider that shells out to `wl-copy` and `wl-paste`: >vim9 + + vim9script + + def Available(): bool + return executable('wl-copy') && executable('wl-paste') + enddef + + def Copy(reg: string, type: string, str: list) + var args = "wl-copy" + + if reg == "*" + args ..= " -p" + endif + + system(args, str) + enddef + + def Paste(reg: string): tuple> + var args = "wl-paste --type text/plain;charset=utf-8" + + if reg == "*" + args ..= " -p" + endif + + return ("", systemlist(args)) + enddef + + v:clipproviders["wl_clipboard"] = { + available: Available, + copy: { + "+": Copy, + "*": Copy + }, + paste: { + "+": Paste, + "*": Paste + } + } + + set clipmethod=wl_clipboard + + +This relies on the wl-clipboard package being installed +(https://github.com/bugaevc/wl-clipboard). vim:tw=78:ts=8:noet:ft=help:norl diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 62c2ec2e55..2be864882f 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -835,10 +835,6 @@ if has('wayland') call AddOption("wlseat", gettext("Wayland seat to use")) call OptionG("wse", &wse) endif -if has("wayland_focus_steal") - call AddOption("wlsteal", gettext("Enable wayland focus stealing functionality in order to access the clipboard")) - call BinOptionG("wst", &wst) -endif call AddOption("keymodel", gettext("\"startsel\" and/or \"stopsel\"; what special keys can do")) call OptionG("km", &km) diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 0780f8695e..2906d07cd6 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -72,21 +72,21 @@ syn keyword vimOption contained mmd maxmapdepth mm maxmem mmp maxmempattern mmt syn keyword vimOption contained pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sop scrolloffpad sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod " vimOptions: These are the turn-off setting variants {{{2 " GEN_SYN_VIM: vimOption turn-off, START_STR='syn keyword vimOption contained', END_STR='' syn keyword vimOption contained noari noallowrevins noarab noarabic noarshape noarabicshape noacd noautochdir noac noautocomplete noai noautoindent noar noautoread noasd noautoshelldir noaw noautowrite noawa noautowriteall nobk nobackup nobeval noballooneval nobevalterm noballoonevalterm nobin nobinary nobomb nobri nobreakindent nobl nobuflisted nocdh nocdhome nocin nocindent nocp nocompatible nocf noconfirm noci nocopyindent nocsre nocscoperelative nocst nocscopetag nocsverb nocscopeverbose nocrb nocursorbind nocuc nocursorcolumn nocul nocursorline nodeco nodelcombine nodiff nodg nodigraph noed noedcompatible noemo noemoji noeof noendoffile noeol noendofline noea noequalalways noeb noerrorbells noek noesckeys noet noexpandtab noex noexrc nofic nofileignorecase syn keyword vimOption contained nofixeol nofixendofline nofen nofoldenable nofs nofsync nogd nogdefault noguipty nohid nohidden nohk nohkmap nohkp nohkmapp nohls nohlsearch noicon noic noignorecase noimc noimcmdline noimd noimdisable nois noincsearch noinf noinfercase noim noinsertmode nojs nojoinspaces nolnr nolangnoremap nolrm nolangremap nolz nolazyredraw nolbr nolinebreak nolisp nolist nolpl noloadplugins nomagic noml nomodeline nomle nomodelineexpr nomlst nomodelinestrict noma nomodifiable nomod nomodified nomore nomousef nomousefocus nomh nomousehide nomousemev nomousemoveevent nonu nonumber noodev noopendevice nopaste nopi nopreserveindent nopvw nopreviewwindow noprompt noro noreadonly nornu norelativenumber noremap nors norestorescreen nori norevins norl norightleft syn keyword vimOption contained noru noruler noscb noscrollbind noscf noscrollfocus nosecure nossl noshellslash nostmp noshelltemp nosr noshiftround nosn noshortname nosc noshowcmd nosft noshowfulltag nosm noshowmatch nosmd noshowmode noscs nosmartcase nosi nosmartindent nosta nosmarttab nosms nosmoothscroll nospell nosb nosplitbelow nospr nosplitright nosol nostartofline noswf noswapfile notbs notagbsearch notr notagrelative notgst notagstack notbidi notermbidi notgc notermguicolors notsy notermsync noterse nota notextauto notx notextmode notop notildeop noto notimeout notitle nottimeout notbi nottybuiltin notf nottyfast noudf noundofile novb novisualbell nowarn nowiv noweirdinvert nowic nowildignorecase nowmnu nowildmenu nowfb nowinfixbuf nowfh nowinfixheight -syn keyword vimOption contained nowfw nowinfixwidth nowst nowlsteal nowrap nows nowrapscan nowrite nowa nowriteany nowb nowritebackup noxtermcodes +syn keyword vimOption contained nowfw nowinfixwidth nowrap nows nowrapscan nowrite nowa nowriteany nowb nowritebackup noxtermcodes " vimOptions: These are the invertible variants {{{2 " GEN_SYN_VIM: vimOption invertible, START_STR='syn keyword vimOption contained', END_STR='' syn keyword vimOption contained invari invallowrevins invarab invarabic invarshape invarabicshape invacd invautochdir invac invautocomplete invai invautoindent invar invautoread invasd invautoshelldir invaw invautowrite invawa invautowriteall invbk invbackup invbeval invballooneval invbevalterm invballoonevalterm invbin invbinary invbomb invbri invbreakindent invbl invbuflisted invcdh invcdhome invcin invcindent invcp invcompatible invcf invconfirm invci invcopyindent invcsre invcscoperelative invcst invcscopetag invcsverb invcscopeverbose invcrb invcursorbind invcuc invcursorcolumn invcul invcursorline invdeco invdelcombine invdiff invdg invdigraph inved invedcompatible invemo invemoji inveof invendoffile inveol invendofline invea invequalalways inveb inverrorbells syn keyword vimOption contained invek invesckeys invet invexpandtab invex invexrc invfic invfileignorecase invfixeol invfixendofline invfen invfoldenable invfs invfsync invgd invgdefault invguipty invhid invhidden invhk invhkmap invhkp invhkmapp invhls invhlsearch invicon invic invignorecase invimc invimcmdline invimd invimdisable invis invincsearch invinf invinfercase invim invinsertmode invjs invjoinspaces invlnr invlangnoremap invlrm invlangremap invlz invlazyredraw invlbr invlinebreak invlisp invlist invlpl invloadplugins invmagic invml invmodeline invmle invmodelineexpr invmlst invmodelinestrict invma invmodifiable invmod invmodified invmore invmousef invmousefocus invmh invmousehide invmousemev invmousemoveevent invnu invnumber invodev invopendevice invpaste syn keyword vimOption contained invpi invpreserveindent invpvw invpreviewwindow invprompt invro invreadonly invrnu invrelativenumber invremap invrs invrestorescreen invri invrevins invrl invrightleft invru invruler invscb invscrollbind invscf invscrollfocus invsecure invssl invshellslash invstmp invshelltemp invsr invshiftround invsn invshortname invsc invshowcmd invsft invshowfulltag invsm invshowmatch invsmd invshowmode invscs invsmartcase invsi invsmartindent invsta invsmarttab invsms invsmoothscroll invspell invsb invsplitbelow invspr invsplitright invsol invstartofline invswf invswapfile invtbs invtagbsearch invtr invtagrelative invtgst invtagstack invtbidi invtermbidi invtgc invtermguicolors invtsy invtermsync invterse invta invtextauto invtx invtextmode -syn keyword vimOption contained invtop invtildeop invto invtimeout invtitle invttimeout invtbi invttybuiltin invtf invttyfast invudf invundofile invvb invvisualbell invwarn invwiv invweirdinvert invwic invwildignorecase invwmnu invwildmenu invwfb invwinfixbuf invwfh invwinfixheight invwfw invwinfixwidth invwst invwlsteal invwrap invws invwrapscan invwrite invwa invwriteany invwb invwritebackup invxtermcodes +syn keyword vimOption contained invtop invtildeop invto invtimeout invtitle invttimeout invtbi invttybuiltin invtf invttyfast invudf invundofile invvb invvisualbell invwarn invwiv invweirdinvert invwic invwildignorecase invwmnu invwildmenu invwfb invwinfixbuf invwfh invwinfixheight invwfw invwinfixwidth invwrap invws invwrapscan invwrite invwa invwriteany invwb invwritebackup invxtermcodes " termcap codes (which can also be set) {{{2 " GEN_SYN_VIM: vimOption term output code, START_STR='syn keyword vimOption contained', END_STR='skipwhite nextgroup=vimSetEqual,vimSetMod' syn keyword vimOption contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo t_BS t_ES skipwhite nextgroup=vimSetEqual,vimSetMod @@ -111,7 +111,7 @@ syn keyword vimOptionVarName contained mfd maxfuncdepth mmd maxmapdepth mm maxme syn keyword vimOptionVarName contained pmbcs printmbcharset pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sop scrolloffpad sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote syn keyword vimOptionVarName contained sr shiftround sw shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack syn keyword vimOptionVarName contained tcldll term tbidi termbidi tenc termencoding tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore -syn keyword vimOptionVarName contained wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wst wlsteal wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes +syn keyword vimOptionVarName contained wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes " GEN_SYN_VIM: vimOption term output code variable, START_STR='syn keyword vimOptionVarName contained', END_STR='' syn keyword vimOptionVarName contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo t_BS t_ES syn keyword vimOptionVarName contained t_F1 t_F2 t_F3 t_F4 t_F5 t_F6 t_F7 t_F8 t_F9 t_k1 t_K1 t_k2 t_k3 t_K3 t_k4 t_K4 t_k5 t_K5 t_k6 t_K6 t_k7 t_K7 t_k8 t_K8 t_k9 t_K9 t_KA t_kb t_kB t_KB t_KC t_kd t_kD t_KD t_KE t_KF t_KG t_kh t_KH t_kI t_KI t_KJ t_KK t_kl t_KL t_kN t_kP t_kr t_ku @@ -126,9 +126,9 @@ syn match vimOptionVarName contained "t_k;" " unsupported settings: some were supported by vi but don't do anything in vim {{{2 " GEN_SYN_VIM: Missing vimOption, START_STR='syn keyword vimErrSetting contained', END_STR='' -syn keyword vimErrSetting contained akm altkeymap anti antialias ap autoprint bf beautify biosk bioskey consk conskey fk fkmap fl flash gr graphic ht hardtabs macatsui mesg novice open opt optimize oft osfiletype redraw slow slowopen sourceany w1200 w300 w9600 -syn keyword vimErrSetting contained noakm noaltkeymap noanti noantialias noap noautoprint nobf nobeautify nobiosk nobioskey noconsk noconskey nofk nofkmap nofl noflash nogr nographic nomacatsui nomesg nonovice noopen noopt nooptimize noredraw noslow noslowopen nosourceany -syn keyword vimErrSetting contained invakm invaltkeymap invanti invantialias invap invautoprint invbf invbeautify invbiosk invbioskey invconsk invconskey invfk invfkmap invfl invflash invgr invgraphic invmacatsui invmesg invnovice invopen invopt invoptimize invredraw invslow invslowopen invsourceany +syn keyword vimErrSetting contained akm altkeymap anti antialias ap autoprint bf beautify biosk bioskey consk conskey fk fkmap fl flash gr graphic ht hardtabs macatsui mesg novice open opt optimize oft osfiletype redraw slow slowopen sourceany w1200 w300 w9600 wst wlsteal +syn keyword vimErrSetting contained noakm noaltkeymap noanti noantialias noap noautoprint nobf nobeautify nobiosk nobioskey noconsk noconskey nofk nofkmap nofl noflash nogr nographic nomacatsui nomesg nonovice noopen noopt nooptimize noredraw noslow noslowopen nosourceany nowst nowlsteal +syn keyword vimErrSetting contained invakm invaltkeymap invanti invantialias invap invautoprint invbf invbeautify invbiosk invbioskey invconsk invconskey invfk invfkmap invfl invflash invgr invgraphic invmacatsui invmesg invnovice invopen invopt invoptimize invredraw invslow invslowopen invsourceany invwst invwlsteal " AutoCmd Events {{{2 syn case ignore diff --git a/src/Makefile b/src/Makefile index 9493691941..2a8ff097bd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1616,8 +1616,6 @@ genwaylandproto: # Needed for parallel jobs to work auto/wayland/ext-data-control-v1.h: auto/wayland/ext-data-control-v1.c auto/wayland/wlr-data-control-unstable-v1.h: auto/wayland/wlr-data-control-unstable-v1.c -auto/wayland/primary-selection-unstable-v1.h: auto/wayland/primary-selection-unstable-v1.c -auto/wayland/xdg-shell.h: auto/wayland/xdg-shell.c # Unittest files JSON_TEST_SRC = json_test.c @@ -3674,12 +3672,6 @@ objects/wlr-data-control-unstable-v1.o: auto/wayland/wlr-data-control-unstable-v objects/ext-data-control-v1.o: auto/wayland/ext-data-control-v1.c $(CCC) $(WAYLAND_CFLAGS) $(WAYLAND_CPPFLAGS) -o $@ auto/wayland/ext-data-control-v1.c -objects/xdg-shell.o: auto/wayland/xdg-shell.c - $(CCC) $(WAYLAND_CFLAGS) $(WAYLAND_CPPFLAGS) -o $@ auto/wayland/xdg-shell.c - -objects/primary-selection-unstable-v1.o: auto/wayland/primary-selection-unstable-v1.c - $(CCC) $(WAYLAND_CFLAGS) $(WAYLAND_CPPFLAGS) -o $@ auto/wayland/primary-selection-unstable-v1.c - objects/netbeans.o: netbeans.c $(CCC) -o $@ netbeans.c @@ -4561,17 +4553,13 @@ objects/gui_gtk_gresources.o: auto/gui_gtk_gresources.c objects/wlr-data-control-unstable-v1.o: \ auto/wayland/wlr-data-control-unstable-v1.c objects/ext-data-control-v1.o: auto/wayland/ext-data-control-v1.c -objects/xdg-shell.o: auto/wayland/xdg-shell.c -objects/primary-selection-unstable-v1.o: \ - auto/wayland/primary-selection-unstable-v1.c objects/wayland.o: auto/osdef.h wayland.c vim.h protodef.h auto/config.h feature.h os_unix.h \ ascii.h keymap.h termdefs.h macros.h option.h beval.h \ structs.h regexp.h gui.h libvterm/include/vterm.h \ libvterm/include/vterm_keycodes.h alloc.h \ ex_cmds.h spell.h proto.h globals.h errors.h wayland.h \ auto/wayland/wlr-data-control-unstable-v1.h \ - auto/wayland/ext-data-control-v1.h auto/wayland/xdg-shell.h \ - auto/wayland/primary-selection-unstable-v1.h + auto/wayland/ext-data-control-v1.h objects/vterm_encoding.o: auto/osdef.h libvterm/src/encoding.c libvterm/src/vterm_internal.h \ libvterm/include/vterm.h libvterm/include/vterm_keycodes.h \ libvterm/src/encoding/DECdrawing.inc libvterm/src/encoding/uk.inc diff --git a/src/auto/configure b/src/auto/configure index 8e73d97b1e..1705525156 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -858,7 +858,6 @@ enable_farsi enable_xim enable_fontset with_wayland -enable_wayland_focus_steal with_x enable_gui enable_gtk2_check @@ -1539,9 +1538,6 @@ Optional Features: --disable-farsi Deprecated. --enable-xim Include XIM input support. --enable-fontset Include X fontset output support. - --enable-wayland-focus-steal - Include focus stealing support for Wayland - clipboard. --enable-gui=OPTS X11 GUI. default=auto OPTS=auto/no/gtk2/gnome2/gtk3/motif/haiku/photon/carbon --enable-gtk2-check If auto-select GUI, check for GTK+ 2 default=yes --enable-gnome-check If GTK GUI, check for GNOME default=no @@ -9268,30 +9264,6 @@ printf %s "checking for wayland... " >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 printf "%s\n" "yes" >&6; } - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-wayland-focus-steal argument" >&5 -printf %s "checking --enable-wayland-focus-steal argument... " >&6; } - # Check whether --enable-wayland-focus-steal was given. -if test ${enable_wayland_focus_steal+y} -then : - enableval=$enable_wayland_focus_steal; enable_wayland_fs=$enableval -else case e in #( - e) enable_wayland_fs="yes" ;; -esac -fi - - - if test "$enable_wayland_fs" = "yes" -then : - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } - printf "%s\n" "#define FEAT_WAYLAND_CLIPBOARD_FS 1" >>confdefs.h - -else case e in #( - e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } ;; -esac -fi - printf "%s\n" "#define HAVE_WAYLAND 1" >>confdefs.h WAYLAND_CPPFLAGS=`$PKG_CONFIG --cflags-only-I wayland-client` @@ -9308,16 +9280,6 @@ fi objects/ext-data-control-v1.o \ objects/wayland.o" - if test "$enable_wayland_fs" = "yes" -then : - as_fn_append WAYLAND_SRC " \ - auto/wayland/xdg-shell.c \ - auto/wayland/primary-selection-unstable-v1.c" - as_fn_append WAYLAND_OBJ " \ - objects/xdg-shell.o \ - objects/primary-selection-unstable-v1.o" -fi - diff --git a/src/auto/wayland/Makefile b/src/auto/wayland/Makefile index e88b3b86e7..6d0534ec82 100644 --- a/src/auto/wayland/Makefile +++ b/src/auto/wayland/Makefile @@ -1,16 +1,10 @@ # List of files to generate GEN_XML = protocols/ext-data-control-v1.xml \ - protocols/primary-selection-unstable-v1.xml \ - protocols/wlr-data-control-unstable-v1.xml \ - protocols/xdg-shell.xml + protocols/wlr-data-control-unstable-v1.xml GEN_SRC = ext-data-control-v1.c \ - primary-selection-unstable-v1.c \ - wlr-data-control-unstable-v1.c \ - xdg-shell.c + wlr-data-control-unstable-v1.c GEN_INCLUDE = ext-data-control-v1.h \ - primary-selection-unstable-v1.h \ - wlr-data-control-unstable-v1.h \ - xdg-shell.h + wlr-data-control-unstable-v1.h # Default target all: $(GEN_SRC) $(GEN_INCLUDE) @@ -29,16 +23,6 @@ wlr-data-control-unstable-v1.c: wlr-data-control-unstable-v1.h: wayland-scanner client-header protocols/wlr-data-control-unstable-v1.xml $@ -primary-selection-unstable-v1.c: - wayland-scanner private-code protocols/primary-selection-unstable-v1.xml $@ -primary-selection-unstable-v1.h: - wayland-scanner client-header protocols/primary-selection-unstable-v1.xml $@ - -xdg-shell.c: - wayland-scanner private-code protocols/xdg-shell.xml $@ -xdg-shell.h: - wayland-scanner client-header protocols/xdg-shell.xml $@ - $(GEN_SRC) $(GEN_INCLUDE): $(GEN_XML) $(GEN_XML): diff --git a/src/auto/wayland/protocols/primary-selection-unstable-v1.xml b/src/auto/wayland/protocols/primary-selection-unstable-v1.xml deleted file mode 100644 index e5a39e34ce..0000000000 --- a/src/auto/wayland/protocols/primary-selection-unstable-v1.xml +++ /dev/null @@ -1,225 +0,0 @@ - - - - Copyright © 2015, 2016 Red Hat - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - - - This protocol provides the ability to have a primary selection device to - match that of the X server. This primary selection is a shortcut to the - common clipboard selection, where text just needs to be selected in order - to allow copying it elsewhere. The de facto way to perform this action - is the middle mouse button, although it is not limited to this one. - - Clients wishing to honor primary selection should create a primary - selection source and set it as the selection through - wp_primary_selection_device.set_selection whenever the text selection - changes. In order to minimize calls in pointer-driven text selection, - it should happen only once after the operation finished. Similarly, - a NULL source should be set when text is unselected. - - wp_primary_selection_offer objects are first announced through the - wp_primary_selection_device.data_offer event. Immediately after this event, - the primary data offer will emit wp_primary_selection_offer.offer events - to let know of the mime types being offered. - - When the primary selection changes, the client with the keyboard focus - will receive wp_primary_selection_device.selection events. Only the client - with the keyboard focus will receive such events with a non-NULL - wp_primary_selection_offer. Across keyboard focus changes, previously - focused clients will receive wp_primary_selection_device.events with a - NULL wp_primary_selection_offer. - - In order to request the primary selection data, the client must pass - a recent serial pertaining to the press event that is triggering the - operation, if the compositor deems the serial valid and recent, the - wp_primary_selection_source.send event will happen in the other end - to let the transfer begin. The client owning the primary selection - should write the requested data, and close the file descriptor - immediately. - - If the primary selection owner client disappeared during the transfer, - the client reading the data will receive a - wp_primary_selection_device.selection event with a NULL - wp_primary_selection_offer, the client should take this as a hint - to finish the reads related to the no longer existing offer. - - The primary selection owner should be checking for errors during - writes, merely cancelling the ongoing transfer if any happened. - - - - - The primary selection device manager is a singleton global object that - provides access to the primary selection. It allows to create - wp_primary_selection_source objects, as well as retrieving the per-seat - wp_primary_selection_device objects. - - - - - Create a new primary selection source. - - - - - - - Create a new data device for a given seat. - - - - - - - - Destroy the primary selection device manager. - - - - - - - - Replaces the current selection. The previous owner of the primary - selection will receive a wp_primary_selection_source.cancelled event. - - To unset the selection, set the source to NULL. - - - - - - - - Introduces a new wp_primary_selection_offer object that may be used - to receive the current primary selection. Immediately following this - event, the new wp_primary_selection_offer object will send - wp_primary_selection_offer.offer events to describe the offered mime - types. - - - - - - - The wp_primary_selection_device.selection event is sent to notify the - client of a new primary selection. This event is sent after the - wp_primary_selection.data_offer event introducing this object, and after - the offer has announced its mimetypes through - wp_primary_selection_offer.offer. - - The data_offer is valid until a new offer or NULL is received - or until the client loses keyboard focus. The client must destroy the - previous selection data_offer, if any, upon receiving this event. - - - - - - - Destroy the primary selection device. - - - - - - - A wp_primary_selection_offer represents an offer to transfer the contents - of the primary selection clipboard to the client. Similar to - wl_data_offer, the offer also describes the mime types that the data can - be converted to and provides the mechanisms for transferring the data - directly to the client. - - - - - To transfer the contents of the primary selection clipboard, the client - issues this request and indicates the mime type that it wants to - receive. The transfer happens through the passed file descriptor - (typically created with the pipe system call). The source client writes - the data in the mime type representation requested and then closes the - file descriptor. - - The receiving client reads from the read end of the pipe until EOF and - closes its end, at which point the transfer is complete. - - - - - - - - Destroy the primary selection offer. - - - - - - Sent immediately after creating announcing the - wp_primary_selection_offer through - wp_primary_selection_device.data_offer. One event is sent per offered - mime type. - - - - - - - - The source side of a wp_primary_selection_offer, it provides a way to - describe the offered data and respond to requests to transfer the - requested contents of the primary selection clipboard. - - - - - This request adds a mime type to the set of mime types advertised to - targets. Can be called several times to offer multiple types. - - - - - - - Destroy the primary selection source. - - - - - - Request for the current primary selection contents from the client. - Send the specified mime type over the passed file descriptor, then - close it. - - - - - - - - This primary selection source is no longer valid. The client should - clean up and destroy this primary selection source. - - - - diff --git a/src/auto/wayland/protocols/xdg-shell.xml b/src/auto/wayland/protocols/xdg-shell.xml deleted file mode 100644 index c4d4685a3f..0000000000 --- a/src/auto/wayland/protocols/xdg-shell.xml +++ /dev/null @@ -1,1415 +0,0 @@ - - - - - Copyright © 2008-2013 Kristian Høgsberg - Copyright © 2013 Rafael Antognolli - Copyright © 2013 Jasper St. Pierre - Copyright © 2010-2013 Intel Corporation - Copyright © 2015-2017 Samsung Electronics Co., Ltd - Copyright © 2015-2017 Red Hat Inc. - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice (including the next - paragraph) shall be included in all copies or substantial portions of the - Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - DEALINGS IN THE SOFTWARE. - - - - - The xdg_wm_base interface is exposed as a global object enabling clients - to turn their wl_surfaces into windows in a desktop environment. It - defines the basic functionality needed for clients and the compositor to - create windows that can be dragged, resized, maximized, etc, as well as - creating transient windows such as popup menus. - - - - - - - - - - - - - - - Destroy this xdg_wm_base object. - - Destroying a bound xdg_wm_base object while there are surfaces - still alive created by this xdg_wm_base object instance is illegal - and will result in a defunct_surfaces error. - - - - - - Create a positioner object. A positioner object is used to position - surfaces relative to some parent surface. See the interface description - and xdg_surface.get_popup for details. - - - - - - - This creates an xdg_surface for the given surface. While xdg_surface - itself is not a role, the corresponding surface may only be assigned - a role extending xdg_surface, such as xdg_toplevel or xdg_popup. It is - illegal to create an xdg_surface for a wl_surface which already has an - assigned role and this will result in a role error. - - This creates an xdg_surface for the given surface. An xdg_surface is - used as basis to define a role to a given surface, such as xdg_toplevel - or xdg_popup. It also manages functionality shared between xdg_surface - based surface roles. - - See the documentation of xdg_surface for more details about what an - xdg_surface is and how it is used. - - - - - - - - A client must respond to a ping event with a pong request or - the client may be deemed unresponsive. See xdg_wm_base.ping - and xdg_wm_base.error.unresponsive. - - - - - - - The ping event asks the client if it's still alive. Pass the - serial specified in the event back to the compositor by sending - a "pong" request back with the specified serial. See xdg_wm_base.pong. - - Compositors can use this to determine if the client is still - alive. It's unspecified what will happen if the client doesn't - respond to the ping request, or in what timeframe. Clients should - try to respond in a reasonable amount of time. The “unresponsive” - error is provided for compositors that wish to disconnect unresponsive - clients. - - A compositor is free to ping in any way it wants, but a client must - always respond to any xdg_wm_base object it created. - - - - - - - - The xdg_positioner provides a collection of rules for the placement of a - child surface relative to a parent surface. Rules can be defined to ensure - the child surface remains within the visible area's borders, and to - specify how the child surface changes its position, such as sliding along - an axis, or flipping around a rectangle. These positioner-created rules are - constrained by the requirement that a child surface must intersect with or - be at least partially adjacent to its parent surface. - - See the various requests for details about possible rules. - - At the time of the request, the compositor makes a copy of the rules - specified by the xdg_positioner. Thus, after the request is complete the - xdg_positioner object can be destroyed or reused; further changes to the - object will have no effect on previous usages. - - For an xdg_positioner object to be considered complete, it must have a - non-zero size set by set_size, and a non-zero anchor rectangle set by - set_anchor_rect. Passing an incomplete xdg_positioner object when - positioning a surface raises an invalid_positioner error. - - - - - - - - - Notify the compositor that the xdg_positioner will no longer be used. - - - - - - Set the size of the surface that is to be positioned with the positioner - object. The size is in surface-local coordinates and corresponds to the - window geometry. See xdg_surface.set_window_geometry. - - If a zero or negative size is set the invalid_input error is raised. - - - - - - - - Specify the anchor rectangle within the parent surface that the child - surface will be placed relative to. The rectangle is relative to the - window geometry as defined by xdg_surface.set_window_geometry of the - parent surface. - - When the xdg_positioner object is used to position a child surface, the - anchor rectangle may not extend outside the window geometry of the - positioned child's parent surface. - - If a negative size is set the invalid_input error is raised. - - - - - - - - - - - - - - - - - - - - - - Defines the anchor point for the anchor rectangle. The specified anchor - is used derive an anchor point that the child surface will be - positioned relative to. If a corner anchor is set (e.g. 'top_left' or - 'bottom_right'), the anchor point will be at the specified corner; - otherwise, the derived anchor point will be centered on the specified - edge, or in the center of the anchor rectangle if no edge is specified. - - - - - - - - - - - - - - - - - - - Defines in what direction a surface should be positioned, relative to - the anchor point of the parent surface. If a corner gravity is - specified (e.g. 'bottom_right' or 'top_left'), then the child surface - will be placed towards the specified gravity; otherwise, the child - surface will be centered over the anchor point on any axis that had no - gravity specified. If the gravity is not in the ‘gravity’ enum, an - invalid_input error is raised. - - - - - - - The constraint adjustment value define ways the compositor will adjust - the position of the surface, if the unadjusted position would result - in the surface being partly constrained. - - Whether a surface is considered 'constrained' is left to the compositor - to determine. For example, the surface may be partly outside the - compositor's defined 'work area', thus necessitating the child surface's - position be adjusted until it is entirely inside the work area. - - The adjustments can be combined, according to a defined precedence: 1) - Flip, 2) Slide, 3) Resize. - - - - Don't alter the surface position even if it is constrained on some - axis, for example partially outside the edge of an output. - - - - - Slide the surface along the x axis until it is no longer constrained. - - First try to slide towards the direction of the gravity on the x axis - until either the edge in the opposite direction of the gravity is - unconstrained or the edge in the direction of the gravity is - constrained. - - Then try to slide towards the opposite direction of the gravity on the - x axis until either the edge in the direction of the gravity is - unconstrained or the edge in the opposite direction of the gravity is - constrained. - - - - - Slide the surface along the y axis until it is no longer constrained. - - First try to slide towards the direction of the gravity on the y axis - until either the edge in the opposite direction of the gravity is - unconstrained or the edge in the direction of the gravity is - constrained. - - Then try to slide towards the opposite direction of the gravity on the - y axis until either the edge in the direction of the gravity is - unconstrained or the edge in the opposite direction of the gravity is - constrained. - - - - - Invert the anchor and gravity on the x axis if the surface is - constrained on the x axis. For example, if the left edge of the - surface is constrained, the gravity is 'left' and the anchor is - 'left', change the gravity to 'right' and the anchor to 'right'. - - If the adjusted position also ends up being constrained, the resulting - position of the flip_x adjustment will be the one before the - adjustment. - - - - - Invert the anchor and gravity on the y axis if the surface is - constrained on the y axis. For example, if the bottom edge of the - surface is constrained, the gravity is 'bottom' and the anchor is - 'bottom', change the gravity to 'top' and the anchor to 'top'. - - The adjusted position is calculated given the original anchor - rectangle and offset, but with the new flipped anchor and gravity - values. - - If the adjusted position also ends up being constrained, the resulting - position of the flip_y adjustment will be the one before the - adjustment. - - - - - Resize the surface horizontally so that it is completely - unconstrained. - - - - - Resize the surface vertically so that it is completely unconstrained. - - - - - - - Specify how the window should be positioned if the originally intended - position caused the surface to be constrained, meaning at least - partially outside positioning boundaries set by the compositor. The - adjustment is set by constructing a bitmask describing the adjustment to - be made when the surface is constrained on that axis. - - If no bit for one axis is set, the compositor will assume that the child - surface should not change its position on that axis when constrained. - - If more than one bit for one axis is set, the order of how adjustments - are applied is specified in the corresponding adjustment descriptions. - - The default adjustment is none. - - - - - - - Specify the surface position offset relative to the position of the - anchor on the anchor rectangle and the anchor on the surface. For - example if the anchor of the anchor rectangle is at (x, y), the surface - has the gravity bottom|right, and the offset is (ox, oy), the calculated - surface position will be (x + ox, y + oy). The offset position of the - surface is the one used for constraint testing. See - set_constraint_adjustment. - - An example use case is placing a popup menu on top of a user interface - element, while aligning the user interface element of the parent surface - with some user interface element placed somewhere in the popup surface. - - - - - - - - - - When set reactive, the surface is reconstrained if the conditions used - for constraining changed, e.g. the parent window moved. - - If the conditions changed and the popup was reconstrained, an - xdg_popup.configure event is sent with updated geometry, followed by an - xdg_surface.configure event. - - - - - - Set the parent window geometry the compositor should use when - positioning the popup. The compositor may use this information to - determine the future state the popup should be constrained using. If - this doesn't match the dimension of the parent the popup is eventually - positioned against, the behavior is undefined. - - The arguments are given in the surface-local coordinate space. - - - - - - - - Set the serial of an xdg_surface.configure event this positioner will be - used in response to. The compositor may use this information together - with set_parent_size to determine what future state the popup should be - constrained using. - - - - - - - - An interface that may be implemented by a wl_surface, for - implementations that provide a desktop-style user interface. - - It provides a base set of functionality required to construct user - interface elements requiring management by the compositor, such as - toplevel windows, menus, etc. The types of functionality are split into - xdg_surface roles. - - Creating an xdg_surface does not set the role for a wl_surface. In order - to map an xdg_surface, the client must create a role-specific object - using, e.g., get_toplevel, get_popup. The wl_surface for any given - xdg_surface can have at most one role, and may not be assigned any role - not based on xdg_surface. - - A role must be assigned before any other requests are made to the - xdg_surface object. - - The client must call wl_surface.commit on the corresponding wl_surface - for the xdg_surface state to take effect. - - Creating an xdg_surface from a wl_surface which has a buffer attached or - committed is a client error, and any attempts by a client to attach or - manipulate a buffer prior to the first xdg_surface.configure call must - also be treated as errors. - - After creating a role-specific object and setting it up (e.g. by sending - the title, app ID, size constraints, parent, etc), the client must - perform an initial commit without any buffer attached. The compositor - will reply with initial wl_surface state such as - wl_surface.preferred_buffer_scale followed by an xdg_surface.configure - event. The client must acknowledge it and is then allowed to attach a - buffer to map the surface. - - Mapping an xdg_surface-based role surface is defined as making it - possible for the surface to be shown by the compositor. Note that - a mapped surface is not guaranteed to be visible once it is mapped. - - For an xdg_surface to be mapped by the compositor, the following - conditions must be met: - (1) the client has assigned an xdg_surface-based role to the surface - (2) the client has set and committed the xdg_surface state and the - role-dependent state to the surface - (3) the client has committed a buffer to the surface - - A newly-unmapped surface is considered to have met condition (1) out - of the 3 required conditions for mapping a surface if its role surface - has not been destroyed, i.e. the client must perform the initial commit - again before attaching a buffer. - - - - - - - - - - - - - - Destroy the xdg_surface object. An xdg_surface must only be destroyed - after its role object has been destroyed, otherwise - a defunct_role_object error is raised. - - - - - - This creates an xdg_toplevel object for the given xdg_surface and gives - the associated wl_surface the xdg_toplevel role. - - See the documentation of xdg_toplevel for more details about what an - xdg_toplevel is and how it is used. - - - - - - - This creates an xdg_popup object for the given xdg_surface and gives - the associated wl_surface the xdg_popup role. - - If null is passed as a parent, a parent surface must be specified using - some other protocol, before committing the initial state. - - See the documentation of xdg_popup for more details about what an - xdg_popup is and how it is used. - - - - - - - - - The window geometry of a surface is its "visible bounds" from the - user's perspective. Client-side decorations often have invisible - portions like drop-shadows which should be ignored for the - purposes of aligning, placing and constraining windows. - - The window geometry is double-buffered state, see wl_surface.commit. - - When maintaining a position, the compositor should treat the (x, y) - coordinate of the window geometry as the top left corner of the window. - A client changing the (x, y) window geometry coordinate should in - general not alter the position of the window. - - Once the window geometry of the surface is set, it is not possible to - unset it, and it will remain the same until set_window_geometry is - called again, even if a new subsurface or buffer is attached. - - If never set, the value is the full bounds of the surface, - including any subsurfaces. This updates dynamically on every - commit. This unset is meant for extremely simple clients. - - The arguments are given in the surface-local coordinate space of - the wl_surface associated with this xdg_surface, and may extend outside - of the wl_surface itself to mark parts of the subsurface tree as part of - the window geometry. - - When applied, the effective window geometry will be the set window - geometry clamped to the bounding rectangle of the combined - geometry of the surface of the xdg_surface and the associated - subsurfaces. - - The effective geometry will not be recalculated unless a new call to - set_window_geometry is done and the new pending surface state is - subsequently applied. - - The width and height of the effective window geometry must be - greater than zero. Setting an invalid size will raise an - invalid_size error. - - - - - - - - - - When a configure event is received, if a client commits the - surface in response to the configure event, then the client - must make an ack_configure request sometime before the commit - request, passing along the serial of the configure event. - - For instance, for toplevel surfaces the compositor might use this - information to move a surface to the top left only when the client has - drawn itself for the maximized or fullscreen state. - - If the client receives multiple configure events before it - can respond to one, it only has to ack the last configure event. - Acking a configure event that was never sent raises an invalid_serial - error. - - A client is not required to commit immediately after sending - an ack_configure request - it may even ack_configure several times - before its next surface commit. - - A client may send multiple ack_configure requests before committing, but - only the last request sent before a commit indicates which configure - event the client really is responding to. - - Sending an ack_configure request consumes the serial number sent with - the request, as well as serial numbers sent by all configure events - sent on this xdg_surface prior to the configure event referenced by - the committed serial. - - It is an error to issue multiple ack_configure requests referencing a - serial from the same configure event, or to issue an ack_configure - request referencing a serial from a configure event issued before the - event identified by the last ack_configure request for the same - xdg_surface. Doing so will raise an invalid_serial error. - - - - - - - The configure event marks the end of a configure sequence. A configure - sequence is a set of one or more events configuring the state of the - xdg_surface, including the final xdg_surface.configure event. - - Where applicable, xdg_surface surface roles will during a configure - sequence extend this event as a latched state sent as events before the - xdg_surface.configure event. Such events should be considered to make up - a set of atomically applied configuration states, where the - xdg_surface.configure commits the accumulated state. - - Clients should arrange their surface for the new states, and then send - an ack_configure request with the serial sent in this configure event at - some point before committing the new surface. - - If the client receives multiple configure events before it can respond - to one, it is free to discard all but the last event it received. - - - - - - - - - This interface defines an xdg_surface role which allows a surface to, - among other things, set window-like properties such as maximize, - fullscreen, and minimize, set application-specific metadata like title and - id, and well as trigger user interactive operations such as interactive - resize and move. - - A xdg_toplevel by default is responsible for providing the full intended - visual representation of the toplevel, which depending on the window - state, may mean things like a title bar, window controls and drop shadow. - - Unmapping an xdg_toplevel means that the surface cannot be shown - by the compositor until it is explicitly mapped again. - All active operations (e.g., move, resize) are canceled and all - attributes (e.g. title, state, stacking, ...) are discarded for - an xdg_toplevel surface when it is unmapped. The xdg_toplevel returns to - the state it had right after xdg_surface.get_toplevel. The client - can re-map the toplevel by performing a commit without any buffer - attached, waiting for a configure event and handling it as usual (see - xdg_surface description). - - Attaching a null buffer to a toplevel unmaps the surface. - - - - - This request destroys the role surface and unmaps the surface; - see "Unmapping" behavior in interface section for details. - - - - - - - - - - - - Set the "parent" of this surface. This surface should be stacked - above the parent surface and all other ancestor surfaces. - - Parent surfaces should be set on dialogs, toolboxes, or other - "auxiliary" surfaces, so that the parent is raised when the dialog - is raised. - - Setting a null parent for a child surface unsets its parent. Setting - a null parent for a surface which currently has no parent is a no-op. - - Only mapped surfaces can have child surfaces. Setting a parent which - is not mapped is equivalent to setting a null parent. If a surface - becomes unmapped, its children's parent is set to the parent of - the now-unmapped surface. If the now-unmapped surface has no parent, - its children's parent is unset. If the now-unmapped surface becomes - mapped again, its parent-child relationship is not restored. - - The parent toplevel must not be one of the child toplevel's - descendants, and the parent must be different from the child toplevel, - otherwise the invalid_parent protocol error is raised. - - - - - - - Set a short title for the surface. - - This string may be used to identify the surface in a task bar, - window list, or other user interface elements provided by the - compositor. - - The string must be encoded in UTF-8. - - - - - - - Set an application identifier for the surface. - - The app ID identifies the general class of applications to which - the surface belongs. The compositor can use this to group multiple - surfaces together, or to determine how to launch a new application. - - For D-Bus activatable applications, the app ID is used as the D-Bus - service name. - - The compositor shell will try to group application surfaces together - by their app ID. As a best practice, it is suggested to select app - ID's that match the basename of the application's .desktop file. - For example, "org.freedesktop.FooViewer" where the .desktop file is - "org.freedesktop.FooViewer.desktop". - - Like other properties, a set_app_id request can be sent after the - xdg_toplevel has been mapped to update the property. - - See the desktop-entry specification [0] for more details on - application identifiers and how they relate to well-known D-Bus - names and .desktop files. - - [0] https://standards.freedesktop.org/desktop-entry-spec/ - - - - - - - Clients implementing client-side decorations might want to show - a context menu when right-clicking on the decorations, giving the - user a menu that they can use to maximize or minimize the window. - - This request asks the compositor to pop up such a window menu at - the given position, relative to the local surface coordinates of - the parent surface. There are no guarantees as to what menu items - the window menu contains, or even if a window menu will be drawn - at all. - - This request must be used in response to some sort of user action - like a button press, key press, or touch down event. - - - - - - - - - - Start an interactive, user-driven move of the surface. - - This request must be used in response to some sort of user action - like a button press, key press, or touch down event. The passed - serial is used to determine the type of interactive move (touch, - pointer, etc). - - The server may ignore move requests depending on the state of - the surface (e.g. fullscreen or maximized), or if the passed serial - is no longer valid. - - If triggered, the surface will lose the focus of the device - (wl_pointer, wl_touch, etc) used for the move. It is up to the - compositor to visually indicate that the move is taking place, such as - updating a pointer cursor, during the move. There is no guarantee - that the device focus will return when the move is completed. - - - - - - - - These values are used to indicate which edge of a surface - is being dragged in a resize operation. - - - - - - - - - - - - - - - Start a user-driven, interactive resize of the surface. - - This request must be used in response to some sort of user action - like a button press, key press, or touch down event. The passed - serial is used to determine the type of interactive resize (touch, - pointer, etc). - - The server may ignore resize requests depending on the state of - the surface (e.g. fullscreen or maximized). - - If triggered, the client will receive configure events with the - "resize" state enum value and the expected sizes. See the "resize" - enum value for more details about what is required. The client - must also acknowledge configure events using "ack_configure". After - the resize is completed, the client will receive another "configure" - event without the resize state. - - If triggered, the surface also will lose the focus of the device - (wl_pointer, wl_touch, etc) used for the resize. It is up to the - compositor to visually indicate that the resize is taking place, - such as updating a pointer cursor, during the resize. There is no - guarantee that the device focus will return when the resize is - completed. - - The edges parameter specifies how the surface should be resized, and - is one of the values of the resize_edge enum. Values not matching - a variant of the enum will cause the invalid_resize_edge protocol error. - The compositor may use this information to update the surface position - for example when dragging the top left corner. The compositor may also - use this information to adapt its behavior, e.g. choose an appropriate - cursor image. - - - - - - - - - The different state values used on the surface. This is designed for - state values like maximized, fullscreen. It is paired with the - configure event to ensure that both the client and the compositor - setting the state can be synchronized. - - States set in this way are double-buffered, see wl_surface.commit. - - - - The surface is maximized. The window geometry specified in the configure - event must be obeyed by the client, or the xdg_wm_base.invalid_surface_state - error is raised. - - The client should draw without shadow or other - decoration outside of the window geometry. - - - - - The surface is fullscreen. The window geometry specified in the - configure event is a maximum; the client cannot resize beyond it. For - a surface to cover the whole fullscreened area, the geometry - dimensions must be obeyed by the client. For more details, see - xdg_toplevel.set_fullscreen. - - - - - The surface is being resized. The window geometry specified in the - configure event is a maximum; the client cannot resize beyond it. - Clients that have aspect ratio or cell sizing configuration can use - a smaller size, however. - - - - - Client window decorations should be painted as if the window is - active. Do not assume this means that the window actually has - keyboard or pointer focus. - - - - - The window is currently in a tiled layout and the left edge is - considered to be adjacent to another part of the tiling grid. - - The client should draw without shadow or other decoration outside of - the window geometry on the left edge. - - - - - The window is currently in a tiled layout and the right edge is - considered to be adjacent to another part of the tiling grid. - - The client should draw without shadow or other decoration outside of - the window geometry on the right edge. - - - - - The window is currently in a tiled layout and the top edge is - considered to be adjacent to another part of the tiling grid. - - The client should draw without shadow or other decoration outside of - the window geometry on the top edge. - - - - - The window is currently in a tiled layout and the bottom edge is - considered to be adjacent to another part of the tiling grid. - - The client should draw without shadow or other decoration outside of - the window geometry on the bottom edge. - - - - - The surface is currently not ordinarily being repainted; for - example because its content is occluded by another window, or its - outputs are switched off due to screen locking. - - - - - The left edge of the window is currently constrained, meaning it - shouldn't attempt to resize from that edge. It can for example mean - it's tiled next to a monitor edge on the constrained side of the - window. - - - - - The right edge of the window is currently constrained, meaning it - shouldn't attempt to resize from that edge. It can for example mean - it's tiled next to a monitor edge on the constrained side of the - window. - - - - - The top edge of the window is currently constrained, meaning it - shouldn't attempt to resize from that edge. It can for example mean - it's tiled next to a monitor edge on the constrained side of the - window. - - - - - The bottom edge of the window is currently constrained, meaning it - shouldn't attempt to resize from that edge. It can for example mean - it's tiled next to a monitor edge on the constrained side of the - window. - - - - - - - Set a maximum size for the window. - - The client can specify a maximum size so that the compositor does - not try to configure the window beyond this size. - - The width and height arguments are in window geometry coordinates. - See xdg_surface.set_window_geometry. - - Values set in this way are double-buffered, see wl_surface.commit. - - The compositor can use this information to allow or disallow - different states like maximize or fullscreen and draw accurate - animations. - - Similarly, a tiling window manager may use this information to - place and resize client windows in a more effective way. - - The client should not rely on the compositor to obey the maximum - size. The compositor may decide to ignore the values set by the - client and request a larger size. - - If never set, or a value of zero in the request, means that the - client has no expected maximum size in the given dimension. - As a result, a client wishing to reset the maximum size - to an unspecified state can use zero for width and height in the - request. - - Requesting a maximum size to be smaller than the minimum size of - a surface is illegal and will result in an invalid_size error. - - The width and height must be greater than or equal to zero. Using - strictly negative values for width or height will result in a - invalid_size error. - - - - - - - - Set a minimum size for the window. - - The client can specify a minimum size so that the compositor does - not try to configure the window below this size. - - The width and height arguments are in window geometry coordinates. - See xdg_surface.set_window_geometry. - - Values set in this way are double-buffered, see wl_surface.commit. - - The compositor can use this information to allow or disallow - different states like maximize or fullscreen and draw accurate - animations. - - Similarly, a tiling window manager may use this information to - place and resize client windows in a more effective way. - - The client should not rely on the compositor to obey the minimum - size. The compositor may decide to ignore the values set by the - client and request a smaller size. - - If never set, or a value of zero in the request, means that the - client has no expected minimum size in the given dimension. - As a result, a client wishing to reset the minimum size - to an unspecified state can use zero for width and height in the - request. - - Requesting a minimum size to be larger than the maximum size of - a surface is illegal and will result in an invalid_size error. - - The width and height must be greater than or equal to zero. Using - strictly negative values for width and height will result in a - invalid_size error. - - - - - - - - Maximize the surface. - - After requesting that the surface should be maximized, the compositor - will respond by emitting a configure event. Whether this configure - actually sets the window maximized is subject to compositor policies. - The client must then update its content, drawing in the configured - state. The client must also acknowledge the configure when committing - the new content (see ack_configure). - - It is up to the compositor to decide how and where to maximize the - surface, for example which output and what region of the screen should - be used. - - If the surface was already maximized, the compositor will still emit - a configure event with the "maximized" state. - - If the surface is in a fullscreen state, this request has no direct - effect. It may alter the state the surface is returned to when - unmaximized unless overridden by the compositor. - - - - - - Unmaximize the surface. - - After requesting that the surface should be unmaximized, the compositor - will respond by emitting a configure event. Whether this actually - un-maximizes the window is subject to compositor policies. - If available and applicable, the compositor will include the window - geometry dimensions the window had prior to being maximized in the - configure event. The client must then update its content, drawing it in - the configured state. The client must also acknowledge the configure - when committing the new content (see ack_configure). - - It is up to the compositor to position the surface after it was - unmaximized; usually the position the surface had before maximizing, if - applicable. - - If the surface was already not maximized, the compositor will still - emit a configure event without the "maximized" state. - - If the surface is in a fullscreen state, this request has no direct - effect. It may alter the state the surface is returned to when - unmaximized unless overridden by the compositor. - - - - - - Make the surface fullscreen. - - After requesting that the surface should be fullscreened, the - compositor will respond by emitting a configure event. Whether the - client is actually put into a fullscreen state is subject to compositor - policies. The client must also acknowledge the configure when - committing the new content (see ack_configure). - - The output passed by the request indicates the client's preference as - to which display it should be set fullscreen on. If this value is NULL, - it's up to the compositor to choose which display will be used to map - this surface. - - If the surface doesn't cover the whole output, the compositor will - position the surface in the center of the output and compensate with - with border fill covering the rest of the output. The content of the - border fill is undefined, but should be assumed to be in some way that - attempts to blend into the surrounding area (e.g. solid black). - - If the fullscreened surface is not opaque, the compositor must make - sure that other screen content not part of the same surface tree (made - up of subsurfaces, popups or similarly coupled surfaces) are not - visible below the fullscreened surface. - - - - - - - Make the surface no longer fullscreen. - - After requesting that the surface should be unfullscreened, the - compositor will respond by emitting a configure event. - Whether this actually removes the fullscreen state of the client is - subject to compositor policies. - - Making a surface unfullscreen sets states for the surface based on the following: - * the state(s) it may have had before becoming fullscreen - * any state(s) decided by the compositor - * any state(s) requested by the client while the surface was fullscreen - - The compositor may include the previous window geometry dimensions in - the configure event, if applicable. - - The client must also acknowledge the configure when committing the new - content (see ack_configure). - - - - - - Request that the compositor minimize your surface. There is no - way to know if the surface is currently minimized, nor is there - any way to unset minimization on this surface. - - If you are looking to throttle redrawing when minimized, please - instead use the wl_surface.frame event for this, as this will - also work with live previews on windows in Alt-Tab, Expose or - similar compositor features. - - - - - - This configure event asks the client to resize its toplevel surface or - to change its state. The configured state should not be applied - immediately. See xdg_surface.configure for details. - - The width and height arguments specify a hint to the window - about how its surface should be resized in window geometry - coordinates. See set_window_geometry. - - If the width or height arguments are zero, it means the client - should decide its own window dimension. This may happen when the - compositor needs to configure the state of the surface but doesn't - have any information about any previous or expected dimension. - - The states listed in the event specify how the width/height - arguments should be interpreted, and possibly how it should be - drawn. - - Clients must send an ack_configure in response to this event. See - xdg_surface.configure and xdg_surface.ack_configure for details. - - - - - - - - - The close event is sent by the compositor when the user - wants the surface to be closed. This should be equivalent to - the user clicking the close button in client-side decorations, - if your application has any. - - This is only a request that the user intends to close the - window. The client may choose to ignore this request, or show - a dialog to ask the user to save their data, etc. - - - - - - - - The configure_bounds event may be sent prior to a xdg_toplevel.configure - event to communicate the bounds a window geometry size is recommended - to constrain to. - - The passed width and height are in surface coordinate space. If width - and height are 0, it means bounds is unknown and equivalent to as if no - configure_bounds event was ever sent for this surface. - - The bounds can for example correspond to the size of a monitor excluding - any panels or other shell components, so that a surface isn't created in - a way that it cannot fit. - - The bounds may change at any point, and in such a case, a new - xdg_toplevel.configure_bounds will be sent, followed by - xdg_toplevel.configure and xdg_surface.configure. - - - - - - - - - - - - - - - - - This event advertises the capabilities supported by the compositor. If - a capability isn't supported, clients should hide or disable the UI - elements that expose this functionality. For instance, if the - compositor doesn't advertise support for minimized toplevels, a button - triggering the set_minimized request should not be displayed. - - The compositor will ignore requests it doesn't support. For instance, - a compositor which doesn't advertise support for minimized will ignore - set_minimized requests. - - Compositors must send this event once before the first - xdg_surface.configure event. When the capabilities change, compositors - must send this event again and then send an xdg_surface.configure - event. - - The configured state should not be applied immediately. See - xdg_surface.configure for details. - - The capabilities are sent as an array of 32-bit unsigned integers in - native endianness. - - - - - - - - A popup surface is a short-lived, temporary surface. It can be used to - implement for example menus, popovers, tooltips and other similar user - interface concepts. - - A popup can be made to take an explicit grab. See xdg_popup.grab for - details. - - When the popup is dismissed, a popup_done event will be sent out, and at - the same time the surface will be unmapped. See the xdg_popup.popup_done - event for details. - - Explicitly destroying the xdg_popup object will also dismiss the popup and - unmap the surface. Clients that want to dismiss the popup when another - surface of their own is clicked should dismiss the popup using the destroy - request. - - A newly created xdg_popup will be stacked on top of all previously created - xdg_popup surfaces associated with the same xdg_toplevel. - - The parent of an xdg_popup must be mapped (see the xdg_surface - description) before the xdg_popup itself. - - The client must call wl_surface.commit on the corresponding wl_surface - for the xdg_popup state to take effect. - - - - - - - - - This destroys the popup. Explicitly destroying the xdg_popup - object will also dismiss the popup, and unmap the surface. - - If this xdg_popup is not the "topmost" popup, the - xdg_wm_base.not_the_topmost_popup protocol error will be sent. - - - - - - This request makes the created popup take an explicit grab. An explicit - grab will be dismissed when the user dismisses the popup, or when the - client destroys the xdg_popup. This can be done by the user clicking - outside the surface, using the keyboard, or even locking the screen - through closing the lid or a timeout. - - If the compositor denies the grab, the popup will be immediately - dismissed. - - This request must be used in response to some sort of user action like a - button press, key press, or touch down event. The serial number of the - event should be passed as 'serial'. - - The parent of a grabbing popup must either be an xdg_toplevel surface or - another xdg_popup with an explicit grab. If the parent is another - xdg_popup it means that the popups are nested, with this popup now being - the topmost popup. - - Nested popups must be destroyed in the reverse order they were created - in, e.g. the only popup you are allowed to destroy at all times is the - topmost one. - - When compositors choose to dismiss a popup, they may dismiss every - nested grabbing popup as well. When a compositor dismisses popups, it - will follow the same dismissing order as required from the client. - - If the topmost grabbing popup is destroyed, the grab will be returned to - the parent of the popup, if that parent previously had an explicit grab. - - If the parent is a grabbing popup which has already been dismissed, this - popup will be immediately dismissed. If the parent is a popup that did - not take an explicit grab, an error will be raised. - - During a popup grab, the client owning the grab will receive pointer - and touch events for all their surfaces as normal (similar to an - "owner-events" grab in X11 parlance), while the top most grabbing popup - will always have keyboard focus. - - - - - - - - This event asks the popup surface to configure itself given the - configuration. The configured state should not be applied immediately. - See xdg_surface.configure for details. - - The x and y arguments represent the position the popup was placed at - given the xdg_positioner rule, relative to the upper left corner of the - window geometry of the parent surface. - - For version 2 or older, the configure event for an xdg_popup is only - ever sent once for the initial configuration. Starting with version 3, - it may be sent again if the popup is setup with an xdg_positioner with - set_reactive requested, or in response to xdg_popup.reposition requests. - - - - - - - - - - The popup_done event is sent out when a popup is dismissed by the - compositor. The client should destroy the xdg_popup object at this - point. - - - - - - - - Reposition an already-mapped popup. The popup will be placed given the - details in the passed xdg_positioner object, and a - xdg_popup.repositioned followed by xdg_popup.configure and - xdg_surface.configure will be emitted in response. Any parameters set - by the previous positioner will be discarded. - - The passed token will be sent in the corresponding - xdg_popup.repositioned event. The new popup position will not take - effect until the corresponding configure event is acknowledged by the - client. See xdg_popup.repositioned for details. The token itself is - opaque, and has no other special meaning. - - If multiple reposition requests are sent, the compositor may skip all - but the last one. - - If the popup is repositioned in response to a configure event for its - parent, the client should send an xdg_positioner.set_parent_configure - and possibly an xdg_positioner.set_parent_size request to allow the - compositor to properly constrain the popup. - - If the popup is repositioned together with a parent that is being - resized, but not in response to a configure event, the client should - send an xdg_positioner.set_parent_size request. - - - - - - - - The repositioned event is sent as part of a popup configuration - sequence, together with xdg_popup.configure and lastly - xdg_surface.configure to notify the completion of a reposition request. - - The repositioned event is to notify about the completion of a - xdg_popup.reposition request. The token argument is the token passed - in the xdg_popup.reposition request. - - Immediately after this event is emitted, xdg_popup.configure and - xdg_surface.configure will be sent with the updated size and position, - as well as a new configure serial. - - The client should optionally update the content of the popup, but must - acknowledge the new popup configuration for the new position to take - effect. See xdg_surface.ack_configure for details. - - - - - - diff --git a/src/clipboard.c b/src/clipboard.c index 454b686092..e47c9a5adf 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -42,39 +42,6 @@ static int clip_provider_is_available(char_u *provider); # include "wayland.h" -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - -// Structures used for focus stealing -typedef struct { - struct wl_shm_pool *pool; - int fd; - - struct wl_buffer *buffer; - bool available; - - int width; - int height; - int stride; - int size; -} clip_wl_buffer_store_T; - -typedef struct { - void *user_data; - void (*on_focus)(void *data, uint32_t serial); - - struct wl_surface *surface; - struct wl_keyboard *keyboard; - - struct { - struct xdg_surface *surface; - struct xdg_toplevel *toplevel; - } shell; - - bool got_focus; -} clip_wl_fs_surface_T; // fs = focus steal - -# endif // FEAT_WAYLAND_CLIPBOARD_FS - // Represents either the regular or primary selection typedef struct { char_u *contents; // Non-null if we own selection, @@ -84,10 +51,6 @@ typedef struct { // else NULL if we don't. vwl_data_offer_T *offer; // Current offer for the selection -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - bool requires_focus; // If focus needs to be given to us to - // work -# endif bool own_success; // Used by clip_wl_own_selection() bool available; // If selection is ready to serve/use @@ -101,10 +64,6 @@ typedef struct { typedef struct { vwl_seat_T *seat; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - clip_wl_buffer_store_T *fs_buffer; -# endif - clip_wl_selection_T regular; clip_wl_selection_T primary; } clip_wl_T; @@ -2361,290 +2320,6 @@ clip_wl_get_selection_type(clip_wl_selection_T *sel) return WAYLAND_SELECTION_NONE; } -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -/* - * If globals required for focus stealing method are available. - */ - static bool -clip_wl_focus_stealing_available(void) -{ - return wayland_ct->gobjects.wl_compositor != NULL && - wayland_ct->gobjects.wl_shm != NULL && - wayland_ct->gobjects.xdg_wm_base != NULL; -} - -/* - * Called when compositor isn't using the buffer anymore, we can reuse it - * again. - */ - static void -wl_buffer_listener_release( - void *data, - struct wl_buffer *buffer UNUSED) -{ - clip_wl_buffer_store_T *store = data; - - store->available = true; -} - -static struct wl_buffer_listener wl_buffer_listener = { - .release = wl_buffer_listener_release -}; - -/* - * Destroy a buffer store structure. - */ - static void -clip_wl_destroy_buffer_store(clip_wl_buffer_store_T *store) -{ - if (store == NULL) - return; - if (store->buffer != NULL) - wl_buffer_destroy(store->buffer); - if (store->pool != NULL) - wl_shm_pool_destroy(store->pool); - - close(store->fd); - - vim_free(store); -} - -/* - * Initialize a buffer and its backing memory pool. - */ - static clip_wl_buffer_store_T * -clip_wl_init_buffer_store(int width, int height) -{ - int fd, r; - clip_wl_buffer_store_T *store; - - store = alloc(sizeof(*store)); - - if (store == NULL) - return NULL; - - store->available = false; - - store->width = width; - store->height = height; - store->stride = store->width * 4; - store->size = store->stride * store->height; - - fd = mch_create_anon_file(); - r = ftruncate(fd, store->size); - - if (r == -1) - { - if (fd >= 0) - close(fd); - return NULL; - } - - store->pool = wl_shm_create_pool( - wayland_ct->gobjects.wl_shm, - fd, - store->size); - store->buffer = wl_shm_pool_create_buffer( - store->pool, - 0, - store->width, - store->height, - store->stride, - WL_SHM_FORMAT_ARGB8888); - - store->fd = fd; - - wl_buffer_add_listener(store->buffer, &wl_buffer_listener, store); - - if (vwl_connection_roundtrip(wayland_ct) == FAIL) - { - clip_wl_destroy_buffer_store(store); - return NULL; - } - - store->available = true; - - return store; -} - -/* - * Configure xdg_surface - */ - static void -xdg_surface_listener_configure( - void *data UNUSED, - struct xdg_surface *surface, - uint32_t serial) -{ - xdg_surface_ack_configure(surface, serial); -} - - -static struct xdg_surface_listener xdg_surface_listener = { - .configure = xdg_surface_listener_configure -}; - -/* - * Destroy a focus stealing structure. - */ - static void -clip_wl_destroy_fs_surface(clip_wl_fs_surface_T *store) -{ - if (store == NULL) - return; - if (store->shell.toplevel != NULL) - xdg_toplevel_destroy(store->shell.toplevel); - if (store->shell.surface != NULL) - xdg_surface_destroy(store->shell.surface); - if (store->surface != NULL) - wl_surface_destroy(store->surface); - if (store->keyboard != NULL) - { - if (wl_keyboard_get_version(store->keyboard) >= 3) - wl_keyboard_release(store->keyboard); - else - wl_keyboard_destroy(store->keyboard); - } - vim_free(store); -} - -VWL_FUNCS_DUMMY_KEYBOARD_EVENTS() - -/* - * Called when the keyboard focus is on our surface - */ - static void -clip_wl_fs_keyboard_listener_enter( - void *data, - struct wl_keyboard *keyboard UNUSED, - uint32_t serial, - struct wl_surface *surface UNUSED, - struct wl_array *keys UNUSED) -{ - clip_wl_fs_surface_T *store = data; - - store->got_focus = true; - - if (store->on_focus != NULL) - store->on_focus(store->user_data, serial); -} - - -static struct wl_keyboard_listener vwl_fs_keyboard_listener = { - .enter = clip_wl_fs_keyboard_listener_enter, - .key = clip_wl_fs_keyboard_listener_key, - .keymap = clip_wl_fs_keyboard_listener_keymap, - .leave = clip_wl_fs_keyboard_listener_leave, - .modifiers = clip_wl_fs_keyboard_listener_modifiers, - .repeat_info = clip_wl_fs_keyboard_listener_repeat_info -}; - -/* - * Create an invisible surface in order to gain focus and call on_focus() with - * serial that was given. - */ - static int -clip_wl_init_fs_surface( - vwl_seat_T *seat, - clip_wl_buffer_store_T *buffer_store, - void (*on_focus)(void *, uint32_t), - void *user_data) -{ - clip_wl_fs_surface_T *store; -# ifdef ELAPSED_FUNC - elapsed_T start_tv; -# endif - - if (wayland_ct->gobjects.wl_compositor == NULL - || wayland_ct->gobjects.xdg_wm_base == NULL - || buffer_store == NULL - || seat == NULL) - return FAIL; - - store = ALLOC_CLEAR_ONE(clip_wl_fs_surface_T); - - if (store == NULL) - return FAIL; - - // Get keyboard - store->keyboard = vwl_seat_get_keyboard(seat); - - if (store->keyboard == NULL) - goto fail; - - wl_keyboard_add_listener(store->keyboard, &vwl_fs_keyboard_listener, store); - - if (vwl_connection_dispatch(wayland_ct) < 0) - goto fail; - - store->surface = wl_compositor_create_surface( - wayland_ct->gobjects.wl_compositor); - store->shell.surface = xdg_wm_base_get_xdg_surface( - wayland_ct->gobjects.xdg_wm_base, store->surface); - store->shell.toplevel = xdg_surface_get_toplevel(store->shell.surface); - - xdg_toplevel_set_title(store->shell.toplevel, "Vim clipboard"); - - xdg_surface_add_listener(store->shell.surface, - &xdg_surface_listener, NULL); - - wl_surface_commit(store->surface); - - store->on_focus = on_focus; - store->user_data = user_data; - store->got_focus = FALSE; - - if (vwl_connection_roundtrip(wayland_ct) == FAIL) - goto fail; - - // We may get the enter event early, if we do then we will set `got_focus` - // to TRUE. - if (store->got_focus) - goto early_exit; - - // Buffer hasn't been released yet, abort. This shouldn't happen but still - // check for it. - if (!buffer_store->available) - goto fail; - - buffer_store->available = false; - - wl_surface_attach(store->surface, buffer_store->buffer, 0, 0); - wl_surface_damage(store->surface, 0, 0, - buffer_store->width, buffer_store->height); - wl_surface_commit(store->surface); - - // Dispatch events until we receive the enter event. Add a max delay of - // 'p_wtm' when waiting for it (may be longer depending on how long we poll - // when dispatching events) -# ifdef ELAPSED_FUNC - ELAPSED_INIT(start_tv); -# endif - - while (vwl_connection_dispatch(wayland_ct) >= 0) - { - if (store->got_focus) - break; - -# ifdef ELAPSED_FUNC - if (ELAPSED_FUNC(start_tv) >= p_wtm) - goto fail; -# endif - } -early_exit: - clip_wl_destroy_fs_surface(store); - vwl_connection_flush(wayland_ct); - - return OK; -fail: - clip_wl_destroy_fs_surface(store); - vwl_connection_flush(wayland_ct); - - return FAIL; -} - -# endif // FEAT_WAYLAND_CLIPBOARD_FS - static bool wl_data_offer_listener_event_offer( void *data UNUSED, @@ -2811,23 +2486,6 @@ clip_init_wayland(void) clip_wl.primary.device = clip_wl.regular.device; } -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - if (clip_wl.regular.available - && clip_wl.regular.manager->protocol == VWL_DATA_PROTOCOL_CORE - && clip_wl_focus_stealing_available()) - clip_wl.regular.requires_focus = true; - if (clip_wl.primary.available - && clip_wl.primary.manager->protocol == VWL_DATA_PROTOCOL_PRIMARY - && clip_wl_focus_stealing_available()) - clip_wl.primary.requires_focus = true; - - if (clip_wl.regular.requires_focus || clip_wl.primary.requires_focus) - { - // Initialize buffer to use for focus stealing - clip_wl.fs_buffer = clip_wl_init_buffer_store(1, 1); - } -# endif - if (!clip_wl.regular.available && !clip_wl.primary.available) return FAIL; @@ -2857,10 +2515,6 @@ clip_uninit_wayland(void) clip_lose_selection(&clip_plus); } -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - clip_wl_destroy_buffer_store(clip_wl.fs_buffer); -# endif - // Don't want to double free if (clip_wl.regular.manager != clip_wl.primary.manager) vwl_data_device_manager_discard(clip_wl.primary.manager); @@ -3032,23 +2686,10 @@ clip_wl_request_selection(Clipboard_T *cbd) if (!sel->available) goto clear; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - if (sel->requires_focus) - { - // We don't care about the on_focus callback since once we gain - // focus the data offer events will come immediately. - if (clip_wl_init_fs_surface(clip_wl.seat, - clip_wl.fs_buffer, NULL, NULL) == FAIL) - goto clear; - } - else -# endif - { - // Dispatch any events that still queued up before checking for a data - // offer. - if (vwl_connection_roundtrip(wayland_ct) == FAIL) - goto clear; - } + // Dispatch any events that still queued up before checking for a data + // offer. + if (vwl_connection_roundtrip(wayland_ct) == FAIL) + goto clear; if (sel->offer == NULL) goto clear; @@ -3249,16 +2890,7 @@ clip_wl_own_selection(Clipboard_T *cbd) vwl_data_source_offer(sel->source, supported_mimes[i]); sel->own_success = false; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - if (sel->requires_focus) - { - if (clip_wl_init_fs_surface(clip_wl.seat, clip_wl.fs_buffer, - clip_wl_do_set_selection, sel) == FAIL) - goto fail; - } - else -# endif - clip_wl_do_set_selection(sel, 0); + clip_wl_do_set_selection(sel, 0); if (!sel->own_success) goto fail; diff --git a/src/config.h.in b/src/config.h.in index f15d8800cd..699fde7ae0 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -12,9 +12,6 @@ /* Define unless no Wayland support found */ #undef HAVE_WAYLAND -/* Define if you want focus stealing support with Wayland clipboard */ -#undef FEAT_WAYLAND_CLIPBOARD_FS - /* Define when terminfo support found */ #undef TERMINFO diff --git a/src/configure.ac b/src/configure.ac index 3787701e3b..5f79bb6a0e 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -2461,18 +2461,6 @@ if test "$with_wayland" = yes; then if "$PKG_CONFIG" --exists 'wayland-client'; then AC_MSG_RESULT([yes]) - AC_MSG_CHECKING(--enable-wayland-focus-steal argument) - AC_ARG_ENABLE(wayland-focus-steal, - [AS_HELP_STRING([--enable-wayland-focus-steal], - [Include focus stealing support for Wayland clipboard.])], - [enable_wayland_fs=$enableval], - enable_wayland_fs="yes") - - AS_IF([test "$enable_wayland_fs" = "yes"], - [AC_MSG_RESULT([yes]) - AC_DEFINE(FEAT_WAYLAND_CLIPBOARD_FS)], - [AC_MSG_RESULT([no])]) - AC_DEFINE(HAVE_WAYLAND) WAYLAND_CPPFLAGS=`$PKG_CONFIG --cflags-only-I wayland-client` WAYLAND_CFLAGS=`$PKG_CONFIG --cflags-only-other wayland-client` @@ -2488,14 +2476,6 @@ if test "$with_wayland" = yes; then objects/ext-data-control-v1.o \ objects/wayland.o" - AS_IF([test "$enable_wayland_fs" = "yes"], - [AS_VAR_APPEND([WAYLAND_SRC], " \ - auto/wayland/xdg-shell.c \ - auto/wayland/primary-selection-unstable-v1.c") - AS_VAR_APPEND([WAYLAND_OBJ], " \ - objects/xdg-shell.o \ - objects/primary-selection-unstable-v1.o")]) - AC_SUBST(WAYLAND_CPPFLAGS) AC_SUBST(WAYLAND_CFLAGS) AC_SUBST(WAYLAND_LIBS) diff --git a/src/evalfunc.c b/src/evalfunc.c index f4f6a802ca..5026d1b13b 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -7681,13 +7681,6 @@ f_has(typval_T *argvars, typval_T *rettv) 1 #else 0 -#endif - }, - {"wayland_focus_steal", -#ifdef FEAT_WAYLAND_CLIPBOARD_FS - 1 -#else - 0 #endif }, {"wildignore", 1}, diff --git a/src/feature.h b/src/feature.h index 2918fe18d9..3f3c67a176 100644 --- a/src/feature.h +++ b/src/feature.h @@ -925,13 +925,6 @@ # endif #endif -/* - * +wayland_focus_steal Focus stealing support for Wayland clipboard - */ -#if !defined(FEAT_WAYLAND_CLIPBOARD) && defined(FEAT_WAYLAND_CLIPBOARD_FS) -# undef FEAT_WAYLAND_CLIPBOARD_FS -#endif - /* * +dnd Drag'n'drop support. Always used for the GTK+ GUI. */ diff --git a/src/option.h b/src/option.h index e5f9de8e16..c6607a6262 100644 --- a/src/option.h +++ b/src/option.h @@ -1154,9 +1154,6 @@ EXTERN long p_wmw; // 'winminwidth' EXTERN long p_wiw; // 'winwidth' #ifdef FEAT_WAYLAND EXTERN char_u *p_wse; // 'wlseat' -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -EXTERN int p_wst; // 'wlsteal' -# endif EXTERN long p_wtm; // 'wltimeoutlen' #endif #if defined(MSWIN) && defined(FEAT_TERMINAL) diff --git a/src/optiondefs.h b/src/optiondefs.h index 0214a34b1d..0a93b70f8e 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -3039,14 +3039,9 @@ static struct vimoption options[] = {(char_u *)NULL, (char_u *)0L} #endif SCTX_INIT}, - {"wlsteal", "wst", P_BOOL|P_VI_DEF, -#ifdef FEAT_WAYLAND_CLIPBOARD_FS - (char_u *)&p_wst, PV_NONE, did_set_wlsteal, NULL, - {(char_u *)FALSE, (char_u *)0L} -#else + {"wlsteal", "wst", P_BOOL|P_VI_DEF, // Deprecated (char_u *)NULL, PV_NONE, NULL, NULL, {(char_u *)NULL, (char_u *)0L} -#endif SCTX_INIT}, {"wltimeoutlen", "wtm", P_NUM|P_VI_DEF, #ifdef FEAT_WAYLAND diff --git a/src/po/vim.pot b/src/po/vim.pot index 4cc00d1143..94794e7769 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-04-20 17:22+0000\n" +"POT-Creation-Date: 2026-04-20 17:54+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -9916,10 +9916,6 @@ msgstr "" msgid "Wayland seat to use" msgstr "" -msgid "" -"Enable wayland focus stealing functionality in order to access the clipboard" -msgstr "" - msgid "\"startsel\" and/or \"stopsel\"; what special keys can do" msgstr "" diff --git a/src/proto/wayland.pro b/src/proto/wayland.pro index f4f87d988b..770b5e1114 100644 --- a/src/proto/wayland.pro +++ b/src/proto/wayland.pro @@ -3,7 +3,6 @@ int vwl_connection_flush(vwl_connection_T *self); int vwl_connection_dispatch(vwl_connection_T *self); int vwl_connection_roundtrip(vwl_connection_T *self); vwl_seat_T *vwl_connection_get_seat(vwl_connection_T *self, const char *label); -struct wl_keyboard *vwl_seat_get_keyboard(vwl_seat_T *self); int wayland_init_connection(const char *display); void wayland_uninit_connection(void); int wayland_prepare_read(void); diff --git a/src/testdir/test_wayland.vim b/src/testdir/test_wayland.vim index c690d0cfe9..9049075637 100644 --- a/src/testdir/test_wayland.vim +++ b/src/testdir/test_wayland.vim @@ -31,31 +31,6 @@ func s:PreTest() set cpm=wayland endfunc -func s:SetupFocusStealing() - CheckFeature wayland_focus_steal - if !executable('wayland-info') - throw "Skipped: wayland-info program not available" - endif - - " Starting a headless compositor won't expose a keyboard capability for its - " seat, so we must use the user's existing Wayland session if they are in one. - let $WAYLAND_DISPLAY = s:old_wayland_display - - exe 'wlrestore! ' .. $WAYLAND_DISPLAY - - " Check if we have keyboard capability for seat - if system("wayland-info -i wl_seat | grep capabilities") !~? "keyboard" - throw "Skipped: seat does not have keyboard" - endif - - let $VIM_WAYLAND_FORCE_FS=1 - wlrestore! -endfunc - -func s:UnsetupFocusStealing() - unlet $VIM_WAYLAND_FORCE_FS -endfunc - func s:CheckClientserver() CheckFeature clientserver @@ -487,31 +462,6 @@ func Test_wayland_seat() set wlseat& endfunc -" Test focus stealing -func Test_wayland_focus_steal() - CheckFeature wayland_focus_steal - call s:PreTest() - call s:SetupFocusStealing() - - call system('wl-copy regular') - - call assert_equal('regular', getreg('+')) - - call system('wl-copy -p primary') - - call assert_equal('primary', getreg('*')) - - call setreg('+', 'REGULAR') - - call assert_equal('REGULAR', system('wl-paste -n')) - - call setreg('*', 'PRIMARY') - - call assert_equal('PRIMARY', system('wl-paste -p -n')) - - call s:UnsetupFocusStealing() -endfunc - " Test when environment is not suitable for Wayland func Test_wayland_bad_environment() call s:PreTest() @@ -564,32 +514,6 @@ func Test_wayland_lost_selection() endfunc -" Same as above but for the focus stealing method -func Test_wayland_lost_selection_focus_steal() - call s:PreTest() - call s:SetupFocusStealing() - - call setreg('+', 'regular') - call setreg('*', 'primary') - - call assert_equal('regular', getreg('+')) - call assert_equal('primary', getreg('*')) - - call system('wl-copy overwrite') - call system('wl-copy -p overwrite') - - call assert_equal('overwrite', getreg('+')) - call assert_equal('overwrite-primary', getreg('*')) - - call setreg('+', 'regular') - call setreg('*', 'primary') - - call assert_equal('regular', getreg('+')) - call assert_equal('primary', getreg('*')) - - call s:UnsetupFocusStealing() -endfunc - " Test when there are no supported mime types for the selection func Test_wayland_no_mime_types_supported() call s:PreTest() diff --git a/src/version.c b/src/version.c index eaaa793d8b..3f85f2f579 100644 --- a/src/version.c +++ b/src/version.c @@ -663,11 +663,6 @@ static char *(features[]) = "+wayland_clipboard", #else "-wayland_clipboard", -#endif -#ifdef FEAT_WAYLAND_CLIPBOARD_FS - "+wayland_focus_steal", -#else - "-wayland_focus_steal", #endif "+wildignore", "+wildmenu", @@ -734,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 382, /**/ 381, /**/ diff --git a/src/wayland.c b/src/wayland.c index 6461337aab..7dedf62990 100644 --- a/src/wayland.c +++ b/src/wayland.c @@ -22,12 +22,6 @@ * times, and saves new selections/offers as events come in. When we * want retrieve the selection, the currently saved data offer is * used from the respective data device. - * - * The focus stealing code is implemented in clipboard.c, and is - * based off of wl-clipboard's implementation. The idea using of - * extensive macros to reduce boilerplate code also comes from - * wl-clipboard as well. The project page for wl-clipboard can be - * found here: https://github.com/bugaevc/wl-clipboard */ #include "vim.h" @@ -336,38 +330,6 @@ wl_registry_listener_event_global( ct->gobjects.ext_data_control_manager_v1 = wl_registry_bind(registry, name, &ext_data_control_manager_v1_interface, 1); - -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - else if (p_wst) - { - if (STRCMP(interface, wl_data_device_manager_interface.name) == 0) - ct->gobjects.wl_data_device_manager = - wl_registry_bind(registry, name, - &wl_data_device_manager_interface, 1); - - else if (STRCMP(interface, wl_shm_interface.name) == 0) - ct->gobjects.wl_shm = - wl_registry_bind(registry, name, - &wl_shm_interface, 1); - - else if (STRCMP(interface, wl_compositor_interface.name) == 0) - ct->gobjects.wl_compositor = - wl_registry_bind(registry, name, - &wl_compositor_interface, 1); - - else if (STRCMP(interface, xdg_wm_base_interface.name) == 0) - ct->gobjects.xdg_wm_base = - wl_registry_bind(registry, name, - &xdg_wm_base_interface, 1); - - else if (STRCMP(interface, - zwp_primary_selection_device_manager_v1_interface.name) - == 0) - ct->gobjects.zwp_primary_selection_device_manager_v1 = - wl_registry_bind(registry, name, - &zwp_primary_selection_device_manager_v1_interface, 1); - } -# endif // FEAT_WAYLAND_CLIPBOARD_FS #endif // FEAT_WAYLAND_CLIPBOARD } @@ -386,28 +348,11 @@ wl_registry_listener_event_global_remove( { } -#ifdef FEAT_WAYLAND_CLIPBOARD_FS - static void -xdg_wm_base_listener_event_ping( - void *data UNUSED, - struct xdg_wm_base *xdg_base, - uint32_t serial) -{ - xdg_wm_base_pong(xdg_base, serial); -} -#endif - static const struct wl_registry_listener wl_registry_listener = { .global = wl_registry_listener_event_global, .global_remove = wl_registry_listener_event_global_remove }; -#ifdef FEAT_WAYLAND_CLIPBOARD_FS -static const struct xdg_wm_base_listener xdg_wm_base_listener = { - .ping = xdg_wm_base_listener_event_ping -}; -#endif - static void vwl_connection_destroy(vwl_connection_T *self); #ifdef FEAT_WAYLAND_CLIPBOARD @@ -428,10 +373,7 @@ static void vwl_connection_destroy(vwl_connection_T *self); vwl_connection_new(const char *display) { vwl_connection_T *ct; -#ifdef FEAT_WAYLAND_CLIPBOARD_FS - const char_u *env; - bool force_fs; -#endif + if (wayland_no_connect) return NULL; @@ -469,46 +411,12 @@ vwl_connection_new(const char *display) wl_registry_add_listener(ct->registry.proxy, &wl_registry_listener, ct); -#ifdef FEAT_WAYLAND_CLIPBOARD_FS - env = mch_getenv("VIM_WAYLAND_FORCE_FS"); - force_fs = (env != NULL && STRCMP(env, "1") == 0); - - if (force_fs) - p_wst = TRUE; -#endif - if (vwl_connection_roundtrip(ct) == FAIL) { vwl_connection_destroy(ct); return NULL; } -#ifdef FEAT_WAYLAND_CLIPBOARD_FS - if (force_fs) - { - // Force using focus stealing method - VWL_DESTROY_GOBJECT(ct, ext_data_control_manager_v1) - VWL_DESTROY_GOBJECT(ct, zwlr_data_control_manager_v1) - } - - // If data control protocols are available, we don't need the other global - // objects. - else if (VWL_GOBJECT_AVAIL(ct, ext_data_control_manager_v1) - || VWL_GOBJECT_AVAIL(ct, zwlr_data_control_manager_v1)) - { - VWL_DESTROY_GOBJECT(ct, wl_data_device_manager) - VWL_DESTROY_GOBJECT(ct, wl_shm) - VWL_DESTROY_GOBJECT(ct, wl_compositor) - VWL_DESTROY_GOBJECT(ct, xdg_wm_base) - VWL_DESTROY_GOBJECT(ct, zwp_primary_selection_device_manager_v1) - } - - // Start responding to pings from the compositor if we have xdg_wm_base - if (VWL_GOBJECT_AVAIL(ct, xdg_wm_base)) - xdg_wm_base_add_listener(ct->gobjects.xdg_wm_base, - &xdg_wm_base_listener, NULL); -#endif - return ct; } @@ -544,13 +452,6 @@ vwl_connection_destroy(vwl_connection_T *self) # ifdef FEAT_WAYLAND_CLIPBOARD VWL_DESTROY_GOBJECT(self, ext_data_control_manager_v1) VWL_DESTROY_GOBJECT(self, zwlr_data_control_manager_v1) -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - VWL_DESTROY_GOBJECT(self, wl_data_device_manager) - VWL_DESTROY_GOBJECT(self, wl_shm) - VWL_DESTROY_GOBJECT(self, wl_compositor) - VWL_DESTROY_GOBJECT(self, xdg_wm_base) - VWL_DESTROY_GOBJECT(self, zwp_primary_selection_device_manager_v1) -# endif # endif for (int i = 0; i < self->gobjects.seats.ga_len; i++) @@ -592,21 +493,6 @@ vwl_connection_get_seat(vwl_connection_T *self, const char *label) return NULL; } -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -/* - * Get keyboard object from seat and return it. NULL is returned on - * failure such as when a keyboard is not available for seat. - */ - struct wl_keyboard * -vwl_seat_get_keyboard(vwl_seat_T *self) -{ - if (!(self->capabilities & WL_SEAT_CAPABILITY_KEYBOARD)) - return NULL; - - return wl_seat_get_keyboard(self->proxy); -} -# endif - #endif /* @@ -896,27 +782,6 @@ vwl_connection_get_data_device_manager( if (zwlr_data_control_manager_v1_get_version(manager->proxy) >= 2) *supported |= WAYLAND_SELECTION_PRIMARY; } -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - else if (self->gobjects.wl_data_device_manager != NULL - && req_sel == WAYLAND_SELECTION_REGULAR) - { - manager->proxy = self->gobjects.wl_data_device_manager; - manager->protocol = VWL_DATA_PROTOCOL_CORE; - - *supported |= WAYLAND_SELECTION_REGULAR; - } - - if (req_sel == WAYLAND_SELECTION_PRIMARY - && !(*supported & WAYLAND_SELECTION_PRIMARY)) - if (self->gobjects.zwp_primary_selection_device_manager_v1 != NULL) - { - manager->proxy = - self->gobjects.zwp_primary_selection_device_manager_v1; - manager->protocol = VWL_DATA_PROTOCOL_PRIMARY; - - *supported |= WAYLAND_SELECTION_PRIMARY; - } -# endif if (!(*supported & req_sel)) { @@ -944,16 +809,6 @@ vwl_data_device_manager_get_data_device( device->proxy = zwlr_data_control_manager_v1_get_data_device( self->proxy, seat->proxy); break; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - case VWL_DATA_PROTOCOL_CORE: - device->proxy = wl_data_device_manager_get_data_device( - self->proxy, seat->proxy); - break; - case VWL_DATA_PROTOCOL_PRIMARY: - device->proxy = zwp_primary_selection_device_manager_v1_get_device( - self->proxy, seat->proxy); - break; -# endif default: vim_free(device); return NULL; @@ -978,17 +833,6 @@ vwl_data_device_manager_create_data_source(vwl_data_device_manager_T *self) source->proxy = zwlr_data_control_manager_v1_create_data_source( self->proxy); break; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - case VWL_DATA_PROTOCOL_CORE: - source->proxy = wl_data_device_manager_create_data_source( - self->proxy); - break; - case VWL_DATA_PROTOCOL_PRIMARY: - source->proxy = - zwp_primary_selection_device_manager_v1_create_source( - self->proxy); - break; -# endif default: vim_free(source); return NULL; @@ -1018,18 +862,6 @@ vwl_data_device_wrap_offer_proxy(vwl_data_device_T *self, void *proxy) return offer; } -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -# define VWL_CODE_DATA_PROXY_FS_DESTROY(type) \ - case VWL_DATA_PROTOCOL_CORE: \ - wl_data_##type##_destroy(self->proxy); \ - break; \ - case VWL_DATA_PROTOCOL_PRIMARY: \ - zwp_primary_selection_##type##_v1_destroy(self->proxy); \ - break; -# else -# define VWL_CODE_DATA_PROXY_FS_DESTROY(type) -# endif - # define VWL_FUNC_DATA_PROXY_DESTROY(type) \ void \ vwl_data_##type##_destroy(vwl_data_##type##_T *self) \ @@ -1044,7 +876,6 @@ vwl_data_device_wrap_offer_proxy(vwl_data_device_T *self, void *proxy) case VWL_DATA_PROTOCOL_WLR: \ zwlr_data_control_##type##_v1_destroy(self->proxy); \ break; \ - VWL_CODE_DATA_PROXY_FS_DESTROY(type) \ default: \ break; \ } \ @@ -1067,14 +898,6 @@ vwl_data_offer_destroy(vwl_data_offer_T *self) case VWL_DATA_PROTOCOL_WLR: zwlr_data_control_offer_v1_destroy(self->proxy); break; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - case VWL_DATA_PROTOCOL_CORE: - wl_data_offer_destroy(self->proxy); - break; - case VWL_DATA_PROTOCOL_PRIMARY: - zwp_primary_selection_offer_v1_destroy(self->proxy); - break; -# endif default: break; } @@ -1147,11 +970,6 @@ VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER( ext_data_control_device_v1, ext_data_control_offer_v1) VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER( zwlr_data_control_device_v1, zwlr_data_control_offer_v1) -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER(wl_data_device, wl_data_offer) -VWL_FUNC_DATA_DEVICE_EVENT_DATA_OFFER( - zwp_primary_selection_device_v1, zwp_primary_selection_offer_v1) -# endif VWL_FUNC_DATA_DEVICE_EVENT_SELECTION( ext_data_control_device_v1, ext_data_control_offer_v1 @@ -1165,13 +983,6 @@ VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION( VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION( zwlr_data_control_device_v1, zwlr_data_control_offer_v1 ) -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -VWL_FUNC_DATA_DEVICE_EVENT_SELECTION( - wl_data_device, wl_data_offer -) -VWL_FUNC_DATA_DEVICE_EVENT_PRIMARY_SELECTION( - zwp_primary_selection_device_v1, zwp_primary_selection_offer_v1) -# endif VWL_FUNC_DATA_DEVICE_EVENT_FINISHED(ext_data_control_device_v1) VWL_FUNC_DATA_DEVICE_EVENT_FINISHED(zwlr_data_control_device_v1) @@ -1192,18 +1003,6 @@ static const struct zwlr_data_control_device_v1_listener zwlr_data_control_device_v1_listener_event_primary_selection, .finished = zwlr_data_control_device_v1_listener_event_finished }; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -static const struct wl_data_device_listener wl_data_device_listener = { - .data_offer = wl_data_device_listener_event_data_offer, - .selection = wl_data_device_listener_event_selection, -}; -static const struct zwp_primary_selection_device_v1_listener - zwp_primary_selection_device_v1_listener = { - .data_offer = zwp_primary_selection_device_v1_listener_event_data_offer, - .selection = - zwp_primary_selection_device_v1_listener_event_primary_selection, -}; -# endif # define VWL_FUNC_DATA_SOURCE_EVENT_SEND(source_type) \ static void \ @@ -1226,17 +1025,9 @@ static const struct zwp_primary_selection_device_v1_listener VWL_FUNC_DATA_SOURCE_EVENT_SEND(ext_data_control_source_v1) VWL_FUNC_DATA_SOURCE_EVENT_SEND(zwlr_data_control_source_v1) -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -VWL_FUNC_DATA_SOURCE_EVENT_SEND(wl_data_source) -VWL_FUNC_DATA_SOURCE_EVENT_SEND(zwp_primary_selection_source_v1) -# endif VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(ext_data_control_source_v1) VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(zwlr_data_control_source_v1) -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(wl_data_source) -VWL_FUNC_DATA_SOURCE_EVENT_CANCELLED(zwp_primary_selection_source_v1) -# endif static const struct ext_data_control_source_v1_listener ext_data_control_source_v1_listener = { @@ -1248,17 +1039,6 @@ static const struct zwlr_data_control_source_v1_listener .send = zwlr_data_control_source_v1_listener_event_send, .cancelled = zwlr_data_control_source_v1_listener_event_cancelled }; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -static const struct wl_data_source_listener wl_data_source_listener = { - .send = wl_data_source_listener_event_send, - .cancelled = wl_data_source_listener_event_cancelled -}; -static const struct zwp_primary_selection_source_v1_listener - zwp_primary_selection_source_v1_listener = { - .send = zwp_primary_selection_source_v1_listener_event_send, - .cancelled = zwp_primary_selection_source_v1_listener_event_cancelled -}; -# endif # define VWL_FUNC_DATA_OFFER_EVENT_OFFER(offer_type) \ static void \ @@ -1282,10 +1062,6 @@ static const struct zwp_primary_selection_source_v1_listener VWL_FUNC_DATA_OFFER_EVENT_OFFER(ext_data_control_offer_v1) VWL_FUNC_DATA_OFFER_EVENT_OFFER(zwlr_data_control_offer_v1) -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -VWL_FUNC_DATA_OFFER_EVENT_OFFER(wl_data_offer) -VWL_FUNC_DATA_OFFER_EVENT_OFFER(zwp_primary_selection_offer_v1) -# endif static const struct ext_data_control_offer_v1_listener ext_data_control_offer_v1_listener = { @@ -1295,30 +1071,6 @@ static const struct zwlr_data_control_offer_v1_listener zwlr_data_control_offer_v1_listener = { .offer = zwlr_data_control_offer_v1_listener_event_offer }; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -static const struct wl_data_offer_listener - wl_data_offer_listener = { - .offer = wl_data_offer_listener_event_offer -}; -static const struct zwp_primary_selection_offer_v1_listener - zwp_primary_selection_offer_v1_listener = { - .offer = zwp_primary_selection_offer_v1_listener_event_offer -}; -# endif - -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -# define VWL_CODE_DATA_PROXY_FS_ADD_LISTENER(type) \ - case VWL_DATA_PROTOCOL_CORE: \ - wl_data_##type##_add_listener(self->proxy, \ - &wl_data_##type##_listener, self); \ - break; \ - case VWL_DATA_PROTOCOL_PRIMARY: \ - zwp_primary_selection_##type##_v1_add_listener(self->proxy, \ - &zwp_primary_selection_##type##_v1_listener, self); \ - break; -# else -# define VWL_CODE_DATA_PROXY_FS_ADD_LISTENER(type) -# endif # define VWL_FUNC_DATA_PROXY_ADD_LISTENER(type) \ void \ @@ -1341,7 +1093,6 @@ static const struct zwp_primary_selection_offer_v1_listener zwlr_data_control_##type##_v1_add_listener(self->proxy, \ &zwlr_data_control_##type##_v1_listener, self); \ break; \ - VWL_CODE_DATA_PROXY_FS_ADD_LISTENER(type) \ default: \ break; \ } \ @@ -1350,7 +1101,6 @@ static const struct zwp_primary_selection_offer_v1_listener VWL_FUNC_DATA_PROXY_ADD_LISTENER(device) VWL_FUNC_DATA_PROXY_ADD_LISTENER(source) VWL_FUNC_DATA_PROXY_ADD_LISTENER(offer) - /* * Set the given selection to source. If a data control protocol is being used, * "serial" is ignored. @@ -1375,11 +1125,6 @@ vwl_data_device_set_selection( case VWL_DATA_PROTOCOL_WLR: zwlr_data_control_device_v1_set_selection(self->proxy, proxy); break; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - case VWL_DATA_PROTOCOL_CORE: - wl_data_device_set_selection(self->proxy, proxy, serial); - break; -# endif default: break; } @@ -1398,12 +1143,6 @@ vwl_data_device_set_selection( self->proxy, proxy ); break; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - case VWL_DATA_PROTOCOL_PRIMARY: - zwp_primary_selection_device_v1_set_selection( - self->proxy, proxy, serial); - break; -# endif default: break; } @@ -1421,14 +1160,6 @@ vwl_data_source_offer(vwl_data_source_T *self, const char *mime_type) case VWL_DATA_PROTOCOL_WLR: zwlr_data_control_source_v1_offer(self->proxy, mime_type); break; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - case VWL_DATA_PROTOCOL_CORE: - wl_data_source_offer(self->proxy, mime_type); - break; - case VWL_DATA_PROTOCOL_PRIMARY: - zwp_primary_selection_source_v1_offer(self->proxy, mime_type); - break; -# endif default: break; } @@ -1448,14 +1179,6 @@ vwl_data_offer_receive( case VWL_DATA_PROTOCOL_WLR: zwlr_data_control_offer_v1_receive(self->proxy, mime_type, fd); break; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - case VWL_DATA_PROTOCOL_CORE: - wl_data_offer_receive(self->proxy, mime_type, fd); - break; - case VWL_DATA_PROTOCOL_PRIMARY: - zwp_primary_selection_offer_v1_receive(self->proxy, mime_type, fd); - break; -# endif default: break; } diff --git a/src/wayland.h b/src/wayland.h index 80eb52cc33..398e9d3e60 100644 --- a/src/wayland.h +++ b/src/wayland.h @@ -19,10 +19,6 @@ #ifdef FEAT_WAYLAND_CLIPBOARD # include "auto/wayland/wlr-data-control-unstable-v1.h" # include "auto/wayland/ext-data-control-v1.h" -# ifdef FEAT_WAYLAND_CLIPBOARD_FS -# include "auto/wayland/xdg-shell.h" -# include "auto/wayland/primary-selection-unstable-v1.h" -# endif #endif #ifdef FEAT_WAYLAND_CLIPBOARD @@ -32,10 +28,6 @@ typedef enum { VWL_DATA_PROTOCOL_NONE, VWL_DATA_PROTOCOL_EXT, VWL_DATA_PROTOCOL_WLR, -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - VWL_DATA_PROTOCOL_CORE, - VWL_DATA_PROTOCOL_PRIMARY -# endif } vwl_data_protocol_T; #endif // FEAT_WAYLAND_CLIPBOARD @@ -68,14 +60,6 @@ struct vwl_connection_S { #ifdef FEAT_WAYLAND_CLIPBOARD struct zwlr_data_control_manager_v1 *zwlr_data_control_manager_v1; struct ext_data_control_manager_v1 *ext_data_control_manager_v1; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - struct wl_data_device_manager *wl_data_device_manager; - struct wl_shm *wl_shm; - struct wl_compositor *wl_compositor; - struct xdg_wm_base *xdg_wm_base; - struct zwp_primary_selection_device_manager_v1 - *zwp_primary_selection_device_manager_v1; -# endif #endif } gobjects; }; @@ -149,61 +133,6 @@ struct vwl_data_device_manager_S { vwl_data_protocol_T protocol; }; -# ifdef FEAT_WAYLAND_CLIPBOARD_FS - -// Dummy functions to handle keyboard events we don't care about. - -# define VWL_FUNCS_DUMMY_KEYBOARD_EVENTS() \ - static void \ -clip_wl_fs_keyboard_listener_keymap( \ - void *data UNUSED, \ - struct wl_keyboard *keyboard UNUSED, \ - uint32_t format UNUSED, \ - int fd, \ - uint32_t size UNUSED) \ -{ \ - close(fd); \ -} \ - static void \ -clip_wl_fs_keyboard_listener_leave( \ - void *data UNUSED, \ - struct wl_keyboard *keyboard UNUSED, \ - uint32_t serial UNUSED, \ - struct wl_surface *surface UNUSED) \ -{ \ -} \ - static void \ -clip_wl_fs_keyboard_listener_key( \ - void *data UNUSED, \ - struct wl_keyboard *keyboard UNUSED, \ - uint32_t serial UNUSED, \ - uint32_t time UNUSED, \ - uint32_t key UNUSED, \ - uint32_t state UNUSED) \ -{ \ -} \ - static void \ -clip_wl_fs_keyboard_listener_modifiers( \ - void *data UNUSED, \ - struct wl_keyboard *keyboard UNUSED, \ - uint32_t serial UNUSED, \ - uint32_t mods_depressed UNUSED, \ - uint32_t mods_latched UNUSED, \ - uint32_t mods_locked UNUSED, \ - uint32_t group UNUSED) \ -{ \ -} \ - static void \ -clip_wl_fs_keyboard_listener_repeat_info( \ - void *data UNUSED, \ - struct wl_keyboard *keyboard UNUSED, \ - int32_t rate UNUSED, \ - int32_t delay UNUSED) \ -{ \ -} - -# endif - #endif // FEAT_WAYLAND_CLIPBOARD // Global Wayland connection. Is also set to NULL when the connection is lost. From 405e2fb6d54d5653523809e2853d99d1c000a5fc Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Tue, 21 Apr 2026 19:03:02 +0000 Subject: [PATCH 090/545] patch 9.2.0383: [security]: runtime(netrw): shell-injection via sftp: and file: URLs Problem: runtime(netrw): shell-injection via sftp: and file: URLs (Joshua Rogers) Solution: Escape temporary file names, harden filename suffix regex, drop unused g:netrw_tmpfile_escape variable Supported by AI Signed-off-by: Christian Brabandt --- runtime/doc/pi_netrw.txt | 4 --- runtime/doc/tags | 1 - .../pack/dist/opt/netrw/autoload/netrw.vim | 18 ++++++----- runtime/pack/dist/opt/netrw/doc/netrw.txt | 4 --- src/testdir/test_plugin_netrw.vim | 30 +++++++++++++++++++ src/version.c | 2 ++ 6 files changed, 43 insertions(+), 16 deletions(-) diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index a86cac36ba..2d98a8407b 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -2854,10 +2854,6 @@ your browsing preferences. (see also: |netrw-settings|) such as listing, file removal, etc. default: ssh - *g:netrw_tmpfile_escape* =' &;' - escape() is applied to all temporary files - to escape these characters. - *g:netrw_timefmt* specify format string to vim's strftime(). The default, "%c", is "the preferred date and time representation for the current diff --git a/runtime/doc/tags b/runtime/doc/tags index 720eb330f7..2b0e6cab39 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -7970,7 +7970,6 @@ g:netrw_ssh_browse_reject pi_netrw.txt /*g:netrw_ssh_browse_reject* g:netrw_ssh_cmd pi_netrw.txt /*g:netrw_ssh_cmd* g:netrw_sshport pi_netrw.txt /*g:netrw_sshport* g:netrw_timefmt pi_netrw.txt /*g:netrw_timefmt* -g:netrw_tmpfile_escape pi_netrw.txt /*g:netrw_tmpfile_escape* g:netrw_uid pi_netrw.txt /*g:netrw_uid* g:netrw_use_noswf pi_netrw.txt /*g:netrw_use_noswf* g:netrw_use_nt_rcp pi_netrw.txt /*g:netrw_use_nt_rcp* diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index e3028cb28e..538ab0377b 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -26,6 +26,8 @@ " 2026 Apr 05 by Vim Project Fix netrw#RFC2396() #19913 " 2026 Apr 15 by Vim Project Add missing escape() " 2026 Apr 19 by Vim Project expand ~ on Windows #20003 +" 2026 Apr 21 by Vim Project fix shell-injection via tempfile suffix (sftp://, file://) +" 2026 Apr 21 by Vim Project drop unused g:netrw_tmpfile_escape " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -400,7 +402,6 @@ else call s:NetrwInit("g:netrw_glob_escape",'*[]?`{~$\') endif call s:NetrwInit("g:netrw_menu_escape",'.&? \') -call s:NetrwInit("g:netrw_tmpfile_escape",' &;') call s:NetrwInit("s:netrw_map_escape","<|\n\r\\\\"") if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4') let s:treedepthstring= "│ " @@ -1821,14 +1822,14 @@ function netrw#NetRead(mode,...) "......................................... " NetRead: (sftp) NetRead Method #9 {{{3 elseif b:netrw_method == 9 - call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".netrw#os#Escape(g:netrw_machine.":".b:netrw_fname,1)." ".tmpfile) + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".netrw#os#Escape(g:netrw_machine.":".b:netrw_fname,1)." ".netrw#os#Escape(tmpfile,1)) let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice "......................................... " NetRead: (file) NetRead Method #10 {{{3 elseif b:netrw_method == 10 && exists("g:netrw_file_cmd") - call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_file_cmd." ".netrw#os#Escape(b:netrw_fname,1)." ".tmpfile) + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_file_cmd." ".netrw#os#Escape(b:netrw_fname,1)." ".netrw#os#Escape(tmpfile,1)) let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) let b:netrw_lastfile = choice @@ -8965,14 +8966,17 @@ function s:GetTempfile(fname) endif " use fname's suffix for the temporary file + " Restrict the suffix to word characters so shell metacharacters in a + " remote filename (e.g. sftp://host/foo.txt;id) cannot ride along into + " the tempfile name and out into a downstream shell command. if a:fname != "" - if a:fname =~ '\.[^./]\+$' + if a:fname =~ '\.\w\+$' if a:fname =~ '\.tar\.gz$' || a:fname =~ '\.tar\.bz2$' || a:fname =~ '\.tar\.xz$' - let suffix = ".tar".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') + let suffix = ".tar".substitute(a:fname,'^.*\(\.\w\+\)$','\1','e') elseif a:fname =~ '.txz$' - let suffix = ".txz".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') + let suffix = ".txz".substitute(a:fname,'^.*\(\.\w\+\)$','\1','e') else - let suffix = substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') + let suffix = substitute(a:fname,'^.*\(\.\w\+\)$','\1','e') endif let tmpfile= substitute(tmpfile,'\.tmp$','','e') let tmpfile .= suffix diff --git a/runtime/pack/dist/opt/netrw/doc/netrw.txt b/runtime/pack/dist/opt/netrw/doc/netrw.txt index 01a5bda597..144bab5fb3 100644 --- a/runtime/pack/dist/opt/netrw/doc/netrw.txt +++ b/runtime/pack/dist/opt/netrw/doc/netrw.txt @@ -2854,10 +2854,6 @@ your browsing preferences. (see also: |netrw-settings|) such as listing, file removal, etc. default: ssh - *g:netrw_tmpfile_escape* =' &;' - escape() is applied to all temporary files - to escape these characters. - *g:netrw_timefmt* specify format string to vim's strftime(). The default, "%c", is "the preferred date and time representation for the current diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim index 6ee731d88f..893e424362 100644 --- a/src/testdir/test_plugin_netrw.vim +++ b/src/testdir/test_plugin_netrw.vim @@ -604,6 +604,36 @@ func Test_netrw_FileUrlEdit_pipe_injection() call assert_false(filereadable(fname), 'Command injection via pipe in file URL') endfunc +" The remote filename after '.' was allowed to contain shell metacharacters +" and rode unescaped into the tempfile name passed to sftp/file_cmd, giving a +" shell injection on :e sftp://host/foo.txt;. +func Test_netrw_tempfile_suffix_injection() + CheckUnix + CheckExecutable id + let save_sftp = g:netrw_sftp_cmd + let save_file = exists('g:netrw_file_cmd') ? g:netrw_file_cmd : v:null + let g:netrw_sftp_cmd = 'true' + let g:netrw_file_cmd = 'true' + let fname = 'Xrce_marker' + try + call delete(fname) + sil! call netrw#NetRead(2, 'sftp://localhost/foo.txt;id>'..fname) + call assert_false(filereadable(fname), 'Command injection via sftp:// tempfile suffix') + + call delete(fname) + sil! call netrw#NetRead(2, 'file://localhost/foo.txt;id>'..fname) + call assert_false(filereadable(fname), 'Command injection via file:// tempfile suffix') + finally + call delete(fname) + let g:netrw_sftp_cmd = save_sftp + if save_file is v:null + unlet! g:netrw_file_cmd + else + let g:netrw_file_cmd = save_file + endif + endtry +endfunc + func Test_netrw_RFC2396() let fname = 'a%20b' call assert_equal('a b', netrw#RFC2396(fname)) diff --git a/src/version.c b/src/version.c index 3f85f2f579..895c22e9c0 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 383, /**/ 382, /**/ From d4fb31762ea0b9de6fffb529c4ffee509621f74c Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Tue, 21 Apr 2026 19:21:24 +0000 Subject: [PATCH 091/545] patch 9.2.0384: stale Insstart after cursor move breaks undo Problem: A command executed from Insert mode can sync undo and move the cursor before the next edit. stop_arrow() saved the new cursor line for undo, but left Insstart at the previous insertion point. A line-start backspace could then delete lines above the saved line without saving the joined range, leaving a pending undo entry whose bottom resolved above its top and raising E340. Solution: Update Insstart and Insstart_textlen after the pending undo save so the next edit starts from the command-updated cursor position (Jaehwang Jung). closes: #20031 AI-assisted: Codex Signed-off-by: Jaehwang Jung Signed-off-by: Christian Brabandt --- src/edit.c | 5 +++++ src/testdir/test_undo.vim | 14 ++++++++++++++ src/version.c | 2 ++ 3 files changed, 21 insertions(+) diff --git a/src/edit.c b/src/edit.c index 60cae0e15c..f15cc55f32 100644 --- a/src/edit.c +++ b/src/edit.c @@ -2502,7 +2502,12 @@ stop_arrow(void) else if (ins_need_undo) { if (u_save_cursor() == OK) + { + // A command or event may have moved the cursor after syncing undo. + Insstart = curwin->w_cursor; + Insstart_textlen = (colnr_T)linetabsize_str(ml_get_curline()); ins_need_undo = FALSE; + } } #ifdef FEAT_FOLDING diff --git a/src/testdir/test_undo.vim b/src/testdir/test_undo.vim index ad724aa04d..97b77f423d 100644 --- a/src/testdir/test_undo.vim +++ b/src/testdir/test_undo.vim @@ -924,5 +924,19 @@ func Test_restore_cursor_position_after_undo() bw! endfunc +func Test_undo_line_backspace_after_insert_cmd_cursor_movement() + new + setlocal backspace=eol undolevels=100 + call setline(1, ['', '', 'abc', 'def']) + call cursor(4, 1) + + let v:errmsg = '' + call feedkeys("i\setlocal undolevels=101 | call cursor(3, 1)\" + \ .. "\\\u", 'xt') + + call assert_equal('', v:errmsg) + call assert_equal(['', '', 'abc', 'def'], getline(1, '$')) + bwipe! +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 895c22e9c0..2a8b84404f 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 384, /**/ 383, /**/ From 16d28548d2d3c49733750b6ab7a50bc05ab65e0c Mon Sep 17 00:00:00 2001 From: Jon Parise Date: Tue, 21 Apr 2026 19:25:54 +0000 Subject: [PATCH 092/545] runtime(graphql): Update syntax script to September 2025 spec A new graphqlEscape syntax graph has been introduced to represent escape sequences within strings, including the braced unicode form (\u{H+}) added in the September 2025 specification. See: https://spec.graphql.org/September2025/ closes: #20030 Signed-off-by: Jon Parise Signed-off-by: Christian Brabandt --- runtime/syntax/graphql.vim | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/runtime/syntax/graphql.vim b/runtime/syntax/graphql.vim index 01d5ca25ff..53ab1cc93f 100644 --- a/runtime/syntax/graphql.vim +++ b/runtime/syntax/graphql.vim @@ -4,7 +4,7 @@ " Filenames: *.graphql *.graphqls *.gql " URL: https://github.com/jparise/vim-graphql " License: MIT -" Last Change: 2024 Dec 21 +" Last Change: 2026 Apr 21 if !exists('main_syntax') if exists('b:current_syntax') @@ -26,8 +26,13 @@ syn match graphqlOperator "\M..." display syn keyword graphqlBoolean true false syn keyword graphqlNull null syn match graphqlNumber "-\=\<\%(0\|[1-9]\d*\)\%(\.\d\+\)\=\%([eE][-+]\=\d\+\)\=\>" display -syn region graphqlString start=+"+ skip=+\\\\\|\\"+ end=+"\|$+ -syn region graphqlString start=+"""+ skip=+\\"""+ end=+"""+ +syn region graphqlString start=+"+ skip=+\\\\\|\\"+ end=+"\|$+ contains=graphqlEscape +syn region graphqlString start=+"""+ skip=+\\"""+ end=+"""+ contains=graphqlEscape + +syn match graphqlEscape +\\["\\/bfnrt]+ contained display +syn match graphqlEscape +\\u\x\{4}+ contained display +syn match graphqlEscape +\\u{\x\+}+ contained display +syn match graphqlEscape +\\""\"+ contained display syn keyword graphqlKeyword repeatable nextgroup=graphqlKeyword skipwhite syn keyword graphqlKeyword on nextgroup=graphqlType,graphqlDirectiveLocation skipwhite @@ -45,11 +50,11 @@ syn match graphqlVariable "\<\$\h\w*\>" display syn match graphqlName "\<\h\w*\>" display syn match graphqlType "\<_*\u\w*\>" display -" https://spec.graphql.org/October2021/#ExecutableDirectiveLocation +" https://spec.graphql.org/September2025/#ExecutableDirectiveLocation syn keyword graphqlDirectiveLocation QUERY MUTATION SUBSCRIPTION FIELD syn keyword graphqlDirectiveLocation FRAGMENT_DEFINITION FRAGMENT_SPREAD syn keyword graphqlDirectiveLocation INLINE_FRAGMENT VARIABLE_DEFINITION -" https://spec.graphql.org/October2021/#TypeSystemDirectiveLocation +" https://spec.graphql.org/September2025/#TypeSystemDirectiveLocation syn keyword graphqlDirectiveLocation SCHEMA SCALAR OBJECT FIELD_DEFINITION syn keyword graphqlDirectiveLocation ARGUMENT_DEFINITION INTERFACE UNION syn keyword graphqlDirectiveLocation ENUM ENUM_VALUE INPUT_OBJECT @@ -73,6 +78,7 @@ hi def link graphqlBoolean Boolean hi def link graphqlNull Keyword hi def link graphqlNumber Number hi def link graphqlString String +hi def link graphqlEscape Special hi def link graphqlDirective PreProc hi def link graphqlDirectiveLocation Special From 3918f3232f874b3c96f8bfccc5049711f16c252e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 21 Apr 2026 19:34:18 +0000 Subject: [PATCH 093/545] runtime(doc): fix incorrect description of 'scrolloffpad' closes: #20029 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- runtime/doc/quickref.txt | 4 ++-- runtime/doc/version9.txt | 6 +++--- runtime/optwin.vim | 4 ++-- src/po/vim.pot | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index 1ada2bf9b3..acc5dbdd04 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -1,4 +1,4 @@ -*quickref.txt* For Vim version 9.2. Last change: 2026 Apr 15 +*quickref.txt* For Vim version 9.2. Last change: 2026 Apr 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -895,7 +895,7 @@ Short explanation of each option: *option-list* 'scrollfocus' 'scf' scroll wheel applies to window under pointer 'scrolljump' 'sj' minimum number of lines to scroll 'scrolloff' 'so' minimum nr. of lines above and below cursor -'scrolloffpad' 'sop' keep 'scrolloff' context at end of file +'scrolloffpad' 'sop' vertically center cursor at end of file 'scrollopt' 'sbo' how 'scrollbind' should behave 'sections' 'sect' nroff macros that separate sections 'secure' secure mode for reading .vimrc in current dir diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 12d46cffb8..90d563c902 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 20 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52656,8 +52656,8 @@ Autocommands: ~ Options: ~ 'modelinestrict' Only allow safe options to be set from a modeline. -'pumopt' Additional options for the popup menu -'scrolloffpad' Keep 'scrolloff' context at end of file +'pumopt' Additional options for the popup menu. +'scrolloffpad' Vertically center cursor at end of file. 'statuslineopt' Extra window-local options for the 'statusline', to configure the height. 't_BS' Begin synchronized update. diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 2be864882f..1ecd602d3b 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: The Vim Project -" Last Change: 2026 Apr 15 +" Last Change: 2026 Apr 21 " Former Maintainer: Bram Moolenaar " If there already is an option window, jump to that one. @@ -351,7 +351,7 @@ call append("$", "\t" .. s:local_to_window) call BinOptionL("sms") call AddOption("scrolloff", gettext("number of screen lines to show around the cursor")) call append("$", " \tset so=" . &so) -call AddOption("scrolloffpad", gettext("keep 'scrolloff' context even at end of file")) +call AddOption("scrolloffpad", gettext("vertically center cursor even at end of file")) call append("$", " \tset sop=" . &sop) call AddOption("wrap", gettext("long lines wrap")) call append("$", "\t" .. s:local_to_window) diff --git a/src/po/vim.pot b/src/po/vim.pot index 94794e7769..e3206e8712 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-04-20 17:54+0000\n" +"POT-Creation-Date: 2026-04-21 19:33+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -9380,7 +9380,7 @@ msgstr "" msgid "number of screen lines to show around the cursor" msgstr "" -msgid "keep 'scrolloff' context even at end of file" +msgid "vertically center cursor even at end of file" msgstr "" msgid "long lines wrap" From 33f3965087b01dccf4382ed419d34799ffd66cd9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 21 Apr 2026 19:41:37 +0000 Subject: [PATCH 094/545] patch 9.2.0385: Integer overflow with "ze" and large 'sidescrolloff' Problem: Integer overflow with "ze" and large 'sidescrolloff'. Solution: Check for overflow to avoid negative w_leftcol (zeertzjq). closes: #20026 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/move.c | 17 ++++++++--------- src/normal.c | 4 +++- src/testdir/test_normal.vim | 25 +++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 38 insertions(+), 10 deletions(-) diff --git a/src/move.c b/src/move.c index 7e3be27264..943eb519f8 100644 --- a/src/move.c +++ b/src/move.c @@ -1184,9 +1184,9 @@ curwin_col_off2(void) curs_columns( int may_scroll) // when TRUE, may scroll horizontally { - int diff; + long diff; int extra; // offset for first screen line - int off_left, off_right; + long off_left, off_right; int n; int p_lines; int width1; // text width for first screen line @@ -1306,13 +1306,12 @@ curs_columns( #endif /* * If Cursor is left of the screen, scroll rightwards. - * If Cursor is right of the screen, scroll leftwards + * If Cursor is right of the screen, scroll leftwards. * If we get closer to the edge than 'sidescrolloff', scroll a little - * extra + * extra. */ - off_left = (int)startcol - (int)curwin->w_leftcol - siso; - off_right = (int)endcol - (int)(curwin->w_leftcol + curwin->w_width - - siso) + 1; + off_left = startcol - curwin->w_leftcol - siso; + off_right = endcol - curwin->w_leftcol - (curwin->w_width - siso) + 1; if (off_left < 0 || off_right > 0) { if (off_left < 0) @@ -1329,9 +1328,9 @@ curs_columns( if (diff < p_ss) diff = p_ss; if (off_left < 0) - new_leftcol = curwin->w_leftcol - diff; + new_leftcol = curwin->w_leftcol - (int)diff; else - new_leftcol = curwin->w_leftcol + diff; + new_leftcol = curwin->w_leftcol + (int)diff; } if (new_leftcol < 0) new_leftcol = 0; diff --git a/src/normal.c b/src/normal.c index 2c46bf31fa..b402aa81da 100644 --- a/src/normal.c +++ b/src/normal.c @@ -2791,8 +2791,10 @@ nv_zet(cmdarg_T *cap) n = curwin->w_width - curwin_col_off(); if ((long)col + siso < n) col = 0; + else if (siso - n < INT_MAX - col) + col = (int)(col + siso - n + 1); else - col = col + siso - n + 1; + col = INT_MAX; if (curwin->w_leftcol != col) { curwin->w_leftcol = col; diff --git a/src/testdir/test_normal.vim b/src/testdir/test_normal.vim index 4f435610bd..eea7891236 100644 --- a/src/testdir/test_normal.vim +++ b/src/testdir/test_normal.vim @@ -1196,6 +1196,31 @@ func Test_normal17_z_scroll_hor2() bw! endfunc +func Test_large_sidescrolloff_no_overflow() + 10new + 20vsp + setlocal nowrap sidescrolloff=2147483647 + call setline(1, repeat('a', 40)) + + normal! $ + redraw! + call assert_equal(29, winsaveview().leftcol) + + normal! zs + redraw! + call assert_equal(29, winsaveview().leftcol) + + normal! ze + redraw! + call assert_equal(29, winsaveview().leftcol) + + normal! 0 + redraw! + call assert_equal(0, winsaveview().leftcol) + + bw! +endfunc + " Test for commands that scroll the window horizontally. Test with folds. " H, M, L, CTRL-E, CTRL-Y, CTRL-U, CTRL-D, PageUp, PageDown commands func Test_vert_scroll_cmds() diff --git a/src/version.c b/src/version.c index 2a8b84404f..aa38e1a35c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 385, /**/ 384, /**/ From 10040bc9cde340c52b5093cacb1d60fd2e621883 Mon Sep 17 00:00:00 2001 From: "D. Ben Knoble" Date: Tue, 21 Apr 2026 19:59:07 +0000 Subject: [PATCH 095/545] runtime(sh): allow "#" in special derefs Code like ${!#} flags the "#" as shDerefWordError [1]; the "!prefix" syntax region delegates to one of the shDerefSpecial handlers via @shDerefList, but it misses the "#" case as valid for ${##} and ${!#}. [1]: https://vi.stackexchange.com/q/48617/10604 Correct that. Indirection is only valid in Bash in Ksh, so rearrange the "!" handling to be conditional. closes: #20016 Helped-by: Christian Brabandt Signed-off-by: D. Ben Knoble Signed-off-by: Christian Brabandt --- runtime/syntax/sh.vim | 5 ++++- runtime/syntax/testdir/dumps/sh_03_01.dump | 2 +- runtime/syntax/testdir/dumps/sh_03_02.dump | 12 ++++++------ runtime/syntax/testdir/dumps/sh_bash_00.dump | 2 +- runtime/syntax/testdir/dumps/sh_bash_01.dump | 6 +++--- runtime/syntax/testdir/dumps/sh_bash_02.dump | 12 ++++++------ runtime/syntax/testdir/input/sh_03.sh | 4 ++++ runtime/syntax/testdir/input/sh_bash.bash | 8 ++++++-- 8 files changed, 31 insertions(+), 20 deletions(-) diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim index 05eb488d53..d022059d72 100644 --- a/runtime/syntax/sh.vim +++ b/runtime/syntax/sh.vim @@ -24,6 +24,7 @@ " 2026 Feb 15 improve comment handling #19414 " 2026 Mar 23 improve matching of function definitions #19638 " 2026 Apr 02 improve matching of function definitions #19849 +" 2026 Apr 19 improve detection of special variables #20016 " }}} " Version: 208 " Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH @@ -751,13 +752,15 @@ endif if exists("b:is_bash") syn region shDeref matchgroup=PreProc start="\${!" end="\*\=}" contains=@shDerefList,shDerefOffset syn match shDerefVar contained "{\@<=!\h\w*" nextgroup=@shDerefVarList + syn match shDerefSpecial contained "\({!\)\@<=[[:alnum:]*#@_]\+" nextgroup=@shDerefVarList,shDerefOp endif if (exists("b:is_kornshell") && !exists("b:is_ksh88")) syn match shDerefVar contained "{\@<=!\h\w*[[:alnum:]_.]*" nextgroup=@shDerefVarList + syn match shDerefSpecial contained "\({!\)\@<=[[:alnum:]*#@_]\+" nextgroup=@shDerefVarList,shDerefOp endif syn match shDerefSpecial contained "{\@<=[-*@?0]" nextgroup=shDerefOp,shDerefOffset,shDerefOpError -syn match shDerefSpecial contained "\({[#!]\)\@<=[[:alnum:]*@_]\+" nextgroup=@shDerefVarList,shDerefOp +syn match shDerefSpecial contained "\({[#]\)\@<=[[:alnum:]*@_]\+" nextgroup=@shDerefVarList,shDerefOp syn match shDerefVar contained "{\@<=\h\w*" nextgroup=@shDerefVarList syn match shDerefVar contained '\d' nextgroup=@shDerefVarList if exists("b:is_kornshell") || exists("b:is_posix") diff --git a/runtime/syntax/testdir/dumps/sh_03_01.dump b/runtime/syntax/testdir/dumps/sh_03_01.dump index 3ac5b4bc7a..81e2c87de5 100644 --- a/runtime/syntax/testdir/dumps/sh_03_01.dump +++ b/runtime/syntax/testdir/dumps/sh_03_01.dump @@ -17,4 +17,4 @@ |:+0#0000e05&| +0#0000000&|'+0#af5f00255&|$+0#e000002&|{|V|a|r|i|a|b|l|e|B|:|-|$|{|V|a|r|i|a|b|l|e|C|:|-|e|n|g|}@1|'+0#af5f00255&| +0#0000000&@39 @75 |#+0#0000e05&| |A|n|o|t|h|e|r| |t|e|s|t| +0#0000000&@60 -@57|1|8|,|0|-|1| @7|8|1|%| +@57|1|8|,|0|-|1| @7|6|5|%| diff --git a/runtime/syntax/testdir/dumps/sh_03_02.dump b/runtime/syntax/testdir/dumps/sh_03_02.dump index 6d03697c7b..f34582e496 100644 --- a/runtime/syntax/testdir/dumps/sh_03_02.dump +++ b/runtime/syntax/testdir/dumps/sh_03_02.dump @@ -1,7 +1,11 @@ |#+0#0000e05#ffffff0| |A|n|o|t|h|e|r| |t|e|s|t| +0#0000000&@60 |V+0#00e0e07&|a|r|i|a|b|l|e|=+0#0000000&|$+0#e000e06&|{|V|a|r|i|a|b|l|e|B|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|C|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|D|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|E|:+0#af5f00255&|=|e+0#0000000&|n|g|}+0#e000e06&@3| +0#0000000&@6 @7|:+0#0000e05&| +0#0000000&@7|$+0#e000e06&|{|V|a|r|i|a|b|l|e|B|:+0#af5f00255&|=|$+0#e000e06&|{|V|a|r|i|a|b|l|e|C|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|D|:+0#af5f00255&|-|$+0#e000e06&|{|V|a|r|i|a|b|l|e|E|:+0#af5f00255&|=|e+0#0000000&|n|g|}+0#e000e06&@3 -> +0#0000000&@74 +| +0#0000000&@74 +|#+0#0000e05&| |s|p|e|c|i|a|l| |v|a|r|i|a|b|l|e|s| +0#0000000&@55 +>e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|!|#|}|"+0#af5f00255&| +0#e000002&@11|#+0#0000e05&| |l|a|s|t| |p|o|s|i|t|i|o|n|a|l| |a|r|g|u|m|e|n|t| +0#0000000&@24 +|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|!|@|}|"+0#af5f00255&| +0#e000002&@11|#+0#0000e05&| |d|e|r|e|f| +0#0000000&@43 +|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|#|P|A|T|H|}|"+0#af5f00255&| +0#e000002&@8|#+0#0000e05&| |l|e|n|g|t|h| +0#0000000&@42 |~+0#4040ff13&| @73 |~| @73 |~| @73 @@ -13,8 +17,4 @@ |~| @73 |~| @73 |~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -| +0#0000000&@56|3@1|,|0|-|1| @7|B|o|t| +| +0#0000000&@56|3|5|,|1| @9|B|o|t| diff --git a/runtime/syntax/testdir/dumps/sh_bash_00.dump b/runtime/syntax/testdir/dumps/sh_bash_00.dump index 85966ebd75..15093831d7 100644 --- a/runtime/syntax/testdir/dumps/sh_bash_00.dump +++ b/runtime/syntax/testdir/dumps/sh_bash_00.dump @@ -14,7 +14,7 @@ |e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|s+0#e000002&|e|v|e|n|'+0#af5f00255&| +0#e000002&@3|;+0#e000e06&|}| +0#0000000&@48 |e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|e+0#e000002&|i|g|h|t|'+0#af5f00255&|;+0#e000e06&| @2|}| +0#0000000&@49 |t+0#af5f00255&|y|p|e|s|e|t| +0#e000e06&|n+0#00e0e07&|i|n|e|=+0#0000000&|$+0#e000e06&|{| |p+0#af5f00255&|w|d|;| +0#e000e06&|}| +0#0000000&@52 -|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|n+0#e000002&|i|n|e|'+0#af5f00255&| +0#e000002&|;+0#e000e06&| | +0#0000000&@52 +|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|n+0#e000002&|i|n|e|'+0#af5f00255&| +0#e000002&|;+0#e000e06&| +0#0000000&@53 | +0#e000e06&|}| +0#0000000&@72 @75 |i|s|_|b|a|s|h|:| |1|,| @45|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/sh_bash_01.dump b/runtime/syntax/testdir/dumps/sh_bash_01.dump index 18961a7983..1a65e23fc2 100644 --- a/runtime/syntax/testdir/dumps/sh_bash_01.dump +++ b/runtime/syntax/testdir/dumps/sh_bash_01.dump @@ -1,7 +1,7 @@ |e+0#af5f00255#ffffff0|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|s+0#e000002&|e|v|e|n|'+0#af5f00255&| +0#e000002&@3|;+0#e000e06&|}| +0#0000000&@48 |e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|e+0#e000002&|i|g|h|t|'+0#af5f00255&|;+0#e000e06&| @2|}| +0#0000000&@49 |t+0#af5f00255&|y|p|e|s|e|t| +0#e000e06&|n+0#00e0e07&|i|n|e|=+0#0000000&|$+0#e000e06&|{| |p+0#af5f00255&|w|d|;| +0#e000e06&|}| +0#0000000&@52 -|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|n+0#e000002&|i|n|e|'+0#af5f00255&| +0#e000002&|;+0#e000e06&| | +0#0000000&@52 +|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{| |e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|n+0#e000002&|i|n|e|'+0#af5f00255&| +0#e000002&|;+0#e000e06&| +0#0000000&@53 | +0#e000e06&|}| +0#0000000&@72 > @74 |v+0#e000e06&|a|l|s|u|b|f|u|n|c|(|)| |{| +0#0000000&@60 @@ -12,9 +12,9 @@ |p+0#af5f00255&|r|i|n|t|f| +0#e000e06&|'+0#af5f00255&|%+0#e000002&|s|\|n|'+0#af5f00255&| +0#e000e06&|"+0#af5f00255&|$+0#e000e06&|{|||v|a|l|s|u|b|f|u|n|c| |t|w|e|l|v|e| @4|;|}|"+0#af5f00255&| +0#0000000&@31 |u+0#00e0e07&|n|l|u|c|k|y|=+0#0000000&|$+0#e000e06&|{||+0#af5f00255&|v+0#e000e06&|a|l|s|u|b|f|u|n|c| |t|h|i|r|t|e@1|n| +0#0000000&@44 |}+0#e000e06&| +0#0000000&@73 -|t+0#af5f00255&|y|p|e|s|e|t| +0#e000e06&|n+0#00e0e07&|o|t|a|f|l|o|a|t|=+0#0000000&|$+0#e000e06&|{||+0#af5f00255&|v+0#e000e06&|a|l|s|u|b|f|u|n|c| |n|o|t|a|n|u|m|b|e|r| @5|;+0#af5f00255&| +0#e000e06&| +0#0000000&@24 +|t+0#af5f00255&|y|p|e|s|e|t| +0#e000e06&|n+0#00e0e07&|o|t|a|f|l|o|a|t|=+0#0000000&|$+0#e000e06&|{||+0#af5f00255&|v+0#e000e06&|a|l|s|u|b|f|u|n|c| |n|o|t|a|n|u|m|b|e|r| @5|;+0#af5f00255&| +0#0000000&@25 | +0#e000e06&|}| +0#0000000&@72 |e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|u|n|l|u|c|k|y| +0#e000002&|$+0#e000e06&|n|o|t|a|n|u|m|b|e|r| +0#0000000&@49 |$+0#e000e06&|{||+0#af5f00255&|e|c|h|o| +0#e000002&|f|o|u|r|t|e@1|n|;+0#af5f00255&|}+0#e000e06&| +0#0000000&@56 |$+0#e000e06&|{||+0#af5f00255&|e|c|h|o| +0#e000002&|f|i|f|t|e@1|n| +0#0000000&@59 -@57|1|9|,|0|-|1| @7|9|2|%| +@57|1|9|,|0|-|1| @7|7|2|%| diff --git a/runtime/syntax/testdir/dumps/sh_bash_02.dump b/runtime/syntax/testdir/dumps/sh_bash_02.dump index 677a1ab127..f7ea1a3001 100644 --- a/runtime/syntax/testdir/dumps/sh_bash_02.dump +++ b/runtime/syntax/testdir/dumps/sh_bash_02.dump @@ -1,5 +1,9 @@ |$+0#e000e06#ffffff0|{||+0#af5f00255&|e|c|h|o| +0#e000002&|f|i|f|t|e@1|n| +0#0000000&@59 ->}+0#e000e06&| +0#0000000&@73 +|}+0#e000e06&| +0#0000000&@73 +@75 +|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|!|#|}|"+0#af5f00255&| +0#e000002&@11|#+0#0000e05&| |l|a|s|t| |p|o|s|i|t|i|o|n|a|l| |a|r|g|u|m|e|n|t| +0#0000000&@24 +|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|!|@|}|"+0#af5f00255&| +0#e000002&@11|#+0#0000e05&| |d|e|r|e|f| +0#0000000&@43 +>e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|{|#|P|A|T|H|}|"+0#af5f00255&| +0#e000002&@8|#+0#0000e05&| |l|e|n|g|t|h| +0#0000000&@42 |~+0#4040ff13&| @73 |~| @73 |~| @73 @@ -13,8 +17,4 @@ |~| @73 |~| @73 |~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -| +0#0000000&@56|3@1|,|1| @9|B|o|t| +| +0#0000000&@56|3|7|,|1| @9|B|o|t| diff --git a/runtime/syntax/testdir/input/sh_03.sh b/runtime/syntax/testdir/input/sh_03.sh index 8dd6dab9c3..d2ac8f0a4a 100644 --- a/runtime/syntax/testdir/input/sh_03.sh +++ b/runtime/syntax/testdir/input/sh_03.sh @@ -31,3 +31,7 @@ Variable="${VariableB:-${VariableC:-eng}}" # :- is ksh and bash Variable=${VariableB:-${VariableC:-${VariableD:-${VariableE:=eng}}}} : ${VariableB:=${VariableC:-${VariableD:-${VariableE:=eng}}}} +# special variables +echo "${!#}" # last positional argument +echo "${!@}" # deref +echo "${#PATH}" # length diff --git a/runtime/syntax/testdir/input/sh_bash.bash b/runtime/syntax/testdir/input/sh_bash.bash index 35b536c4e3..798ce44ebe 100644 --- a/runtime/syntax/testdir/input/sh_bash.bash +++ b/runtime/syntax/testdir/input/sh_bash.bash @@ -14,7 +14,7 @@ echo ${ echo 'six' echo ${ echo 'seven' ;} echo ${ echo 'eight'; } typeset nine=${ pwd; } -echo ${ echo 'nine' ; +echo ${ echo 'nine' ; } valsubfunc() { @@ -25,9 +25,13 @@ echo "${|valsubfunc eleven; }" printf '%s\n' "${|valsubfunc twelve ;}" unlucky=${|valsubfunc thirteen } -typeset notafloat=${|valsubfunc notanumber ; +typeset notafloat=${|valsubfunc notanumber ; } echo $unlucky $notanumber ${|echo fourteen;} ${|echo fifteen } + +echo "${!#}" # last positional argument +echo "${!@}" # deref +echo "${#PATH}" # length From 2ea4a7c3b73468683249fe194707c8d7e67ecdb3 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 21 Apr 2026 20:20:30 +0000 Subject: [PATCH 096/545] patch 9.2.0386: No scroll/scrollbar support in the tabpanel Problem: No scroll/scrollbar support in the tabpanel Solution: Add support for it (Yasuhiro Matsumoto) closes: #19979 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 19 ++- runtime/doc/tabpage.txt | 33 ++++- runtime/doc/tags | 1 + runtime/doc/version9.txt | 3 + src/buffer.c | 9 +- src/mouse.c | 55 +++++++- src/proto/tabpanel.pro | 4 + src/tabpanel.c | 229 +++++++++++++++++++++++++++++++--- src/testdir/test_tabpanel.vim | 62 ++++++++- src/version.c | 2 + 10 files changed, 396 insertions(+), 21 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index c02d40eaa8..d2611db095 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 20 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -9065,6 +9065,23 @@ A jump table for the options with a short description can be found at |Q_op|. tab panel will not be displayed. (default 20) + scroll Enable mouse wheel scrolling over the tabpanel + area when the tab list exceeds the visible + screen height. The scroll step is controlled + by 'mousescroll'. When disabled (the default), + the tabpanel shows the page containing the + current tab, with no way to view tabs outside + that page. + + scrollbar Reserve a one-column scrollbar in the tabpanel + showing the current scroll position. The + scrollbar uses the |hl-PmenuSbar| and + |hl-PmenuThumb| highlight groups for the track + and thumb respectively. Clicking on the + scrollbar column jumps the thumb to that + position; the thumb can also be dragged. + Implies "scroll". + vert Use a vertical separator for tabpanel. The vertical separator character is taken from "tpl_vert" in 'fillchars'. diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index 3e307b648a..3130c185f8 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -1,4 +1,4 @@ -*tabpage.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -482,6 +482,37 @@ The vertical separator character is taken from "tpl_vert" in 'fillchars'. You can customize the appearance of the tab page labels using the highlight groups: |hl-TabPanel| |hl-TabPanelSel| |hl-TabPanelFill| +SCROLLING IN THE TABPANEL: *tabpanel-scroll* + +When the total height of the tab list exceeds the visible screen height, the +tabpanel by default displays the "page" that contains the current tab and +offers no way to view tabs outside that page. + +To make the tabpanel scrollable, add "scroll" to 'tabpanelopt': > + :set tabpanelopt+=scroll + +With "scroll" enabled, mouse wheel events over the tabpanel area scroll the +tab list up or down. The scroll step follows the 'mousescroll' setting. +Wheel events inside the tabpanel area are consumed by the tabpanel and do not +trigger || or || mappings. + +To additionally show a vertical scrollbar indicating the current scroll +position, use "scrollbar": > + :set tabpanelopt+=scrollbar + +The "scrollbar" value implies "scroll". A one-column scrollbar is reserved at +the edge of the tabpanel; clicking on the scrollbar column moves the thumb to +the click position, and the thumb can be dragged to scroll continuously. + +When "vert" is combined with "scrollbar", the scrollbar is drawn next to the +vertical separator, on the panel side. + +The scrollbar uses the |hl-PmenuSbar| highlight group for the track and +|hl-PmenuThumb| for the thumb. + +The scroll offset is remembered across redraws but is reset when "scroll" or +"scrollbar" is toggled off and back on. + ============================================================================== 6. Setting 'guitablabel' *setting-guitablabel* diff --git a/runtime/doc/tags b/runtime/doc/tags index 2b0e6cab39..c4cc01bdc9 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -10889,6 +10889,7 @@ tabpagebuflist() builtin.txt /*tabpagebuflist()* tabpagenr() builtin.txt /*tabpagenr()* tabpagewinnr() builtin.txt /*tabpagewinnr()* tabpanel tabpage.txt /*tabpanel* +tabpanel-scroll tabpage.txt /*tabpanel-scroll* tag tagsrch.txt /*tag* tag-! tagsrch.txt /*tag-!* tag-binary-search tagsrch.txt /*tag-binary-search* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 90d563c902..90cd1a3efb 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52616,6 +52616,9 @@ Other ~ - Allow mouse clickable regions in the 'statusline', 'tabline' and the 'tabpanel' using the |stl-%[FuncName]| atom. - Enable reflow support in the |:terminal|. +- Added "scroll" and "scrollbar" sub-options to 'tabpanelopt' so the + tabpanel can scroll when the tab list exceeds the visible screen + height. Platform specific ~ ----------------- diff --git a/src/buffer.c b/src/buffer.c index 7ea9e93511..44e504c537 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -4946,6 +4946,9 @@ build_stl_str_hl_local( maxwid = 50; } } + // Keep the uncapped value for %N[FuncName] click-region IDs; the 50 + // cap below applies only when minwid is used as a padding width. + int raw_minwid = minwid * l; minwid = (minwid > 50 ? 50 : minwid) * l; if (*s == '(') { @@ -5306,7 +5309,11 @@ build_stl_str_hl_local( { stl_items[curitem].stl_type = ClickFunc; stl_items[curitem].stl_start = p; - stl_items[curitem].stl_minwid = minwid; + // The stl_minwid field is overloaded: it may be the + // "min" part of %. used for padding, or an + // identifier passed to the %N[FuncName] callback. Store + // the uncapped value so IDs above 50 are preserved. + stl_items[curitem].stl_minwid = raw_minwid; stl_items[curitem].stl_clickfunc = vim_strnsave(s, rb - s); s = rb + 1; diff --git a/src/mouse.c b/src/mouse.c index f6ab770040..93fdd31824 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -240,6 +240,9 @@ do_mouse( int in_status_line; // mouse in status line static int in_tab_line = FALSE; // mouse clicked in tab line static int in_tabpanel = FALSE; // mouse clicked in tabpanel +#ifdef FEAT_TABPANEL + static bool in_tabpanel_scrollbar = false; // dragging tabpanel scrollbar +#endif int in_sep_line; // mouse in vertical separator line int c1, c2; #if defined(FEAT_FOLDING) @@ -346,6 +349,9 @@ do_mouse( got_click = TRUE; in_tab_line = FALSE; in_tabpanel = FALSE; +#ifdef FEAT_TABPANEL + in_tabpanel_scrollbar = false; +#endif } else { @@ -354,15 +360,31 @@ do_mouse( if (!is_drag) // release, reset got_click { got_click = FALSE; - if (in_tab_line || in_tabpanel) + if (in_tab_line || in_tabpanel +#ifdef FEAT_TABPANEL + || in_tabpanel_scrollbar +#endif + ) { in_tab_line = FALSE; in_tabpanel = FALSE; +#ifdef FEAT_TABPANEL + in_tabpanel_scrollbar = false; +#endif return FALSE; } } } +#ifdef FEAT_TABPANEL + // Continue a scrollbar drag before any tab-selection handling. + if (is_drag && in_tabpanel_scrollbar) + { + tabpanel_drag_scrollbar(mouse_row); + return FALSE; + } +#endif + // CTRL right mouse button does CTRL-T if (is_click && (mod_mask & MOD_MASK_CTRL) && which_button == MOUSE_RIGHT) { @@ -494,6 +516,15 @@ do_mouse( if (mouse_col < firstwin->w_wincol || mouse_col >= firstwin->w_wincol + topframe->fr_width) { + // A click on the scrollbar column starts a drag interaction and + // preempts tab-selection. + if (is_click && !is_drag && mouse_on_tabpanel_scrollbar()) + { + in_tabpanel_scrollbar = TRUE; + tabpanel_drag_scrollbar(mouse_row); + return FALSE; + } + // Dispatch 'tabpanel' %[FuncName] click regions before falling through // to tab-page selection. On drag events fall through to the normal // tab-drag handling. @@ -1276,6 +1307,17 @@ ins_mousescroll(int dir) cap.oap = &oa; cap.arg = dir; +#ifdef FEAT_TABPANEL + if (mouse_row >= 0 && mouse_col >= 0 + && (dir == MSCR_UP || dir == MSCR_DOWN) + && mouse_on_tabpanel()) + { + (void)tabpanel_scroll(dir == MSCR_UP ? 1 : -1, + mouse_vert_step > 0 ? mouse_vert_step : 3); + return; + } +#endif + switch (dir) { case MSCR_UP: @@ -2409,6 +2451,17 @@ nv_mousescroll(cmdarg_T *cap) { win_T *old_curwin = curwin; +#ifdef FEAT_TABPANEL + if (mouse_row >= 0 && mouse_col >= 0 + && (cap->arg == MSCR_UP || cap->arg == MSCR_DOWN) + && mouse_on_tabpanel()) + { + (void)tabpanel_scroll(cap->arg == MSCR_UP ? 1 : -1, + mouse_vert_step > 0 ? mouse_vert_step : 3); + return; + } +#endif + if (mouse_row >= 0 && mouse_col >= 0) { // Find the window at the mouse pointer coordinates. diff --git a/src/proto/tabpanel.pro b/src/proto/tabpanel.pro index d62d13f3a1..72726d89ea 100644 --- a/src/proto/tabpanel.pro +++ b/src/proto/tabpanel.pro @@ -4,4 +4,8 @@ int tabpanel_width(void); int tabpanel_leftcol(void); void draw_tabpanel(void); int get_tabpagenr_on_tabpanel(void); +bool mouse_on_tabpanel(void); +bool mouse_on_tabpanel_scrollbar(void); +bool tabpanel_drag_scrollbar(int screen_row); +bool tabpanel_scroll(int dir, int count); /* vim: set ft=c : */ diff --git a/src/tabpanel.c b/src/tabpanel.c index 4138f9760a..07be48e44c 100644 --- a/src/tabpanel.c +++ b/src/tabpanel.c @@ -20,6 +20,7 @@ static void do_by_tplmode(int tplmode, int col_start, int col_end, static void tabpanel_free_click_regions(void); static void tabpanel_append_click_regions(stl_clickrec_T *clicktab, char_u *buf, int row, int col_start, int col_end, int tabnr); +static void draw_tabpanel_scrollbar(int screen_col); // set pcurtab_row. don't redraw tabpanel. #define TPLMODE_GET_CURTAB_ROW 0 @@ -31,6 +32,7 @@ static void tabpanel_append_click_regions(stl_clickrec_T *clicktab, #define TPL_FILLCHAR ' ' #define VERT_LEN 1 +#define SCROLL_LEN 1 // tpl_align's values #define ALIGN_LEFT 0 @@ -40,7 +42,12 @@ static char_u *opt_name = (char_u *)"tabpanel"; static int opt_scope = OPT_LOCAL; static int tpl_align = ALIGN_LEFT; static int tpl_columns = 20; -static int tpl_is_vert = FALSE; +static bool tpl_is_vert = false; +static bool tpl_scroll = false; +static bool tpl_scrollbar = false; +static int tpl_scroll_offset = 0; +static int tpl_total_rows = 0; +static int tpl_scrollbar_col = -1; // screen column of scrollbar, -1 if none typedef struct { win_T *wp; @@ -61,7 +68,9 @@ tabpanelopt_changed(void) char_u *p; int new_align = ALIGN_LEFT; long new_columns = 20; - int new_is_vert = FALSE; + bool new_is_vert = false; + bool new_scroll = false; + bool new_scrollbar = false; p = p_tplo; while (*p != NUL) @@ -92,7 +101,18 @@ tabpanelopt_changed(void) else if (STRNCMP(p, "vert", 4) == 0) { p += 4; - new_is_vert = TRUE; + new_is_vert = true; + } + else if (STRNCMP(p, "scrollbar", 9) == 0) + { + p += 9; + new_scrollbar = true; + new_scroll = true; + } + else if (STRNCMP(p, "scroll", 6) == 0) + { + p += 6; + new_scroll = true; } if (*p != ',' && *p != NUL) @@ -104,6 +124,10 @@ tabpanelopt_changed(void) tpl_align = new_align; tpl_columns = new_columns; tpl_is_vert = new_is_vert; + if (tpl_scroll != new_scroll) + tpl_scroll_offset = 0; + tpl_scroll = new_scroll; + tpl_scrollbar = new_scrollbar; shell_new_columns(); return OK; @@ -266,39 +290,65 @@ draw_tabpanel(void) // Reset got_int to avoid build_stl_str_hl() isn't evaluated. got_int = FALSE; + int sb_len = tpl_scrollbar ? SCROLL_LEN : 0; + int sb_screen_col = -1; + if (tpl_is_vert) { if (is_right) { // draw main contents in tabpanel - do_by_tplmode(TPLMODE_GET_CURTAB_ROW, VERT_LEN, + do_by_tplmode(TPLMODE_GET_CURTAB_ROW, VERT_LEN + sb_len, maxwidth - VERT_LEN, &curtab_row, NULL); - do_by_tplmode(TPLMODE_REDRAW, VERT_LEN, maxwidth, &curtab_row, - NULL); + do_by_tplmode(TPLMODE_REDRAW, VERT_LEN + sb_len, maxwidth, + &curtab_row, NULL); // draw vert separator in tabpanel for (vsrow = 0; vsrow < Rows; vsrow++) screen_putchar(curwin->w_fill_chars.tpl_vert, vsrow, topframe->fr_width, vs_attr); + if (tpl_scrollbar) + sb_screen_col = topframe->fr_width + VERT_LEN; } else { // draw main contents in tabpanel - do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth - VERT_LEN, - &curtab_row, NULL); - do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - VERT_LEN, + do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, + maxwidth - VERT_LEN - sb_len, &curtab_row, NULL); + do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - VERT_LEN - sb_len, &curtab_row, NULL); // draw vert separator in tabpanel for (vsrow = 0; vsrow < Rows; vsrow++) screen_putchar(curwin->w_fill_chars.tpl_vert, vsrow, maxwidth - VERT_LEN, vs_attr); + if (tpl_scrollbar) + sb_screen_col = maxwidth - VERT_LEN - SCROLL_LEN; } } else { - do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth, &curtab_row, NULL); - do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth, &curtab_row, NULL); + if (is_right) + { + do_by_tplmode(TPLMODE_GET_CURTAB_ROW, sb_len, maxwidth, + &curtab_row, NULL); + do_by_tplmode(TPLMODE_REDRAW, sb_len, maxwidth, &curtab_row, NULL); + if (tpl_scrollbar) + sb_screen_col = topframe->fr_width; + } + else + { + do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth - sb_len, + &curtab_row, NULL); + do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - sb_len, + &curtab_row, NULL); + if (tpl_scrollbar) + sb_screen_col = maxwidth - SCROLL_LEN; + } } + tpl_scrollbar_col = sb_screen_col; + if (sb_screen_col >= 0) + draw_tabpanel_scrollbar(sb_screen_col); + got_int |= saved_got_int; // A user function may reset KeyTyped, restore it. @@ -556,8 +606,13 @@ do_by_tplmode( args.col_end = col_end; if (tplmode != TPLMODE_GET_CURTAB_ROW && args.maxrow > 0) - while (args.offsetrow + args.maxrow <= *pcurtab_row) - args.offsetrow += args.maxrow; + { + if (tpl_scroll) + args.offsetrow = tpl_scroll_offset; + else + while (args.offsetrow + args.maxrow <= *pcurtab_row) + args.offsetrow += args.maxrow; + } tp = first_tabpage; @@ -579,8 +634,13 @@ do_by_tplmode( if (tplmode == TPLMODE_GET_CURTAB_ROW) { *pcurtab_row = row; - do_unlet((char_u *)"g:actual_curtabpage", TRUE); - break; + // When scroll mode is active keep iterating so tpl_total_rows + // receives the true content height; otherwise bail out early. + if (!tpl_scroll) + { + do_unlet((char_u *)"g:actual_curtabpage", TRUE); + break; + } } } else @@ -608,7 +668,8 @@ do_by_tplmode( stl_hlrec_T *tabtab; stl_clickrec_T *clicktab = NULL; - if (args.maxrow <= row - args.offsetrow) + if (tplmode != TPLMODE_GET_CURTAB_ROW + && args.maxrow <= row - args.offsetrow) break; buf[0] = NUL; @@ -677,6 +738,142 @@ do_by_tplmode( // fill the area of TabPanelFill. screen_fill_tailing_area(tplmode, MAX(row - args.offsetrow, 0), args.maxrow, args.col_start, args.col_end, attr_tplf); + + // Capture the true content height during the GET_CURTAB_ROW pass, which + // ignores maxrow and therefore walks every tab. REDRAW stops at the + // visible edge so its "row" is clamped and unusable here. + if (tplmode == TPLMODE_GET_CURTAB_ROW && tpl_scroll) + tpl_total_rows = row; +} + +/* + * Draw the tabpanel scrollbar (track + thumb) at screen column 'screen_col'. + * The scrollbar spans the full screen height. The thumb position and size + * are derived from tpl_scroll_offset, tpl_total_rows and Rows. + */ + static void +draw_tabpanel_scrollbar(int screen_col) +{ + int attr_sb = HL_ATTR(HLF_PSB); + int attr_thumb = HL_ATTR(HLF_PST); + int thumb_top = 0; + int thumb_height = 0; + + if (tpl_total_rows > Rows && Rows > 0) + { + thumb_height = Rows * Rows / tpl_total_rows; + if (thumb_height < 1) + thumb_height = 1; + thumb_top = Rows * tpl_scroll_offset / tpl_total_rows; + if (thumb_top + thumb_height > Rows) + thumb_top = Rows - thumb_height; + if (thumb_top < 0) + thumb_top = 0; + } + + for (int r = 0; r < Rows; r++) + { + bool on_thumb = thumb_height > 0 + && r >= thumb_top && r < thumb_top + thumb_height; + screen_putchar(TPL_FILLCHAR, r, screen_col, + on_thumb ? attr_thumb : attr_sb); + } +} + +/* + * Return true if the mouse is currently positioned over the tabpanel area. + */ + bool +mouse_on_tabpanel(void) +{ + if (tabpanel_width() == 0) + return false; + return mouse_col < firstwin->w_wincol + || mouse_col >= firstwin->w_wincol + topframe->fr_width; +} + +/* + * Return true if the mouse is currently on the scrollbar column. + * The scrollbar column is tracked by draw_tabpanel() and is -1 when the + * scrollbar is not enabled or not yet drawn. + */ + bool +mouse_on_tabpanel_scrollbar(void) +{ + return tpl_scrollbar && tpl_scrollbar_col >= 0 + && mouse_col == tpl_scrollbar_col; +} + +/* + * Move the scrollbar thumb so it is vertically centred on screen row + * 'screen_row', updating tpl_scroll_offset accordingly. Used for both + * initial clicks and subsequent drag events. + * Returns true if the event was consumed (offset changed or not). + */ + bool +tabpanel_drag_scrollbar(int screen_row) +{ + int thumb_height; + int max_offset; + int track_range; + int thumb_top; + int new_offset; + + if (!tpl_scrollbar || Rows <= 0 || tpl_total_rows <= Rows) + return false; + + thumb_height = Rows * Rows / tpl_total_rows; + if (thumb_height < 1) + thumb_height = 1; + track_range = Rows - thumb_height; + if (track_range <= 0) + return true; + + max_offset = tpl_total_rows - Rows; + thumb_top = screen_row - thumb_height / 2; + if (thumb_top < 0) + thumb_top = 0; + if (thumb_top > track_range) + thumb_top = track_range; + + new_offset = thumb_top * max_offset / track_range; + if (new_offset != tpl_scroll_offset) + { + tpl_scroll_offset = new_offset; + redraw_tabpanel = TRUE; + } + return true; +} + +/* + * Scroll the tabpanel by 'count' rows in direction 'dir' (1 = down, -1 = up). + * Returns true if the offset changed and a redraw was scheduled. + * Has no effect unless 'tabpanelopt' contains "scroll". + */ + bool +tabpanel_scroll(int dir, int count) +{ + int max_offset; + int new_offset; + + if (!tpl_scroll || tabpanel_width() == 0) + return false; + + max_offset = tpl_total_rows - Rows; + if (max_offset < 0) + max_offset = 0; + + new_offset = tpl_scroll_offset + (dir > 0 ? count : -count); + if (new_offset < 0) + new_offset = 0; + if (new_offset > max_offset) + new_offset = max_offset; + if (new_offset == tpl_scroll_offset) + return false; + + tpl_scroll_offset = new_offset; + redraw_tabpanel = TRUE; + return true; } #endif // FEAT_TABPANEL diff --git a/src/testdir/test_tabpanel.vim b/src/testdir/test_tabpanel.vim index 7803a0eb4d..427360aeda 100644 --- a/src/testdir/test_tabpanel.vim +++ b/src/testdir/test_tabpanel.vim @@ -962,8 +962,68 @@ func Test_tabpanel_large_columns() call assert_fails(':set tabpanelopt=columns:-1', 'E474:') endfunc +func Test_tabpanel_scrollopt_accepted() + " 'scroll' / 'scrollbar' must be accepted in 'tabpanelopt'. + set tabpanelopt=scroll + call assert_equal('scroll', &tabpanelopt) + set tabpanelopt=scrollbar + call assert_equal('scrollbar', &tabpanelopt) + + " Combination with other values. + set tabpanelopt=align:right,scroll + call assert_equal('align:right,scroll', &tabpanelopt) + set tabpanelopt=columns:15,vert,scrollbar + call assert_equal('columns:15,vert,scrollbar', &tabpanelopt) + set tabpanelopt=align:right,columns:12,vert,scrollbar + call assert_equal('align:right,columns:12,vert,scrollbar', &tabpanelopt) + + " Unknown values must still fail. + call assert_fails(':set tabpanelopt=scrol', 'E474:') + call assert_fails(':set tabpanelopt=scrollbarx', 'E474:') + + call s:reset() +endfunc + +func Test_tabpanel_scroll_many_tabs() + let save_lines = &lines + let save_showtabpanel = &showtabpanel + let save_tabpanelopt = &tabpanelopt + + " Make the screen short so the tab list exceeds the visible height. + set lines=8 + set showtabpanel=2 + set tabpanelopt=scroll + for i in range(20) + tabnew + endfor + + " Should not crash with many tabs and scroll enabled. + redraw! + + " Switching to scrollbar resets the offset but must also not crash. + set tabpanelopt=scrollbar + redraw! + + " Disabling scroll returns to normal behavior. + set tabpanelopt= + redraw! + + " Right alignment with scrollbar. + set tabpanelopt=align:right,scrollbar + redraw! + + " Vertical separator with scrollbar. + set tabpanelopt=columns:10,vert,scrollbar + redraw! + + " Cleanup. + %bwipeout! + let &tabpanelopt = save_tabpanelopt + let &showtabpanel = save_showtabpanel + let &lines = save_lines +endfunc + func Test_tabpanel_variable_height() - CheckFeature tabpanel let save_lines = &lines let save_showtabpanel = &showtabpanel diff --git a/src/version.c b/src/version.c index aa38e1a35c..36a8da0782 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 386, /**/ 385, /**/ From cee8fd73eb54a3196ac3cc2292a0e6910aeaff75 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Tue, 21 Apr 2026 20:46:12 +0000 Subject: [PATCH 097/545] patch 9.2.0387: DECRQM request may leave stray chars in terminal Problem: Sending DECRQM from handle_version_response() caused DECRPM responses to arrive during user input processing, leaving bytes in typebuf when clear_showcmd() ran. This made visual-mode showcmd (e.g. "7" line count after V) intermittently disappear, failing many screendump tests on CI. Solution: Move DECRQM request out of handle_version_response() and send it at startup via may_req_decrqm(), following the existing may_req_termresponse() and may_req_bg_color() pattern. Add TPR_DECRQM property set per terminal from the DA2 reply, and route DECRQM sends through a may_req_decrqm() helper using the termrequest_T pattern, skipping terminals known to mishandle it (Foxe Chen, Hirohito Higashi). fixes: #19852 closes: #19938 Co-Authored-By: Claude Opus 4.6 (1M context) Co-Authored-By: Hirohito Higashi Co-Authored-By: Foxe Chen Signed-off-by: Hirohito Higashi Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 6 ++- src/main.c | 7 +-- src/proto/term.pro | 2 +- src/term.c | 80 ++++++++++++++++++++++++++-------- src/testdir/test_termcodes.vim | 18 +++++++- src/version.c | 2 + 6 files changed, 90 insertions(+), 25 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 0fe02ac944..9ea0ae82cc 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 20 +*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -11990,6 +11990,7 @@ terminalprops() *terminalprops()* underline_rgb whether |t_8u| works ** mouse mouse type supported kitty whether Kitty terminal was detected + decrqm whether sending DECRQM sequences work ** value 'u' for unknown, 'y' for yes, 'n' for no @@ -12009,6 +12010,9 @@ terminalprops() *terminalprops()* For "mouse" the value 'u' is unknown + If "decrqm" is 'y', then Vim will query support for the + 'termsync' and 'termresize' ("inband") options. + Also see: - 'ambiwidth' - detected by using |t_u7|. - |v:termstyleresp| and |v:termblinkresp| for the response to diff --git a/src/main.c b/src/main.c index bfabcbbeda..e0d0e7e37f 100644 --- a/src/main.c +++ b/src/main.c @@ -892,10 +892,11 @@ vim_main2(void) may_req_termresponse(); may_req_bg_color(); + + // Same reason as for termresponse: don't want the terminal sending out + // the DECRPM response after Vim has exited. + may_req_decrqm(); # endif - // Same reason for termresponse, don't want the terminal sending out the - // DECRPM response after Vim has exited. - send_decrqm_modes(); // start in insert mode if (p_im) diff --git a/src/proto/term.pro b/src/proto/term.pro index 82c794f4f9..5ec909f46c 100644 --- a/src/proto/term.pro +++ b/src/proto/term.pro @@ -60,6 +60,7 @@ void stoptermcap(void); void may_req_termresponse(void); void check_terminal_behavior(void); void may_req_bg_color(void); +void may_req_decrqm(void); int swapping_screen(void); void scroll_start(void); void cursor_on_force(void); @@ -96,7 +97,6 @@ void swap_tcap(void); void ansi_color2rgb(int nr, char_u *r, char_u *g, char_u *b, char_u *ansi_idx); void cterm_color2rgb(int nr, char_u *r, char_u *g, char_u *b, char_u *ansi_idx); int term_replace_keycodes(char_u *ta_buf, int ta_len, int len_arg); -void send_decrqm_modes(void); void term_disable_dec(void); void term_set_win_resize(bool state); void term_set_sync_output(int flags); diff --git a/src/term.c b/src/term.c index 882df66551..876e15d950 100644 --- a/src/term.c +++ b/src/term.c @@ -154,6 +154,9 @@ static termrequest_T rcs_status = TERMREQUEST_INIT; // Request window's position report: static termrequest_T winpos_status = TERMREQUEST_INIT; +// Request DECRQM (DEC mode) report: +static termrequest_T decrqm_status = TERMREQUEST_INIT; + static termrequest_T *all_termrequests[] = { &crv_status, &u7_status, @@ -165,6 +168,7 @@ static termrequest_T *all_termrequests[] = { &rbm_status, &rcs_status, &winpos_status, + &decrqm_status, NULL }; @@ -1539,8 +1543,10 @@ typedef struct { #define TPR_MOUSE 3 // term response indicates kitty #define TPR_KITTY 4 +// can send DECRQM requests to terminal +#define TPR_DECRQM 5 // table size -#define TPR_COUNT 5 +#define TPR_COUNT 6 static termprop_T term_props[TPR_COUNT]; @@ -1564,6 +1570,8 @@ init_term_props(int all) term_props[TPR_MOUSE].tpr_set_by_termresponse = TRUE; term_props[TPR_KITTY].tpr_name = "kitty"; term_props[TPR_KITTY].tpr_set_by_termresponse = FALSE; + term_props[TPR_DECRQM].tpr_name = "decrqm"; + term_props[TPR_DECRQM].tpr_set_by_termresponse = TRUE; for (i = 0; i < TPR_COUNT; ++i) if (all || term_props[i].tpr_set_by_termresponse) @@ -4304,6 +4312,36 @@ may_req_bg_color(void) } } +/* + * Query the settings for the DEC modes we support via DECRQM. + * Only sent once, and only when the terminal is known not to dislike it + * (i.e. TPR_DECRQM is TPR_YES, or still TPR_UNKNOWN when the version response + * has not yet been received). + * The DECRPM responses are caught in handle_csi(). + */ + void +may_req_decrqm(void) +{ + if (decrqm_status.tr_progress == STATUS_GET + && term_props[TPR_DECRQM].tpr_status != TPR_NO + && can_get_termresponse() + && starting == 0) + { + MAY_WANT_TO_LOG_THIS; + LOG_TR1("Sending DECRQM requests"); + for (int i = 0; i < (int)ARRAY_LENGTH(dec_modes); i++) + { + vim_snprintf((char *)IObuff, IOSIZE, "\033[?%d$p", dec_modes[i]); + out_str(IObuff); + } + termrequest_sent(&decrqm_status); + // check for the characters now, otherwise they might be eaten by + // get_keystroke() + out_flush(); + (void)vpeekc_nomap(); + } +} + #endif /* @@ -5172,21 +5210,27 @@ handle_version_response(int first, int *arg, int argc, char_u *tp) may_adjust_color_count(256); // Libvterm can handle SGR mouse reporting. term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR; + term_props[TPR_DECRQM].tpr_status = TPR_YES; } if (version == 95) { // Mac Terminal.app sends 1;95;0 + // + // Terminal.app doesn't seem to handle DECRQM sequences + // properly, see issue #19852. if (arg[0] == 1 && arg[2] == 0) { term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES; term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR; + term_props[TPR_DECRQM].tpr_status = TPR_NO; } // iTerm2 sends 0;95;0 else if (arg[0] == 0 && arg[2] == 0) { // iTerm2 can do SGR mouse reporting term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR; + term_props[TPR_DECRQM].tpr_status = TPR_YES; } // old iTerm2 sends 0;95; else if (arg[0] == 0 && arg[2] == -1) @@ -5212,6 +5256,7 @@ handle_version_response(int first, int *arg, int argc, char_u *tp) term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR; else if (version >= 95) term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_XTERM2; + term_props[TPR_DECRQM].tpr_status = TPR_YES; } // Detect terminals that set $TERM to something like @@ -5224,11 +5269,15 @@ handle_version_response(int first, int *arg, int argc, char_u *tp) // Assuming any version number over 2500 is not an // xterm (without the limit for rxvt and screen). if (arg[1] >= 2500) + { term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES; + term_props[TPR_DECRQM].tpr_status = TPR_YES; + } else if (version == 136 && arg[2] == 0) { term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES; + term_props[TPR_DECRQM].tpr_status = TPR_YES; // PuTTY sends 0;136;0 if (arg[0] == 0) @@ -5252,6 +5301,7 @@ handle_version_response(int first, int *arg, int argc, char_u *tp) // Kitty can handle SGR mouse reporting. term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR; + term_props[TPR_DECRQM].tpr_status = TPR_YES; } // GNU screen sends 83;30600;0, 83;40500;0, etc. @@ -5262,6 +5312,9 @@ handle_version_response(int first, int *arg, int argc, char_u *tp) { term_props[TPR_CURSOR_STYLE].tpr_status = TPR_NO; term_props[TPR_CURSOR_BLINK].tpr_status = TPR_NO; + term_props[TPR_DECRQM].tpr_status = TPR_NO; // screen doesn't seem + // to handle DECRQM + // sequences } // Xterm first responded to this request at patch level @@ -5753,6 +5806,13 @@ handle_csi( key_name[0] = (int)KS_EXTRA; key_name[1] = (int)KE_IGNORE; +#ifdef FEAT_TERMRESPONSE + // Mark the DECRQM request as answered so it is not sent again and + // stoptermcap() does not wait for it. + if (decrqm_status.tr_progress == STATUS_SENT) + decrqm_status.tr_progress = STATUS_GOT; +#endif + if (setting >= 0 && setting <= 4) { LOG_TRN("Received DECRPM mode %d: %s", arg[0], tp); @@ -7987,24 +8047,6 @@ term_replace_keycodes(char_u *ta_buf, int ta_len, int len_arg) return len; } -/* - * Query the settings for the DEC modes we support - */ - void -send_decrqm_modes(void) -{ - if (termcap_active && cur_tmode == TMODE_RAW) - { - // Request setting of relevant DEC modes via DECRQM - for (int i = 0; i < (int)ARRAY_LENGTH(dec_modes); i++) - { - vim_snprintf((char *)IObuff, IOSIZE, "\033[?%d$p", dec_modes[i]); - out_str(IObuff); - } - out_flush(); - } -} - /* * Should be called when cleaning up terminal state. */ diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index edf64d2830..eaf60f53be 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -359,7 +359,7 @@ func Test_term_mouse_middle_click_insert_mode() let &term = save_term let &ttymouse = save_ttymouse call test_override('no_query_mouse', 0) - close! + bw! endfunc " Test for switching window using mouse in insert mode @@ -1720,6 +1720,7 @@ func Test_xx01_term_style_response() \ underline_rgb: 'u', \ mouse: 's', \ kitty: 'u', + \ decrqm: 'y' \ }, terminalprops()) set t_RV= @@ -1755,6 +1756,7 @@ func Test_xx02_iTerm2_response() \ underline_rgb: 'u', \ mouse: 's', \ kitty: 'u', + \ decrqm: 'y' \ }, terminalprops()) set t_RV= @@ -1775,6 +1777,7 @@ func Run_libvterm_konsole_response(code) \ underline_rgb: 'u', \ mouse: 's', \ kitty: 'u', + \ decrqm: 'y' \ }, terminalprops()) endfunc @@ -1818,6 +1821,7 @@ func Test_xx04_Mac_Terminal_response() \ underline_rgb: 'y', \ mouse: 's', \ kitty: 'u', + \ decrqm: 'n' \ }, terminalprops()) call assert_equal("\[58;2;%lu;%lu;%lum", &t_8u) @@ -1849,6 +1853,7 @@ func Test_xx05_mintty_response() \ underline_rgb: 'y', \ mouse: 's', \ kitty: 'u', + \ decrqm: 'y' \ }, terminalprops()) set t_RV= @@ -1885,6 +1890,7 @@ func Test_xx06_screen_response() \ underline_rgb: 'y', \ mouse: 's', \ kitty: 'u', + \ decrqm: 'n' \ }, terminalprops()) set t_RV= @@ -1910,6 +1916,7 @@ func Do_check_t_8u_set_reset(set_by_user) \ underline_rgb: 'u', \ mouse: 's', \ kitty: 'u', + \ decrqm: 'y' \ }, terminalprops()) call assert_equal(a:set_by_user ? default_value : '', &t_8u) endfunc @@ -1949,6 +1956,7 @@ func Test_xx07_xterm_response() \ underline_rgb: 'y', \ mouse: 'u', \ kitty: 'u', + \ decrqm: 'y' \ }, terminalprops()) " xterm >= 95 < 277 "xterm2" @@ -1965,6 +1973,7 @@ func Test_xx07_xterm_response() \ underline_rgb: 'u', \ mouse: '2', \ kitty: 'u', + \ decrqm: 'y' \ }, terminalprops()) " xterm >= 277: "sgr" @@ -1981,6 +1990,7 @@ func Test_xx07_xterm_response() \ underline_rgb: 'u', \ mouse: 's', \ kitty: 'u', + \ decrqm: 'y' \ }, terminalprops()) " xterm >= 279: "sgr" and cursor_style not reset; also check t_8u reset, @@ -2010,6 +2020,7 @@ func Test_xx08_kitty_response() \ underline_rgb: 'y', \ mouse: 's', \ kitty: 'y', + \ decrqm: 'y' \ }, terminalprops()) call feedkeys("\[?1u") " simulate the kitty keyboard protocol is enabled @@ -3055,6 +3066,11 @@ func Test_term_win_resize() let buf = RunVimInTerminal('-S XTestWinResize', #{rows: 15, cols: 20}) + " Must add a delay, since status report is sent internally by vim only when + " version response is received, which may come after we send the status report + " here. + sleep 100m + " Send status report call term_sendkeys(buf, "\[?2048;1$y") call TermWait(buf) diff --git a/src/version.c b/src/version.c index 36a8da0782..b2be3d98b5 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 387, /**/ 386, /**/ From f194676c936d9f3a8479a72afbbe9f244039e27e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 22 Apr 2026 16:09:06 +0000 Subject: [PATCH 098/545] patch 9.2.0388: strange indent in update_topline() Problem: strange indent in update_topline() Solution: Fix the indentation (zeertzjq) closes: #20033 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/move.c | 45 +++++++++++++++++++++++---------------------- src/version.c | 2 ++ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/src/move.c b/src/move.c index 943eb519f8..14271240fd 100644 --- a/src/move.c +++ b/src/move.c @@ -477,39 +477,40 @@ update_topline(void) { if (curwin->w_cursor.lnum < curwin->w_botline) { - if (((long)curwin->w_cursor.lnum + if (((long)curwin->w_cursor.lnum >= (long)curwin->w_botline - *so_ptr #ifdef FEAT_FOLDING || hasAnyFolding(curwin) #endif )) - { - lineoff_T loff; - - // Cursor is (a few lines) above botline, check if there are - // 'scrolloff' window lines below the cursor. If not, need to - // scroll. - n = eof_pressure ? 0 : curwin->w_empty_rows; - loff.lnum = curwin->w_cursor.lnum; + { + lineoff_T loff; + + // Cursor is (a few lines) above botline, check if there + // are 'scrolloff' window lines below the cursor. + // If not, need to scroll. + n = eof_pressure ? 0 : curwin->w_empty_rows; + loff.lnum = curwin->w_cursor.lnum; #ifdef FEAT_FOLDING - // In a fold go to its last line. - (void)hasFolding(loff.lnum, NULL, &loff.lnum); + // In a fold go to its last line. + (void)hasFolding(loff.lnum, NULL, &loff.lnum); #endif #ifdef FEAT_DIFF - loff.fill = 0; - n += curwin->w_filler_rows; + loff.fill = 0; + n += curwin->w_filler_rows; #endif - loff.height = 0; - while (loff.lnum < curwin->w_botline + loff.height = 0; + while (loff.lnum < curwin->w_botline #ifdef FEAT_DIFF - && (loff.lnum + 1 < curwin->w_botline || loff.fill == 0) + && (loff.lnum + 1 < curwin->w_botline + || loff.fill == 0) #endif - ) - { - n += loff.height; - if (n >= *so_ptr) - break; - botline_forw(&loff); + ) + { + n += loff.height; + if (n >= *so_ptr) + break; + botline_forw(&loff); } if (n >= *so_ptr && !eof_pressure) // sufficient context, no need to scroll diff --git a/src/version.c b/src/version.c index b2be3d98b5..95f20bc670 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 388, /**/ 387, /**/ From 44f707da6a06b8310bfe23f5477c7653215e1230 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Wed, 22 Apr 2026 20:22:25 +0000 Subject: [PATCH 099/545] patch 9.2.0389: DECRQM still leaves stray "pp" on Apple Terminal.app Problem: DECRQM was still sent to Apple Terminal.app before its DA2 reply was processed, leaving a literal "pp" on screen. may_req_decrqm() was called from vim_main2() right after may_req_termresponse(), at which point term_props[TPR_DECRQM].tpr_status was still TPR_UNKNOWN, so the `!= TPR_NO` guard let the request through (after v9.2.0387) Solution: Send DECRQM from handle_version_response() once term_props[TPR_DECRQM].tpr_status == TPR_YES, the same pattern already used for t_RS (TPR_CURSOR_STYLE) and t_RC (TPR_CURSOR_BLINK), which deliberately wait for the DA2 reply to avoid the same echo-on-screen issue on Apple Terminal.app and Gnome terminal. Drop the now-unused may_req_decrqm() helper and its call site in vim_main2() (Hirohito Higashi). fixes: #19852 related: #19938 closes: #20038 Co-Authored-By: Claude Opus 4.7 (1M context) Co-Authored-By: John Marriott Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/main.c | 4 ---- src/proto/term.pro | 1 - src/term.c | 53 ++++++++++++++++++---------------------------- src/version.c | 2 ++ 4 files changed, 23 insertions(+), 37 deletions(-) diff --git a/src/main.c b/src/main.c index e0d0e7e37f..cac5db145e 100644 --- a/src/main.c +++ b/src/main.c @@ -892,10 +892,6 @@ vim_main2(void) may_req_termresponse(); may_req_bg_color(); - - // Same reason as for termresponse: don't want the terminal sending out - // the DECRPM response after Vim has exited. - may_req_decrqm(); # endif // start in insert mode diff --git a/src/proto/term.pro b/src/proto/term.pro index 5ec909f46c..9653a35af3 100644 --- a/src/proto/term.pro +++ b/src/proto/term.pro @@ -60,7 +60,6 @@ void stoptermcap(void); void may_req_termresponse(void); void check_terminal_behavior(void); void may_req_bg_color(void); -void may_req_decrqm(void); int swapping_screen(void); void scroll_start(void); void cursor_on_force(void); diff --git a/src/term.c b/src/term.c index 876e15d950..07eb5974b7 100644 --- a/src/term.c +++ b/src/term.c @@ -690,15 +690,17 @@ static tcap_entry_T builtin_sync_output[] = { }; #endif +#ifdef FEAT_TERMRESPONSE /* * List of DECRQM modes that Vim supports */ static const int dec_modes[] = { 2026, // Synchronized output -#ifdef UNIX +# ifdef UNIX 2048 // In-band terminal resize events -#endif +# endif }; +#endif #ifdef FEAT_TERMGUICOLORS /* @@ -4312,36 +4314,6 @@ may_req_bg_color(void) } } -/* - * Query the settings for the DEC modes we support via DECRQM. - * Only sent once, and only when the terminal is known not to dislike it - * (i.e. TPR_DECRQM is TPR_YES, or still TPR_UNKNOWN when the version response - * has not yet been received). - * The DECRPM responses are caught in handle_csi(). - */ - void -may_req_decrqm(void) -{ - if (decrqm_status.tr_progress == STATUS_GET - && term_props[TPR_DECRQM].tpr_status != TPR_NO - && can_get_termresponse() - && starting == 0) - { - MAY_WANT_TO_LOG_THIS; - LOG_TR1("Sending DECRQM requests"); - for (int i = 0; i < (int)ARRAY_LENGTH(dec_modes); i++) - { - vim_snprintf((char *)IObuff, IOSIZE, "\033[?%d$p", dec_modes[i]); - out_str(IObuff); - } - termrequest_sent(&decrqm_status); - // check for the characters now, otherwise they might be eaten by - // get_keystroke() - out_flush(); - (void)vpeekc_nomap(); - } -} - #endif /* @@ -5398,6 +5370,23 @@ handle_version_response(int first, int *arg, int argc, char_u *tp) need_flush = TRUE; } + // Only request DEC modes via DECRQM when the terminal is known to + // handle it. Not for Apple Terminal.app or GNU screen, they echo + // the trailing "p" to the screen. See issue #19852. + if (decrqm_status.tr_progress == STATUS_GET + && term_props[TPR_DECRQM].tpr_status == TPR_YES) + { + MAY_WANT_TO_LOG_THIS; + LOG_TR1("Sending DECRQM requests"); + for (int i = 0; i < (int)ARRAY_LENGTH(dec_modes); i++) + { + vim_snprintf((char *)IObuff, IOSIZE, "\033[?%d$p", dec_modes[i]); + out_str(IObuff); + } + termrequest_sent(&decrqm_status); + need_flush = TRUE; + } + if (need_flush) out_flush(); #endif diff --git a/src/version.c b/src/version.c index 95f20bc670..bfd90a13fc 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 389, /**/ 388, /**/ From 521eac1877355d408c8c57bc31947cc86f31f41d Mon Sep 17 00:00:00 2001 From: Bruno Belanyi Date: Thu, 23 Apr 2026 15:56:30 +0000 Subject: [PATCH 100/545] patch 9.2.0390: filetype: some Beancount files are not recognized Problem: filetype: some Beancount files are not recognized Solution: Detect *.bean files as beancount filetype (Bruno Belanyi) closes: #20037 Signed-off-by: Bruno Belanyi Signed-off-by: Christian Brabandt --- runtime/autoload/dist/ft.vim | 3 ++- src/testdir/test_filetype.vim | 2 +- src/version.c | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 7b7fb2954d..761ff644cd 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -3,7 +3,7 @@ vim9script # Vim functions for file type detection # # Maintainer: The Vim Project -# Last Change: 2026 Apr 20 +# Last Change: 2026 Apr 23 # Former Maintainer: Bram Moolenaar # These functions are moved here from runtime/filetype.vim to make startup @@ -1798,6 +1798,7 @@ const ft_from_ext = { # BDF font "bdf": "bdf", # Beancount + "bean": "beancount", "beancount": "beancount", # BibTeX bibliography database file "bib": "bib", diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 583dad4d1c..5040ac0e61 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -138,7 +138,7 @@ def s:GetFilenameChecks(): dict> bass: ['file.bass'], bc: ['file.bc'], bdf: ['file.bdf'], - beancount: ['file.beancount'], + beancount: ['file.beancount', 'file.bean'], bib: ['file.bib'], bicep: ['file.bicep'], bicep-params: ['file.bicepparam'], diff --git a/src/version.c b/src/version.c index bfd90a13fc..2e73a46d4f 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 390, /**/ 389, /**/ From 0f9218851dc91a855c3d186ccd05f550907cf37e Mon Sep 17 00:00:00 2001 From: Vitaliy Gromov <101171374+VitaliyGromov@users.noreply.github.com> Date: Thu, 23 Apr 2026 16:03:31 +0000 Subject: [PATCH 101/545] =?UTF-8?q?translation(ru):=20Fix=20typo=20in=20"?= =?UTF-8?q?=D0=BC=D0=BE=D0=B6=D0=B5"=20to=20"=D0=BC=D0=BE=D0=B6=D0=B5?= =?UTF-8?q?=D1=82=D0=B5"=20in=20lang/README.ru.txt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes: #20047 Signed-off-by: Vitaliy Gromov <101171374+VitaliyGromov@users.noreply.github.com> Signed-off-by: Christian Brabandt --- lang/README.ru.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/README.ru.txt b/lang/README.ru.txt index 774cfbbd5b..c794e4db46 100644 --- a/lang/README.ru.txt +++ b/lang/README.ru.txt @@ -137,7 +137,7 @@ README_vms.txt VMS ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ -Для системе macOS вы може использовать MacVim: https://macvim.org +Для системе macOS вы можете использовать MacVim: https://macvim.org Последние новости о редакторе Vim можно найти на его домашней странице: https://www.vim.org/ From 10b57290d3c15988fdf8b6290ea47c08656af80a Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 25 Apr 2026 16:49:21 +0000 Subject: [PATCH 102/545] patch 9.2.0391: tests: Comment in test_vim9_cmd breaks syntax highlighting Problem: tests: Comment in test_vim9_cmd breaks syntax highlighting Solution: Move the comment into the function and use the # as comment marker instead Signed-off-by: Christian Brabandt --- src/testdir/test_vim9_cmd.vim | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 1380440c40..22f33edf5c 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -2103,8 +2103,8 @@ def s:check_previewpopup(expected_title: string) set previewpopup& enddef -" Test for the 'previewpopup' option def Test_previewpopup() + # Test for the 'previewpopup' option CheckFeature quickfix set previewpopup=height:10,width:60 pedit Xppfile diff --git a/src/version.c b/src/version.c index 2e73a46d4f..41c1753695 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 391, /**/ 390, /**/ From 5f7c52408ff2ec21b94f0c14809030bc72162022 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 25 Apr 2026 17:46:51 +0000 Subject: [PATCH 103/545] runtime(doc): Update the ga output to match the actual output Signed-off-by: Christian Brabandt --- runtime/doc/various.txt | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 1a625b3503..c22cef3cc8 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 9.2. Last change: 2026 Apr 20 +*various.txt* For Vim version 9.2. Last change: 2026 Apr 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -60,10 +60,9 @@ ga Print the ascii value of the character under the but printable according to the 'isprint' option, the non-printable version is also given. - When the character is larger than 127, the form - is also printed. For example: - <~A> 129, Hex 81, Octal 201 ~ -

<|~> 254, Hex fe, Octal 376 ~ + When the character is larger than 127: + <<81>> 129, Hex 0081, Octal 201 ~ +

254, Hex 00fe, Octal 376 ~ (where

is a special character) The character in a file is stored internally as From 1940bcb243984a2586a4b0b8efb3a8728f6a5b54 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 25 Apr 2026 17:50:47 +0000 Subject: [PATCH 104/545] patch 9.2.0392: tests: Some tests are flaky Problem: tests: Some tests are flaky and cause CI to fail Solution: Add WaitForAsserts() calls to reduce flakiness closes: #20050 Signed-off-by: Christian Brabandt --- src/testdir/test_breakindent.vim | 1 + src/testdir/test_digraph.vim | 9 +++------ src/testdir/test_display.vim | 1 + src/testdir/test_edit.vim | 1 + src/testdir/test_highlight.vim | 1 + src/testdir/test_plugin_matchparen.vim | 1 + src/testdir/test_search.vim | 2 ++ src/testdir/test_search_stat.vim | 11 +++++++++-- src/testdir/test_tabpanel.vim | 1 + src/testdir/test_vim9_cmd.vim | 2 +- src/testdir/test_visual.vim | 1 + src/version.c | 2 ++ 12 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/testdir/test_breakindent.vim b/src/testdir/test_breakindent.vim index bb164f70ea..b309108c94 100644 --- a/src/testdir/test_breakindent.vim +++ b/src/testdir/test_breakindent.vim @@ -988,6 +988,7 @@ func Test_visual_starts_before_skipcol() let buf = RunVimInTerminal('-S XvisualStartsBeforeSkipcol', #{rows: 6}) call term_sendkeys(buf, "v$") + call WaitForAssert({-> assert_match('VISUAL.*\d', term_getline(buf, 6))}, 1000) call VerifyScreenDump(buf, 'Test_visual_starts_before_skipcol_1', {}) call term_sendkeys(buf, "\:setlocal showbreak=+++\gv") call VerifyScreenDump(buf, 'Test_visual_starts_before_skipcol_2', {}) diff --git a/src/testdir/test_digraph.vim b/src/testdir/test_digraph.vim index 96b2b63baf..23d8a83aad 100644 --- a/src/testdir/test_digraph.vim +++ b/src/testdir/test_digraph.vim @@ -510,14 +510,11 @@ func Test_entering_digraph() CheckRunVimInTerminal let buf = RunVimInTerminal('', {'rows': 6}) call term_sendkeys(buf, "i\") - call TermWait(buf) - call assert_equal('?', term_getline(buf, 1)) + call WaitForAssert({-> assert_equal('?', term_getline(buf, 1))}, 1000) call term_sendkeys(buf, "1") - call TermWait(buf) - call assert_equal('1', term_getline(buf, 1)) + call WaitForAssert({-> assert_equal('1', term_getline(buf, 1))}, 1000) call term_sendkeys(buf, "2") - call TermWait(buf) - call assert_equal('½', term_getline(buf, 1)) + call WaitForAssert({-> assert_equal('½', term_getline(buf, 1))}, 1000) call StopVimInTerminal(buf) endfunc diff --git a/src/testdir/test_display.vim b/src/testdir/test_display.vim index cbb4f4fc6d..5fee797162 100644 --- a/src/testdir/test_display.vim +++ b/src/testdir/test_display.vim @@ -260,6 +260,7 @@ func Test_display_scroll_update_visual() let buf = RunVimInTerminal('-S XupdateVisual.vim', #{rows: 8, cols: 60}) call term_sendkeys(buf, "VG7kk") + call WaitForAssert({-> assert_match('VISUAL.*\d\+\s\+\d', term_getline(buf, 8))}, 1000) call VerifyScreenDump(buf, 'Test_display_scroll_update_visual', {}) call StopVimInTerminal(buf) diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim index 6ac115d775..196933f7d9 100644 --- a/src/testdir/test_edit.vim +++ b/src/testdir/test_edit.vim @@ -2070,6 +2070,7 @@ func Test_edit_ctrl_r_failed() " trying to insert a blob produces an error call term_sendkeys(buf, "i\=0z\") + call WaitForAssert({-> assert_match('^E976:', term_getline(buf, 5))}, 1000) " ending Insert mode should put the cursor back on the ':' call term_sendkeys(buf, ":\") diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim index ef593b8b35..1a017083bf 100644 --- a/src/testdir/test_highlight.vim +++ b/src/testdir/test_highlight.vim @@ -765,6 +765,7 @@ func Test_visual_sbr() let buf = RunVimInTerminal('-S Xtest_visual_sbr', {'rows': 6,'columns': 60}) call term_sendkeys(buf, "v$") + call WaitForAssert({-> assert_match('VISUAL.*\d\+\s\+\d', term_getline(buf, 6))}, 1000) call VerifyScreenDump(buf, 'Test_visual_sbr_1', {}) " clean up diff --git a/src/testdir/test_plugin_matchparen.vim b/src/testdir/test_plugin_matchparen.vim index a05fc5ddd2..cc6513bb6f 100644 --- a/src/testdir/test_plugin_matchparen.vim +++ b/src/testdir/test_plugin_matchparen.vim @@ -23,6 +23,7 @@ func Test_visual_block_scroll() let buf = RunVimInTerminal('-S '.filename, #{rows: 7}) call term_sendkeys(buf, "V\\") + call WaitForAssert({-> assert_match('VISUAL.*\d\+\s\+\d', term_getline(buf, 7))}, 1000) call VerifyScreenDump(buf, 'Test_display_visual_block_scroll', {}) call StopVimInTerminal(buf) diff --git a/src/testdir/test_search.vim b/src/testdir/test_search.vim index 0a50681751..c4178fdece 100644 --- a/src/testdir/test_search.vim +++ b/src/testdir/test_search.vim @@ -1053,6 +1053,7 @@ func Test_hlsearch_and_visual() \ ], 'Xhlvisual_script', 'D') let buf = RunVimInTerminal('-S Xhlvisual_script', {'rows': 6, 'cols': 40}) call term_sendkeys(buf, "vjj") + call WaitForAssert({-> assert_match('VISUAL.*-\d', term_getline(buf, 6))}, 1000) call VerifyScreenDump(buf, 'Test_hlsearch_visual_1', {}) call term_sendkeys(buf, "\") @@ -2200,6 +2201,7 @@ func Test_incsearch_highlighting_newline() [CODE] call writefile(commands, 'Xincsearch_nl', 'D') let buf = RunVimInTerminal('-S Xincsearch_nl', {'rows': 5, 'cols': 10}) + call TermWait(buf, 100) call term_sendkeys(buf, '/test') call VerifyScreenDump(buf, 'Test_incsearch_newline1', {}) " Need to send one key at a time to force a redraw diff --git a/src/testdir/test_search_stat.vim b/src/testdir/test_search_stat.vim index b2a81070b6..59d21c4e74 100644 --- a/src/testdir/test_search_stat.vim +++ b/src/testdir/test_search_stat.vim @@ -411,16 +411,23 @@ func Test_search_stat_and_incsearch() call writefile(lines, 'Xsearchstat_inc', 'D') let buf = RunVimInTerminal('-S Xsearchstat_inc', #{rows: 10}) + call TermWait(buf, 100) call term_sendkeys(buf, "/abc") call TermWait(buf) + " The first 3 chars on line 2 should have highlighting, but the following not + " So assert the attr value of those 4 chars + call WaitForAssert({-> assert_true( + \ term_scrape(buf, 2)[0].attr == term_scrape(buf, 2)[1].attr && + \ term_scrape(buf, 2)[1].attr == term_scrape(buf, 2)[2].attr && + \ term_scrape(buf, 2)[2].attr != term_scrape(buf, 2)[3].attr)}, 1000) call VerifyScreenDump(buf, 'Test_searchstat_inc_1', {}) call term_sendkeys(buf, "\") - call TermWait(buf) + call WaitForAssert({-> assert_match('^3', term_getline(buf, 1))}, 1000) call VerifyScreenDump(buf, 'Test_searchstat_inc_2', {}) call term_sendkeys(buf, "\") - call TermWait(buf) + call WaitForAssert({-> assert_match('^1', term_getline(buf, 1))}, 1000) call VerifyScreenDump(buf, 'Test_searchstat_inc_3', {}) call term_sendkeys(buf, "\:qa\") diff --git a/src/testdir/test_tabpanel.vim b/src/testdir/test_tabpanel.vim index 427360aeda..0f6aa0b568 100644 --- a/src/testdir/test_tabpanel.vim +++ b/src/testdir/test_tabpanel.vim @@ -551,6 +551,7 @@ function Test_tabpanel_visual() let buf = RunVimInTerminal('-S XTest_tabpanel_visual', {'rows': 10, 'cols': 45}) call term_sendkeys(buf, "v2w") + call WaitForAssert({-> assert_match('VISUAL.*\d', term_getline(buf, 10))}, 1000) call VerifyScreenDump(buf, 'Test_tabpanel_visual_0', {}) call term_sendkeys(buf, "\0jw") call term_sendkeys(buf, "v2wge") diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index 22f33edf5c..b54a58b509 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -952,7 +952,7 @@ func Test_command_modifier_confirm() call term_sendkeys(buf, ":call Getout()\n") call WaitForAssert({-> assert_match('(Y)es, \[N\]o: ', term_getline(buf, 8))}, 1000) call term_sendkeys(buf, "y") - call WaitForAssert({-> assert_match('(Y)es, \[N\]o: ', term_getline(buf, 8))}, 1000) + call WaitForAssert({-> assert_match('Press ENTER or type command to continue', term_getline(buf, 8))}, 1000) call term_sendkeys(buf, "\") call TermWait(buf) call StopVimInTerminal(buf) diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim index ac27354d48..d92f651d9a 100644 --- a/src/testdir/test_visual.vim +++ b/src/testdir/test_visual.vim @@ -1321,6 +1321,7 @@ func Test_visual_block_with_virtualedit() let buf = RunVimInTerminal('-S XTest_block', {'rows': 8, 'cols': 50}) call term_sendkeys(buf, "\gg$") + call WaitForAssert({-> assert_match('VISUAL.*\dx\d', term_getline(buf, 8))}, 1000) call VerifyScreenDump(buf, 'Test_visual_block_with_virtualedit', {}) call term_sendkeys(buf, "\gg\G$") diff --git a/src/version.c b/src/version.c index 41c1753695..8d6be2e96c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 392, /**/ 391, /**/ From 32a30cb5a0d973863fc6258517af49ef20fbafe5 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Sat, 25 Apr 2026 17:55:55 +0000 Subject: [PATCH 105/545] runtime(doc): Update docs about tabpanel closes: #20067 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/tabpage.txt | 10 +++++----- runtime/doc/version9.txt | 7 +++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index 3130c185f8..5083618917 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -1,4 +1,4 @@ -*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 21 +*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -484,15 +484,15 @@ groups: |hl-TabPanel| |hl-TabPanelSel| |hl-TabPanelFill| SCROLLING IN THE TABPANEL: *tabpanel-scroll* -When the total height of the tab list exceeds the visible screen height, the -tabpanel by default displays the "page" that contains the current tab and -offers no way to view tabs outside that page. +When the total height of the tab page list exceeds the visible screen height, +the tabpanel by default displays the "page" that contains the current tab page +and offers no way to view tab pages outside that page. To make the tabpanel scrollable, add "scroll" to 'tabpanelopt': > :set tabpanelopt+=scroll With "scroll" enabled, mouse wheel events over the tabpanel area scroll the -tab list up or down. The scroll step follows the 'mousescroll' setting. +tab page list up or down. The scroll step follows the 'mousescroll' setting. Wheel events inside the tabpanel area are consumed by the tabpanel and do not trigger || or || mappings. diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 90cd1a3efb..16ed792f99 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 21 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52616,9 +52616,8 @@ Other ~ - Allow mouse clickable regions in the 'statusline', 'tabline' and the 'tabpanel' using the |stl-%[FuncName]| atom. - Enable reflow support in the |:terminal|. -- Added "scroll" and "scrollbar" sub-options to 'tabpanelopt' so the - tabpanel can scroll when the tab list exceeds the visible screen - height. +- Added "scroll" and "scrollbar" sub-options to 'tabpanelopt' so the tabpanel + can scroll when the tab page list exceeds the visible screen height. Platform specific ~ ----------------- From 0c2ac3480a76b46ab63852c417255506f7c22117 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 26 Apr 2026 07:57:14 +0000 Subject: [PATCH 106/545] patch 9.2.0393: MS-Windows: link error with XPM support on UCRT64 Problem: MS-Windows: link error with XPM support on UCRT64 Solution: Make_cyg_ming.mak: allow XPM to point to system layout with X11/ headers (Yasuhiro Matsumoto) When XPM is set to a system prefix such as /msys64/ucrt64 (where xpm.h lives in include/X11/), the existing -I flags miss the header. Add -I $(XPM)/include/X11 so both the bundled layout (xpm/include/xpm.h) and the system layout (include/X11/xpm.h) work. closes: #20064 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/Make_cyg_ming.mak | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 0cb83ac484..f5b86eeb42 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -744,7 +744,7 @@ XPM = no endif ifdef XPM ifneq ($(XPM),no) -CFLAGS += -DFEAT_XPM_W32 -I $(XPM)/include -I $(XPM)/../include +CFLAGS += -DFEAT_XPM_W32 -I $(XPM)/include -I $(XPM)/include/X11 -I $(XPM)/../include endif endif diff --git a/src/version.c b/src/version.c index 8d6be2e96c..d4b5c51000 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 393, /**/ 392, /**/ From e7eef554f3b1f00e87d545e221d35f3cf3fd8134 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 26 Apr 2026 08:05:33 +0000 Subject: [PATCH 107/545] CI: Bump the github-actions group across 1 directory with 3 updates Bumps the github-actions group with 3 updates in the / directory: [msys2/setup-msys2](https://github.com/msys2/setup-msys2), [actions/cache](https://github.com/actions/cache) and [github/codeql-action](https://github.com/github/codeql-action). Updates `msys2/setup-msys2` from 2.31.0 to 2.31.1 - [Release notes](https://github.com/msys2/setup-msys2/releases) - [Changelog](https://github.com/msys2/setup-msys2/blob/main/CHANGELOG.md) - [Commits](https://github.com/msys2/setup-msys2/compare/v2.31.0...v2.31.1) Updates `actions/cache` from 5.0.4 to 5.0.5 - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v5.0.4...v5.0.5) Updates `github/codeql-action` from 4.35.1 to 4.35.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v4.35.1...v4.35.2) --- updated-dependencies: - dependency-name: msys2/setup-msys2 dependency-version: 2.31.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: actions/cache dependency-version: 5.0.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action dependency-version: 4.35.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... closes: #20063 Signed-off-by: dependabot[bot] Signed-off-by: Christian Brabandt --- .github/workflows/ci.yml | 4 ++-- .github/workflows/codeql-analysis.yml | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f5f65430f..fc0a42f6ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -435,7 +435,7 @@ jobs: echo "SODIUM_DIR=${SODIUM_DIR}" >> $GITHUB_ENV echo "GETTEXT_PATH=D:\gettext${{ matrix.arch == 'x64' && '64' || '32' }}" >> $GITHUB_ENV - - uses: msys2/setup-msys2@v2.31.0 + - uses: msys2/setup-msys2@v2.31.1 if: matrix.toolchain == 'mingw' with: update: true @@ -459,7 +459,7 @@ jobs: echo %GETTEXT64_URL%>> urls.txt - name: Cache downloaded files - uses: actions/cache@v5.0.4 + uses: actions/cache@v5.0.5 with: path: downloads key: ${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('urls.txt') }} diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ed78c8ff2c..4c546fc570 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -48,7 +48,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4.35.1 + uses: github/codeql-action/init@v4.35.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -59,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v4.35.1 + uses: github/codeql-action/autobuild@v4.35.2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -73,4 +73,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4.35.1 + uses: github/codeql-action/analyze@v4.35.2 From 1de887681d278c8add0d28fbac1fe06642aa7d85 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 26 Apr 2026 08:07:18 +0000 Subject: [PATCH 108/545] No guidance for AI coding agents Problem: No guidance for AI coding agents working in the Vim repository. Solution: Add AGENTS.md to the repository documenting build and test commands, repository layout, commit format, C and Vim9-script conventions, test conventions, help file style, and release policy, so that AI agents can produce patches that match project expectations. closes: #20039 Signed-off-by: Christian Brabandt --- AGENTS.md | 325 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Filelist | 1 + 2 files changed, 326 insertions(+) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000..291c07900b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,325 @@ +# AGENTS.md + +Guidance for AI coding agents working in the Vim repository. + +## Project + +Vim is a text editor written in C. The canonical repository is +https://github.com/vim/vim. The code is old and has grown organically over +the past 30+ years. Some files are vendored from upstream projects +(`src/xdiff`, `src/libvterm`); parts of the runtime are occasionally shared +with forks like Neovim. + +Vim strives to be portable across several different operating systems and +aims to be a stable, robust editor gradually developing new features while +remaining backwards compatible as much as possible. + +At the same time, Vim can be compiled with different feature sets, from the +POSIX compatible minimal vi to a full-fledged GUI editor which includes +additional scripting interfaces. + +See `runtime/doc/develop.txt` for the high level design goals. + +## Build and test + + # Full build on Unix/Linux (from src/): + make + + # Run the full test suite: + make test + + # Generate proto files + make proto + + # Run a single test file: + cd src/testdir && make test_name.res + +Output is in testdir/messages and testdir/test.log + +Builds on Windows depend on the Environment, see `src/INSTALLpc.txt` +for Cygwin/MSYS and MSVC ways to build Vim + +Before submitting any patch, at minimum: +1. The build succeeds without new warnings. +2. Relevant tests pass. +3. The code matches the style of the file being edited. + +## Layout + +- `src/` - the C source. Subsystem names are usually obvious from filenames + (`buffer.c`, `window.c`, `search.c`, `vim9compile.c`, etc.). +- `src/proto/` - function prototypes, one `.pro` file per source file. + Regenerated; do not hand-edit unless you know what you're doing. +- `src/po` - Translations +- `src/xxd` - for the xxd subproject +- `src/xdiff` - for the xdiff library (imported from git) +- `src/libvterm` - for the libvterm library +- `src/testdir/` - tests. Vim-script files named `test_*.vim`. + Screendump expected output lives in `src/testdir/dumps/`. +- `runtime/doc/` - user-facing documentation in Vim help format, when updating, + also update the Last Change header +- `runtime/syntax/generator` - Syntax script for Vim Script, automatically generated + from Vims source +- `runtime/` - runtime files shipped with Vim, when updating, also update the + Last Change header and a short description if this file has no maintainer + If the file has a maintainer, changes should go via them (so make a merge + request against the upstream repo instead) +- `src/version.c` - contains the `included_patches[]` list. Every + patch touching anything below `src/` (with the exception of `src/po`) needs a + new entry at the top, will be updated only when merging into + the master tree. + +## Commit format + +Vim uses a strict commit message format. The subject line is a +one-sentence **problem statement**, not a description of the fix: + + patch 9.2.NNNN: short description of the problem + + Problem: Restatement of the problem as a full sentence, possibly + with a reporter attribution in parentheses. + Solution: Short description of the fix, ending with the author's + name in parentheses. + + optional longer description of the problem and solution goes here in prose. + Do not use bullet points. + + fixes: #NNNN + related: #NNNN + closes: #NNNN + + Co-authored-by: Name + Signed-off-by: Author Name + +Rules: + +- **Subject line states the problem**, not the solution. "fix typo" is + wrong; "typo in foo() causes OOB read" is right. +- **Problem line is a full sentence with a trailing period.** It mirrors + the subject. +- **Solution line ends with `(Author Name)`** — parentheses, period + after them. +- **Longer prose**, if any, goes after the Problem/Solution header +- **`fixes:` references the issue** the patch fixes. + **`closes:` references the PR** that introduces the fix. + **`related:` references related issues**, including issues that caused this + one. + All can appear. Colon, aligned, no trailing period. +- **`Signed-off-by:` is required** — DCO. +- **`Co-Authored-By:` is allowed** and is the accepted way to + acknowledge AI assistance transparently. Human + coauthors should usually also have their own Signed-off-by. + +## C code conventions + +- **Indentation is 4 spaces per level.** Existing files use tabs with + `ts=8 sts=4 sw=4 noet` (set by the modeline in the file), + so tabs of width 8 appear where two levels of indent collapse. `sign.c`, + `sound.c`, and any new file must use spaces only and follow the style from + the .editorconfig file. +- **Opening braces go on their own line (Allman style)** — for function + definitions and for control-flow constructs (`if`/`else`/`for`/`while`/ + `do`) alike. +- **Function definitions**: return type on its own indented line, with + the function name beginning on the next line. +- Initialize locals where a reader cannot trivially see the first + assignment (common for pointers and return-value accumulators). + Don't add `= 0` initializers for values that are always assigned + before use — they can hide real uninitialized-read bugs from + the compiler. +- `for (int i = 0; ...)` loop declarations are fine in files that + use them; older files may declare the counter at the top of the + block. +- **Function-scope declarations at the top of a block** is the historical + style, but mid-block declarations are acceptable in files that have + adopted them. Match the surrounding code. +- **Custom types end in `_T`** (e.g., `buf_T`, `linenr_T`, `pos_T`). + Never use `_t` — it collides with POSIX typedefs. +- **C language is C95 plus specific C99 features**: `//` comments, + mixed declarations and statements, `__func__`, `bool`/`_Bool`, + variadic macros, compound literals, `static inline`, trailing enum + commas. Do not reach for later C standards — Vim still must build + with Compaq C on OpenVMS. See `*assumptions-C-compiler*` in + `develop.txt` for the full list. +- **`bool` / `true` / `false` are acceptable.** Vim is transitioning + from `int` with `TRUE`/`FALSE` to C99 `bool`. Do not "fix" `bool` + back to `int`. Within a single patch, be consistent — don't mix + `true` and `TRUE` in new code. +- **Do not mass-convert** `TRUE`/`FALSE` to `true`/`false` across files + unless that is the patch's explicit purpose. Opportunistic + conversions create noise in diffs. +- **`STRLEN_LITERAL("...")`** should be used when the length of a + string literal is needed. Avoid `STRLEN()` on literals. +- **`vim_snprintf_safelen()`** returns the written length; prefer it + over `vim_snprintf()` when the length is then needed. +- **Prefer `dict_add_string_len()`** when the string length is already + known, over `dict_add_string()` which calls `STRLEN()`. +- **String/buffer parameters go `(char_u *buf, size_t buflen)`** — + length alongside pointer, in bytes. Use `size_t` for byte counts, + `int` only where required by legacy APIs. +- **Guards before divisions.** Check for divisor zero explicitly, even + when a composite earlier guard would prevent it. Relying on + transitive guards is fragile. +- When introducing new allocations, verify the cleanup paths handle all exit + conditions (early return, error branches, etc). + +**Use Vim wrappers instead of libc where one exists:** + +| libc | Vim | Why | +|---------------|------------------------|-----------------------------| +| `free()` | `vim_free()` | Tolerates NULL | +| `malloc()` | `alloc()` / `lalloc()` | Checks for OOM | +| `strcpy()` | `STRCPY()` | Cast for `char_u *` | +| `strchr()` | `vim_strchr()` | Handles special characters | +| `strrchr()` | `vim_strrchr()` | Handles special characters | +| `memcpy()` | `mch_memmove()` | Handles overlapping copies | +| `bcopy()` | `mch_memmove()` | Handles overlapping copies | +| `memset()` | `vim_memset()` | Uniform across systems | +| `isspace()` | `vim_isspace()` | Handles bytes > 127 | +| `iswhite()` | `vim_iswhite()` | TRUE only for tab and space | + +Further rules, not spelled out here, live in `runtime/doc/develop.txt`: + +- `*style-names*` — reserved name patterns (`is*`, `to*`, `str*`, `mem*`, + `wcs*`, `.*_t`, `__.*`), forbidden identifiers (`delete`, `this`, `new`, + `time`, `index`), and the 31-character function-name limit. +- `*style-spaces*`, `*style-examples*` — spacing and one-statement-per-line. +- `*style-various*` — `FEAT_` feature prefix, uppercase `#define`, + `#ifdef HAVE_X` rather than `#if HAVE_X`, no `'\"'`. +- `*assumptions-makefiles*` — POSIX.1-2001 `make` only in the main + Makefiles (no `%` rules, `:=`, `.ONESHELL`, GNU conditionals). +- Vim uses `char_u` instead of `char` type +- Vim uses the macros `STRLEN`, `STRCPY`, `STRCMP`, `STRCAT` that work + with the `char_u` type. +- `*style-clang-format*` — `sign.c` and `sound.c` are formatted with + `clang-format`; re-run it after editing those files. + +## Vim9 script conventions (in tests and runtime files) + +- Write modern Vim style (new files can use Vim9 script, but compatibility + with Neovim and other forks is a concern, so in doubt please ask!) +- **Drop `l:` prefix from local variables** in Vim-script tests. +- **Don't add `CheckFeature` inside individual tests** if it's already + at the top of the file. +- If a test file doesn't gate features at the top, add CheckFeature to + individual tests that depend on specific build features. + +## Test conventions + +- Tests are in `src/testdir/test_*.vim`. +- Reproducible tests beat "it doesn't crash" tests. If a patch fixes + a rendering bug, add a screendump test. If it fixes incorrect output, + assert the output. +- Add comprehensive tests for newly added features and include them + in existing tests if possible +- **Screendump tests** use `CheckScreendump`, `RunVimInTerminal`, + `VerifyScreenDump`, and live dumps in `src/testdir/dumps/`. +- `v9.CheckScriptSuccess(lines)` / `v9.CheckScriptFailure(lines, error, lnum)` + are the standard way to test Vim9 script behavior at script-load time. +- When fixing a bug reported as an issue, include a test that + reproduces the original report, not just a minimal synthetic case. +- Tests for Syntax runtime are in `runtime/syntax/testdir` +- Tests for Indent runtime are in `runtime/indent/testdir` + +## Common gotchas + +- **Distinguish what code enforces from what docs claim.** If a patch + changes documented behavior, say so in the Problem/Solution. +- **Generated files** (`src/auto/configure`, generated Wayland protocol + C, etc.) should only be regenerated when their source changes. + Mixing unrelated regeneration into a functional patch creates noise. + +## Documentation + +- User-facing option or feature changes require a `runtime/doc/*.txt` + update in the same patch. +- When editing an existing help file, bump the `Last change:` header + at the top. + +### Help file style + +See `runtime/doc/helphelp.txt` (`*help-writing*`) for the authoritative +reference. Key conventions: + +- **File header**: first line is `*filename.txt*` then a tab then a + short description. That description appears under `LOCAL ADDITIONS` + in `help.txt`. The version and `Last change:` date go on the second + line, right aligned. +- **Modeline**: every help file ends with a Vim modeline — typically + `vim:tw=78:ts=8:noet:ft=help:norl:`. +- **Layout**: `'textwidth'` 78, `'tabstop'` 8, indent and align with + tab characters. Two spaces between sentences. Run `:retab` + (not `:retab!`, and review the diff) after editing. +- **Tags** are defined as `*tag-name*`, usually right-aligned on the + line where the thing they name is introduced. Tag names must be + unique across all of `runtime/doc/`; for plugin help, prefix with + the plugin name. +- **Cross-references inside help text**: + - `|tag-name|` — hot-link to an existing tag. + - `` `:cmd` `` — Ex command, highlighted as a code block. + - `'option'` — option name, in single quotes. + - `` or `CTRL-X` — special keys. + - `{placeholder}` — user-supplied argument. +- **Sections** are separated by a line of `=` starting in column 1. + Column or subsection headings end with `~` to trigger heading + highlighting. +- **Code blocks** start with `>` at the end of the introducing line + and end with `<` as the first non-blank on a later line (any line + starting in column 1 also implicitly closes the block). Use `>vim` + (or another language name) to request syntax highlighting inside + the block. +- **Notation** — `Note`, `Todo`, `Error` and a few similar words are + auto-highlighted; do not try to fake the highlighting by other means. +- **Language**: gender-neutral language is preferred for new or updated + text; existing wording does not need to be rewritten for this alone. + +## Release policy + +Vim alternates between development cycles and stability periods — see +`runtime/doc/develop.txt` `*design-policy*`. + +- **During a stability period** only clear bug fixes, security fixes, + documentation updates, translations, and runtime file updates are + accepted. No new features, no backwards-incompatible changes. +- **Once released in a minor version**, C-core features must stay + backwards-compatible. Runtime files have a bit more flexibility so + their maintainers can correct old behavior. +- **Deprecated features** stay reachable via config (do not hard-error), + are documented as deprecated, can be disabled at compile time, and + may be removed in a later cycle. + +## Security + +Before reporting a suspected security issue or submitting a patch +that touches security-sensitive code, read `SECURITY.md`. Follow +the disclosure process described there. + +## Before submitting + +1. Commit message follows the format above. +2. All modified code compiles without new warnings. +3. Tests pass, and new functionality has regression tests. +4. Documentation is updated for user-visible changes. +5. Signed-off-by is present. +6. Diff contains only changes relevant to the stated problem — + no stray whitespace fixes, no unrelated refactors, no unrelated + regeneration of `auto/configure`. +7. For multi-patch series: each commit compiles and passes its own + tests. A known-broken intermediate state that a later patch fixes + is not acceptable — squash instead. + +## When in doubt + +- Make the smallest possible change to achieve the goal. Do not rewrite + entire files or functions when a targeted edit suffices. +- Read surrounding code and match its style rather than imposing an + "improvement." +- Err toward smaller, more focused patches. A patch that does three + things is three patches. +- If a patch fixes a symptom of a deeper bug, say so in the Problem + and acknowledge the scope limitation in the Solution. +- Before claiming a bug exists, reproduce it. Before claiming code does X, read + the code. Do not rely on training-data memory of file contents. +- Before running shell commands that modify files outside the working tree, + install packages, push branches, or invoke network operations, confirm with + the user. diff --git a/Filelist b/Filelist index 4832028577..311a1031af 100644 --- a/Filelist +++ b/Filelist @@ -1198,6 +1198,7 @@ IGNORE = \ .github/FUNDING.yml \ .github/labeler.yml \ .github/workflows/label.yml \ + AGENTS.md \ SECURITY.md \ ci/unlisted.make \ ci/hlgroups.make \ From 807f2cd789e2005bd1853f6c743ec9883dfb7f91 Mon Sep 17 00:00:00 2001 From: svardew Date: Sun, 26 Apr 2026 08:16:59 +0000 Subject: [PATCH 109/545] CI: Fix syntax error in lychee URL checker fixes: #20040 Signed-off-by: svardew Signed-off-by: Christian Brabandt --- ci/lychee.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/lychee.toml b/ci/lychee.toml index 941a2d7f6f..8b9f0099f2 100644 --- a/ci/lychee.toml +++ b/ci/lychee.toml @@ -13,7 +13,7 @@ extensions = ["c", "h", "md", "html", "txt"] accept = ["100..=103", "200..=299", "429"] # Retry each request a few times on transient network errors -retries = 2 +max_retries = 2 retry_wait_time = 2 # Timeout per request in seconds From 8f9cde4592f1067c0104e1255314880d40441aa2 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 26 Apr 2026 08:22:23 +0000 Subject: [PATCH 110/545] patch 9.2.0394: xxd: offsets greater than LONG_MAX print as negative Problem: xxd: offsets greater than LONG_MAX print as negative Solution: Use "%lu" to print unsigned long value (Yasuhiro Matsumoto) closes: #20055 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/testdir/test_xxd.vim | 2 +- src/version.c | 2 ++ src/xxd/xxd.c | 5 +++-- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/testdir/test_xxd.vim b/src/testdir/test_xxd.vim index 472dfd4ee9..430a07ccfb 100644 --- a/src/testdir/test_xxd.vim +++ b/src/testdir/test_xxd.vim @@ -476,7 +476,7 @@ func Test_xxd_buffer_overflow() endif new let input = repeat('A', 256) - call writefile(['-9223372036854775808: ' . repeat("\e[1;32m41\e[0m ", 256) . ' ' . "\e[1;32m" . repeat('A', 256) . "\e[0m"], 'Xxdexpected', 'D') + call writefile(['9223372036854775808: ' . repeat("\e[1;32m41\e[0m ", 256) . ' ' . "\e[1;32m" . repeat('A', 256) . "\e[0m"], 'Xxdexpected', 'D') exe 'r! printf ' . input . '| ' . s:xxd_cmd . ' -Ralways -g1 -c256 -d -o 9223372036854775808 > Xxdout' call assert_equalfile('Xxdexpected', 'Xxdout') call delete('Xxdout') diff --git a/src/version.c b/src/version.c index d4b5c51000..0946dbadc1 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 394, /**/ 393, /**/ diff --git a/src/xxd/xxd.c b/src/xxd/xxd.c index 535f0add32..fb45f6c284 100644 --- a/src/xxd/xxd.c +++ b/src/xxd/xxd.c @@ -74,6 +74,7 @@ * 26.11.2025 update indent in exit_with_usage() * 19.03.2026 Add -t option to end output with terminating null * 25.03.2026 Fix color output issues + * 26.04.2026 Use unsigned long for printing offsets * * (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com) * @@ -154,7 +155,7 @@ extern void perror __P((char *)); # endif #endif -char version[] = "xxd 2026-03-25 by Juergen Weigert et al."; +char version[] = "xxd 2026-04-26 by Juergen Weigert et al."; #ifdef WIN32 char osver[] = " (Win32)"; #else @@ -1172,7 +1173,7 @@ main(int argc, char *argv[]) { if (p == 0) { - addrlen = sprintf(l, decimal_offset ? "%08ld:" : "%08lx:", + addrlen = sprintf(l, decimal_offset ? "%08lu:" : "%08lx:", ((unsigned long)(n + seekoff + displayoff))); for (c = addrlen; c < LLEN_NO_COLOR; l[c++] = ' ') ; From 8d9c383aaff17f5991f739a6d0f06017022b2198 Mon Sep 17 00:00:00 2001 From: "D. Ben Knoble" Date: Sun, 26 Apr 2026 08:33:59 +0000 Subject: [PATCH 111/545] patch 9.2.0395: tests: Test_backupskip() may read from $HOME Problem: tests: Test_backupskip() may read from $HOME Solution: Set $HOME to an empty value, use --clean (D Ben Knoble) Even though we unset HOME, we can see via scriptnames that user files are still sourced! One of my installed plugins warns when not compiled with +python3, so this test has a "press Enter" prompt. Use `--clean` like most other GetVimProg()'s do to fix it. Some tests use `system()` instead, but that turns this test into a failure rather than passing; I'm not sure why other tests don't suffer from this. To prove to ourselves, we can use code like this: diff --git i/src/testdir/test_options.vim w/src/testdir/test_options.vim index a408e20e1..044364a54 100644 --- i/src/testdir/test_options.vim +++ w/src/testdir/test_options.vim @@ -1179,6 +1179,7 @@ func Test_backupskip() " P_NODUP). Run this in a separate instance and write v:errors in a file, " so that we see what happens on startup. let after =<< trim [CODE] + call writefile([execute('scriptnames')], 'foo') let bsklist = split(&backupskip, ',') call assert_equal(uniq(copy(bsklist)), bsklist) call writefile(['errors:'] + v:errors, 'Xtestout') @@ -1196,7 +1197,7 @@ func Test_backupskip() " unset $HOME, so that it won't try to read init files let saveenv['HOME'] = getenv("HOME") call setenv('HOME', v:null) - exe 'silent !' . cmd + exe 'silent !' . cmd .. ' --cmd "echo &rtp"' call assert_equal(['errors:'], readfile('Xtestout')) " restore environment variables Here, that causes "foo" to include a bunch of files under ~/.vim. I'm not sure why this happens, but lets paper over it for the test. We can also tell that (orthogonal to --clean) setting HOME='' works too. Let's do that in addition since unsetting HOME isn't quite enough. closes: #20051 Signed-off-by: D. Ben Knoble Signed-off-by: Christian Brabandt --- src/testdir/test_options.vim | 6 +++--- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index 76a1bff344..b1be224467 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -1185,7 +1185,7 @@ func Test_backupskip() qall [CODE] call writefile(after, 'Xafter', 'D') - let cmd = GetVimProg() . ' --not-a-term -S Xafter --cmd "set enc=utf8"' + let cmd = GetVimProg() . ' --clean --not-a-term -S Xafter --cmd "set enc=utf8"' let saveenv = {} for var in ['TMPDIR', 'TMP', 'TEMP'] @@ -1193,9 +1193,9 @@ func Test_backupskip() call setenv(var, '/duplicate/path') endfor - " unset $HOME, so that it won't try to read init files + " set $HOME='', so that Vim won't try to read init files let saveenv['HOME'] = getenv("HOME") - call setenv('HOME', v:null) + call setenv('HOME', '') exe 'silent !' . cmd call assert_equal(['errors:'], readfile('Xtestout')) diff --git a/src/version.c b/src/version.c index 0946dbadc1..369938147f 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 395, /**/ 394, /**/ From 0ca1121d1f3db831bd84a8f25f95484a90467d87 Mon Sep 17 00:00:00 2001 From: Peter Cardenas <16930781+PeterCardenas@users.noreply.github.com> Date: Sun, 26 Apr 2026 08:42:57 +0000 Subject: [PATCH 112/545] runtime(help): make help omnifunc global closes: #20024 Signed-off-by: Peter Cardenas <16930781+PeterCardenas@users.noreply.github.com> Signed-off-by: Christian Brabandt --- runtime/ftplugin/help.vim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/ftplugin/help.vim b/runtime/ftplugin/help.vim index e8651b5d59..e77484bffb 100644 --- a/runtime/ftplugin/help.vim +++ b/runtime/ftplugin/help.vim @@ -4,6 +4,7 @@ " Last Change: 2025 Apr 08 " 2025 Apr 08 by Vim project (set 'omnifunc' and 'iskeyword', #17073) " 2025 Aug 08 by Vim project (unset comment options, #17889) +" 2026 Apr 26 by Vim project (make HelpComplete global, #20024) if exists("b:did_ftplugin") finish @@ -17,14 +18,14 @@ let b:undo_ftplugin = "setl isk< fo< tw< cole< cocu< keywordprg< omnifunc< comme setl comments= cms= -setlocal formatoptions+=tcroql textwidth=78 keywordprg=:help omnifunc=s:HelpComplete +setlocal formatoptions+=tcroql textwidth=78 keywordprg=:help omnifunc=HelpComplete let &l:iskeyword='!-~,^*,^|,^",192-255' if has("conceal") setlocal cole=2 cocu=nc endif -if !exists('*s:HelpComplete') - func s:HelpComplete(findstart, base) +if !exists('HelpComplete') + func HelpComplete(findstart, base) if a:findstart let colnr = col('.') - 1 " Get the column number before the cursor let line = getline('.') From bf6892aaabd52bbfdc24730e77a05ba91675a1d7 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 26 Apr 2026 09:18:07 +0000 Subject: [PATCH 113/545] runtime(doc): Update documentation on :z Signed-off-by: Christian Brabandt --- runtime/doc/various.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index c22cef3cc8..4403b6c6e4 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 9.2. Last change: 2026 Apr 25 +*various.txt* For Vim version 9.2. Last change: 2026 Apr 26 VIM REFERENCE MANUAL by Bram Moolenaar @@ -184,6 +184,9 @@ g8 Print the hex values of the bytes used in the If the mark is "=", a line of dashes is printed around the current line. + If the 'number' or 'relativenumber' option is set, + absolute line numbers will be included in the output. + *:z!* :[range]z![+-^.=][count] Like ":z", but when [count] is not specified, it From 2baef82cd104639d5a0d57941afdc961ce52ccb2 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 26 Apr 2026 09:22:38 +0000 Subject: [PATCH 114/545] patch 9.2.0396: tests: Test_error_callback_terminal is flaky on macOS Problem: tests: Test_error_callback_terminal is flaky on macOS Solution: Use WaitForAssert() to make it more reliable (Yasuhiro Matsumoto). term_wait() only waits for terminal screen updates and does not guarantee that the err_io 'pipe' callback has fired, so on macOS the assert_match() against g:error frequently runs before sh has written "sh: XXXX: not found", causing the test to flake. Replace term_wait() + assert_match() with WaitForAssert() so each assertion polls until the callback delivers the expected output. Also defer sending "exit" until the stderr message has been seen, to avoid losing the stderr write to the exit race. closes: #20075 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/testdir/test_channel.vim | 8 ++++---- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index 23f8a0fc3e..ceb5532637 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -2922,11 +2922,11 @@ func Test_error_callback_terminal() call assert_equal('RAW', dict.err_mode) call assert_equal('pipe', dict.err_io) call term_sendkeys(buf, "XXXX\") - call term_wait(buf) + " term_wait() does not wait for the err_io 'pipe' callback to fire, so use + " WaitForAssert() to poll until sh has written the error message. + call WaitForAssert({-> assert_match('sh:.*XXXX:.*not found', g:error)}, 5000) call term_sendkeys(buf, "exit\") - call term_wait(buf) - call assert_match('XXX.*exit', g:out) - call assert_match('sh:.*XXXX:.*not found', g:error) + call WaitForAssert({-> assert_match('XXX.*exit', g:out)}, 5000) delfunc s:Out delfunc s:Err diff --git a/src/version.c b/src/version.c index 369938147f..e714ddc2a3 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 396, /**/ 395, /**/ From 1ce03d674a5a91f62769a6dcc2f9b8f249b90761 Mon Sep 17 00:00:00 2001 From: Doug Kearns Date: Sun, 26 Apr 2026 14:26:19 +0000 Subject: [PATCH 115/545] runtime(dockerfile): Update syntax, fix RUN command highlighting Allow for docker comments to be interspersed in a multiline (continued) RUN command argument. fixes: #8364 closes: #19829 Signed-off-by: Doug Kearns Signed-off-by: Christian Brabandt --- runtime/syntax/dockerfile.vim | 3 ++- .../syntax/testdir/dumps/dockerfile_00.dump | 20 +++++++++++++++++++ .../testdir/input/dockerfile.dockerfile | 14 +++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 runtime/syntax/testdir/dumps/dockerfile_00.dump create mode 100644 runtime/syntax/testdir/input/dockerfile.dockerfile diff --git a/runtime/syntax/dockerfile.vim b/runtime/syntax/dockerfile.vim index f1d612f4ad..17b32d2942 100644 --- a/runtime/syntax/dockerfile.vim +++ b/runtime/syntax/dockerfile.vim @@ -1,6 +1,7 @@ " dockerfile.vim - Syntax highlighting for Dockerfiles " Maintainer: Honza Pokorny " Last Change: 2024 Dec 20 +" 2026 Mar 26 by Vim Project: dockerfileShell comments (#19829) " License: BSD " https://docs.docker.com/engine/reference/builder/ @@ -31,7 +32,7 @@ syntax match dockerfileInstruction contained /\v<(SHELL|VOLUME)>/ syntax region dockerfileString contained start=/\v"/ skip=/\v\\./ end=/\v"/ syntax region dockerfileJSON contained keepend start=/\v\[/ skip=/\v\\\_./ end=/\v$/ contains=@JSON -syntax region dockerfileShell contained keepend start=/\v/ skip=/\v\\\_./ end=/\v$/ contains=@Shell +syntax region dockerfileShell contained keepend start=/\v/ skip=/\v\\\_.|^\s*#.*/ end=/\v$/ contains=@Shell syntax region dockerfileValue contained keepend start=/\v/ skip=/\v\\\_./ end=/\v$/ contains=dockerfileString syntax region dockerfileComment start=/\v^\s*#/ end=/\v$/ contains=@Spell diff --git a/runtime/syntax/testdir/dumps/dockerfile_00.dump b/runtime/syntax/testdir/dumps/dockerfile_00.dump new file mode 100644 index 0000000000..6f55b91d7c --- /dev/null +++ b/runtime/syntax/testdir/dumps/dockerfile_00.dump @@ -0,0 +1,20 @@ +>#+0#0000e05#ffffff0| |I|s@1|u|e| |#|8|3|6|4| |(|D|o|c|k|e|r| |s|y|n|t|a|x| |h|i|g|h|l|i|g|h|t|i|n|g| |-| |C|o|m@1|e|n|t|s| |b|r|e|a|k| |u|p| |R|U|N| |h|i|g|h|l|i|g|h|t +|i|n|g| +0#0000000&@71 +|#+0#0000e05&| |i|n| |m|u|l|t|i|-|l|i|n|e| |m|o|d|e|)| +0#0000000&@53 +@75 +|F+0#af5f00255&|R|O|M| +0#0000000&|d|e|b|i|a|n|:|1|0|.|3| @58 +@75 +|R+0#af5f00255&|U|N| +0#0000000&|a|p|t|-|g|e|t| |u|p|d|a|t|e| |-+0#e000e06&|y| +0#0000000&|\+0#af5f00255&| +0#0000000&@51 +@4|&+0#af5f00255&@1| +0#0000000&|a|p|t|-|g|e|t| |u|p|g|r|a|d|e| |-+0#e000e06&|y| +0#0000000&|\+0#af5f00255&| +0#0000000&@47 +@4|&+0#af5f00255&@1| +0#0000000&|a|p|t|-|g|e|t| |i|n|s|t|a|l@1| |-+0#e000e06&|y| +0#0000000&|c|u|r|l| |g+0#af5f00255&|r|e|p| +0#0000000&|s+0#af5f00255&|e|d| +0#0000000&|u|n|z|i|p| |g|i|t| |s|u|d|o| |j|q| |g|e|t@1|e|x|t| |\+0#af5f00255&| +0#0000000&@7 +@4|#+0#0000e05&| |A|z|u|r|e| |C|L|I| +0#0000000&@59 +@4|&+0#af5f00255&@1| +0#0000000&|c+0#af5f00255&|d| +0#0000000&|/|t|m|p| |\+0#af5f00255&| +0#0000000&@58 +@4|&+0#af5f00255&@1| +0#0000000&|c|u|r|l| |-+0#e000e06&|s|L| +0#0000000&|h|t@1|p|s|:|/@1|a|k|a|.|m|s|/|I|n|s|t|a|l@1|A|z|u|r|e|C|L|I|D|e|b| ||+0#af5f00255&| +0#0000000&|b|a|s|h| |\+0#af5f00255&| +0#0000000&@16 +@4|#+0#0000e05&| |C|l|e|a|n|-|u|p| |A|p|t| |C|a|c|h|e|s| +0#0000000&@49 +@4|&+0#af5f00255&@1| +0#0000000&|a|p|t|-|g|e|t| |c|l|e|a|n| |\+0#af5f00255&| +0#0000000&@52 +@4|&+0#af5f00255&@1| +0#0000000&|r+0#af5f00255&|m| +0#0000000&|-+0#e000e06&|r|f| +0#0000000&|/|v|a|r|/|l|i|b|/|a|p|t|/|l|i|s|t|s| @42 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|1|,|1| @10|A|l@1| diff --git a/runtime/syntax/testdir/input/dockerfile.dockerfile b/runtime/syntax/testdir/input/dockerfile.dockerfile new file mode 100644 index 0000000000..0126470e46 --- /dev/null +++ b/runtime/syntax/testdir/input/dockerfile.dockerfile @@ -0,0 +1,14 @@ +# Issue #8364 (Docker syntax highlighting - Comments break up RUN highlighting +# in multi-line mode) + +FROM debian:10.3 + +RUN apt-get update -y \ + && apt-get upgrade -y \ + && apt-get install -y curl grep sed unzip git sudo jq gettext \ + # Azure CLI + && cd /tmp \ + && curl -sL https://aka.ms/InstallAzureCLIDeb | bash \ + # Clean-up Apt Caches + && apt-get clean \ + && rm -rf /var/lib/apt/lists From 3cc7d5071619f30bba2ae4ccfa992dccb8fd5e51 Mon Sep 17 00:00:00 2001 From: Doug Kearns Date: Sun, 26 Apr 2026 14:31:55 +0000 Subject: [PATCH 116/545] runtime(algol68): Add new syntax file, ftplugin and filetype detection - Add a syntax file update to Neville Dempsey's long-serving version - Add a new rudimentary ftplugin - Add filetype detection Changes to the syntax file include: - improved prelude, number and symbol highlighting - prelude highlighting tests - updated boiler plate Note that these runtime files currently target Algol 68 Genie employing the default UPPER stropping regime. Support for GNU Algol 68 should also be usable with the UPPER stropping regime, although somewhat less complete. Full support for the SUPPER stropping regime in GNU Algol 68 is also planned. closes: #19818 Co-authored-by: Doug Kearns Signed-off-by: Doug Kearns Signed-off-by: Janis Papanagnou Signed-off-by: Christian Brabandt --- .github/MAINTAINERS | 2 + runtime/autoload/dist/ft.vim | 2 + runtime/doc/syntax.txt | 16 +- runtime/doc/tags | 4 + runtime/ftplugin/algol68.vim | 47 + runtime/syntax/algol68.vim | 469 ++++ .../dumps/algol68_all_preludes_00.dump | 20 + .../dumps/algol68_all_preludes_01.dump | 20 + .../dumps/algol68_all_preludes_02.dump | 20 + .../dumps/algol68_all_preludes_03.dump | 20 + .../dumps/algol68_all_preludes_04.dump | 20 + .../dumps/algol68_all_preludes_05.dump | 20 + .../dumps/algol68_all_preludes_06.dump | 20 + .../dumps/algol68_all_preludes_07.dump | 20 + .../dumps/algol68_all_preludes_08.dump | 20 + .../dumps/algol68_all_preludes_09.dump | 20 + .../dumps/algol68_all_preludes_10.dump | 20 + .../dumps/algol68_all_preludes_100.dump | 20 + .../dumps/algol68_all_preludes_101.dump | 20 + .../dumps/algol68_all_preludes_102.dump | 20 + .../dumps/algol68_all_preludes_103.dump | 20 + .../dumps/algol68_all_preludes_104.dump | 20 + .../dumps/algol68_all_preludes_105.dump | 20 + .../dumps/algol68_all_preludes_106.dump | 20 + .../dumps/algol68_all_preludes_107.dump | 20 + .../dumps/algol68_all_preludes_108.dump | 20 + .../dumps/algol68_all_preludes_109.dump | 20 + .../dumps/algol68_all_preludes_11.dump | 20 + .../dumps/algol68_all_preludes_110.dump | 20 + .../dumps/algol68_all_preludes_111.dump | 20 + .../dumps/algol68_all_preludes_112.dump | 20 + .../dumps/algol68_all_preludes_113.dump | 20 + .../dumps/algol68_all_preludes_114.dump | 20 + .../dumps/algol68_all_preludes_115.dump | 20 + .../dumps/algol68_all_preludes_116.dump | 20 + .../dumps/algol68_all_preludes_117.dump | 20 + .../dumps/algol68_all_preludes_118.dump | 20 + .../dumps/algol68_all_preludes_119.dump | 20 + .../dumps/algol68_all_preludes_12.dump | 20 + .../dumps/algol68_all_preludes_13.dump | 20 + .../dumps/algol68_all_preludes_14.dump | 20 + .../dumps/algol68_all_preludes_15.dump | 20 + .../dumps/algol68_all_preludes_16.dump | 20 + .../dumps/algol68_all_preludes_17.dump | 20 + .../dumps/algol68_all_preludes_18.dump | 20 + .../dumps/algol68_all_preludes_19.dump | 20 + .../dumps/algol68_all_preludes_20.dump | 20 + .../dumps/algol68_all_preludes_21.dump | 20 + .../dumps/algol68_all_preludes_22.dump | 20 + .../dumps/algol68_all_preludes_23.dump | 20 + .../dumps/algol68_all_preludes_24.dump | 20 + .../dumps/algol68_all_preludes_25.dump | 20 + .../dumps/algol68_all_preludes_26.dump | 20 + .../dumps/algol68_all_preludes_27.dump | 20 + .../dumps/algol68_all_preludes_28.dump | 20 + .../dumps/algol68_all_preludes_29.dump | 20 + .../dumps/algol68_all_preludes_30.dump | 20 + .../dumps/algol68_all_preludes_31.dump | 20 + .../dumps/algol68_all_preludes_32.dump | 20 + .../dumps/algol68_all_preludes_33.dump | 20 + .../dumps/algol68_all_preludes_34.dump | 20 + .../dumps/algol68_all_preludes_35.dump | 20 + .../dumps/algol68_all_preludes_36.dump | 20 + .../dumps/algol68_all_preludes_37.dump | 20 + .../dumps/algol68_all_preludes_38.dump | 20 + .../dumps/algol68_all_preludes_39.dump | 20 + .../dumps/algol68_all_preludes_40.dump | 20 + .../dumps/algol68_all_preludes_41.dump | 20 + .../dumps/algol68_all_preludes_42.dump | 20 + .../dumps/algol68_all_preludes_43.dump | 20 + .../dumps/algol68_all_preludes_44.dump | 20 + .../dumps/algol68_all_preludes_45.dump | 20 + .../dumps/algol68_all_preludes_46.dump | 20 + .../dumps/algol68_all_preludes_47.dump | 20 + .../dumps/algol68_all_preludes_48.dump | 20 + .../dumps/algol68_all_preludes_49.dump | 20 + .../dumps/algol68_all_preludes_50.dump | 20 + .../dumps/algol68_all_preludes_51.dump | 20 + .../dumps/algol68_all_preludes_52.dump | 20 + .../dumps/algol68_all_preludes_53.dump | 20 + .../dumps/algol68_all_preludes_54.dump | 20 + .../dumps/algol68_all_preludes_55.dump | 20 + .../dumps/algol68_all_preludes_56.dump | 20 + .../dumps/algol68_all_preludes_57.dump | 20 + .../dumps/algol68_all_preludes_58.dump | 20 + .../dumps/algol68_all_preludes_59.dump | 20 + .../dumps/algol68_all_preludes_60.dump | 20 + .../dumps/algol68_all_preludes_61.dump | 20 + .../dumps/algol68_all_preludes_62.dump | 20 + .../dumps/algol68_all_preludes_63.dump | 20 + .../dumps/algol68_all_preludes_64.dump | 20 + .../dumps/algol68_all_preludes_65.dump | 20 + .../dumps/algol68_all_preludes_66.dump | 20 + .../dumps/algol68_all_preludes_67.dump | 20 + .../dumps/algol68_all_preludes_68.dump | 20 + .../dumps/algol68_all_preludes_69.dump | 20 + .../dumps/algol68_all_preludes_70.dump | 20 + .../dumps/algol68_all_preludes_71.dump | 20 + .../dumps/algol68_all_preludes_72.dump | 20 + .../dumps/algol68_all_preludes_73.dump | 20 + .../dumps/algol68_all_preludes_74.dump | 20 + .../dumps/algol68_all_preludes_75.dump | 20 + .../dumps/algol68_all_preludes_76.dump | 20 + .../dumps/algol68_all_preludes_77.dump | 20 + .../dumps/algol68_all_preludes_78.dump | 20 + .../dumps/algol68_all_preludes_79.dump | 20 + .../dumps/algol68_all_preludes_80.dump | 20 + .../dumps/algol68_all_preludes_81.dump | 20 + .../dumps/algol68_all_preludes_82.dump | 20 + .../dumps/algol68_all_preludes_83.dump | 20 + .../dumps/algol68_all_preludes_84.dump | 20 + .../dumps/algol68_all_preludes_85.dump | 20 + .../dumps/algol68_all_preludes_86.dump | 20 + .../dumps/algol68_all_preludes_87.dump | 20 + .../dumps/algol68_all_preludes_88.dump | 20 + .../dumps/algol68_all_preludes_89.dump | 20 + .../dumps/algol68_all_preludes_90.dump | 20 + .../dumps/algol68_all_preludes_91.dump | 20 + .../dumps/algol68_all_preludes_92.dump | 20 + .../dumps/algol68_all_preludes_93.dump | 20 + .../dumps/algol68_all_preludes_94.dump | 20 + .../dumps/algol68_all_preludes_95.dump | 20 + .../dumps/algol68_all_preludes_96.dump | 20 + .../dumps/algol68_all_preludes_97.dump | 20 + .../dumps/algol68_all_preludes_98.dump | 20 + .../dumps/algol68_all_preludes_99.dump | 20 + .../testdir/dumps/algol68_denotations_00.dump | 20 + .../testdir/dumps/algol68_denotations_01.dump | 20 + .../testdir/dumps/algol68_denotations_02.dump | 20 + .../testdir/dumps/algol68_denotations_03.dump | 20 + .../testdir/dumps/algol68_denotations_04.dump | 20 + .../testdir/dumps/algol68_denotations_05.dump | 20 + .../testdir/dumps/algol68_denotations_06.dump | 20 + .../testdir/input/algol68_all_preludes.a68 | 2117 +++++++++++++++++ .../testdir/input/algol68_denotations.a68 | 115 + 135 files changed, 5311 insertions(+), 1 deletion(-) create mode 100644 runtime/ftplugin/algol68.vim create mode 100644 runtime/syntax/algol68.vim create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_00.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_01.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_02.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_03.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_04.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_05.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_06.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_07.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_08.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_09.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_10.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_100.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_101.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_102.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_103.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_104.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_105.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_106.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_107.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_108.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_109.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_11.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_110.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_111.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_112.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_113.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_114.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_115.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_116.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_117.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_118.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_119.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_12.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_13.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_14.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_15.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_16.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_17.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_18.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_19.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_20.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_21.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_22.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_23.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_24.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_25.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_26.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_27.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_28.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_29.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_30.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_31.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_32.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_33.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_34.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_35.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_36.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_37.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_38.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_39.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_40.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_41.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_42.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_43.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_44.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_45.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_46.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_47.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_48.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_49.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_50.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_51.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_52.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_53.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_54.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_55.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_56.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_57.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_58.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_59.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_60.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_61.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_62.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_63.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_64.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_65.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_66.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_67.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_68.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_69.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_70.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_71.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_72.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_73.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_74.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_75.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_76.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_77.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_78.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_79.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_80.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_81.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_82.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_83.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_84.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_85.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_86.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_87.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_88.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_89.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_90.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_91.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_92.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_93.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_94.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_95.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_96.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_97.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_98.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_all_preludes_99.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_denotations_00.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_denotations_01.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_denotations_02.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_denotations_03.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_denotations_04.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_denotations_05.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_denotations_06.dump create mode 100644 runtime/syntax/testdir/input/algol68_all_preludes.a68 create mode 100644 runtime/syntax/testdir/input/algol68_denotations.a68 diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS index 17458f2d26..d73c7f10d7 100644 --- a/.github/MAINTAINERS +++ b/.github/MAINTAINERS @@ -133,6 +133,7 @@ runtime/doc/xxd-ru.1 @RestorerZ runtime/doc/xxd-ru.UTF-8.1 @RestorerZ runtime/ftplugin/abaqus.vim @costerwi runtime/ftplugin/abnf.vim @A4-Tacks +runtime/ftplugin/algol68.vim @dkearns runtime/ftplugin/antlr4.vim @jiangyinzuo runtime/ftplugin/apache.vim @dubgeiser runtime/ftplugin/arduino.vim @k-takata @@ -472,6 +473,7 @@ runtime/syntax/shared/hgcommitDiff.vim @vegerot runtime/syntax/abaqus.vim @costerwi runtime/syntax/abnf.vim @A4-Tacks runtime/syntax/aidl.vim @dpelle +runtime/syntax/algol68.vim @dkearns runtime/syntax/amiga.vim @sodero runtime/syntax/ant.vim @dkearns runtime/syntax/antlr4.vim @jiangyinzuo diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 761ff644cd..17b2618231 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -1726,6 +1726,8 @@ const ft_from_ext = { "tdf": "ahdl", # AIDL "aidl": "aidl", + # Algol 68 + "a68": "algol68", # AMPL "run": "ampl", # ANTLR / PCCTS diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 2102debf23..c18c2b5e3a 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 Apr 14 +*syntax.txt* For Vim version 9.2. Last change: 2026 Apr 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -849,6 +849,20 @@ ADA See |ft-ada-syntax| +ALGOL 68 *algol68* *ft-algol68-syntax* + +This syntax file currently targets the Algol 68 Genie project using the +default UPPER stropping regime. It should also be usuable in other +environments using the UPPER stropping regime, though somewhat less complete. + +Highlighting can be further configured with the following variables. + +Variable Highlight ~ +*algol68_no_preludes* no prelude identifiers, procedures or + bold word operators +*algol68_symbolic_operators* all prelude symbolic operators + + ANT *ant.vim* *ft-ant-syntax* The ant syntax file provides syntax highlighting for javascript and python diff --git a/runtime/doc/tags b/runtime/doc/tags index c4cc01bdc9..80b1d18bec 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -6233,6 +6233,9 @@ added-win32-GUI version5.txt /*added-win32-GUI* aff-dic-format spell.txt /*aff-dic-format* after-directory options.txt /*after-directory* aleph options.txt /*aleph* +algol68 syntax.txt /*algol68* +algol68_no_preludes syntax.txt /*algol68_no_preludes* +algol68_symbolic_operators syntax.txt /*algol68_symbolic_operators* alt intro.txt /*alt* alt-input debugger.txt /*alt-input* alternate-file editing.txt /*alternate-file* @@ -7519,6 +7522,7 @@ ft-ada-options ft_ada.txt /*ft-ada-options* ft-ada-plugin ft_ada.txt /*ft-ada-plugin* ft-ada-syntax ft_ada.txt /*ft-ada-syntax* ft-ada-variables ft_ada.txt /*ft-ada-variables* +ft-algol68-syntax syntax.txt /*ft-algol68-syntax* ft-ant-syntax syntax.txt /*ft-ant-syntax* ft-apache-syntax syntax.txt /*ft-apache-syntax* ft-arduino-plugin filetype.txt /*ft-arduino-plugin* diff --git a/runtime/ftplugin/algol68.vim b/runtime/ftplugin/algol68.vim new file mode 100644 index 0000000000..b995a1fcee --- /dev/null +++ b/runtime/ftplugin/algol68.vim @@ -0,0 +1,47 @@ +" Vim filetype plugin +" Language: Algol 68 +" Maintainer: Doug Kearns +" Last Change: 2026 Apr 23 + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +let s:cpo_save = &cpo +set cpo&vim + +" TODO: 'comments' + +setlocal commentstring=#\ %s\ # + +let &l:include='\c\%(^\|;\)\s*\%(PR\|PRAGMAT\)\s\+\%(read\|include\)' + +let b:undo_ftplugin = "setl cms< inc<" + +if exists("loaded_matchit") && !exists("b:match_words") + let b:match_ignorecase = 0 + let b:match_words = '\:\,' .. + \ '\:\:\:\:\,' .. + \ '\:\:\:\:\,' .. + "\ TODO: loops have overlapping start and intermediate keywords like + "\ `TO` which are difficult to match with patterns alone. + \ '\:\' + let b:match_skip = 's:Comment\|String\|PreProc' + let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_skip b:match_words" +endif + +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Algol 68 Source Files (*.a68)\t*.a68\n" + if has("win32") + let b:browsefilter ..= "All Files (*.*)\t*\n" + else + let b:browsefilter ..= "All Files (*)\t*\n" + endif + let b:undo_ftplugin ..= " | unlet! b:browsefilter" +endif + +let &cpo = s:cpo_save +unlet s:cpo_save + +" vim: nowrap sw=2 sts=2 ts=8 diff --git a/runtime/syntax/algol68.vim b/runtime/syntax/algol68.vim new file mode 100644 index 0000000000..129e938074 --- /dev/null +++ b/runtime/syntax/algol68.vim @@ -0,0 +1,469 @@ +" Vim syntax file +" Language: Algol 68 +" Version: 0.4 +" Maintainer: Janis Papanagnou +" Previous Maintainer: NevilleD.ALGOL_68@sgr-a.net +" Last Change: 2026 Apr 23 + +if exists("b:current_syntax") + finish +endif + +syn sync minlines=250 maxlines=500 + +" Algol68 Final Report, unrevised +syn keyword algol68PreProc PRIORITY +syn keyword algol68Operator BTB CTB CONJ QUOTE CT CTAB EITHER SIGN + + +" Algol68 Revised Report +syn keyword algol68Boolean TRUE FALSE +syn keyword algol68Conditional IF THEN ELSE ELIF FI +syn keyword algol68Conditional CASE IN OUT OUSE ESAC +syn keyword algol68Constant NIL SKIP EMPTY +syn keyword algol68Statement MODE OP PRIO PROC +syn keyword algol68Label GOTO +syn match algol68Label "\" +syn keyword algol68Operator ABS REPR ROUND ENTIER ARG BIN LENG SHORTEN ODD +syn keyword algol68Operator SHL SHR ROL ROR UP DOWN LEVEL LWB UPB I RE IM +syn keyword algol68Operator OVER MOD ELEM SET CLEAR +syn keyword algol68Operator LT LE GE GT +syn keyword algol68Operator EQ NE +syn keyword algol68Operator AND OR XOR NOT +" Genie short-circuit pseudo operators +syn keyword algol68Operator THEF ANDF ANDTH ELSF ORF OREL +syn keyword algol68Operator ANDTHEN ORELSE +syn keyword algol68Operator MINUSAB PLUSAB TIMESAB DIVAB OVERAB MODAB PLUSTO +syn keyword algol68Operator IS ISNT OF AT +syn keyword algol68Operator SORT ELEMS +syn keyword algol68Repeat FOR FROM BY UPTO DOWNTO TO WHILE DO UNTIL OD +syn keyword algol68Statement PAR BEGIN END EXIT +syn keyword algol68Struct STRUCT +syn keyword algol68PreProc VECTOR +syn keyword algol68Type FLEX HEAP LOC LONG REF SHORT +syn keyword algol68Type VOID BOOL INT REAL COMPL CHAR STRING COMPLEX +syn keyword algol68Type BITS BYTES FILE CHANNEL PIPE SEMA SOUND +syn keyword algol68Type FORMAT STRUCT UNION +" Genie extensions in addition to ROUND and ENTIER +syn keyword algol68Operator FLOOR CEIL NINT TRUNC FRAC FIX + + " 20011222az: Added new items. +syn keyword algol68Todo contained TODO FIXME XXX DEBUG NOTE + + +" String +syn region algol68String matchgroup=algol68String start=+"+ end=+"+ contains=algol68StringEscape +syn match algol68StringEscape contained '""' +syn match algol68StringEscape contained "\\$" + + +syn match algol68Identifier "\<[a-z][a-z0-9_]*\>" + + +if exists("algol68_symbolic_operators") + syn match algol68SymbolOperator "\\" + syn match algol68SymbolOperator ":=\|=" + syn match algol68SymbolOperator "[~^]" + syn match algol68SymbolOperator "[~^]=" + syn match algol68SymbolOperator "[<>]" + syn match algol68SymbolOperator "[<>]=" + syn match algol68SymbolOperator "\%([-+*%/]\|%\*\)" + syn match algol68SymbolOperator "\%([-+*%/]\|%\*\):=" + syn match algol68SymbolOperator "+=:" + syn match algol68SymbolOperator "*\*\|&" +endif + +syn match algol68Number "\<\d\+\%(\s\+\d\+\)*\>" + +syn match algol68Float "\c\.\d\+\%(\s\+\d\+\)*\%(\s*[e\\⏨]\s*[-+]\?\s*\d\+\%(\s\+\d\+\)*\)\?\>" +syn match algol68Float "\c\<\d\+\%(\s\+\d\+\)*\%(\s*[e\\⏨]\s*[-+]\?\s*\d\+\%(\s\+\d\+\)*\)\>" +syn match algol68Float "\c\<\d\+\%(\s\+\d\+\)*\s*\.\s*\d\+\%(\s\+\d\+\)*\%(\s*[e\\⏨]\s*[-+]\?\s*\d\+\%(\s\+\d\+\)*\)\?\>" + +syn match algol68HexNumber "\c\<2r\s*[01]\+\%(\s\+[01]\+\)*\>" +syn match algol68HexNumber "\c\<4r\s*[0-3]\+\%(\s\+[0-3]\+\)*\>" +syn match algol68HexNumber "\c\<8r\s*[0-7]\+\%(\s\+[0-7]\+\)*\>" +syn match algol68HexNumber "\c\<16r\s*[0-9a-f]\+\%(\s\+[0-9a-f]\+\)*\>" + + +syn region algol68Special start="\$" end="\$" contains=algol68String +syn region algol68Comment start="¢" end="¢" contains=algol68Todo,algol68SpaceError +syn region algol68Comment start="£" end="£" contains=algol68Todo,algol68SpaceError +syn region algol68Comment start="#" end="#" contains=algol68Todo,algol68SpaceError +syn region algol68Comment start="\" end="\" contains=algol68Todo,algol68SpaceError +syn region algol68Comment start="\" end="\" contains=algol68Todo,algol68SpaceError +syn region algol68PreProc start="\" end="\" contains=algol68Todo,algol68SpaceError +syn region algol68PreProc start="\" end="\" contains=algol68Todo,algol68SpaceError +" algol68r +syn region algol68Comment start="{" end="}" contains=algol68Todo,algol68SpaceError +syn region algol68Comment start="{{{" end="}}}" contains=algol68Todo,algol68SpaceError + +" ALGOL 68r +syn keyword algol68PreProc DECS CONTEXT configinfo A68CONFIG KEEP FINISH USE SYSPROCS IOSTATE FORALL +" ALGOL 68c +syn keyword algol68PreProc USING ENVIRON FOREACH ASSERT + +if !exists("algol68_no_preludes") + + +" THE STANDARD ENVIRONMENT + +" Enquiries + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Transput Files and Channels + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Transput Event Routines + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Connections to Files + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Positioning on Files + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" I/O on Files (Standard) + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" I/O on Files (Algol68C) + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Enquiries on Files + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Keyboard Control + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Math Constants + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Math Basic Functions + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!\%(\s\{1,7}[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Math Trigonometric Functions + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + " long-long-sinpi/cospi/tanpi/cotpi + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + " a special case in Genie? + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Random Number Generator + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Garbage Collection and Memory + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" I/O on Strings + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" +" Character Type Tests + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" +" Operations on Characters + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" +" Search in Strings + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Time and Date + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Type Operations + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!\%(\s*[a-z0-9]\)\@!" + +" Runtime + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + +" UNIX EXTENSIONS + +" Environment Functions + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Processes + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" File types and attributes + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Fetching web page contents and sending requests + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Regular expressions in string manipulation + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Curses support + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + +" POSTGRESQL CLIENT ROUTINES + +" Connecting to a server + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Sending queries and retrieving results + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Connection status information + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + +" SOUND + + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn keyword algol68Operator RESOLUTION CHANNELS RATE SAMPLES + + +" DRAWING USING THE GNU PLOTTING UTILITIES + +" Setting up a graphics device + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Specifying colours + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Drawing objects + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Drawing text + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + +" EXTRA NUMERICAL PROCEDURES + +" COMPLEX Functions + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + " cas casin casinh dcas dcasin dcasinh qcas qcasin qcasinh longcas longlongcas + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + " a special case in Genie? + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" REAL Airy Functions + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" REAL Bessel Functions + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + " only a few could be sensibly merged; we keep them apart + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" REAL Elliptic Integrals + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" REAL Error and Gamma Functions + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + " is the following a special case in Genie? + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "gamma\s*\%(\%(inc\s*\%(gsl\|[pq]\)\)\|inv\|star\)\>\%(\s*\%([a-z_]\|\l\d\+\)\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + + +" Scaling Factors + + " strangely missing some common factors (hecto, deca, deci, centi), + " also myria, and the more extreme factors (quetta, ronna, ronto, quecto) + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + +" Physical Constants + +" Fundamental Constants + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Astronomy and Astrophysics + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Atomic and Nuclear Physics + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Time + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Imperial units + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Nautical units + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Volume + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Mass and weight + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Thermal energy and power + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Pressure + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Viscosity + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Light and illumination + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Radioactivity + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + +" Force and energy + syn match algol68Predefined "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + +" Functions from GSL + + syn keyword algol68Operator CV RV T INV PINV MEAN DET TRACE NORM DYAD BEFORE ABOVE + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + +" Functions from R Mathlib + + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + " note: Genie documents 'r rn chisq' but it's missing in the code? + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + syn match algol68Function "\%(\%([a-z_]\|\l\d\+\)\s\+\)\@8\%(\s*[a-z0-9]\)\@!" + + +endif + +" Define the default highlighting. +hi def link algol68Boolean Boolean +hi def link algol68Comment Comment +hi def link algol68Conditional Conditional +hi def link algol68Constant Constant +hi def link algol68Float Float +hi def link algol68Function Function +hi def link algol68Label Label +hi def link algol68MatrixDelimiter Identifier +hi def link algol68HexNumber Number +hi def link algol68Number Number +hi def link algol68Operator Operator +hi def link algol68Predefined Identifier +hi def link algol68PreProc PreProc +hi def link algol68Repeat Repeat +hi def link algol68SpaceError Error +hi def link algol68Statement Statement +hi def link algol68String String +hi def link algol68StringEscape Special +hi def link algol68Struct algol68Statement +hi def link algol68SymbolOperator algol68Operator +hi def link algol68Todo Todo +hi def link algol68Type Type +hi def link algol68ShowTab Error + +let b:current_syntax = "algol68" + +" vim: ts=8 sw=2 diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_00.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_00.dump new file mode 100644 index 0000000000..bc7bf4a65b --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_00.dump @@ -0,0 +1,20 @@ +>#+0#0000e05#ffffff0| +0#0000000&@73 +| +0#0000e05&@3|V|I|M|_|T|E|S|T|_|S|E|T|U|P| |l|e|t| |g|:|a|l|g|o|l|6|8|_|s|y|m|b|o|l|i|c|_|o|p|e|r|a|t|o|r|s| |=| |1| +0#0000000&@19 +|#+0#0000e05&| +0#0000000&@73 +|#+0#0000e05&| +0#0000000&@73 +| +0#0000e05&@3|A|l|g|o|l| |6|8| |p|r|e|l|u|d|e| |i|d|e|n|t|i|f|i|e|r| |f|r|o|m| |t|h|e| |G|e|n|i|e| |s|o|u|r|c|e| |c|o|d|e| |f|i|l|e|s|.| +0#0000000&@9 +| +0#0000e05&@3| +0#0000000&@70 +| +0#0000e05&@3|P|r|o|v|i|d|e|d| |a|s| |a| |b|a|s|e| |t|o| |v|e|r|i|f|y| |t|h|e| |c|o|r@1|e|c|t| |s|y|n|t|a|x| |h|i|g|h|l|i|g|h|t|i|n|g| +0#0000000&@10 +| +0#0000e05&@3|o|f| |t|h|e| |a|l|g|o|l|6|8|.|v|i|m| |s|y|n|t|a|x| |f|i|l|e| |f|o|r| |t|h|e| |V|i|m| |e|d|i|t|o|r|.| +0#0000000&@20 +| +0#0000e05&@3| +0#0000000&@70 +| +0#0000e05&@3|T|h|e|r|e|'|s| |f|o|u|r| |s|e|c|t|i|o|n|s|,| |e|a|c|h| |r|e|p|r|e|s|e|n|t|i|n|g| |a|n| |o|r|i|g|i|n|a|l| |f|i|l|e|,| +0#0000000&@12 +| +0#0000e05&@3|t|h|a|t| |a|r|e| |e|n|c|l|o|s|e|d| |i|n| |t|h|e| |U|n|i|x|-|'|m|o|r|e|'| |f|o|r|m| |t|o| |s|t|a|n|d| |o|u|t|.| +0#0000000&@15 +| +0#0000e05&@3| +0#0000000&@70 +| +0#0000e05&@3|S|o|m|e| |i|n|f|o|r|m|a|l| |c|o|m@1|e|n|t|s| |f|r|o|m| |t|h|e| |s|o|u|r|c|e| |c|o|d|e| |l|e|f|t| |i|n|t|a|c|t| |a|s| +0#0000000&@12 +| +0#0000e05&@3|a| |c|o|m@1|e|n|t| |i|n| |A|l|g|o|l| |6|8| |s|y|n|t|a|x|.| +0#0000000&@41 +| +0#0000e05&@3| +0#0000000&@70 +| +0#0000e05&@3|D|e|p|e|n|d|i|n|g| |o|n| |t|h|e| |l|a|n|g|u|a|g|e| |c|o|n|t|e|x|t| |t|h|e|r|e|'|s| |d|u|p|l|i|c|a|t|e|s| |i|n| |t|h|e| +0#0000000&@11 +| +0#0000e05&@3|l|i|s|t|,| |t|h|a|t| |a|r|e| |k|e|p|t| |f|o|r| |c|l|a|r|i|t|y| |g|i|v|e|n| |t|h|e| |a|s@1|o|c|i|a|t|e|d| |i|n|f|o|r|m|a|l| +0#0000000&@9 +| +0#0000e05&@3|c|o|m@1|e|n|t| |a|s@1|o|c|i|a|t|e|d| |w|i|t|h| |t|h|e|m|.| +0#0000000&@41 +| +0#0000e05&@3| +0#0000000&@70 +@57|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_01.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_01.dump new file mode 100644 index 0000000000..a25666aeec --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_01.dump @@ -0,0 +1,20 @@ +| +0#0000e05#ffffff0@3|a| |c|o|m@1|e|n|t| |i|n| |A|l|g|o|l| |6|8| |s|y|n|t|a|x|.| +0#0000000&@41 +| +0#0000e05&@3| +0#0000000&@70 +| +0#0000e05&@3|D|e|p|e|n|d|i|n|g| |o|n| |t|h|e| |l|a|n|g|u|a|g|e| |c|o|n|t|e|x|t| |t|h|e|r|e|'|s| |d|u|p|l|i|c|a|t|e|s| |i|n| |t|h|e| +0#0000000&@11 +| +0#0000e05&@3|l|i|s|t|,| |t|h|a|t| |a|r|e| |k|e|p|t| |f|o|r| |c|l|a|r|i|t|y| |g|i|v|e|n| |t|h|e| |a|s@1|o|c|i|a|t|e|d| |i|n|f|o|r|m|a|l| +0#0000000&@9 +| +0#0000e05&@3|c|o|m@1|e|n|t| |a|s@1|o|c|i|a|t|e|d| |w|i|t|h| |t|h|e|m|.| +0#0000000&@41 +| +0#0000e05&@2> | +0#0000000&@70 +| +0#0000e05&@3|S|o|m|e| |e|n|t|r|i|e|s| |d|e|l|i|b|e|r|a|t|e|l|y| |c|o|n|t|a|i|n| |t|w|o| |i|d|e|n|t|i|f|i|e|r| |v|a|r|i|a|n|t|s| |t|h|a|t| +0#0000000&@8 +| +0#0000e05&@3|m|a|y| |b|e| |u|s|e|d| |a|s| |a|l|t|e|r|n|a|t|i|v|e| |f|o|r|m| |i|n| |a|n| |A|l|g|o|l| |6|8| |G|e|n|i|e| |p|r|o|g|r|a|m|.| +0#0000000&@9 +| +0#0000e05&@3| +0#0000000&@70 +| +0#0000e05&@3|T|h|e| |i|d|e|n|t|i|f|i|e|r|s| |a|r|e| |p|r|e|s|e|n|t|e|d| |i|n| |t|w|o| |v|a|r|i|a|n|t|s| |s|e|p|a|r|a|t|e|d| |b|y| |a| +0#0000000&@10 +| +0#0000e05&@3|t|a|b|u|l|a|t|o|r|;| |f|i|r|s|t| |w|i|t|h| |s|p|a|c|e|s|,| |t|h|e|n| |i|n| |c|a|n|o|n|i|c|a|l| |f|o|r|m| |w|i|t|h|o|u|t| +0#0000000&@10 +| +0#0000e05&@3|s|p|a|c|e|s|.| |V|a|r|i|a|n|t|s| |w|i|t|h| |s|p|a|c|e|s| |a|r|e| |s|u|p@1|o|r|t|e|d| |b|y| |t|h|e| |V|i|m| |s|y|n|t|a|x| +0#0000000&@10 +| +0#0000e05&@3|f|i|l|e| |a|s| |t|h|e| |A|l|g|o|l| |6|8| |l|a|n|g|u|a|g|e| |d|o|e|s|.| +0#0000000&@35 +| +0#0000e05&@3|B|u|t| |n|o|t|e| |t|h|a|t| |w|h|i|l|e| |A|l|g|o|l| |6|8| |a|l@1|o|w|s| |a|r|b|i|t|r|a|r|y| |s|p|a|c|i|n|g| |w|i|t|h|i|n| +0#0000000&@10 +| +0#0000e05&@3|i|d|e|n|t|i|f|i|e|r|s| |-| |e|v|e|n| |a|s| |e|x|t|r|e|m|e| |a|s| |w|r|i|t|i|n|g| |o|n|e| |c|h|a|r|a|c|t|e|r| |p|e|r| +0#0000000&@12 +| +0#0000e05&@3|l|i|n|e|!| |-| |t|h|e| |V|i|m| |s|y|n|t|a|x| |f|i|l|e| |f|o|r| |A|l|g|o|l| |6|8| |h|a|d| |d|e|l|i|b|e|r|a|t|e|l|y| +0#0000000&@13 +| +0#0000e05&@3|b|e@1|n| |d|e|f|i|n|e|d| |i|n| |a| |w|a|y| |r|e|s|t|r|i|c|t|i|n|g| |t|h|e| |h|i|g|h|l|i|g|h|t|e|d| |o|p|t|i|o|n|s|;| +0#0000000&@12 +| +0#0000e05&@3|s|p|a|c|i|n|g| |c|a|n| |u|s|u|a|l@1|y| |b|e| |i|n|s|e|r|t|e|d| |w|h|e|r|e| |"|n|a|t|u|r|a|l| |w|o|r|d|s|"| |w|o|u|l|d| +0#0000000&@11 +| +0#0000e05&@3|a|l@1|o|w| |a| |s|e|p|a|r|a|t|i|o|n| |(|s|a|y| |f|o|r| |'|i|n|t|w|i|d|t|h|'| |y|o|u| |m|a|y| |w|r|i|t|e|,| |e|.|g|.|,| +0#0000000&@11 +@57|1|9|,|4| @10|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_02.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_02.dump new file mode 100644 index 0000000000..bf271aaf2a --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_02.dump @@ -0,0 +1,20 @@ +| +0#0000e05#ffffff0@3|a|l@1|o|w| |a| |s|e|p|a|r|a|t|i|o|n| |(|s|a|y| |f|o|r| |'|i|n|t|w|i|d|t|h|'| |y|o|u| |m|a|y| |w|r|i|t|e|,| |e|.|g|.|,| +0#0000000&@11 +| +0#0000e05&@3|'|i|n|t| |w|i|d|t|h|'| |b|u|t| |n|o|t| |'|i|n|t| |w|i|d| |t|h|'|;| |t|h|e| |l|a|t@1|e|r| |w|o|u|l|d| |n|o|t| |g|e|t| +0#0000000&@12 +| +0#0000e05&@3|h|i|g|h|l|i|g|h|t|e|d|)|.| +0#0000000&@57 +| +0#0000e05&@3| +0#0000000&@70 +| +0#0000e05&@3|M|o|d|e|l|i|n|e|s| |a|r|e| |s|e|t| |t|o| |s|i|m|p|l|y| |i|n|s|p|e|c|t| |t|h|e| |c|o|r@1|e|c|t| |h|i|g|h|l|i|g|h|t|i|n|g| +0#0000000&@10 +| +0#0000e05&@3>i|n| |t|h|i|s| |f|i|l|e|.| +0#0000000&@57 +| +0#0000e05&@3| +0#0000000&@70 +| +0#0000e05&@3|J|a|n|i|s| |P|a|p|a|n|a|g|n|o|u|,| |2|0|2|6|-|0|4|-|2|3| +0#0000000&@42 +|#+0#0000e05&| +0#0000000&@73 +@75 +|#+0#0000e05&|:@12| +0#0000000&@60 +|p+0#0000e05&|r|e|l|u|d|e|.|c| +0#0000000&@65 +|:+0#0000e05&@12|#| +0#0000000&@60 +@75 +|#+0#0000e05&| |P|r|i|m|i|t|i|v|e| |A|6|8| |m|o|i|d|s|.| |#| +0#0000000&@50 +@2|V+0#00e0003&|O|I|D| +0#0000000&@41|V+0#00e0003&|O|I|D| +0#0000000&@22 +|#+0#0000e05&| |S|t|a|n|d|a|r|d| |p|r|e|c|i|s|i|o|n|.| |#| +0#0000000&@51 +@2|I+0#00e0003&|N|T| +0#0000000&@42|I+0#00e0003&|N|T| +0#0000000&@23 +@2|R+0#00e0003&|E|A|L| +0#0000000&@41|R+0#00e0003&|E|A|L| +0#0000000&@22 +@57|3|7|,|5| @10|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_03.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_03.dump new file mode 100644 index 0000000000..3577dd3b8b --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_03.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|R+0#00e0003&|E|A|L| +0#0000000&@41|R+0#00e0003&|E|A|L| +0#0000000&@22 +@2|C+0#00e0003&|O|M|P|L|E|X| +0#0000000&@38|C+0#00e0003&|O|M|P|L|E|X| +0#0000000&@19 +@2|C+0#00e0003&|O|M|P|L| +0#0000000&@40|C+0#00e0003&|O|M|P|L| +0#0000000&@21 +@2|B+0#00e0003&|I|T|S| +0#0000000&@41|B+0#00e0003&|I|T|S| +0#0000000&@22 +@2|B+0#00e0003&|Y|T|E|S| +0#0000000&@40|B+0#00e0003&|Y|T|E|S| +0#0000000&@21 +>#+0#0000e05&| |M|u|l|t|i|p|l|e| |p|r|e|c|i|s|i|o|n|.| |#| +0#0000000&@51 +@2|I+0#00e0003&|N|T| +0#0000000&@42|I+0#00e0003&|N|T| +0#0000000&@23 +@2|R+0#00e0003&|E|A|L| +0#0000000&@41|R+0#00e0003&|E|A|L| +0#0000000&@22 +@2|C+0#00e0003&|O|M|P|L|E|X| +0#0000000&@38|C+0#00e0003&|O|M|P|L|E|X| +0#0000000&@19 +@2|C+0#00e0003&|O|M|P|L| +0#0000000&@40|C+0#00e0003&|O|M|P|L| +0#0000000&@21 +@2|B+0#00e0003&|I|T|S| +0#0000000&@41|B+0#00e0003&|I|T|S| +0#0000000&@22 +@2|B+0#00e0003&|Y|T|E|S| +0#0000000&@40|B+0#00e0003&|Y|T|E|S| +0#0000000&@21 +@2|R+0#00e0003&|E|A|L| +0#0000000&@41|R+0#00e0003&|E|A|L| +0#0000000&@22 +@2|I+0#00e0003&|N|T| +0#0000000&@42|I+0#00e0003&|N|T| +0#0000000&@23 +@2|C+0#00e0003&|O|M|P|L|E|X| +0#0000000&@38|C+0#00e0003&|O|M|P|L|E|X| +0#0000000&@19 +@2|C+0#00e0003&|O|M|P|L| +0#0000000&@40|C+0#00e0003&|O|M|P|L| +0#0000000&@21 +@2|B+0#00e0003&|I|T|S| +0#0000000&@41|B+0#00e0003&|I|T|S| +0#0000000&@22 +|#+0#0000e05&| |O|t|h|e|r|.| |#| +0#0000000&@64 +@2|B+0#00e0003&|O@1|L| +0#0000000&@41|B+0#00e0003&|O@1|L| +0#0000000&@22 +@57|5@1|,|1| @10|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_04.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_04.dump new file mode 100644 index 0000000000..eb2257c808 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_04.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|B+0#00e0003&|O@1|L| +0#0000000&@41|B+0#00e0003&|O@1|L| +0#0000000&@22 +@2|C+0#00e0003&|H|A|R| +0#0000000&@41|C+0#00e0003&|H|A|R| +0#0000000&@22 +@2|S+0#00e0003&|T|R|I|N|G| +0#0000000&@39|S+0#00e0003&|T|R|I|N|G| +0#0000000&@20 +@2|F+0#00e0003&|I|L|E| +0#0000000&@41|F+0#00e0003&|I|L|E| +0#0000000&@22 +@2|C+0#00e0003&|H|A|N@1|E|L| +0#0000000&@38|C+0#00e0003&|H|A|N@1|E|L| +0#0000000&@19 +@2>P+0#00e0003&|I|P|E| +0#0000000&@41|P+0#00e0003&|I|P|E| +0#0000000&@22 +@2|F+0#00e0003&|O|R|M|A|T| +0#0000000&@39|F+0#00e0003&|O|R|M|A|T| +0#0000000&@20 +@2|S+0#00e0003&|E|M|A| +0#0000000&@41|S+0#00e0003&|E|M|A| +0#0000000&@22 +@2|S+0#00e0003&|O|U|N|D| +0#0000000&@40|S+0#00e0003&|O|U|N|D| +0#0000000&@21 +|#+0#0000e05&| |I|d|e|n|t|i|f|i|e|r|s|.| |#| +0#0000000&@58 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |p|i|;+0#0000000&| @1|q+0#00e0e07&|p|i|;+0#0000000&| @26|l+0#00e0e07&|o|n|g|l|o|n|g|p|i|;+0#0000000&| @1|q+0#00e0e07&|p|i| +0#0000000&@10 +|#+0#0000e05&| |#| +0#0000000&@71 +@2|b+0#00e0e07&|i|t|s| |l|e|n|g|t|h|s|;+0#0000000&| @32|b+0#00e0e07&|i|t|s|l|e|n|g|t|h|s| +0#0000000&@15 +@2|b+0#00e0e07&|i|t|s| |s|h|o|r|t|h|s|;+0#0000000&| @32|b+0#00e0e07&|i|t|s@1|h|o|r|t|h|s| +0#0000000&@15 +@2|b+0#00e0e07&|i|t|s| |w|i|d|t|h|;+0#0000000&| @34|b+0#00e0e07&|i|t|s|w|i|d|t|h| +0#0000000&@17 +@2|b+0#00e0e07&|y|t|e|s| |l|e|n|g|t|h|s|;+0#0000000&| @31|b+0#00e0e07&|y|t|e|s|l|e|n|g|t|h|s| +0#0000000&@14 +@2|b+0#00e0e07&|y|t|e|s| |s|h|o|r|t|h|s|;+0#0000000&| @31|b+0#00e0e07&|y|t|e|s@1|h|o|r|t|h|s| +0#0000000&@14 +@2|b+0#00e0e07&|y|t|e|s| |w|i|d|t|h|;+0#0000000&| @33|b+0#00e0e07&|y|t|e|s|w|i|d|t|h| +0#0000000&@16 +@2|c+0#00e0e07&|o|m|p|l| |l|e|n|g|t|h|s|;+0#0000000&| @31|c+0#00e0e07&|o|m|p|l@1|e|n|g|t|h|s| +0#0000000&@14 +@57|7|3|,|3| @10|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_05.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_05.dump new file mode 100644 index 0000000000..23e72a5ec4 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_05.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|o|m|p|l| |l|e|n|g|t|h|s|;+0#0000000&| @31|c+0#00e0e07&|o|m|p|l@1|e|n|g|t|h|s| +0#0000000&@14 +@2|c+0#00e0e07&|o|m|p|l| |s|h|o|r|t|h|s|;+0#0000000&| @31|c+0#00e0e07&|o|m|p|l|s|h|o|r|t|h|s| +0#0000000&@14 +@2|e+0#00e0e07&|x|p| |w|i|d|t|h|;+0#0000000&| @35|e+0#00e0e07&|x|p|w|i|d|t|h| +0#0000000&@18 +@2|i+0#00e0e07&|n|f|i|n|i|t|y|;+0#0000000&| @36|i+0#00e0e07&|n|f|i|n|i|t|y| +0#0000000&@18 +@2|i+0#00e0e07&|n|f|;+0#0000000&| @41|i+0#00e0e07&|n|f| +0#0000000&@23 +@2>i+0#00e0e07&|n|t| |l|e|n|g|t|h|s|;+0#0000000&| @33|i+0#00e0e07&|n|t|l|e|n|g|t|h|s| +0#0000000&@16 +@2|i+0#00e0e07&|n|t| |s|h|o|r|t|h|s|;+0#0000000&| @33|i+0#00e0e07&|n|t|s|h|o|r|t|h|s| +0#0000000&@16 +@2|i+0#00e0e07&|n|t| |w|i|d|t|h|;+0#0000000&| @35|i+0#00e0e07&|n|t|w|i|d|t|h| +0#0000000&@18 +@2|l+0#00e0e07&|o|n|g| |b|i|t|s| |w|i|d|t|h|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|b|i|t|s|w|i|d|t|h| +0#0000000&@13 +@2|l+0#00e0e07&|o|n|g| |b|y|t|e|s| |w|i|d|t|h|;+0#0000000&| @28|l+0#00e0e07&|o|n|g|b|y|t|e|s|w|i|d|t|h| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |e|x|p| |w|i|d|t|h|;+0#0000000&| @30|l+0#00e0e07&|o|n|g|e|x|p|w|i|d|t|h| +0#0000000&@14 +@2|l+0#00e0e07&|o|n|g| |i|n|t| |w|i|d|t|h|;+0#0000000&| @30|l+0#00e0e07&|o|n|g|i|n|t|w|i|d|t|h| +0#0000000&@14 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |e|x|p|w|i|d|t|h|;+0#0000000&| @26|l+0#00e0e07&|o|n|g|l|o|n|g|e|x|p|w|i|d|t|h| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |i|n|f|i|n|i|t|y|;+0#0000000&| @26|l+0#00e0e07&|o|n|g|l|o|n|g|i|n|f|i|n|i|t|y| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |i|n|f|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|l|o|n|g|i|n|f| +0#0000000&@15 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |i|n|t|w|i|d|t|h|;+0#0000000&| @26|l+0#00e0e07&|o|n|g|l|o|n|g|i|n|t|w|i|d|t|h| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |m|a|x|i|n|t|;+0#0000000&| @28|l+0#00e0e07&|o|n|g|l|o|n|g|m|a|x|i|n|t| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |m|a|x|r|e|a|l|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|l|o|n|g|m|a|x|r|e|a|l| +0#0000000&@11 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |m|i|n|i|n|f|;+0#0000000&| @28|l+0#00e0e07&|o|n|g|l|o|n|g|m|i|n|i|n|f| +0#0000000&@12 +@57|9|1|,|3| @10|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_06.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_06.dump new file mode 100644 index 0000000000..2801b74182 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_06.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |l|o|n|g| |m|i|n|i|n|f|;+0#0000000&| @28|l+0#00e0e07&|o|n|g|l|o|n|g|m|i|n|i|n|f| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |m|i|n|r|e|a|l|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|l|o|n|g|m|i|n|r|e|a|l| +0#0000000&@11 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |m|i|n|u|s| |i|n|f|i|n|i|t|y|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|o|n|g|m|i|n|u|s|i|n|f|i|n|i|t|y| +0#0000000&@5 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |r|e|a|l| |w|i|d|t|h|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|r|e|a|l|w|i|d|t|h| +0#0000000&@9 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |s|m|a|l@1| |r|e|a|l|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|s|m|a|l@1|r|e|a|l| +0#0000000&@9 +@2>l+0#00e0e07&|o|n|g| |r|e|a|l| |w|i|d|t|h|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|r|e|a|l|w|i|d|t|h| +0#0000000&@13 +@2|m+0#00e0e07&|a|x| |a|b|s| |c|h|a|r|;+0#0000000&| @32|m+0#00e0e07&|a|x|a|b|s|c|h|a|r| +0#0000000&@16 +@2|m+0#00e0e07&|a|x| |b|i|t|s|;+0#0000000&| @36|m+0#00e0e07&|a|x|b|i|t|s| +0#0000000&@19 +@2|m+0#00e0e07&|a|x| |i|n|t|;+0#0000000&| @37|m+0#00e0e07&|a|x|i|n|t| +0#0000000&@20 +@2|m+0#00e0e07&|a|x| |r|e|a|l|;+0#0000000&| @36|m+0#00e0e07&|a|x|r|e|a|l| +0#0000000&@19 +@2|m+0#00e0e07&|i|n| |i|n|f|;+0#0000000&| @37|m+0#00e0e07&|i|n|i|n|f| +0#0000000&@20 +@2|m+0#00e0e07&|i|n| |r|e|a|l|;+0#0000000&| @36|m+0#00e0e07&|i|n|r|e|a|l| +0#0000000&@19 +@2|m+0#00e0e07&|i|n|u|s| |i|n|f|i|n|i|t|y|;+0#0000000&| @30|m+0#00e0e07&|i|n|u|s|i|n|f|i|n|i|t|y| +0#0000000&@13 +@2|m+0#00e0e07&|p|r|a|d|i|x|;+0#0000000&| @37|m+0#00e0e07&|p|r|a|d|i|x| +0#0000000&@19 +@2|n+0#00e0e07&|a|n|;+0#0000000&| @41|n+0#00e0e07&|a|n| +0#0000000&@23 +@2|p+0#00e0e07&|i|;+0#0000000&| @42|p+0#00e0e07&|i| +0#0000000&@24 +@2|r+0#00e0e07&|e|a|l| |l|e|n|g|t|h|s|;+0#0000000&| @32|r+0#00e0e07&|e|a|l@1|e|n|g|t|h|s| +0#0000000&@15 +@2|r+0#00e0e07&|e|a|l| |s|h|o|r|t|h|s|;+0#0000000&| @32|r+0#00e0e07&|e|a|l|s|h|o|r|t|h|s| +0#0000000&@15 +@2|r+0#00e0e07&|e|a|l| |w|i|d|t|h|;+0#0000000&| @34|r+0#00e0e07&|e|a|l|w|i|d|t|h| +0#0000000&@17 +@57|1|0|9|,|3| @9|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_07.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_07.dump new file mode 100644 index 0000000000..3e8f59b0e4 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_07.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|r+0#00e0e07&|e|a|l| |w|i|d|t|h|;+0#0000000&| @34|r+0#00e0e07&|e|a|l|w|i|d|t|h| +0#0000000&@17 +@2|s+0#00e0e07&|m|a|l@1| |r|e|a|l|;+0#0000000&| @34|s+0#00e0e07&|m|a|l@1|r|e|a|l| +0#0000000&@17 +@2|c+0#00e0e07&|l|o|c|k|;+0#0000000&| @39|c+0#00e0e07&|l|o|c|k| +0#0000000&@21 +@2|c+0#00e0e07&|p|u| |t|i|m|e|;+0#0000000&| @36|c+0#00e0e07&|p|u|t|i|m|e| +0#0000000&@19 +@2|s+0#00e0e07&|e|c|o|n|d|s|;+0#0000000&| @37|s+0#00e0e07&|e|c|o|n|d|s| +0#0000000&@19 +@2>w+0#00e0e07&|a|l@1| |c|l|o|c|k|;+0#0000000&| @34|w+0#00e0e07&|a|l@1|c|l|o|c|k| +0#0000000&@17 +@2|w+0#00e0e07&|a|l@1| |s|e|c|o|n|d|s|;+0#0000000&| @32|w+0#00e0e07&|a|l@1|s|e|c|o|n|d|s| +0#0000000&@15 +@2|w+0#00e0e07&|a|l@1| |t|i|m|e|;+0#0000000&| @35|w+0#00e0e07&|a|l@1|t|i|m|e| +0#0000000&@18 +@2|b+0#00e0e07&|l|o|c|k|s|;+0#0000000&| @38|b+0#00e0e07&|l|o|c|k|s| +0#0000000&@20 +@2|c+0#00e0e07&|o|l@1|e|c|t|i|o|n|s|;+0#0000000&| @33|c+0#00e0e07&|o|l@1|e|c|t|i|o|n|s| +0#0000000&@15 +@2|c+0#00e0e07&|o|l@1|e|c|t| |s|e|c|o|n|d|s|;+0#0000000&| @29|c+0#00e0e07&|o|l@1|e|c|t|s|e|c|o|n|d|s| +0#0000000&@12 +@2|g+0#00e0e07&|a|r|b|a|g|e|;+0#0000000&| @37|g+0#00e0e07&|a|r|b|a|g|e| +0#0000000&@19 +@2|g+0#00e0e07&|a|r|b|a|g|e| |c|o|l@1|e|c|t|i|o|n|s|;+0#0000000&| @25|g+0#00e0e07&|a|r|b|a|g|e|c|o|l@1|e|c|t|i|o|n|s| +0#0000000&@8 +@2|g+0#00e0e07&|a|r|b|a|g|e| |f|r|e@1|d|;+0#0000000&| @31|g+0#00e0e07&|a|r|b|a|g|e|f|r|e@1|d| +0#0000000&@14 +@2|g+0#00e0e07&|a|r|b|a|g|e| |r|e|f|u|s|e|d|;+0#0000000&| @29|g+0#00e0e07&|a|r|b|a|g|e|r|e|f|u|s|e|d| +0#0000000&@12 +@2|g+0#00e0e07&|a|r|b|a|g|e| |s|e|c|o|n|d|s|;+0#0000000&| @29|g+0#00e0e07&|a|r|b|a|g|e|s|e|c|o|n|d|s| +0#0000000&@12 +@2|o+0#00e0e07&|n| |g|c| |e|v|e|n|t|;+0#0000000&| @33|o+0#00e0e07&|n|g|c|e|v|e|n|t| +0#0000000&@17 +@2|s+0#00e0e07&|w|e@1|p|s|;+0#0000000&| @38|s+0#00e0e07&|w|e@1|p|s| +0#0000000&@20 +@2|s+0#00e0e07&|w|e@1|p|s| |r|e|f|u|s|e|d|;+0#0000000&| @30|s+0#00e0e07&|w|e@1|p|s|r|e|f|u|s|e|d| +0#0000000&@13 +@57|1|2|7|,|3| @9|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_08.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_08.dump new file mode 100644 index 0000000000..451216737e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_08.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|s+0#00e0e07&|w|e@1|p|s| |r|e|f|u|s|e|d|;+0#0000000&| @30|s+0#00e0e07&|w|e@1|p|s|r|e|f|u|s|e|d| +0#0000000&@13 +@2|s+0#00e0e07&|t|a|c|k| |p|o|i|n|t|e|r|;+0#0000000&| @31|s+0#00e0e07&|t|a|c|k|p|o|i|n|t|e|r| +0#0000000&@14 +@2|s+0#00e0e07&|y|s|t|e|m| |s|t|a|c|k| |p|o|i|n|t|e|r|;+0#0000000&| @24|s+0#00e0e07&|y|s|t|e|m|s|t|a|c|k|p|o|i|n|t|e|r| +0#0000000&@8 +@2|s+0#00e0e07&|y|s|t|e|m| |s|t|a|c|k| |s|i|z|e|;+0#0000000&| @27|s+0#00e0e07&|y|s|t|e|m|s|t|a|c|k|s|i|z|e| +0#0000000&@11 +@2|a+0#00e0e07&|c|t|u|a|l| |s|t|a|c|k| |s|i|z|e|;+0#0000000&| @27|a+0#00e0e07&|c|t|u|a|l|s|t|a|c|k|s|i|z|e| +0#0000000&@11 +@2>h+0#00e0e07&|e|a|p| |p|o|i|n|t|e|r|;+0#0000000&| @32|h+0#00e0e07&|e|a|p@1|o|i|n|t|e|r| +0#0000000&@15 +@2|s+0#00e0e07&|y|s|t|e|m| |h|e|a|p| |p|o|i|n|t|e|r|;+0#0000000&| @25|s+0#00e0e07&|y|s|t|e|m|h|e|a|p@1|o|i|n|t|e|r| +0#0000000&@9 +@2|g+0#00e0e07&|c| |h|e|a|p|;+0#0000000&| @37|g+0#00e0e07&|c|h|e|a|p| +0#0000000&@20 +@2|s+0#00e0e07&|w|e@1|p| |h|e|a|p|;+0#0000000&| @34|s+0#00e0e07&|w|e@1|p|h|e|a|p| +0#0000000&@17 +@2|p+0#00e0e07&|r|e@1|m|p|t|i|v|e| |g|c|;+0#0000000&| @31|p+0#00e0e07&|r|e@1|m|p|t|i|v|e|g|c| +0#0000000&@14 +@2|p+0#00e0e07&|r|e@1|m|p|t|i|v|e| |s|w|e@1|p|;+0#0000000&| @28|p+0#00e0e07&|r|e@1|m|p|t|i|v|e|s|w|e@1|p| +0#0000000&@11 +@2|p+0#00e0e07&|r|e@1|m|p|t|i|v|e| |s|w|e@1|p|h|e|a|p|;+0#0000000&| @24|p+0#00e0e07&|r|e@1|m|p|t|i|v|e|s|w|e@1|p|h|e|a|p| +0#0000000&@7 +@2|b+0#00e0e07&|a|c|k|t|r|a|c|e|;+0#0000000&| @35|b+0#00e0e07&|a|c|k|t|r|a|c|e| +0#0000000&@17 +@2|b+0#00e0e07&|r|e|a|k|;+0#0000000&| @39|b+0#00e0e07&|r|e|a|k| +0#0000000&@21 +@2|d+0#00e0e07&|e|b|u|g|;+0#0000000&| @39|d+0#00e0e07&|e|b|u|g| +0#0000000&@21 +@2|m+0#00e0e07&|o|n|i|t|o|r|;+0#0000000&| @37|m+0#00e0e07&|o|n|i|t|o|r| +0#0000000&@19 +@2|a+0#00e0e07&|b|e|n|d|;+0#0000000&| @39|a+0#00e0e07&|b|e|n|d| +0#0000000&@21 +@2|e+0#00e0e07&|v|a|l|u|a|t|e|;+0#0000000&| @36|e+0#00e0e07&|v|a|l|u|a|t|e| +0#0000000&@18 +@2|s+0#00e0e07&|y|s|t|e|m|;+0#0000000&| @38|s+0#00e0e07&|y|s|t|e|m| +0#0000000&@20 +@57|1|4|5|,|3| @9|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_09.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_09.dump new file mode 100644 index 0000000000..54bce6a72c --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_09.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|s+0#00e0e07&|y|s|t|e|m|;+0#0000000&| @38|s+0#00e0e07&|y|s|t|e|m| +0#0000000&@20 +@2|s+0#00e0e07&|l|e@1|p|;+0#0000000&| @39|s+0#00e0e07&|l|e@1|p| +0#0000000&@21 +|#+0#0000e05&| |M|a|c|h|i|n|e| |e|n|v|i|r|o|n| |p|a|r|a|m|e|t|e|r|s|.| |#| +0#0000000&@43 +@2|i+0#00e0e07&|3|2|m|a|c|h|;+0#0000000&| @37|i+0#00e0e07&|3|2|m|a|c|h| +0#0000000&@19 +@2|r+0#00e0e07&|6|4|m|a|c|h|;+0#0000000&| @37|r+0#00e0e07&|6|4|m|a|c|h| +0#0000000&@19 +@2>i+0#00e0e07&|6|4|m|a|c|h|;+0#0000000&| @37|i+0#00e0e07&|6|4|m|a|c|h| +0#0000000&@19 +@2|r+0#00e0e07&|1|2|8|m|a|c|h|;+0#0000000&| @36|r+0#00e0e07&|1|2|8|m|a|c|h| +0#0000000&@18 +|#+0#0000e05&| |B|I|T|S| |p|r|o|c|e|d|u|r|e|s|.| |#| +0#0000000&@54 +@2|b+0#00e0e07&|i|t|s| |p|a|c|k|;+0#0000000&| @35|b+0#00e0e07&|i|t|s|p|a|c|k| +0#0000000&@18 +|#+0#0000e05&| |R|N|G| |p|r|o|c|e|d|u|r|e|s|.| |#| +0#0000000&@55 +@2|f+0#00e0e07&|i|r|s|t| |r|a|n|d|o|m|;+0#0000000&| @32|f+0#00e0e07&|i|r|s|t|r|a|n|d|o|m| +0#0000000&@15 +@2|n+0#00e0e07&|e|x|t| |r|a|n|d|o|m|;+0#0000000&| @33|n+0#00e0e07&|e|x|t|r|a|n|d|o|m| +0#0000000&@16 +@2|r+0#00e0e07&|a|n|d|o|m|;+0#0000000&| @38|r+0#00e0e07&|a|n|d|o|m| +0#0000000&@20 +@2|r+0#00e0e07&|n|d|;+0#0000000&| @41|r+0#00e0e07&|n|d| +0#0000000&@23 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |n|e|x|t|r|a|n|d|o|m|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|n|e|x|t|r|a|n|d|o|m| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |r|a|n|d|o|m|;+0#0000000&| @28|l+0#00e0e07&|o|n|g|l|o|n|g|r|a|n|d|o|m| +0#0000000&@12 +|#+0#0000e05&| |P|r|i|o|r|i|t|i|e|s|.| |#| +0#0000000&@59 +|#+0#0000e05&| |I|N|T| |o|p|s|.| |#| +0#0000000&@62 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@57|1|6|3|,|3| @9|7|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_10.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_10.dump new file mode 100644 index 0000000000..b23d38ab63 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_10.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|S+0#af5f00255&|I|G|N| +0#0000000&@41|S+0#af5f00255&|I|G|N| +0#0000000&@22 +@2|O+0#af5f00255&|D@1| +0#0000000&@42|O+0#af5f00255&|D@1| +0#0000000&@23 +@2>=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@57|1|8|1|,|3| @9|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_100.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_100.dump new file mode 100644 index 0000000000..c2b4407610 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_100.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|s+0#00e0e07&|y|n|c|h|r|o|t|r|o|n| |2|;+0#0000000&| @31|s+0#00e0e07&|y|n|c|h|r|o|t|r|o|n|2| +0#0000000&@14 +@2|t+0#00e0e07&|a|y|l|o|r| |c|o|e|f@1|;+0#0000000&| @32|t+0#00e0e07&|a|y|l|o|r|c|o|e|f@1| +0#0000000&@15 +@2|t+0#00e0e07&|r|a|n|s|p|o|r|t| |2|;+0#0000000&| @33|t+0#00e0e07&|r|a|n|s|p|o|r|t|2| +0#0000000&@16 +@2|t+0#00e0e07&|r|a|n|s|p|o|r|t| |3|;+0#0000000&| @33|t+0#00e0e07&|r|a|n|s|p|o|r|t|3| +0#0000000&@16 +@2|t+0#00e0e07&|r|a|n|s|p|o|r|t| |4|;+0#0000000&| @33|t+0#00e0e07&|r|a|n|s|p|o|r|t|4| +0#0000000&@16 +@2>t+0#00e0e07&|r|a|n|s|p|o|r|t| |5|;+0#0000000&| @33|t+0#00e0e07&|r|a|n|s|p|o|r|t|5| +0#0000000&@16 +@2|z+0#00e0e07&|e|t|a| |i|n|t|;+0#0000000&| @36|z+0#00e0e07&|e|t|a|i|n|t| +0#0000000&@19 +@2|z+0#00e0e07&|e|t|a| |m|1| |i|n|t|;+0#0000000&| @33|z+0#00e0e07&|e|t|a|m|1|i|n|t| +0#0000000&@17 +@2|z+0#00e0e07&|e|t|a| |m|1|;+0#0000000&| @37|z+0#00e0e07&|e|t|a|m|1| +0#0000000&@20 +@2|z+0#00e0e07&|e|t|a|;+0#0000000&| @40|z+0#00e0e07&|e|t|a| +0#0000000&@22 +|#+0#0000e05&| |V|e|c|t|o|r| |a|n|d| |m|a|t|r|i|x| |p|r|e|t@1|y| |p|r|i|n|t|.| |#| +0#0000000&@39 +@2|p+0#00e0e07&|r|i|n|t| |v|e|c|t|o|r|;+0#0000000&| @32|p+0#00e0e07&|r|i|n|t|v|e|c|t|o|r| +0#0000000&@15 +@2|p+0#00e0e07&|r|i|n|t| |m|a|t|r|i|x|;+0#0000000&| @32|p+0#00e0e07&|r|i|n|t|m|a|t|r|i|x| +0#0000000&@15 +|#+0#0000e05&| |V|e|c|t|o|r| |a|n|d| |m|a|t|r|i|x| |m|o|n|a|d|i|c|.| |#| +0#0000000&@44 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|C+0#af5f00255&|V| +0#0000000&@43|C+0#af5f00255&|V| +0#0000000&@24 +@2|R+0#af5f00255&|V| +0#0000000&@43|R+0#af5f00255&|V| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@57|1|7|6@1|,|3| @7|8|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_101.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_101.dump new file mode 100644 index 0000000000..19375644df --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_101.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|T+0#af5f00255&| +0#0000000&@44|T+0#af5f00255&| +0#0000000&@25 +@2|I+0#af5f00255&|N|V| +0#0000000&@42|I+0#af5f00255&|N|V| +0#0000000&@23 +@2|P+0#af5f00255&|I|N|V| +0#0000000&@41|P+0#af5f00255&|I|N|V| +0#0000000&@22 +@2>M+0#af5f00255&|E|A|N| +0#0000000&@41|M+0#af5f00255&|E|A|N| +0#0000000&@22 +@2|D+0#af5f00255&|E|T| +0#0000000&@42|D+0#af5f00255&|E|T| +0#0000000&@23 +@2|T+0#af5f00255&|R|A|C|E| +0#0000000&@40|T+0#af5f00255&|R|A|C|E| +0#0000000&@21 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|T+0#af5f00255&| +0#0000000&@44|T+0#af5f00255&| +0#0000000&@25 +@2|I+0#af5f00255&|N|V| +0#0000000&@42|I+0#af5f00255&|N|V| +0#0000000&@23 +@2|D+0#af5f00255&|E|T| +0#0000000&@42|D+0#af5f00255&|E|T| +0#0000000&@23 +@2|T+0#af5f00255&|R|A|C|E| +0#0000000&@40|T+0#af5f00255&|R|A|C|E| +0#0000000&@21 +|#+0#0000e05&| |V|e|c|t|o|r| |a|n|d| |m|a|t|r|i|x| |d|y|a|d|i|c|.| |#| +0#0000000&@45 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@57|1|7|8|4|,|3| @7|8|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_102.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_102.dump new file mode 100644 index 0000000000..cf8279a312 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_102.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2>-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|-| +0#0000000&@43|/+0#af5f00255&|-| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|B+0#af5f00255&|E|F|O|R|E| +0#0000000&@39|B+0#af5f00255&|E|F|O|R|E| +0#0000000&@20 +@2|A+0#af5f00255&|B|O|V|E| +0#0000000&@40|A+0#af5f00255&|B|O|V|E| +0#0000000&@21 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@57|1|8|0|2|,|3| @7|8|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_103.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_103.dump new file mode 100644 index 0000000000..5fd309b79a --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_103.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2>-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +|#+0#0000e05&| |V|e|c|t|o|r| |a|n|d| |m|a|t|r|i|x| |s|c|a|l|i|n|g|.| |#| +0#0000000&@44 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@57|1|8|2|0|,|3| @7|8|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_104.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_104.dump new file mode 100644 index 0000000000..9ebfa9e47e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_104.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2>/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +|#+0#0000e05&| |M|a|t|r|i|x| |t|i|m|e|s| |v|e|c|t|o|r| |o|r| |m|a|t|r|i|x|.| |#| +0#0000000&@40 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +|#+0#0000e05&| |V|e|c|t|o|r| |a|n|d| |m|a|t|r|i|x| |m|i|s|c|e|l@1|a|n|e|o|u|s|.| |#| +0#0000000&@38 +@2|v+0#00e0e07&|e|c|t|o|r| |e|c|h|o|;+0#0000000&| @33|v+0#00e0e07&|e|c|t|o|r|e|c|h|o| +0#0000000&@16 +@57|1|8|3|8|,|3| @7|8|7|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_105.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_105.dump new file mode 100644 index 0000000000..dbbc5bc5eb --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_105.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|v+0#00e0e07&|e|c|t|o|r| |e|c|h|o|;+0#0000000&| @33|v+0#00e0e07&|e|c|t|o|r|e|c|h|o| +0#0000000&@16 +@2|m+0#00e0e07&|a|t|r|i|x| |e|c|h|o|;+0#0000000&| @33|m+0#00e0e07&|a|t|r|i|x|e|c|h|o| +0#0000000&@16 +@2|c+0#00e0e07&|o|m|p|l| |v|e|c|t|o|r| |e|c|h|o|;+0#0000000&| @27|c+0#00e0e07&|o|m|p|l|v|e|c|t|o|r|e|c|h|o| +0#0000000&@11 +@2|c+0#00e0e07&|o|m|p|l| |m|a|t|r|i|x| |e|c|h|o|;+0#0000000&| @27|c+0#00e0e07&|o|m|p|l|m|a|t|r|i|x|e|c|h|o| +0#0000000&@11 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2>N+0#af5f00255&|O|R|M| +0#0000000&@41|N+0#af5f00255&|O|R|M| +0#0000000&@22 +@2|D+0#af5f00255&|Y|A|D| +0#0000000&@41|D+0#af5f00255&|Y|A|D| +0#0000000&@22 +|#+0#0000e05&| |P|r|i|n|c|i|p|l|e| |C|o|m|p|o|n|e|n|t| |A|n|a|l|y|s|i|s|.| |#| +0#0000000&@41 +@2|p+0#00e0e07&|c|a|c|v|;+0#0000000&| @39|p+0#00e0e07&|c|a|c|v| +0#0000000&@21 +@2|p+0#00e0e07&|c|a|s|v|d|;+0#0000000&| @38|p+0#00e0e07&|c|a|s|v|d| +0#0000000&@20 +|#+0#0000e05&| |T|o|t|a|l| |L|e|a|s|t| |S|q|u|a|r|e| |r|e|g|r|e|s@1|i|o|n|.| |#| +0#0000000&@40 +@2|o+0#00e0e07&|l|s|;+0#0000000&| @41|o+0#00e0e07&|l|s| +0#0000000&@23 +@2|t+0#00e0e07&|l|s|;+0#0000000&| @41|t+0#00e0e07&|l|s| +0#0000000&@23 +|#+0#0000e05&| |P|a|r|t|i|a|l| |L|e|a|s|t| |S|q|u|a|r|e|s| |r|e|g|r|e|s@1|i|o|n|.| |#| +0#0000000&@37 +@2|p+0#00e0e07&|c|r|;+0#0000000&| @41|p+0#00e0e07&|c|r| +0#0000000&@23 +@2|p+0#00e0e07&|l|s|1|;+0#0000000&| @40|p+0#00e0e07&|l|s|1| +0#0000000&@22 +@2|p+0#00e0e07&|l|s|2|;+0#0000000&| @40|p+0#00e0e07&|l|s|2| +0#0000000&@22 +|#+0#0000e05&| |R|o|u|t|i|n|e| |l|e|f|t| |c|o|l|u|m|n|s|,| |a| |G|S|L| |a|l|t|e|r|n|a|t|i|v|e| |t|o| |t|r|i|m@1|i|n|g| |c|o|l|u|m|n|s|.| |#| +0#0000000&@10 +@2|l+0#00e0e07&|e|f|t| |c|o|l|u|m|n|s|;+0#0000000&| @32|l+0#00e0e07&|e|f|t|c|o|l|u|m|n|s| +0#0000000&@15 +@57|1|8|5|6|,|3| @7|8@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_106.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_106.dump new file mode 100644 index 0000000000..1cce87f789 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_106.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|e|f|t| |c|o|l|u|m|n|s|;+0#0000000&| @32|l+0#00e0e07&|e|f|t|c|o|l|u|m|n|s| +0#0000000&@15 +|#+0#0000e05&| |M|o@1|r|e|-|P|e|n|r|o|s|e| |p|s|e|u|d|o| |i|n|v|e|r|s|e|.| |#| +0#0000000&@41 +@2|p+0#00e0e07&|s|e|u|d|o| |i|n|v|;+0#0000000&| @34|p+0#00e0e07&|s|e|u|d|o|i|n|v| +0#0000000&@17 +|#+0#0000e05&| |L|U| |d|e|c|o|m|p|o|s|i|t|i|o|n|.| |#| +0#0000000&@53 +@2|l+0#00e0e07&|u| |d|e|c|o|m|p|;+0#0000000&| @35|l+0#00e0e07&|u|d|e|c|o|m|p| +0#0000000&@18 +@2>l+0#00e0e07&|u| |d|e|t|;+0#0000000&| @38|l+0#00e0e07&|u|d|e|t| +0#0000000&@21 +@2|l+0#00e0e07&|u| |i|n|v|;+0#0000000&| @38|l+0#00e0e07&|u|i|n|v| +0#0000000&@21 +@2|l+0#00e0e07&|u| |s|o|l|v|e|;+0#0000000&| @36|l+0#00e0e07&|u|s|o|l|v|e| +0#0000000&@19 +@2|c+0#00e0e07&|o|m|p|l|e|x| |l|u| |d|e|c|o|m|p|;+0#0000000&| @27|c+0#00e0e07&|o|m|p|l|e|x|l|u|d|e|c|o|m|p| +0#0000000&@11 +@2|c+0#00e0e07&|o|m|p|l|e|x| |l|u| |d|e|t|;+0#0000000&| @30|c+0#00e0e07&|o|m|p|l|e|x|l|u|d|e|t| +0#0000000&@14 +@2|c+0#00e0e07&|o|m|p|l|e|x| |l|u| |i|n|v|;+0#0000000&| @30|c+0#00e0e07&|o|m|p|l|e|x|l|u|i|n|v| +0#0000000&@14 +@2|c+0#00e0e07&|o|m|p|l|e|x| |l|u| |s|o|l|v|e|;+0#0000000&| @28|c+0#00e0e07&|o|m|p|l|e|x|l|u|s|o|l|v|e| +0#0000000&@12 +|#+0#0000e05&| |S|V|D| |d|e|c|o|m|p|o|s|i|t|i|o|n|.| |#| +0#0000000&@52 +@2|s+0#00e0e07&|v|d| |d|e|c|o|m|p|;+0#0000000&| @34|s+0#00e0e07&|v|d@1|e|c|o|m|p| +0#0000000&@17 +@2|s+0#00e0e07&|v|d| |s|o|l|v|e|;+0#0000000&| @35|s+0#00e0e07&|v|d|s|o|l|v|e| +0#0000000&@18 +|#+0#0000e05&| |Q|R| |d|e|c|o|m|p|o|s|i|t|i|o|n|.| |#| +0#0000000&@53 +@2|q+0#00e0e07&|r| |d|e|c|o|m|p|;+0#0000000&| @35|q+0#00e0e07&|r|d|e|c|o|m|p| +0#0000000&@18 +@2|q+0#00e0e07&|r| |s|o|l|v|e|;+0#0000000&| @36|q+0#00e0e07&|r|s|o|l|v|e| +0#0000000&@19 +@2|q+0#00e0e07&|r| |l|s@1|o|l|v|e|;+0#0000000&| @34|q+0#00e0e07&|r|l|s@1|o|l|v|e| +0#0000000&@17 +@57|1|8|7|4|,|3| @7|8|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_107.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_107.dump new file mode 100644 index 0000000000..7bfcf23b43 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_107.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|q+0#00e0e07&|r| |l|s@1|o|l|v|e|;+0#0000000&| @34|q+0#00e0e07&|r|l|s@1|o|l|v|e| +0#0000000&@17 +|#+0#0000e05&| |C|h|o|l|e|s|k|y| |d|e|c|o|m|p|o|s|i|t|i|o|n|.| |#| +0#0000000&@47 +@2|c+0#00e0e07&|h|o|l|e|s|k|y| |d|e|c|o|m|p|;+0#0000000&| @29|c+0#00e0e07&|h|o|l|e|s|k|y|d|e|c|o|m|p| +0#0000000&@12 +@2|c+0#00e0e07&|h|o|l|e|s|k|y| |s|o|l|v|e|;+0#0000000&| @30|c+0#00e0e07&|h|o|l|e|s|k|y|s|o|l|v|e| +0#0000000&@13 +|#+0#0000e05&| |C|o|n|s|t|a|n|t|s| |e|x| |G|S|L|.| |#| +0#0000000&@53 +@2>c+0#00e0e07&|g|s| |s|p|e@1|d| |o|f| |l|i|g|h|t|;+0#0000000&| @26|c+0#00e0e07&|g|s@1|p|e@1|d|o|f|l|i|g|h|t| +0#0000000&@11 +@2|c+0#00e0e07&|g|s| |g|r|a|v|i|t|a|t|i|o|n|a|l| |c|o|n|s|t|a|n|t|;+0#0000000&| @18|c+0#00e0e07&|g|s|g|r|a|v|i|t|a|t|i|o|n|a|l|c|o|n|s|t|a|n|t| +0#0000000&@2 +@2|c+0#00e0e07&|g|s| |p|l|a|n|c|k| |c|o|n|s|t|a|n|t|;+0#0000000&| @25|c+0#00e0e07&|g|s|p|l|a|n|c|k|c|o|n|s|t|a|n|t| +0#0000000&@9 +@2|c+0#00e0e07&|g|s| |p|l|a|n|c|k| |c|o|n|s|t|a|n|t| |b|a|r|;+0#0000000&| @21|c+0#00e0e07&|g|s|p|l|a|n|c|k|c|o|n|s|t|a|n|t|b|a|r| +0#0000000&@6 +@2|c+0#00e0e07&|g|s| |a|s|t|r|o|n|o|m|i|c|a|l| |u|n|i|t|;+0#0000000&| @23|c+0#00e0e07&|g|s|a|s|t|r|o|n|o|m|i|c|a|l|u|n|i|t| +0#0000000&@7 +@2|c+0#00e0e07&|g|s| |l|i|g|h|t| |y|e|a|r|;+0#0000000&| @30|c+0#00e0e07&|g|s|l|i|g|h|t|y|e|a|r| +0#0000000&@14 +@2|c+0#00e0e07&|g|s| |p|a|r|s|e|c|;+0#0000000&| @34|c+0#00e0e07&|g|s|p|a|r|s|e|c| +0#0000000&@17 +@2|c+0#00e0e07&|g|s| |g|r|a|v| |a|c@1|e|l|;+0#0000000&| @30|c+0#00e0e07&|g|s|g|r|a|v|a|c@1|e|l| +0#0000000&@14 +@2|c+0#00e0e07&|g|s| |e|l|e|c|t|r|o|n| |v|o|l|t|;+0#0000000&| @27|c+0#00e0e07&|g|s|e|l|e|c|t|r|o|n|v|o|l|t| +0#0000000&@11 +@2|c+0#00e0e07&|g|s| |m|a|s@1| |e|l|e|c|t|r|o|n|;+0#0000000&| @27|c+0#00e0e07&|g|s|m|a|s@1|e|l|e|c|t|r|o|n| +0#0000000&@11 +@2|c+0#00e0e07&|g|s| |m|a|s@1| |m|u|o|n|;+0#0000000&| @31|c+0#00e0e07&|g|s|m|a|s@1|m|u|o|n| +0#0000000&@15 +@2|c+0#00e0e07&|g|s| |m|a|s@1| |p|r|o|t|o|n|;+0#0000000&| @29|c+0#00e0e07&|g|s|m|a|s@1|p|r|o|t|o|n| +0#0000000&@13 +@2|c+0#00e0e07&|g|s| |m|a|s@1| |n|e|u|t|r|o|n|;+0#0000000&| @28|c+0#00e0e07&|g|s|m|a|s@1|n|e|u|t|r|o|n| +0#0000000&@12 +@2|c+0#00e0e07&|g|s| |r|y|d|b|e|r|g|;+0#0000000&| @33|c+0#00e0e07&|g|s|r|y|d|b|e|r|g| +0#0000000&@16 +@57|1|8|9|2|,|3| @7|8|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_108.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_108.dump new file mode 100644 index 0000000000..2d86a23d9a --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_108.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|g|s| |r|y|d|b|e|r|g|;+0#0000000&| @33|c+0#00e0e07&|g|s|r|y|d|b|e|r|g| +0#0000000&@16 +@2|c+0#00e0e07&|g|s| |b|o|l|t|z|m|a|n@1|;+0#0000000&| @31|c+0#00e0e07&|g|s|b|o|l|t|z|m|a|n@1| +0#0000000&@14 +@2|c+0#00e0e07&|g|s| |b|o|h|r| |m|a|g|n|e|t|o|n|;+0#0000000&| @27|c+0#00e0e07&|g|s|b|o|h|r|m|a|g|n|e|t|o|n| +0#0000000&@11 +@2|c+0#00e0e07&|g|s| |n|u|c|l|e|a|r| |m|a|g|n|e|t|o|n|;+0#0000000&| @24|c+0#00e0e07&|g|s|n|u|c|l|e|a|r|m|a|g|n|e|t|o|n| +0#0000000&@8 +@2|c+0#00e0e07&|g|s| |e|l|e|c|t|r|o|n| |m|a|g|n|e|t|i|c| |m|o|m|e|n|t|;+0#0000000&| @16|c+0#00e0e07&|g|s|e|l|e|c|t|r|o|n|m|a|g|n|e|t|i|c|m|o|m|e|n|t| +0#0000000&@1 +@2>c+0#00e0e07&|g|s| |p|r|o|t|o|n| |m|a|g|n|e|t|i|c| |m|o|m|e|n|t|;+0#0000000&| @18|c+0#00e0e07&|g|s|p|r|o|t|o|n|m|a|g|n|e|t|i|c|m|o|m|e|n|t| +0#0000000&@3 +@2|c+0#00e0e07&|g|s| |m|o|l|a|r| |g|a|s|;+0#0000000&| @31|c+0#00e0e07&|g|s|m|o|l|a|r|g|a|s| +0#0000000&@15 +@2|c+0#00e0e07&|g|s| |s|t|a|n|d|a|r|d| |g|a|s| |v|o|l|u|m|e|;+0#0000000&| @21|c+0#00e0e07&|g|s@1|t|a|n|d|a|r|d|g|a|s|v|o|l|u|m|e| +0#0000000&@6 +@2|c+0#00e0e07&|g|s| |m|i|n|u|t|e|;+0#0000000&| @34|c+0#00e0e07&|g|s|m|i|n|u|t|e| +0#0000000&@17 +@2|c+0#00e0e07&|g|s| |h|o|u|r|;+0#0000000&| @36|c+0#00e0e07&|g|s|h|o|u|r| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |d|a|y|;+0#0000000&| @37|c+0#00e0e07&|g|s|d|a|y| +0#0000000&@20 +@2|c+0#00e0e07&|g|s| |w|e@1|k|;+0#0000000&| @36|c+0#00e0e07&|g|s|w|e@1|k| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |i|n|c|h|;+0#0000000&| @36|c+0#00e0e07&|g|s|i|n|c|h| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |f|o@1|t|;+0#0000000&| @36|c+0#00e0e07&|g|s|f|o@1|t| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |y|a|r|d|;+0#0000000&| @36|c+0#00e0e07&|g|s|y|a|r|d| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |m|i|l|e|;+0#0000000&| @36|c+0#00e0e07&|g|s|m|i|l|e| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |n|a|u|t|i|c|a|l| |m|i|l|e|;+0#0000000&| @27|c+0#00e0e07&|g|s|n|a|u|t|i|c|a|l|m|i|l|e| +0#0000000&@11 +@2|c+0#00e0e07&|g|s| |f|a|t|h|o|m|;+0#0000000&| @34|c+0#00e0e07&|g|s|f|a|t|h|o|m| +0#0000000&@17 +@2|c+0#00e0e07&|g|s| |m|i|l|;+0#0000000&| @37|c+0#00e0e07&|g|s|m|i|l| +0#0000000&@20 +@57|1|9|1|0|,|3| @7|9|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_109.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_109.dump new file mode 100644 index 0000000000..e73a2fc6eb --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_109.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|g|s| |m|i|l|;+0#0000000&| @37|c+0#00e0e07&|g|s|m|i|l| +0#0000000&@20 +@2|c+0#00e0e07&|g|s| |p|o|i|n|t|;+0#0000000&| @35|c+0#00e0e07&|g|s|p|o|i|n|t| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |t|e|x|p|o|i|n|t|;+0#0000000&| @32|c+0#00e0e07&|g|s|t|e|x|p|o|i|n|t| +0#0000000&@15 +@2|c+0#00e0e07&|g|s| |m|i|c|r|o|n|;+0#0000000&| @34|c+0#00e0e07&|g|s|m|i|c|r|o|n| +0#0000000&@17 +@2|c+0#00e0e07&|g|s| |a|n|g|s|t|r|o|m|;+0#0000000&| @32|c+0#00e0e07&|g|s|a|n|g|s|t|r|o|m| +0#0000000&@15 +@2>c+0#00e0e07&|g|s| |h|e|c|t|a|r|e|;+0#0000000&| @33|c+0#00e0e07&|g|s|h|e|c|t|a|r|e| +0#0000000&@16 +@2|c+0#00e0e07&|g|s| |a|c|r|e|;+0#0000000&| @36|c+0#00e0e07&|g|s|a|c|r|e| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |b|a|r|n|;+0#0000000&| @36|c+0#00e0e07&|g|s|b|a|r|n| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |l|i|t|e|r|;+0#0000000&| @35|c+0#00e0e07&|g|s|l|i|t|e|r| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |u|s| |g|a|l@1|o|n|;+0#0000000&| @31|c+0#00e0e07&|g|s|u|s|g|a|l@1|o|n| +0#0000000&@15 +@2|c+0#00e0e07&|g|s| |q|u|a|r|t|;+0#0000000&| @35|c+0#00e0e07&|g|s|q|u|a|r|t| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |p|i|n|t|;+0#0000000&| @36|c+0#00e0e07&|g|s|p|i|n|t| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |c|u|p|;+0#0000000&| @37|c+0#00e0e07&|g|s|c|u|p| +0#0000000&@20 +@2|c+0#00e0e07&|g|s| |f|l|u|i|d| |o|u|n|c|e|;+0#0000000&| @29|c+0#00e0e07&|g|s|f|l|u|i|d|o|u|n|c|e| +0#0000000&@13 +@2|c+0#00e0e07&|g|s| |t|a|b|l|e| |s|p|o@1|n|;+0#0000000&| @29|c+0#00e0e07&|g|s|t|a|b|l|e|s|p|o@1|n| +0#0000000&@13 +@2|c+0#00e0e07&|g|s| |t|e|a| |s|p|o@1|n|;+0#0000000&| @31|c+0#00e0e07&|g|s|t|e|a|s|p|o@1|n| +0#0000000&@15 +@2|c+0#00e0e07&|g|s| |c|a|n|a|d|i|a|n| |g|a|l@1|o|n|;+0#0000000&| @25|c+0#00e0e07&|g|s|c|a|n|a|d|i|a|n|g|a|l@1|o|n| +0#0000000&@9 +@2|c+0#00e0e07&|g|s| |u|k| |g|a|l@1|o|n|;+0#0000000&| @31|c+0#00e0e07&|g|s|u|k|g|a|l@1|o|n| +0#0000000&@15 +@2|c+0#00e0e07&|g|s| |m|i|l|e|s| |p|e|r| |h|o|u|r|;+0#0000000&| @26|c+0#00e0e07&|g|s|m|i|l|e|s|p|e|r|h|o|u|r| +0#0000000&@11 +@57|1|9|2|8|,|3| @7|9|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_11.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_11.dump new file mode 100644 index 0000000000..f3586530df --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_11.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|V|E|R| +0#0000000&@41|O+0#af5f00255&|V|E|R| +0#0000000&@22 +@2>%+0#af5f00255&| +0#0000000&@44|%+0#af5f00255&| +0#0000000&@25 +@2|M+0#af5f00255&|O|D| +0#0000000&@42|M+0#af5f00255&|O|D| +0#0000000&@23 +@2|%+0#af5f00255&|*| +0#0000000&@43|%+0#af5f00255&|*| +0#0000000&@24 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|%+0#af5f00255&|:|=| +0#0000000&@42|%+0#af5f00255&|:|=| +0#0000000&@23 +@2|%+0#af5f00255&|*|:|=| +0#0000000&@41|%+0#af5f00255&|*|:|=| +0#0000000&@22 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@57|1|9@1|,|3| @9|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_110.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_110.dump new file mode 100644 index 0000000000..bc20020c56 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_110.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|g|s| |m|i|l|e|s| |p|e|r| |h|o|u|r|;+0#0000000&| @26|c+0#00e0e07&|g|s|m|i|l|e|s|p|e|r|h|o|u|r| +0#0000000&@11 +@2|c+0#00e0e07&|g|s| |k|i|l|o|m|e|t|e|r|s| |p|e|r| |h|o|u|r|;+0#0000000&| @21|c+0#00e0e07&|g|s|k|i|l|o|m|e|t|e|r|s|p|e|r|h|o|u|r| +0#0000000&@6 +@2|c+0#00e0e07&|g|s| |k|n|o|t|;+0#0000000&| @36|c+0#00e0e07&|g|s|k|n|o|t| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |p|o|u|n|d| |m|a|s@1|;+0#0000000&| @30|c+0#00e0e07&|g|s|p|o|u|n|d|m|a|s@1| +0#0000000&@14 +@2|c+0#00e0e07&|g|s| |o|u|n|c|e| |m|a|s@1|;+0#0000000&| @30|c+0#00e0e07&|g|s|o|u|n|c|e|m|a|s@1| +0#0000000&@14 +@2>c+0#00e0e07&|g|s| |t|o|n|;+0#0000000&| @37|c+0#00e0e07&|g|s|t|o|n| +0#0000000&@20 +@2|c+0#00e0e07&|g|s| |m|e|t|r|i|c|t|o|n|;+0#0000000&| @31|c+0#00e0e07&|g|s|m|e|t|r|i|c|t|o|n| +0#0000000&@14 +@2|c+0#00e0e07&|g|s| |u|k|t|o|n|;+0#0000000&| @35|c+0#00e0e07&|g|s|u|k|t|o|n| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |t|r|o|y| |o|u|n|c|e|;+0#0000000&| @30|c+0#00e0e07&|g|s|t|r|o|y|o|u|n|c|e| +0#0000000&@14 +@2|c+0#00e0e07&|g|s| |c|a|r|a|t|;+0#0000000&| @35|c+0#00e0e07&|g|s|c|a|r|a|t| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |u|n|i|f|i|e|d| |a|t|o|m|i|c| |m|a|s@1|;+0#0000000&| @21|c+0#00e0e07&|g|s|u|n|i|f|i|e|d|a|t|o|m|i|c|m|a|s@1| +0#0000000&@6 +@2|c+0#00e0e07&|g|s| |g|r|a|m| |f|o|r|c|e|;+0#0000000&| @30|c+0#00e0e07&|g|s|g|r|a|m|f|o|r|c|e| +0#0000000&@14 +@2|c+0#00e0e07&|g|s| |p|o|u|n|d| |f|o|r|c|e|;+0#0000000&| @29|c+0#00e0e07&|g|s|p|o|u|n|d|f|o|r|c|e| +0#0000000&@13 +@2|c+0#00e0e07&|g|s| |k|i|l|o| |p|o|u|n|d| |f|o|r|c|e|;+0#0000000&| @24|c+0#00e0e07&|g|s|k|i|l|o|p|o|u|n|d|f|o|r|c|e| +0#0000000&@9 +@2|c+0#00e0e07&|g|s| |p|o|u|n|d|a|l|;+0#0000000&| @33|c+0#00e0e07&|g|s|p|o|u|n|d|a|l| +0#0000000&@16 +@2|c+0#00e0e07&|g|s| |c|a|l|o|r|i|e|;+0#0000000&| @33|c+0#00e0e07&|g|s|c|a|l|o|r|i|e| +0#0000000&@16 +@2|c+0#00e0e07&|g|s| |b|t|u|;+0#0000000&| @37|c+0#00e0e07&|g|s|b|t|u| +0#0000000&@20 +@2|c+0#00e0e07&|g|s| |t|h|e|r|m|;+0#0000000&| @35|c+0#00e0e07&|g|s|t|h|e|r|m| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |h|o|r|s|e|p|o|w|e|r|;+0#0000000&| @30|c+0#00e0e07&|g|s|h|o|r|s|e|p|o|w|e|r| +0#0000000&@13 +@57|1|9|4|6|,|3| @7|9|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_111.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_111.dump new file mode 100644 index 0000000000..664e71a9e5 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_111.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|g|s| |h|o|r|s|e|p|o|w|e|r|;+0#0000000&| @30|c+0#00e0e07&|g|s|h|o|r|s|e|p|o|w|e|r| +0#0000000&@13 +@2|c+0#00e0e07&|g|s| |b|a|r|;+0#0000000&| @37|c+0#00e0e07&|g|s|b|a|r| +0#0000000&@20 +@2|c+0#00e0e07&|g|s| |s|t|d| |a|t|m|o|s|p|h|e|r|e|;+0#0000000&| @26|c+0#00e0e07&|g|s@1|t|d|a|t|m|o|s|p|h|e|r|e| +0#0000000&@10 +@2|c+0#00e0e07&|g|s| |t|o|r@1|;+0#0000000&| @36|c+0#00e0e07&|g|s|t|o|r@1| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |m|e|t|e|r| |o|f| |m|e|r|c|u|r|y|;+0#0000000&| @24|c+0#00e0e07&|g|s|m|e|t|e|r|o|f|m|e|r|c|u|r|y| +0#0000000&@9 +@2>c+0#00e0e07&|g|s| |i|n|c|h| |o|f| |m|e|r|c|u|r|y|;+0#0000000&| @25|c+0#00e0e07&|g|s|i|n|c|h|o|f|m|e|r|c|u|r|y| +0#0000000&@10 +@2|c+0#00e0e07&|g|s| |i|n|c|h| |o|f| |w|a|t|e|r|;+0#0000000&| @27|c+0#00e0e07&|g|s|i|n|c|h|o|f|w|a|t|e|r| +0#0000000&@12 +@2|c+0#00e0e07&|g|s| |p|s|i|;+0#0000000&| @37|c+0#00e0e07&|g|s|p|s|i| +0#0000000&@20 +@2|c+0#00e0e07&|g|s| |p|o|i|s|e|;+0#0000000&| @35|c+0#00e0e07&|g|s|p|o|i|s|e| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |s|t|o|k|e|s|;+0#0000000&| @34|c+0#00e0e07&|g|s@1|t|o|k|e|s| +0#0000000&@17 +@2|c+0#00e0e07&|g|s| |f|a|r|a|d|a|y|;+0#0000000&| @33|c+0#00e0e07&|g|s|f|a|r|a|d|a|y| +0#0000000&@16 +@2|c+0#00e0e07&|g|s| |e|l|e|c|t|r|o|n| |c|h|a|r|g|e|;+0#0000000&| @25|c+0#00e0e07&|g|s|e|l|e|c|t|r|o|n|c|h|a|r|g|e| +0#0000000&@9 +@2|c+0#00e0e07&|g|s| |g|a|u|s@1|;+0#0000000&| @35|c+0#00e0e07&|g|s|g|a|u|s@1| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |s|t|i|l|b|;+0#0000000&| @35|c+0#00e0e07&|g|s@1|t|i|l|b| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |l|u|m|e|n|;+0#0000000&| @35|c+0#00e0e07&|g|s|l|u|m|e|n| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |l|u|x|;+0#0000000&| @37|c+0#00e0e07&|g|s|l|u|x| +0#0000000&@20 +@2|c+0#00e0e07&|g|s| |p|h|o|t|;+0#0000000&| @36|c+0#00e0e07&|g|s|p|h|o|t| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |f|o@1|t| |c|a|n|d|l|e|;+0#0000000&| @29|c+0#00e0e07&|g|s|f|o@1|t|c|a|n|d|l|e| +0#0000000&@13 +@2|c+0#00e0e07&|g|s| |l|a|m|b|e|r|t|;+0#0000000&| @33|c+0#00e0e07&|g|s|l|a|m|b|e|r|t| +0#0000000&@16 +@57|1|9|6|4|,|3| @7|9|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_112.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_112.dump new file mode 100644 index 0000000000..07803b28a7 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_112.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|g|s| |l|a|m|b|e|r|t|;+0#0000000&| @33|c+0#00e0e07&|g|s|l|a|m|b|e|r|t| +0#0000000&@16 +@2|c+0#00e0e07&|g|s| |f|o@1|t| |l|a|m|b|e|r|t|;+0#0000000&| @28|c+0#00e0e07&|g|s|f|o@1|t|l|a|m|b|e|r|t| +0#0000000&@12 +@2|c+0#00e0e07&|g|s| |c|u|r|i|e|;+0#0000000&| @35|c+0#00e0e07&|g|s|c|u|r|i|e| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |r|o|e|n|t|g|e|n|;+0#0000000&| @32|c+0#00e0e07&|g|s|r|o|e|n|t|g|e|n| +0#0000000&@15 +@2|c+0#00e0e07&|g|s| |r|a|d|;+0#0000000&| @37|c+0#00e0e07&|g|s|r|a|d| +0#0000000&@20 +@2>c+0#00e0e07&|g|s| |s|o|l|a|r| |m|a|s@1|;+0#0000000&| @30|c+0#00e0e07&|g|s@1|o|l|a|r|m|a|s@1| +0#0000000&@14 +@2|c+0#00e0e07&|g|s| |b|o|h|r| |r|a|d|i|u|s|;+0#0000000&| @29|c+0#00e0e07&|g|s|b|o|h|r@1|a|d|i|u|s| +0#0000000&@13 +@2|c+0#00e0e07&|g|s| |n|e|w|t|o|n|;+0#0000000&| @34|c+0#00e0e07&|g|s|n|e|w|t|o|n| +0#0000000&@17 +@2|c+0#00e0e07&|g|s| |d|y|n|e|;+0#0000000&| @36|c+0#00e0e07&|g|s|d|y|n|e| +0#0000000&@19 +@2|c+0#00e0e07&|g|s| |j|o|u|l|e|;+0#0000000&| @35|c+0#00e0e07&|g|s|j|o|u|l|e| +0#0000000&@18 +@2|c+0#00e0e07&|g|s| |e|r|g|;+0#0000000&| @37|c+0#00e0e07&|g|s|e|r|g| +0#0000000&@20 +@2|m+0#00e0e07&|k|s|a| |s|p|e@1|d| |o|f| |l|i|g|h|t|;+0#0000000&| @25|m+0#00e0e07&|k|s|a|s|p|e@1|d|o|f|l|i|g|h|t| +0#0000000&@10 +@2|m+0#00e0e07&|k|s|a| |g|r|a|v|i|t|a|t|i|o|n|a|l| |c|o|n|s|t|a|n|t|;+0#0000000&| @17|m+0#00e0e07&|k|s|a|g|r|a|v|i|t|a|t|i|o|n|a|l|c|o|n|s|t|a|n|t| +0#0000000&@1 +@2|m+0#00e0e07&|k|s|a| |p|l|a|n|c|k| |c|o|n|s|t|a|n|t|;+0#0000000&| @24|m+0#00e0e07&|k|s|a|p|l|a|n|c|k|c|o|n|s|t|a|n|t| +0#0000000&@8 +@2|m+0#00e0e07&|k|s|a| |p|l|a|n|c|k| |c|o|n|s|t|a|n|t| |b|a|r|;+0#0000000&| @20|m+0#00e0e07&|k|s|a|p|l|a|n|c|k|c|o|n|s|t|a|n|t|b|a|r| +0#0000000&@5 +@2|m+0#00e0e07&|k|s|a| |v|a|c|u@1|m| |p|e|r|m|e|a|b|i|l|i|t|y|;+0#0000000&| @20|m+0#00e0e07&|k|s|a|v|a|c|u@1|m|p|e|r|m|e|a|b|i|l|i|t|y| +0#0000000&@4 +@2|m+0#00e0e07&|k|s|a| |a|s|t|r|o|n|o|m|i|c|a|l| |u|n|i|t|;+0#0000000&| @22|m+0#00e0e07&|k|s|a@1|s|t|r|o|n|o|m|i|c|a|l|u|n|i|t| +0#0000000&@6 +@2|m+0#00e0e07&|k|s|a| |l|i|g|h|t| |y|e|a|r|;+0#0000000&| @29|m+0#00e0e07&|k|s|a|l|i|g|h|t|y|e|a|r| +0#0000000&@13 +@2|m+0#00e0e07&|k|s|a| |p|a|r|s|e|c|;+0#0000000&| @33|m+0#00e0e07&|k|s|a|p|a|r|s|e|c| +0#0000000&@16 +@57|1|9|8|2|,|3| @7|9|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_113.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_113.dump new file mode 100644 index 0000000000..4168463492 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_113.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|m+0#00e0e07&|k|s|a| |p|a|r|s|e|c|;+0#0000000&| @33|m+0#00e0e07&|k|s|a|p|a|r|s|e|c| +0#0000000&@16 +@2|m+0#00e0e07&|k|s|a| |g|r|a|v| |a|c@1|e|l|;+0#0000000&| @29|m+0#00e0e07&|k|s|a|g|r|a|v|a|c@1|e|l| +0#0000000&@13 +@2|m+0#00e0e07&|k|s|a| |e|l|e|c|t|r|o|n| |v|o|l|t|;+0#0000000&| @26|m+0#00e0e07&|k|s|a|e|l|e|c|t|r|o|n|v|o|l|t| +0#0000000&@10 +@2|m+0#00e0e07&|k|s|a| |m|a|s@1| |e|l|e|c|t|r|o|n|;+0#0000000&| @26|m+0#00e0e07&|k|s|a|m|a|s@1|e|l|e|c|t|r|o|n| +0#0000000&@10 +@2|m+0#00e0e07&|k|s|a| |m|a|s@1| |m|u|o|n|;+0#0000000&| @30|m+0#00e0e07&|k|s|a|m|a|s@1|m|u|o|n| +0#0000000&@14 +@2>m+0#00e0e07&|k|s|a| |m|a|s@1| |p|r|o|t|o|n|;+0#0000000&| @28|m+0#00e0e07&|k|s|a|m|a|s@1|p|r|o|t|o|n| +0#0000000&@12 +@2|m+0#00e0e07&|k|s|a| |m|a|s@1| |n|e|u|t|r|o|n|;+0#0000000&| @27|m+0#00e0e07&|k|s|a|m|a|s@1|n|e|u|t|r|o|n| +0#0000000&@11 +@2|m+0#00e0e07&|k|s|a| |r|y|d|b|e|r|g|;+0#0000000&| @32|m+0#00e0e07&|k|s|a|r|y|d|b|e|r|g| +0#0000000&@15 +@2|m+0#00e0e07&|k|s|a| |b|o|l|t|z|m|a|n@1|;+0#0000000&| @30|m+0#00e0e07&|k|s|a|b|o|l|t|z|m|a|n@1| +0#0000000&@13 +@2|m+0#00e0e07&|k|s|a| |b|o|h|r| |m|a|g|n|e|t|o|n|;+0#0000000&| @26|m+0#00e0e07&|k|s|a|b|o|h|r|m|a|g|n|e|t|o|n| +0#0000000&@10 +@2|m+0#00e0e07&|k|s|a| |n|u|c|l|e|a|r| |m|a|g|n|e|t|o|n|;+0#0000000&| @23|m+0#00e0e07&|k|s|a|n|u|c|l|e|a|r|m|a|g|n|e|t|o|n| +0#0000000&@7 +@2|m+0#00e0e07&|k|s|a| |e|l|e|c|t|r|o|n| |m|a|g|n|e|t|i|c| |m|o|m|e|n|t|;+0#0000000&| @15|m+0#00e0e07&|k|s|a|e|l|e|c|t|r|o|n|m|a|g|n|e|t|i|c|m|o|m|e|n|t| +0#0000000& +@2|m+0#00e0e07&|k|s|a| |p|r|o|t|o|n| |m|a|g|n|e|t|i|c| |m|o|m|e|n|t|;+0#0000000&| @17|m+0#00e0e07&|k|s|a|p|r|o|t|o|n|m|a|g|n|e|t|i|c|m|o|m|e|n|t| +0#0000000&@2 +@2|m+0#00e0e07&|k|s|a| |m|o|l|a|r| |g|a|s|;+0#0000000&| @30|m+0#00e0e07&|k|s|a|m|o|l|a|r|g|a|s| +0#0000000&@14 +@2|m+0#00e0e07&|k|s|a| |s|t|a|n|d|a|r|d| |g|a|s| |v|o|l|u|m|e|;+0#0000000&| @20|m+0#00e0e07&|k|s|a|s|t|a|n|d|a|r|d|g|a|s|v|o|l|u|m|e| +0#0000000&@5 +@2|m+0#00e0e07&|k|s|a| |m|i|n|u|t|e|;+0#0000000&| @33|m+0#00e0e07&|k|s|a|m|i|n|u|t|e| +0#0000000&@16 +@2|m+0#00e0e07&|k|s|a| |h|o|u|r|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|h|o|u|r| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |d|a|y|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|d|a|y| +0#0000000&@19 +@2|m+0#00e0e07&|k|s|a| |w|e@1|k|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|w|e@1|k| +0#0000000&@18 +@57|2|0@2|,|3| @7|9|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_114.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_114.dump new file mode 100644 index 0000000000..c393e36129 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_114.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|m+0#00e0e07&|k|s|a| |w|e@1|k|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|w|e@1|k| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |i|n|c|h|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|i|n|c|h| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |f|o@1|t|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|f|o@1|t| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |y|a|r|d|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|y|a|r|d| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |m|i|l|e|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|m|i|l|e| +0#0000000&@18 +@2>m+0#00e0e07&|k|s|a| |n|a|u|t|i|c|a|l| |m|i|l|e|;+0#0000000&| @26|m+0#00e0e07&|k|s|a|n|a|u|t|i|c|a|l|m|i|l|e| +0#0000000&@10 +@2|m+0#00e0e07&|k|s|a| |f|a|t|h|o|m|;+0#0000000&| @33|m+0#00e0e07&|k|s|a|f|a|t|h|o|m| +0#0000000&@16 +@2|m+0#00e0e07&|k|s|a| |m|i|l|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|m|i|l| +0#0000000&@19 +@2|m+0#00e0e07&|k|s|a| |p|o|i|n|t|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|p|o|i|n|t| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |t|e|x|p|o|i|n|t|;+0#0000000&| @31|m+0#00e0e07&|k|s|a|t|e|x|p|o|i|n|t| +0#0000000&@14 +@2|m+0#00e0e07&|k|s|a| |m|i|c|r|o|n|;+0#0000000&| @33|m+0#00e0e07&|k|s|a|m|i|c|r|o|n| +0#0000000&@16 +@2|m+0#00e0e07&|k|s|a| |a|n|g|s|t|r|o|m|;+0#0000000&| @31|m+0#00e0e07&|k|s|a@1|n|g|s|t|r|o|m| +0#0000000&@14 +@2|m+0#00e0e07&|k|s|a| |h|e|c|t|a|r|e|;+0#0000000&| @32|m+0#00e0e07&|k|s|a|h|e|c|t|a|r|e| +0#0000000&@15 +@2|m+0#00e0e07&|k|s|a| |a|c|r|e|;+0#0000000&| @35|m+0#00e0e07&|k|s|a@1|c|r|e| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |b|a|r|n|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|b|a|r|n| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |l|i|t|e|r|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|l|i|t|e|r| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |u|s| |g|a|l@1|o|n|;+0#0000000&| @30|m+0#00e0e07&|k|s|a|u|s|g|a|l@1|o|n| +0#0000000&@14 +@2|m+0#00e0e07&|k|s|a| |q|u|a|r|t|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|q|u|a|r|t| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |p|i|n|t|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|p|i|n|t| +0#0000000&@18 +@57|2|0|1|8|,|3| @7|9|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_115.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_115.dump new file mode 100644 index 0000000000..b415266b53 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_115.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|m+0#00e0e07&|k|s|a| |p|i|n|t|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|p|i|n|t| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |c|u|p|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|c|u|p| +0#0000000&@19 +@2|m+0#00e0e07&|k|s|a| |f|l|u|i|d| |o|u|n|c|e|;+0#0000000&| @28|m+0#00e0e07&|k|s|a|f|l|u|i|d|o|u|n|c|e| +0#0000000&@12 +@2|m+0#00e0e07&|k|s|a| |t|a|b|l|e| |s|p|o@1|n|;+0#0000000&| @28|m+0#00e0e07&|k|s|a|t|a|b|l|e|s|p|o@1|n| +0#0000000&@12 +@2|m+0#00e0e07&|k|s|a| |t|e|a| |s|p|o@1|n|;+0#0000000&| @30|m+0#00e0e07&|k|s|a|t|e|a|s|p|o@1|n| +0#0000000&@14 +@2>m+0#00e0e07&|k|s|a| |c|a|n|a|d|i|a|n| |g|a|l@1|o|n|;+0#0000000&| @24|m+0#00e0e07&|k|s|a|c|a|n|a|d|i|a|n|g|a|l@1|o|n| +0#0000000&@8 +@2|m+0#00e0e07&|k|s|a| |u|k| |g|a|l@1|o|n|;+0#0000000&| @30|m+0#00e0e07&|k|s|a|u|k|g|a|l@1|o|n| +0#0000000&@14 +@2|m+0#00e0e07&|k|s|a| |m|i|l|e|s| |p|e|r| |h|o|u|r|;+0#0000000&| @25|m+0#00e0e07&|k|s|a|m|i|l|e|s|p|e|r|h|o|u|r| +0#0000000&@10 +@2|m+0#00e0e07&|k|s|a| |k|i|l|o|m|e|t|e|r|s| |p|e|r| |h|o|u|r|;+0#0000000&| @20|m+0#00e0e07&|k|s|a|k|i|l|o|m|e|t|e|r|s|p|e|r|h|o|u|r| +0#0000000&@5 +@2|m+0#00e0e07&|k|s|a| |k|n|o|t|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|k|n|o|t| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |p|o|u|n|d| |m|a|s@1|;+0#0000000&| @29|m+0#00e0e07&|k|s|a|p|o|u|n|d|m|a|s@1| +0#0000000&@13 +@2|m+0#00e0e07&|k|s|a| |o|u|n|c|e| |m|a|s@1|;+0#0000000&| @29|m+0#00e0e07&|k|s|a|o|u|n|c|e|m|a|s@1| +0#0000000&@13 +@2|m+0#00e0e07&|k|s|a| |t|o|n|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|t|o|n| +0#0000000&@19 +@2|m+0#00e0e07&|k|s|a| |m|e|t|r|i|c|t|o|n|;+0#0000000&| @30|m+0#00e0e07&|k|s|a|m|e|t|r|i|c|t|o|n| +0#0000000&@13 +@2|m+0#00e0e07&|k|s|a| |u|k|t|o|n|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|u|k|t|o|n| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |t|r|o|y| |o|u|n|c|e|;+0#0000000&| @29|m+0#00e0e07&|k|s|a|t|r|o|y|o|u|n|c|e| +0#0000000&@13 +@2|m+0#00e0e07&|k|s|a| |c|a|r|a|t|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|c|a|r|a|t| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |u|n|i|f|i|e|d| |a|t|o|m|i|c| |m|a|s@1|;+0#0000000&| @20|m+0#00e0e07&|k|s|a|u|n|i|f|i|e|d|a|t|o|m|i|c|m|a|s@1| +0#0000000&@5 +@2|m+0#00e0e07&|k|s|a| |g|r|a|m| |f|o|r|c|e|;+0#0000000&| @29|m+0#00e0e07&|k|s|a|g|r|a|m|f|o|r|c|e| +0#0000000&@13 +@57|2|0|3|6|,|3| @7|9|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_116.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_116.dump new file mode 100644 index 0000000000..44bad6f4d5 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_116.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|m+0#00e0e07&|k|s|a| |g|r|a|m| |f|o|r|c|e|;+0#0000000&| @29|m+0#00e0e07&|k|s|a|g|r|a|m|f|o|r|c|e| +0#0000000&@13 +@2|m+0#00e0e07&|k|s|a| |p|o|u|n|d| |f|o|r|c|e|;+0#0000000&| @28|m+0#00e0e07&|k|s|a|p|o|u|n|d|f|o|r|c|e| +0#0000000&@12 +@2|m+0#00e0e07&|k|s|a| |k|i|l|o| |p|o|u|n|d| |f|o|r|c|e|;+0#0000000&| @23|m+0#00e0e07&|k|s|a|k|i|l|o|p|o|u|n|d|f|o|r|c|e| +0#0000000&@8 +@2|m+0#00e0e07&|k|s|a| |p|o|u|n|d|a|l|;+0#0000000&| @32|m+0#00e0e07&|k|s|a|p|o|u|n|d|a|l| +0#0000000&@15 +@2|m+0#00e0e07&|k|s|a| |c|a|l|o|r|i|e|;+0#0000000&| @32|m+0#00e0e07&|k|s|a|c|a|l|o|r|i|e| +0#0000000&@15 +@2>m+0#00e0e07&|k|s|a| |b|t|u|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|b|t|u| +0#0000000&@19 +@2|m+0#00e0e07&|k|s|a| |t|h|e|r|m|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|t|h|e|r|m| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |h|o|r|s|e|p|o|w|e|r|;+0#0000000&| @29|m+0#00e0e07&|k|s|a|h|o|r|s|e|p|o|w|e|r| +0#0000000&@12 +@2|m+0#00e0e07&|k|s|a| |b|a|r|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|b|a|r| +0#0000000&@19 +@2|m+0#00e0e07&|k|s|a| |s|t|d|a|t|m|o|s|p|h|e|r|e|;+0#0000000&| @26|m+0#00e0e07&|k|s|a|s|t|d|a|t|m|o|s|p|h|e|r|e| +0#0000000&@9 +@2|m+0#00e0e07&|k|s|a| |t|o|r@1|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|t|o|r@1| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |m|e|t|e|r| |o|f| |m|e|r|c|u|r|y|;+0#0000000&| @23|m+0#00e0e07&|k|s|a|m|e|t|e|r|o|f|m|e|r|c|u|r|y| +0#0000000&@8 +@2|m+0#00e0e07&|k|s|a| |i|n|c|h| |o|f| |m|e|r|c|u|r|y|;+0#0000000&| @24|m+0#00e0e07&|k|s|a|i|n|c|h|o|f|m|e|r|c|u|r|y| +0#0000000&@9 +@2|m+0#00e0e07&|k|s|a| |i|n|c|h| |o|f| |w|a|t|e|r|;+0#0000000&| @26|m+0#00e0e07&|k|s|a|i|n|c|h|o|f|w|a|t|e|r| +0#0000000&@11 +@2|m+0#00e0e07&|k|s|a| |p|s|i|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|p|s|i| +0#0000000&@19 +@2|m+0#00e0e07&|k|s|a| |p|o|i|s|e|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|p|o|i|s|e| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |s|t|o|k|e|s|;+0#0000000&| @33|m+0#00e0e07&|k|s|a|s|t|o|k|e|s| +0#0000000&@16 +@2|m+0#00e0e07&|k|s|a| |f|a|r|a|d|a|y|;+0#0000000&| @32|m+0#00e0e07&|k|s|a|f|a|r|a|d|a|y| +0#0000000&@15 +@2|m+0#00e0e07&|k|s|a| |e|l|e|c|t|r|o|n| |c|h|a|r|g|e|;+0#0000000&| @24|m+0#00e0e07&|k|s|a|e|l|e|c|t|r|o|n|c|h|a|r|g|e| +0#0000000&@8 +@57|2|0|5|4|,|3| @7|9|7|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_117.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_117.dump new file mode 100644 index 0000000000..69f1e2bbb1 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_117.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|m+0#00e0e07&|k|s|a| |e|l|e|c|t|r|o|n| |c|h|a|r|g|e|;+0#0000000&| @24|m+0#00e0e07&|k|s|a|e|l|e|c|t|r|o|n|c|h|a|r|g|e| +0#0000000&@8 +@2|m+0#00e0e07&|k|s|a| |g|a|u|s@1|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|g|a|u|s@1| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |s|t|i|l|b|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|s|t|i|l|b| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |l|u|m|e|n|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|l|u|m|e|n| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |l|u|x|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|l|u|x| +0#0000000&@19 +@2>m+0#00e0e07&|k|s|a| |p|h|o|t|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|p|h|o|t| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |f|o@1|t| |c|a|n|d|l|e|;+0#0000000&| @28|m+0#00e0e07&|k|s|a|f|o@1|t|c|a|n|d|l|e| +0#0000000&@12 +@2|m+0#00e0e07&|k|s|a| |l|a|m|b|e|r|t|;+0#0000000&| @32|m+0#00e0e07&|k|s|a|l|a|m|b|e|r|t| +0#0000000&@15 +@2|m+0#00e0e07&|k|s|a| |f|o@1|t| |l|a|m|b|e|r|t|;+0#0000000&| @27|m+0#00e0e07&|k|s|a|f|o@1|t|l|a|m|b|e|r|t| +0#0000000&@11 +@2|m+0#00e0e07&|k|s|a| |c|u|r|i|e|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|c|u|r|i|e| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |r|o|e|n|t|g|e|n|;+0#0000000&| @31|m+0#00e0e07&|k|s|a|r|o|e|n|t|g|e|n| +0#0000000&@14 +@2|m+0#00e0e07&|k|s|a| |r|a|d|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|r|a|d| +0#0000000&@19 +@2|m+0#00e0e07&|k|s|a| |s|o|l|a|r| |m|a|s@1|;+0#0000000&| @29|m+0#00e0e07&|k|s|a|s|o|l|a|r|m|a|s@1| +0#0000000&@13 +@2|m+0#00e0e07&|k|s|a| |b|o|h|r| |r|a|d|i|u|s|;+0#0000000&| @28|m+0#00e0e07&|k|s|a|b|o|h|r@1|a|d|i|u|s| +0#0000000&@12 +@2|m+0#00e0e07&|k|s|a| |v|a|c|u@1|m| |p|e|r|m|i|t@1|i|v|i|t|y|;+0#0000000&| @20|m+0#00e0e07&|k|s|a|v|a|c|u@1|m|p|e|r|m|i|t@1|i|v|i|t|y| +0#0000000&@4 +@2|m+0#00e0e07&|k|s|a| |n|e|w|t|o|n|;+0#0000000&| @33|m+0#00e0e07&|k|s|a|n|e|w|t|o|n| +0#0000000&@16 +@2|m+0#00e0e07&|k|s|a| |d|y|n|e|;+0#0000000&| @35|m+0#00e0e07&|k|s|a|d|y|n|e| +0#0000000&@18 +@2|m+0#00e0e07&|k|s|a| |j|o|u|l|e|;+0#0000000&| @34|m+0#00e0e07&|k|s|a|j|o|u|l|e| +0#0000000&@17 +@2|m+0#00e0e07&|k|s|a| |e|r|g|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|e|r|g| +0#0000000&@19 +@57|2|0|7|2|,|3| @7|9|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_118.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_118.dump new file mode 100644 index 0000000000..91261ce304 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_118.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|m+0#00e0e07&|k|s|a| |e|r|g|;+0#0000000&| @36|m+0#00e0e07&|k|s|a|e|r|g| +0#0000000&@19 +@2|n+0#00e0e07&|u|m| |f|i|n|e| |s|t|r|u|c|t|u|r|e|;+0#0000000&| @26|n+0#00e0e07&|u|m|f|i|n|e|s|t|r|u|c|t|u|r|e| +0#0000000&@10 +@2|n+0#00e0e07&|u|m| |a|v|o|g|a|d|r|o|;+0#0000000&| @32|n+0#00e0e07&|u|m|a|v|o|g|a|d|r|o| +0#0000000&@15 +@2|n+0#00e0e07&|u|m| |y|o|t@1|a|;+0#0000000&| @35|n+0#00e0e07&|u|m|y|o|t@1|a| +0#0000000&@18 +@2|n+0#00e0e07&|u|m| |z|e|t@1|a|;+0#0000000&| @35|n+0#00e0e07&|u|m|z|e|t@1|a| +0#0000000&@18 +@2>n+0#00e0e07&|u|m| |e|x|a|;+0#0000000&| @37|n+0#00e0e07&|u|m|e|x|a| +0#0000000&@20 +@2|n+0#00e0e07&|u|m| |p|e|t|a|;+0#0000000&| @36|n+0#00e0e07&|u|m|p|e|t|a| +0#0000000&@19 +@2|n+0#00e0e07&|u|m| |t|e|r|a|;+0#0000000&| @36|n+0#00e0e07&|u|m|t|e|r|a| +0#0000000&@19 +@2|n+0#00e0e07&|u|m| |g|i|g|a|;+0#0000000&| @36|n+0#00e0e07&|u|m|g|i|g|a| +0#0000000&@19 +@2|n+0#00e0e07&|u|m| |m|e|g|a|;+0#0000000&| @36|n+0#00e0e07&|u|m@1|e|g|a| +0#0000000&@19 +@2|n+0#00e0e07&|u|m| |k|i|l|o|;+0#0000000&| @36|n+0#00e0e07&|u|m|k|i|l|o| +0#0000000&@19 +@2|n+0#00e0e07&|u|m| |m|i|l@1|i|;+0#0000000&| @35|n+0#00e0e07&|u|m@1|i|l@1|i| +0#0000000&@18 +@2|n+0#00e0e07&|u|m| |m|i|c|r|o|;+0#0000000&| @35|n+0#00e0e07&|u|m@1|i|c|r|o| +0#0000000&@18 +@2|n+0#00e0e07&|u|m| |n|a|n|o|;+0#0000000&| @36|n+0#00e0e07&|u|m|n|a|n|o| +0#0000000&@19 +@2|n+0#00e0e07&|u|m| |p|i|c|o|;+0#0000000&| @36|n+0#00e0e07&|u|m|p|i|c|o| +0#0000000&@19 +@2|n+0#00e0e07&|u|m| |f|e|m|t|o|;+0#0000000&| @35|n+0#00e0e07&|u|m|f|e|m|t|o| +0#0000000&@18 +@2|n+0#00e0e07&|u|m| |a|t@1|o|;+0#0000000&| @36|n+0#00e0e07&|u|m|a|t@1|o| +0#0000000&@19 +@2|n+0#00e0e07&|u|m| |z|e|p|t|o|;+0#0000000&| @35|n+0#00e0e07&|u|m|z|e|p|t|o| +0#0000000&@18 +@2|n+0#00e0e07&|u|m| |y|o|c|t|o|;+0#0000000&| @35|n+0#00e0e07&|u|m|y|o|c|t|o| +0#0000000&@18 +@57|2|0|9|0|,|3| @7|9@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_119.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_119.dump new file mode 100644 index 0000000000..519b205d1c --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_119.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|n+0#00e0e07&|u|m| |y|o|c|t|o|;+0#0000000&| @35|n+0#00e0e07&|u|m|y|o|c|t|o| +0#0000000&@18 +|#+0#0000e05&| |F@1|T|.| |#| +0#0000000&@66 +@2|p+0#00e0e07&|r|i|m|e|f|a|c|t|o|r|s|;+0#0000000&| @32|p+0#00e0e07&|r|i|m|e|f|a|c|t|o|r|s| +0#0000000&@14 +@2|f+0#00e0e07&@1|t| |c|o|m|p|l|e|x| |f|o|r|w|a|r|d|;+0#0000000&| @25|f+0#00e0e07&@1|t|c|o|m|p|l|e|x|f|o|r|w|a|r|d| +0#0000000&@9 +@2|f+0#00e0e07&@1|t| |c|o|m|p|l|e|x| |b|a|c|k|w|a|r|d|;+0#0000000&| @24|f+0#00e0e07&@1|t|c|o|m|p|l|e|x|b|a|c|k|w|a|r|d| +0#0000000&@8 +@2>f+0#00e0e07&@1|t| |c|o|m|p|l|e|x| |i|n|v|e|r|s|e|;+0#0000000&| @25|f+0#00e0e07&@1|t|c|o|m|p|l|e|x|i|n|v|e|r|s|e| +0#0000000&@9 +@2|f+0#00e0e07&@1|t| |f|o|r|w|a|r|d|;+0#0000000&| @33|f+0#00e0e07&@1|t|f|o|r|w|a|r|d| +0#0000000&@16 +@2|f+0#00e0e07&@1|t| |b|a|c|k|w|a|r|d|;+0#0000000&| @32|f+0#00e0e07&@1|t|b|a|c|k|w|a|r|d| +0#0000000&@15 +@2|f+0#00e0e07&@1|t| |i|n|v|e|r|s|e|;+0#0000000&| @33|f+0#00e0e07&@1|t|i|n|v|e|r|s|e| +0#0000000&@16 +|#+0#0000e05&| |L|a|p|l|a|c|e|.| |#| +0#0000000&@62 +@2|l+0#00e0e07&|a|p|l|a|c|e|;+0#0000000&| @37|l+0#00e0e07&|a|p|l|a|c|e| +0#0000000&@19 +@75 +@75 +|#+0#0000e05&| |v|i|m|:| |s|y|n|t|a|x|=|a|l|g|o|l|6|8| |t|s|=|4|8| +0#0000000&@47 +|#+0#0000e05&| +0#0000000&@73 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|2|1|0|8|,|3| @7|B|o|t| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_12.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_12.dump new file mode 100644 index 0000000000..9f38c85585 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_12.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|O+0#af5f00255&|V|E|R|A|B| +0#0000000&@39|O+0#af5f00255&|V|E|R|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|O|D|A|B| +0#0000000&@40|M+0#af5f00255&|O|D|A|B| +0#0000000&@21 +|#+0#0000e05&| |R|E|A|L| |o|p|s|.| |#| +0#0000000&@61 +@2>++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|S+0#af5f00255&|I|G|N| +0#0000000&@41|S+0#af5f00255&|I|G|N| +0#0000000&@22 +@2|R+0#af5f00255&|O|U|N|D| +0#0000000&@40|R+0#af5f00255&|O|U|N|D| +0#0000000&@21 +@2|E+0#af5f00255&|N|T|I|E|R| +0#0000000&@39|E+0#af5f00255&|N|T|I|E|R| +0#0000000&@20 +@2|F+0#af5f00255&|L|O@1|R| +0#0000000&@40|F+0#af5f00255&|L|O@1|R| +0#0000000&@21 +@2|C+0#af5f00255&|E|I|L| +0#0000000&@41|C+0#af5f00255&|E|I|L| +0#0000000&@22 +@2|N+0#af5f00255&|I|N|T| +0#0000000&@41|N+0#af5f00255&|I|N|T| +0#0000000&@22 +@2|T+0#af5f00255&|R|U|N|C| +0#0000000&@40|T+0#af5f00255&|R|U|N|C| +0#0000000&@21 +@2|F+0#af5f00255&|R|A|C| +0#0000000&@41|F+0#af5f00255&|R|A|C| +0#0000000&@22 +@2|F+0#af5f00255&|I|X| +0#0000000&@42|F+0#af5f00255&|I|X| +0#0000000&@23 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@57|2|1|7|,|3| @8|1|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_13.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_13.dump new file mode 100644 index 0000000000..91dec8a6e7 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_13.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2>>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@57|2|3|5|,|3| @8|1|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_14.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_14.dump new file mode 100644 index 0000000000..45e2623bc7 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_14.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2>++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|D+0#af5f00255&|I|V|A|B| +0#0000000&@40|D+0#af5f00255&|I|V|A|B| +0#0000000&@21 +|#+0#0000e05&| |P|r|o|c|e|d|u|r|e|s| |#| +0#0000000&@60 +@2|a+0#00e0e07&|r|c| |c|o|s| |d|g|;+0#0000000&| @1|a+0#00e0e07&|c|o|s| |d|g|;+0#0000000&| @24|a+0#00e0e07&|r|c@1|o|s|d|g|;+0#0000000&| @1|a+0#00e0e07&|c|o|s|d|g| +0#0000000&@9 +@2|a+0#00e0e07&|r|c| |c|o|s|h|;+0#0000000&| @1|a+0#00e0e07&|c|o|s|h|;+0#0000000&| @28|a+0#00e0e07&|r|c@1|o|s|h|;+0#0000000&| @1|a+0#00e0e07&|c|o|s|h| +0#0000000&@11 +@2|a+0#00e0e07&|r|c| |c|o|t|;+0#0000000&| @1|a+0#00e0e07&|c|o|t|;+0#0000000&| @30|a+0#00e0e07&|r|c@1|o|t|;+0#0000000&| @1|a+0#00e0e07&|c|o|t| +0#0000000&@13 +@2|a+0#00e0e07&|r|c| |c|o|t| |d|g|;+0#0000000&| @1|a+0#00e0e07&|c|o|t| |d|g|;+0#0000000&| @24|a+0#00e0e07&|r|c@1|o|t|d|g|;+0#0000000&| @1|a+0#00e0e07&|c|o|t|d|g| +0#0000000&@9 +@2|a+0#00e0e07&|r|c| |c|s|c|;+0#0000000&| @1|a+0#00e0e07&|c|s|c|;+0#0000000&| @30|a+0#00e0e07&|r|c@1|s|c|;+0#0000000&| @1|a+0#00e0e07&|c|s|c| +0#0000000&@13 +@57|2|5|3|,|3| @8|1@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_15.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_15.dump new file mode 100644 index 0000000000..b27569acd8 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_15.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|a+0#00e0e07&|r|c| |c|s|c|;+0#0000000&| @1|a+0#00e0e07&|c|s|c|;+0#0000000&| @30|a+0#00e0e07&|r|c@1|s|c|;+0#0000000&| @1|a+0#00e0e07&|c|s|c| +0#0000000&@13 +@2|a+0#00e0e07&|r|c| |c|s|c| |d|g|;+0#0000000&| @1|a+0#00e0e07&|c|s|c| |d|g|;+0#0000000&| @24|a+0#00e0e07&|r|c@1|s|c|d|g|;+0#0000000&| @1|a+0#00e0e07&|c|s|c|d|g| +0#0000000&@9 +@2|a+0#00e0e07&|r|c| |s|e|c|;+0#0000000&| @1|a+0#00e0e07&|s|e|c|;+0#0000000&| @30|a+0#00e0e07&|r|c|s|e|c|;+0#0000000&| @1|a+0#00e0e07&|s|e|c| +0#0000000&@13 +@2|a+0#00e0e07&|r|c| |s|e|c| |d|g|;+0#0000000&| @1|a+0#00e0e07&|s|e|c| |d|g|;+0#0000000&| @24|a+0#00e0e07&|r|c|s|e|c|d|g|;+0#0000000&| @1|a+0#00e0e07&|s|e|c|d|g| +0#0000000&@9 +@2|a+0#00e0e07&|r|c| |s|i|n| |d|g|;+0#0000000&| @1|a+0#00e0e07&|s|i|n| |d|g|;+0#0000000&| @24|a+0#00e0e07&|r|c|s|i|n|d|g|;+0#0000000&| @1|a+0#00e0e07&|s|i|n|d|g| +0#0000000&@9 +@2>a+0#00e0e07&|r|c| |s|i|n|h|;+0#0000000&| @1|a+0#00e0e07&|s|i|n|h|;+0#0000000&| @28|a+0#00e0e07&|r|c|s|i|n|h|;+0#0000000&| @1|a+0#00e0e07&|s|i|n|h| +0#0000000&@11 +@2|a+0#00e0e07&|r|c| |t|a|n| |d|g|;+0#0000000&| @1|a+0#00e0e07&|t|a|n| |d|g|;+0#0000000&| @24|a+0#00e0e07&|r|c|t|a|n|d|g|;+0#0000000&| @1|a+0#00e0e07&|t|a|n|d|g| +0#0000000&@9 +@2|a+0#00e0e07&|r|c| |t|a|n|h|;+0#0000000&| @1|a+0#00e0e07&|t|a|n|h|;+0#0000000&| @28|a+0#00e0e07&|r|c|t|a|n|h|;+0#0000000&| @1|a+0#00e0e07&|t|a|n|h| +0#0000000&@11 +@2|a+0#00e0e07&|r|c| |c|o|s|;+0#0000000&| @1|a+0#00e0e07&|c|o|s|;+0#0000000&| @30|a+0#00e0e07&|r|c@1|o|s|;+0#0000000&| @1|a+0#00e0e07&|c|o|s| +0#0000000&@13 +@2|a+0#00e0e07&|r|c| |s|i|n|;+0#0000000&| @1|a+0#00e0e07&|s|i|n|;+0#0000000&| @30|a+0#00e0e07&|r|c|s|i|n|;+0#0000000&| @1|a+0#00e0e07&|s|i|n| +0#0000000&@13 +@2|a+0#00e0e07&|r|c| |t|a|n|;+0#0000000&| @1|a+0#00e0e07&|t|a|n|;+0#0000000&| @30|a+0#00e0e07&|r|c|t|a|n|;+0#0000000&| @1|a+0#00e0e07&|t|a|n| +0#0000000&@13 +@2|c+0#00e0e07&|a|s|;+0#0000000&| @41|c+0#00e0e07&|a|s| +0#0000000&@23 +@2|c+0#00e0e07&|b|r|t|;+0#0000000&| @40|c+0#00e0e07&|b|r|t| +0#0000000&@22 +@2|c+0#00e0e07&|o|s| |d|g|;+0#0000000&| @38|c+0#00e0e07&|o|s|d|g| +0#0000000&@21 +@2|c+0#00e0e07&|o|s|h|;+0#0000000&| @40|c+0#00e0e07&|o|s|h| +0#0000000&@22 +@2|c+0#00e0e07&|o|s| |p|i|;+0#0000000&| @38|c+0#00e0e07&|o|s|p|i| +0#0000000&@21 +@2|c+0#00e0e07&|o|t| |d|g|;+0#0000000&| @38|c+0#00e0e07&|o|t|d|g| +0#0000000&@21 +@2|c+0#00e0e07&|o|t|;+0#0000000&| @41|c+0#00e0e07&|o|t| +0#0000000&@23 +@2|c+0#00e0e07&|o|t| |p|i|;+0#0000000&| @38|c+0#00e0e07&|o|t|p|i| +0#0000000&@21 +@57|2|7|1|,|3| @8|1|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_16.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_16.dump new file mode 100644 index 0000000000..2b6ac0a03e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_16.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|o|t| |p|i|;+0#0000000&| @38|c+0#00e0e07&|o|t|p|i| +0#0000000&@21 +@2|c+0#00e0e07&|s|c| |d|g|;+0#0000000&| @38|c+0#00e0e07&|s|c|d|g| +0#0000000&@21 +@2|c+0#00e0e07&|s|c|;+0#0000000&| @41|c+0#00e0e07&|s|c| +0#0000000&@23 +@2|c+0#00e0e07&|u|r|t|;+0#0000000&| @40|c+0#00e0e07&|u|r|t| +0#0000000&@22 +@2|e+0#00e0e07&|r|f|c|;+0#0000000&| @40|e+0#00e0e07&|r|f|c| +0#0000000&@22 +@2>e+0#00e0e07&|r|f|;+0#0000000&| @41|e+0#00e0e07&|r|f| +0#0000000&@23 +@2|g+0#00e0e07&|a|m@1|a|;+0#0000000&| @39|g+0#00e0e07&|a|m@1|a| +0#0000000&@21 +@2|i+0#00e0e07&|n|v| |e|r|f|c|;+0#0000000&| @36|i+0#00e0e07&|n|v|e|r|f|c| +0#0000000&@19 +@2|i+0#00e0e07&|n|v| |e|r|f|;+0#0000000&| @37|i+0#00e0e07&|n|v|e|r|f| +0#0000000&@20 +@2|i+0#00e0e07&|n|v|e|r|s|e| |e|r|f|c|;+0#0000000&| @32|i+0#00e0e07&|n|v|e|r|s|e@1|r|f|c| +0#0000000&@15 +@2|i+0#00e0e07&|n|v|e|r|s|e| |e|r|f|;+0#0000000&| @33|i+0#00e0e07&|n|v|e|r|s|e@1|r|f| +0#0000000&@16 +@2|l+0#00e0e07&|n|1|p|;+0#0000000&| @40|l+0#00e0e07&|n|1|p| +0#0000000&@22 +@2|l+0#00e0e07&|n| |g|a|m@1|a|;+0#0000000&| @36|l+0#00e0e07&|n|g|a|m@1|a| +0#0000000&@19 +@2|s+0#00e0e07&|e|c| |d|g|;+0#0000000&| @38|s+0#00e0e07&|e|c|d|g| +0#0000000&@21 +@2|s+0#00e0e07&|e|c|;+0#0000000&| @41|s+0#00e0e07&|e|c| +0#0000000&@23 +@2|s+0#00e0e07&|i|n| |d|g|;+0#0000000&| @38|s+0#00e0e07&|i|n|d|g| +0#0000000&@21 +@2|s+0#00e0e07&|i|n|h|;+0#0000000&| @40|s+0#00e0e07&|i|n|h| +0#0000000&@22 +@2|s+0#00e0e07&|i|n| |p|i|;+0#0000000&| @38|s+0#00e0e07&|i|n|p|i| +0#0000000&@21 +@2|t+0#00e0e07&|a|n| |d|g|;+0#0000000&| @38|t+0#00e0e07&|a|n|d|g| +0#0000000&@21 +@57|2|8|9|,|3| @8|1|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_17.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_17.dump new file mode 100644 index 0000000000..79865e1ab9 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_17.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|t+0#00e0e07&|a|n| |d|g|;+0#0000000&| @38|t+0#00e0e07&|a|n|d|g| +0#0000000&@21 +@2|t+0#00e0e07&|a|n|h|;+0#0000000&| @40|t+0#00e0e07&|a|n|h| +0#0000000&@22 +@2|t+0#00e0e07&|a|n| |p|i|;+0#0000000&| @38|t+0#00e0e07&|a|n|p|i| +0#0000000&@21 +@2|c+0#00e0e07&|o|s|;+0#0000000&| @41|c+0#00e0e07&|o|s| +0#0000000&@23 +@2|e+0#00e0e07&|x|p|;+0#0000000&| @41|e+0#00e0e07&|x|p| +0#0000000&@23 +@2>l+0#00e0e07&|n|;+0#0000000&| @42|l+0#00e0e07&|n| +0#0000000&@24 +@2|l+0#00e0e07&|o|g|;+0#0000000&| @41|l+0#00e0e07&|o|g| +0#0000000&@23 +@2|s+0#00e0e07&|i|n|;+0#0000000&| @41|s+0#00e0e07&|i|n| +0#0000000&@23 +@2|s+0#00e0e07&|q|r|t|;+0#0000000&| @40|s+0#00e0e07&|q|r|t| +0#0000000&@22 +@2|t+0#00e0e07&|a|n|;+0#0000000&| @41|t+0#00e0e07&|a|n| +0#0000000&@23 +|#+0#0000e05&| |M|i|s|c|e|l@1|a|n|e|o|u|s|.| |#| +0#0000000&@56 +@2|a+0#00e0e07&|r|c| |t|a|n|2|;+0#0000000&| @1|a+0#00e0e07&|t|a|n|2|;+0#0000000&| @28|a+0#00e0e07&|r|c|t|a|n|2|;+0#0000000&| @1|a+0#00e0e07&|t|a|n|2| +0#0000000&@11 +@2|a+0#00e0e07&|r|c| |t|a|n|2| |d|g|;+0#0000000&| @1|a+0#00e0e07&|t|a|n|2| |d|g|;+0#0000000&| @22|a+0#00e0e07&|r|c|t|a|n|2|d|g|;+0#0000000&| @1|a+0#00e0e07&|t|a|n|2|d|g| +0#0000000&@7 +@2|b+0#00e0e07&|e|t|a|;+0#0000000&| @40|b+0#00e0e07&|e|t|a| +0#0000000&@22 +@2|b+0#00e0e07&|e|t|a|i|n|c|;+0#0000000&| @37|b+0#00e0e07&|e|t|a|i|n|c| +0#0000000&@19 +@2|c+0#00e0e07&|h|o@1|s|e|;+0#0000000&| @38|c+0#00e0e07&|h|o@1|s|e| +0#0000000&@20 +@2|f+0#00e0e07&|a|c|t|;+0#0000000&| @40|f+0#00e0e07&|a|c|t| +0#0000000&@22 +@2|g+0#00e0e07&|a|m@1|a| |i|n|c|;+0#0000000&| @35|g+0#00e0e07&|a|m@1|a|i|n|c| +0#0000000&@18 +@2|g+0#00e0e07&|a|m@1|a| |i|n|c|f|;+0#0000000&| @34|g+0#00e0e07&|a|m@1|a|i|n|c|f| +0#0000000&@17 +@57|3|0|7|,|3| @8|1|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_18.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_18.dump new file mode 100644 index 0000000000..8e25d09c0e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_18.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|g+0#00e0e07&|a|m@1|a| |i|n|c|f|;+0#0000000&| @34|g+0#00e0e07&|a|m@1|a|i|n|c|f| +0#0000000&@17 +@2|g+0#00e0e07&|a|m@1|a| |i|n|c|g|;+0#0000000&| @34|g+0#00e0e07&|a|m@1|a|i|n|c|g| +0#0000000&@17 +@2|g+0#00e0e07&|a|m@1|a| |i|n|c|g|f|;+0#0000000&| @33|g+0#00e0e07&|a|m@1|a|i|n|c|g|f| +0#0000000&@16 +@2|l+0#00e0e07&|j|e|1|2|6|;+0#0000000&| @38|l+0#00e0e07&|j|e|1|2|6| +0#0000000&@20 +@2|l+0#00e0e07&|j|f|1|2|6|;+0#0000000&| @38|l+0#00e0e07&|j|f|1|2|6| +0#0000000&@20 +@2>l+0#00e0e07&|n| |b|e|t|a|;+0#0000000&| @37|l+0#00e0e07&|n|b|e|t|a| +0#0000000&@20 +@2|l+0#00e0e07&|n| |c|h|o@1|s|e|;+0#0000000&| @35|l+0#00e0e07&|n|c|h|o@1|s|e| +0#0000000&@18 +@2|l+0#00e0e07&|n| |f|a|c|t|;+0#0000000&| @37|l+0#00e0e07&|n|f|a|c|t| +0#0000000&@20 +|#+0#0000e05&| |C|O|M|P|L|E|X| |o|p|s|.| |#| +0#0000000&@58 +@2|I+0#af5f00255&| +0#0000000&@44|I+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|*| +0#0000000&@43|++0#af5f00255&|*| +0#0000000&@24 +@2|I+0#af5f00255&| +0#0000000&@44|I+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|*| +0#0000000&@43|++0#af5f00255&|*| +0#0000000&@24 +@2|R+0#af5f00255&|E| +0#0000000&@43|R+0#af5f00255&|E| +0#0000000&@24 +@2|I+0#af5f00255&|M| +0#0000000&@43|I+0#af5f00255&|M| +0#0000000&@24 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|A+0#af5f00255&|R|G| +0#0000000&@42|A+0#af5f00255&|R|G| +0#0000000&@23 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@57|3|2|5|,|3| @8|1|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_19.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_19.dump new file mode 100644 index 0000000000..480bf6793d --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_19.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|C+0#af5f00255&|O|N|J| +0#0000000&@41|C+0#af5f00255&|O|N|J| +0#0000000&@22 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2>^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@57|3|4|3|,|3| @8|1|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_20.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_20.dump new file mode 100644 index 0000000000..1c330f82c3 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_20.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|D+0#af5f00255&|I|V|A|B| +0#0000000&@40|D+0#af5f00255&|I|V|A|B| +0#0000000&@21 +@2>c+0#00e0e07&|o|m|p|l|e|x| |a|r|c@1|o|s|h|;+0#0000000&| @1|c+0#00e0e07&|a|c|o|s|h|;+0#0000000&| @20|c+0#00e0e07&|o|m|p|l|e|x|a|r|c@1|o|s|h|;+0#0000000&| @1|c+0#00e0e07&|a|c|o|s|h| +0#0000000&@3 +@2|c+0#00e0e07&|o|m|p|l|e|x| |a|r|c@1|o|s|;+0#0000000&| @1|c+0#00e0e07&|a|c|o|s|;+0#0000000&| @22|c+0#00e0e07&|o|m|p|l|e|x|a|r|c@1|o|s|;+0#0000000&| @1|c+0#00e0e07&|a|c|o|s| +0#0000000&@5 +@2|c+0#00e0e07&|o|m|p|l|e|x| |a|r|c|s|i|n|h|;+0#0000000&| @1|c+0#00e0e07&|a|s|i|n|h|;+0#0000000&| @20|c+0#00e0e07&|o|m|p|l|e|x|a|r|c|s|i|n|h|;+0#0000000&| @1|c+0#00e0e07&|a|s|i|n|h| +0#0000000&@3 +@2|c+0#00e0e07&|o|m|p|l|e|x| |a|r|c|s|i|n|;+0#0000000&| @1|c+0#00e0e07&|a|s|i|n|;+0#0000000&| @22|c+0#00e0e07&|o|m|p|l|e|x|a|r|c|s|i|n|;+0#0000000&| @1|c+0#00e0e07&|a|s|i|n| +0#0000000&@5 +@2|c+0#00e0e07&|o|m|p|l|e|x| |a|r|c|t|a|n|h|;+0#0000000&| @1|c+0#00e0e07&|a|t|a|n|h|;+0#0000000&| @20|c+0#00e0e07&|o|m|p|l|e|x|a|r|c|t|a|n|h|;+0#0000000&| @1|c+0#00e0e07&|a|t|a|n|h| +0#0000000&@3 +@2|c+0#00e0e07&|o|m|p|l|e|x| |a|r|c|t|a|n|;+0#0000000&| @1|c+0#00e0e07&|a|t|a|n|;+0#0000000&| @22|c+0#00e0e07&|o|m|p|l|e|x|a|r|c|t|a|n|;+0#0000000&| @1|c+0#00e0e07&|a|t|a|n| +0#0000000&@5 +@2|c+0#00e0e07&|o|m|p|l|e|x| |c|o|s|h|;+0#0000000&| @1|c+0#00e0e07&@1|o|s|h|;+0#0000000&| @24|c+0#00e0e07&|o|m|p|l|e|x|c|o|s|h|;+0#0000000&| @1|c+0#00e0e07&@1|o|s|h| +0#0000000&@7 +@2|c+0#00e0e07&|o|m|p|l|e|x| |c|o|s|;+0#0000000&| @1|c+0#00e0e07&@1|o|s|;+0#0000000&| @26|c+0#00e0e07&|o|m|p|l|e|x|c|o|s|;+0#0000000&| @1|c+0#00e0e07&@1|o|s| +0#0000000&@9 +@2|c+0#00e0e07&|o|m|p|l|e|x| |e|x|p|;+0#0000000&| @1|c+0#00e0e07&|e|x|p|;+0#0000000&| @26|c+0#00e0e07&|o|m|p|l|e|x|e|x|p|;+0#0000000&| @1|c+0#00e0e07&|e|x|p| +0#0000000&@9 +@2|c+0#00e0e07&|o|m|p|l|e|x| |l|n|;+0#0000000&| @1|c+0#00e0e07&|l|n|;+0#0000000&| @28|c+0#00e0e07&|o|m|p|l|e|x|l|n|;+0#0000000&| @1|c+0#00e0e07&|l|n| +0#0000000&@11 +@2|c+0#00e0e07&|o|m|p|l|e|x| |s|i|n|h|;+0#0000000&| @1|c+0#00e0e07&|s|i|n|h|;+0#0000000&| @24|c+0#00e0e07&|o|m|p|l|e|x|s|i|n|h|;+0#0000000&| @1|c+0#00e0e07&|s|i|n|h| +0#0000000&@7 +@2|c+0#00e0e07&|o|m|p|l|e|x| |s|i|n|;+0#0000000&| @1|c+0#00e0e07&|s|i|n|;+0#0000000&| @26|c+0#00e0e07&|o|m|p|l|e|x|s|i|n|;+0#0000000&| @1|c+0#00e0e07&|s|i|n| +0#0000000&@9 +@2|c+0#00e0e07&|o|m|p|l|e|x| |s|q|r|t|;+0#0000000&| @1|c+0#00e0e07&|s|q|r|t|;+0#0000000&| @24|c+0#00e0e07&|o|m|p|l|e|x|s|q|r|t|;+0#0000000&| @1|c+0#00e0e07&|s|q|r|t| +0#0000000&@7 +@2|c+0#00e0e07&|o|m|p|l|e|x| |t|a|n|h|;+0#0000000&| @1|c+0#00e0e07&|t|a|n|h|;+0#0000000&| @24|c+0#00e0e07&|o|m|p|l|e|x|t|a|n|h|;+0#0000000&| @1|c+0#00e0e07&|t|a|n|h| +0#0000000&@7 +@57|3|6|1|,|3| @8|1|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_21.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_21.dump new file mode 100644 index 0000000000..9b019463e4 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_21.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|o|m|p|l|e|x| |t|a|n|h|;+0#0000000&| @1|c+0#00e0e07&|t|a|n|h|;+0#0000000&| @24|c+0#00e0e07&|o|m|p|l|e|x|t|a|n|h|;+0#0000000&| @1|c+0#00e0e07&|t|a|n|h| +0#0000000&@7 +@2|c+0#00e0e07&|o|m|p|l|e|x| |t|a|n|;+0#0000000&| @1|c+0#00e0e07&|t|a|n|;+0#0000000&| @26|c+0#00e0e07&|o|m|p|l|e|x|t|a|n|;+0#0000000&| @1|c+0#00e0e07&|t|a|n| +0#0000000&@9 +|#+0#0000e05&| |B|O@1|L| |o|p|s|.| |#| +0#0000000&@61 +@2|N+0#af5f00255&|O|T| +0#0000000&@42|N+0#af5f00255&|O|T| +0#0000000&@23 +@2|~+0#af5f00255&| +0#0000000&@44|~+0#af5f00255&| +0#0000000&@25 +@2>A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|O+0#af5f00255&|R| +0#0000000&@43|O+0#af5f00255&|R| +0#0000000&@24 +@2|A+0#af5f00255&|N|D| +0#0000000&@42|A+0#af5f00255&|N|D| +0#0000000&@23 +@2|&+0#af5f00255&| +0#0000000&@44|&+0#af5f00255&| +0#0000000&@25 +@2|X+0#af5f00255&|O|R| +0#0000000&@42|X+0#af5f00255&|O|R| +0#0000000&@23 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +|#+0#0000e05&| |C|H|A|R| |o|p|s|.| |#| +0#0000000&@61 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@57|3|7|9|,|3| @8|1|7|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_22.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_22.dump new file mode 100644 index 0000000000..3475ff99b5 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_22.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2>>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|R+0#af5f00255&|E|P|R| +0#0000000&@41|R+0#af5f00255&|E|P|R| +0#0000000&@22 +@2|i+0#00e0e07&|s| |a|l|n|u|m|;+0#0000000&| @36|i+0#00e0e07&|s|a|l|n|u|m| +0#0000000&@19 +@2|i+0#00e0e07&|s| |a|l|p|h|a|;+0#0000000&| @36|i+0#00e0e07&|s|a|l|p|h|a| +0#0000000&@19 +@2|i+0#00e0e07&|s| |c|n|t|r|l|;+0#0000000&| @36|i+0#00e0e07&|s|c|n|t|r|l| +0#0000000&@19 +@2|i+0#00e0e07&|s| |d|i|g|i|t|;+0#0000000&| @36|i+0#00e0e07&|s|d|i|g|i|t| +0#0000000&@19 +@57|3|9|7|,|3| @8|1|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_23.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_23.dump new file mode 100644 index 0000000000..12eb44a95e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_23.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|i+0#00e0e07&|s| |d|i|g|i|t|;+0#0000000&| @36|i+0#00e0e07&|s|d|i|g|i|t| +0#0000000&@19 +@2|i+0#00e0e07&|s| |g|r|a|p|h|;+0#0000000&| @36|i+0#00e0e07&|s|g|r|a|p|h| +0#0000000&@19 +@2|i+0#00e0e07&|s| |l|o|w|e|r|;+0#0000000&| @36|i+0#00e0e07&|s|l|o|w|e|r| +0#0000000&@19 +@2|i+0#00e0e07&|s| |p|r|i|n|t|;+0#0000000&| @36|i+0#00e0e07&|s|p|r|i|n|t| +0#0000000&@19 +@2|i+0#00e0e07&|s| |p|u|n|c|t|;+0#0000000&| @36|i+0#00e0e07&|s|p|u|n|c|t| +0#0000000&@19 +@2>i+0#00e0e07&|s| |s|p|a|c|e|;+0#0000000&| @36|i+0#00e0e07&|s@1|p|a|c|e| +0#0000000&@19 +@2|i+0#00e0e07&|s| |u|p@1|e|r|;+0#0000000&| @36|i+0#00e0e07&|s|u|p@1|e|r| +0#0000000&@19 +@2|i+0#00e0e07&|s| |x|d|i|g|i|t|;+0#0000000&| @35|i+0#00e0e07&|s|x|d|i|g|i|t| +0#0000000&@18 +@2|t+0#00e0e07&|o| |l|o|w|e|r|;+0#0000000&| @36|t+0#00e0e07&|o|l|o|w|e|r| +0#0000000&@19 +@2|t+0#00e0e07&|o| |u|p@1|e|r|;+0#0000000&| @36|t+0#00e0e07&|o|u|p@1|e|r| +0#0000000&@19 +|#+0#0000e05&| |B|I|T|S| |o|p|s|.| |#| +0#0000000&@61 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|B+0#af5f00255&|I|N| +0#0000000&@42|B+0#af5f00255&|I|N| +0#0000000&@23 +@2|N+0#af5f00255&|O|T| +0#0000000&@42|N+0#af5f00255&|O|T| +0#0000000&@23 +@2|~+0#af5f00255&| +0#0000000&@44|~+0#af5f00255&| +0#0000000&@25 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@57|4|1|5|,|3| @8|1|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_24.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_24.dump new file mode 100644 index 0000000000..c0185292f8 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_24.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2>L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|A+0#af5f00255&|N|D| +0#0000000&@42|A+0#af5f00255&|N|D| +0#0000000&@23 +@2|&+0#af5f00255&| +0#0000000&@44|&+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|R| +0#0000000&@43|O+0#af5f00255&|R| +0#0000000&@24 +@2|X+0#af5f00255&|O|R| +0#0000000&@42|X+0#af5f00255&|O|R| +0#0000000&@23 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|V|E|R| +0#0000000&@41|O+0#af5f00255&|V|E|R| +0#0000000&@22 +@57|4|3@1|,|3| @8|2|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_25.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_25.dump new file mode 100644 index 0000000000..0e8d04760d --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_25.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|O+0#af5f00255&|V|E|R| +0#0000000&@41|O+0#af5f00255&|V|E|R| +0#0000000&@22 +@2|M+0#af5f00255&|O|D| +0#0000000&@42|M+0#af5f00255&|O|D| +0#0000000&@23 +@2|S+0#af5f00255&|H|L| +0#0000000&@42|S+0#af5f00255&|H|L| +0#0000000&@23 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|S+0#af5f00255&|H|R| +0#0000000&@42|S+0#af5f00255&|H|R| +0#0000000&@23 +@2>D+0#af5f00255&|O|W|N| +0#0000000&@41|D+0#af5f00255&|O|W|N| +0#0000000&@22 +@2|R+0#af5f00255&|O|L| +0#0000000&@42|R+0#af5f00255&|O|L| +0#0000000&@23 +@2|R+0#af5f00255&|O|R| +0#0000000&@42|R+0#af5f00255&|O|R| +0#0000000&@23 +@2|E+0#af5f00255&|L|E|M| +0#0000000&@41|E+0#af5f00255&|L|E|M| +0#0000000&@22 +@2|S+0#af5f00255&|E|T| +0#0000000&@42|S+0#af5f00255&|E|T| +0#0000000&@23 +@2|C+0#af5f00255&|L|E|A|R| +0#0000000&@40|C+0#af5f00255&|L|E|A|R| +0#0000000&@21 +|#+0#0000e05&| |L|O|N|G| |L|O|N|G| |I|N|T| |i|n| |s|o|f|t|w|a|r|e| |#| +0#0000000&@45 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|S+0#af5f00255&|I|G|N| +0#0000000&@41|S+0#af5f00255&|I|G|N| +0#0000000&@22 +@2|O+0#af5f00255&|D@1| +0#0000000&@42|O+0#af5f00255&|D@1| +0#0000000&@23 +@2|E+0#af5f00255&|N|T|I|E|R| +0#0000000&@39|E+0#af5f00255&|N|T|I|E|R| +0#0000000&@20 +@2|R+0#af5f00255&|O|U|N|D| +0#0000000&@40|R+0#af5f00255&|O|U|N|D| +0#0000000&@21 +@57|4|5|1|,|3| @8|2|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_26.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_26.dump new file mode 100644 index 0000000000..bb71bb913a --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_26.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|R+0#af5f00255&|O|U|N|D| +0#0000000&@40|R+0#af5f00255&|O|U|N|D| +0#0000000&@21 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|V|E|R| +0#0000000&@41|O+0#af5f00255&|V|E|R| +0#0000000&@22 +@2>%+0#af5f00255&| +0#0000000&@44|%+0#af5f00255&| +0#0000000&@25 +@2|M+0#af5f00255&|O|D| +0#0000000&@42|M+0#af5f00255&|O|D| +0#0000000&@23 +@2|%+0#af5f00255&|*| +0#0000000&@43|%+0#af5f00255&|*| +0#0000000&@24 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|%+0#af5f00255&|:|=| +0#0000000&@42|%+0#af5f00255&|:|=| +0#0000000&@23 +@2|%+0#af5f00255&|*|:|=| +0#0000000&@41|%+0#af5f00255&|*|:|=| +0#0000000&@22 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|O+0#af5f00255&|V|E|R|A|B| +0#0000000&@39|O+0#af5f00255&|V|E|R|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|O|D|A|B| +0#0000000&@40|M+0#af5f00255&|O|D|A|B| +0#0000000&@21 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@57|4|6|9|,|3| @8|2@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_27.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_27.dump new file mode 100644 index 0000000000..c71921f202 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_27.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2>L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|I+0#af5f00255&| +0#0000000&@44|I+0#af5f00255&| +0#0000000&@25 +@57|4|8|7|,|3| @8|2@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_28.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_28.dump new file mode 100644 index 0000000000..ad55f9ac9c --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_28.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|I+0#af5f00255&| +0#0000000&@44|I+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|*| +0#0000000&@43|++0#af5f00255&|*| +0#0000000&@24 +|#+0#0000e05&| |L|O|N|G| |L|O|N|G| |R|E|A|L| |i|n| |s|o|f|t|w|a|r|e| |#| +0#0000000&@44 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2>-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|S+0#af5f00255&|I|G|N| +0#0000000&@41|S+0#af5f00255&|I|G|N| +0#0000000&@22 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c@1|o|s|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|s| |d|g|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c@1|o|s|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|s|d|g| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c@1|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|s|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c@1|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|s|h| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c@1|o|t|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|t| |d|g|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c@1|o|t|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|t|d|g| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c@1|o|t|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|t|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c@1|o|t|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|t| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c@1|s|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|c|s|c| |d|g|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c@1|s|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|c|s|c|d|g| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c@1|s|c|;+0#0000000&| @1|q+0#00e0e07&|a|c|s|c|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c@1|s|c|;+0#0000000&| @1|q+0#00e0e07&|a|c|s|c| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|s|e|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|s|e|c| |d|g|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|s|e|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|s|e|c|d|g| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|s|e|c|;+0#0000000&| @1|q+0#00e0e07&|a|s|e|c|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|s|e|c|;+0#0000000&| @1|q+0#00e0e07&|a|s|e|c| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|s|i|n|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|s|i|n| |d|g|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|s|i|n|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|s|i|n|d|g| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|s|i|n|h|;+0#0000000&| @1|q+0#00e0e07&|a|s|i|n|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|s|i|n|h|;+0#0000000&| @1|q+0#00e0e07&|a|s|i|n|h| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|t|a|n|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n| |d|g|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|t|a|n|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|d|g| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|h| +0#0000000&@2 +@57|5|0|5|,|3| @8|2|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_29.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_29.dump new file mode 100644 index 0000000000..6baa1feeee --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_29.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|h| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|a|s|;+0#0000000&| @1|q+0#00e0e07&|c|a|s|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|c|a|s|;+0#0000000&| @1|q+0#00e0e07&|c|a|s| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|b|r|t|;+0#0000000&| @1|q+0#00e0e07&|c|b|r|t|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|o|n|g|c|b|r|t|;+0#0000000&| @1|q+0#00e0e07&|c|b|r|t| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|s|d|g|;+0#0000000&| @1|q+0#00e0e07&|c|o|s| |d|g|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|s|d|g|;+0#0000000&| @1|q+0#00e0e07&|c|o|s|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|c|o|s|h|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|c|o|s|h| +0#0000000&@6 +@2>l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|s|p|i|;+0#0000000&| @1|q+0#00e0e07&|c|o|s| |p|i|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|s|p|i|;+0#0000000&| @1|q+0#00e0e07&|c|o|s|p|i| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|t|d|g|;+0#0000000&| @1|q+0#00e0e07&|c|o|t| |d|g|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|t|d|g|;+0#0000000&| @1|q+0#00e0e07&|c|o|t|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|t|p|i|;+0#0000000&| @1|q+0#00e0e07&|c|o|t| |p|i|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|t|p|i|;+0#0000000&| @1|q+0#00e0e07&|c|o|t|p|i| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|t|;+0#0000000&| @1|q+0#00e0e07&|c|o|t|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|t|;+0#0000000&| @1|q+0#00e0e07&|c|o|t| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|s|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|c|s|c| |d|g|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|c|s|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|c|s|c|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|s|c|;+0#0000000&| @1|q+0#00e0e07&|c|s|c|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|c|s|c|;+0#0000000&| @1|q+0#00e0e07&|c|s|c| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|u|r|t|;+0#0000000&| @1|q+0#00e0e07&|c|u|r|t|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|o|n|g|c|u|r|t|;+0#0000000&| @1|q+0#00e0e07&|c|u|r|t| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |e|r|f|c|;+0#0000000&| @1|q+0#00e0e07&|e|r|f|c|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|o|n|g|e|r|f|c|;+0#0000000&| @1|q+0#00e0e07&|e|r|f|c| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |e|r|f|;+0#0000000&| @1|q+0#00e0e07&|e|r|f|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|e|r|f|;+0#0000000&| @1|q+0#00e0e07&|e|r|f| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |g|a|m@1|a|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|o|n|g@1|a|m@1|a|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |i|n|v|e|r|f|c|;+0#0000000&| @1|q+0#00e0e07&|i|n|v|e|r|f|c|;+0#0000000&| @16|l+0#00e0e07&|o|n|g|l|o|n|g|i|n|v|e|r|f|c|;+0#0000000&| @1|q+0#00e0e07&|i|n|v|e|r|f|c| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |i|n|v|e|r|f|;+0#0000000&| @1|q+0#00e0e07&|i|n|v|e|r|f|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|l|o|n|g|i|n|v|e|r|f|;+0#0000000&| @1|q+0#00e0e07&|i|n|v|e|r|f| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |l|n|g|a|m@1|a|;+0#0000000&| @1|q+0#00e0e07&|l|n|g|a|m@1|a|;+0#0000000&| @16|l+0#00e0e07&|o|n|g|l|o|n|g|l|n|g|a|m@1|a|;+0#0000000&| @1|q+0#00e0e07&|l|n|g|a|m@1|a| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |s|e|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|s|e|c| |d|g|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|s|e|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|s|e|c|d|g| +0#0000000&@4 +@57|5|2|3|,|3| @8|2|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_30.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_30.dump new file mode 100644 index 0000000000..7ac016fcc9 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_30.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |l|o|n|g| |s|e|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|s|e|c| |d|g|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|s|e|c|d|g|;+0#0000000&| @1|q+0#00e0e07&|s|e|c|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |s|e|c|;+0#0000000&| @1|q+0#00e0e07&|s|e|c|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|s|e|c|;+0#0000000&| @1|q+0#00e0e07&|s|e|c| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |s|i|n|d|g|;+0#0000000&| @1|q+0#00e0e07&|s|i|n| |d|g|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|s|i|n|d|g|;+0#0000000&| @1|q+0#00e0e07&|s|i|n|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |s|i|n|h|;+0#0000000&| @1|q+0#00e0e07&|s|i|n|h|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|o|n|g|s|i|n|h|;+0#0000000&| @1|q+0#00e0e07&|s|i|n|h| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |s|i|n|p|i|;+0#0000000&| @1|q+0#00e0e07&|s|i|n| |p|i|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|s|i|n|p|i|;+0#0000000&| @1|q+0#00e0e07&|s|i|n|p|i| +0#0000000&@4 +@2>l+0#00e0e07&|o|n|g| |l|o|n|g| |t|a|n|d|g|;+0#0000000&| @1|q+0#00e0e07&|t|a|n| |d|g|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|t|a|n|d|g|;+0#0000000&| @1|q+0#00e0e07&|t|a|n|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|t|a|n|h|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|o|n|g|t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|t|a|n|h| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |t|a|n|p|i|;+0#0000000&| @1|q+0#00e0e07&|t|a|n| |p|i|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|l|o|n|g|t|a|n|p|i|;+0#0000000&| @1|q+0#00e0e07&|t|a|n|p|i| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c@1|o|s|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|s|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c@1|o|s|;+0#0000000&| @1|q+0#00e0e07&|a|c|o|s| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|s|i|n|;+0#0000000&| @1|q+0#00e0e07&|a|s|i|n|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|s|i|n|;+0#0000000&| @1|q+0#00e0e07&|a|s|i|n| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|t|a|n|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|t|a|n|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|s|;+0#0000000&| @1|q+0#00e0e07&|c|o|s|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|s|;+0#0000000&| @1|q+0#00e0e07&|c|o|s| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |e|x|p|;+0#0000000&| @1|q+0#00e0e07&|e|x|p|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|e|x|p|;+0#0000000&| @1|q+0#00e0e07&|e|x|p| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |l|n|;+0#0000000&| @1|q+0#00e0e07&|l|n|;+0#0000000&| @26|l+0#00e0e07&|o|n|g|l|o|n|g|l|n|;+0#0000000&| @1|q+0#00e0e07&|l|n| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |l|o|g|;+0#0000000&| @1|q+0#00e0e07&|l|o|g|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|l|o|g|;+0#0000000&| @1|q+0#00e0e07&|l|o|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |s|i|n|;+0#0000000&| @1|q+0#00e0e07&|s|i|n|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|s|i|n|;+0#0000000&| @1|q+0#00e0e07&|s|i|n| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |s|q|r|t|;+0#0000000&| @1|q+0#00e0e07&|s|q|r|t|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|o|n|g|s|q|r|t|;+0#0000000&| @1|q+0#00e0e07&|s|q|r|t| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |t|a|n|;+0#0000000&| @1|q+0#00e0e07&|t|a|n|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|t|a|n|;+0#0000000&| @1|q+0#00e0e07&|t|a|n| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|t|a|n|2|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|2|d|g|;+0#0000000&| @14|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|t|a|n|2|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|@+0#4040ff13&@2 +| +0#0000000&@56|5|4|1|,|3| @8|2|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_31.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_31.dump new file mode 100644 index 0000000000..ff8ad4d618 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_31.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|t|a|n|2|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|2|d|g|;+0#0000000&| @14|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|t|a|n|2|d|g|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|2|d +|g| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |a|r|c|t|a|n|2|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|2|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|l|o|n|g|a|r|c|t|a|n|2|;+0#0000000&| @1|q+0#00e0e07&|a|t|a|n|2| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |b|e|t|a|;+0#0000000&| @1|q+0#00e0e07&|b|e|t|a|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|o|n|g|b|e|t|a|;+0#0000000&| @1|q+0#00e0e07&|b|e|t|a| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |g|a|m@1|a| |i|n|c|f|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a|i|n|c|f|;+0#0000000&| @11|l+0#00e0e07&|o|n|g|l|o|n|g@1|a|m@1|a|i|n|c|f|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a|i +|n|c|f| +0#0000000&@71 +@2>l+0#00e0e07&|o|n|g| |l|o|n|g| |g|a|m@1|a| |i|n|c|g|f|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a|i|n|c|g|f|;+0#0000000&| @9|l+0#00e0e07&|o|n|g|l|o|n|g@1|a|m@1|a|i|n|c|g|f|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a +|i|n|c|g|f| +0#0000000&@69 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |g|a|m@1|a| |i|n|c|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a|i|n|c|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|l|o|n|g@1|a|m@1|a|i|n|c|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a|i|n +|c| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |l|n| |b|e|t|a|;+0#0000000&| @1|q+0#00e0e07&|l|n|b|e|t|a|;+0#0000000&| @17|l+0#00e0e07&|o|n|g|l|o|n|g|l|n|b|e|t|a|;+0#0000000&| @1|q+0#00e0e07&|l|n|b|e|t|a| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |b|e|t|a| |i|n|c|;+0#0000000&| @1|q+0#00e0e07&|b|e|t|a|i|n|c|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|b|e|t|a|i|n|c|;+0#0000000&| @1|q+0#00e0e07&|b|e|t|a|i|n|c| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |g|a|m@1|a| |i|n|c|g|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a|i|n|c|g|;+0#0000000&| @11|l+0#00e0e07&|o|n|g|l|o|n|g@1|a|m@1|a|i|n|c|g|;+0#0000000&| @1|q+0#00e0e07&|g|a|m@1|a|i +|n|c|g| +0#0000000&@71 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@57|5@1|8|,|3| @8|2|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_32.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_32.dump new file mode 100644 index 0000000000..562b64dd18 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_32.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2>*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|D+0#af5f00255&|I|V|A|B| +0#0000000&@40|D+0#af5f00255&|I|V|A|B| +0#0000000&@21 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@57|5|7|2|,|3| @8|2|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_33.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_33.dump new file mode 100644 index 0000000000..82b9fd2321 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_33.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2>>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|I+0#af5f00255&| +0#0000000&@44|I+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|*| +0#0000000&@43|++0#af5f00255&|*| +0#0000000&@24 +|#+0#0000e05&| |L|O|N|G| |L|O|N|G| |C|O|M|P|L|E|X| |i|n| |s|o|f|t|w|a|r|e| |#| +0#0000000&@41 +@2|R+0#af5f00255&|E| +0#0000000&@43|R+0#af5f00255&|E| +0#0000000&@24 +@2|I+0#af5f00255&|M| +0#0000000&@43|I+0#af5f00255&|M| +0#0000000&@24 +@2|A+0#af5f00255&|R|G| +0#0000000&@42|A+0#af5f00255&|R|G| +0#0000000&@23 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@57|5|9|0|,|3| @8|2|7|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_34.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_34.dump new file mode 100644 index 0000000000..345dd915ee --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_34.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|C+0#af5f00255&|O|N|J| +0#0000000&@41|C+0#af5f00255&|O|N|J| +0#0000000&@22 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2>/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@57|6|0|8|,|3| @8|2|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_35.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_35.dump new file mode 100644 index 0000000000..a1d2333f79 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_35.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|D+0#af5f00255&|I|V|A|B| +0#0000000&@40|D+0#af5f00255&|I|V|A|B| +0#0000000&@21 +@2>l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |c|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|c|a|c|o|s|h|;+0#0000000&| @8|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|a|r|c@1|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|c +|a|c|o|s|h| +0#0000000&@69 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |c|o|s|;+0#0000000&| @1|q+0#00e0e07&|c|a|c|o|s|;+0#0000000&| @10|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|a|r|c@1|o|s|;+0#0000000&| @1|q+0#00e0e07&|c|a +|c|o|s| +0#0000000&@71 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |s|i|n|h|;+0#0000000&| @1|q+0#00e0e07&|c|a|s|i|n|h|;+0#0000000&| @8|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|a|r|c|s|i|n|h|;+0#0000000&| @1|q+0#00e0e07&|c +|a|s|i|n|h| +0#0000000&@69 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |s|i|n|;+0#0000000&| @1|q+0#00e0e07&|c|a|s|i|n|;+0#0000000&| @10|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|a|r|c|s|i|n|;+0#0000000&| @1|q+0#00e0e07&|c|a +|s|i|n| +0#0000000&@71 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|c|a|t|a|n|h|;+0#0000000&| @8|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|a|r|c|t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|c +|a|t|a|n|h| +0#0000000&@69 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |t|a|n|;+0#0000000&| @1|q+0#00e0e07&|c|a|t|a|n|;+0#0000000&| @10|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|a|r|c|t|a|n|;+0#0000000&| @1|q+0#00e0e07&|c|a +|t|a|n| +0#0000000&@71 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |c|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|c@1|o|s|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|c|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|c@1|o|s +|h| +0#0000000&@73 +@57|6|2|6|,|3| @8|2|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_36.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_36.dump new file mode 100644 index 0000000000..4feb62a709 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_36.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |c|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|c@1|o|s|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|c|o|s|h|;+0#0000000&| @1|q+0#00e0e07&|c@1|o|s +|h| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |c|o|s|;+0#0000000&| @1|q+0#00e0e07&|c@1|o|s|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|c|o|s|;+0#0000000&| @1|q+0#00e0e07&|c@1|o|s| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |e|x|p|;+0#0000000&| @1|q+0#00e0e07&|c|e|x|p|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|e|x|p|;+0#0000000&| @1|q+0#00e0e07&|c|e|x|p| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |l|n|;+0#0000000&| @1|q+0#00e0e07&|c|l|n|;+0#0000000&| @17|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|l|n|;+0#0000000&| @1|q+0#00e0e07&|c|l|n| +0#0000000&@2 +@2>l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |s|i|n|h|;+0#0000000&| @1|q+0#00e0e07&|c|s|i|n|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|s|i|n|h|;+0#0000000&| @1|q+0#00e0e07&|c|s|i|n +|h| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |s|i|n|;+0#0000000&| @1|q+0#00e0e07&|c|s|i|n|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|s|i|n|;+0#0000000&| @1|q+0#00e0e07&|c|s|i|n| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |s|q|r|t|;+0#0000000&| @1|q+0#00e0e07&|c|s|q|r|t|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|s|q|r|t|;+0#0000000&| @1|q+0#00e0e07&|c|s|q|r +|t| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|c|t|a|n|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|t|a|n|h|;+0#0000000&| @1|q+0#00e0e07&|c|t|a|n +|h| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |c|o|m|p|l|e|x| |t|a|n|;+0#0000000&| @1|q+0#00e0e07&|c|t|a|n|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|l|o|n|g|c|o|m|p|l|e|x|t|a|n|;+0#0000000&| @1|q+0#00e0e07&|c|t|a|n| +0#0000000& +|#+0#0000e05&| |B|Y|T|E|S| |o|p|s|.| |#| +0#0000000&@60 +@2|b+0#00e0e07&|y|t|e|s|p|a|c|k|;+0#0000000&| @35|b+0#00e0e07&|y|t|e|s|p|a|c|k| +0#0000000&@17 +@2|E+0#af5f00255&|L|E|M| +0#0000000&@41|E+0#af5f00255&|L|E|M| +0#0000000&@22 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@57|6|3|6|,|3| @8|3|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_37.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_37.dump new file mode 100644 index 0000000000..c2fda26665 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_37.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|++0#af5f00255&|=|:| +0#0000000&@42|++0#af5f00255&|=|:| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|T|O| +0#0000000&@39|P+0#af5f00255&|L|U|S|T|O| +0#0000000&@20 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2>~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +|#+0#0000e05&| |L|O|N|G| |B|Y|T|E|S| |o|p|s|.| |#| +0#0000000&@55 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@57|6|5|1|,|3| @8|3|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_38.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_38.dump new file mode 100644 index 0000000000..55f691cd19 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_38.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|l+0#00e0e07&|o|n|g| |b|y|t|e|s| |p|a|c|k|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|b|y|t|e|s|p|a|c|k| +0#0000000&@13 +@2|E+0#af5f00255&|L|E|M| +0#0000000&@41|E+0#af5f00255&|L|E|M| +0#0000000&@22 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2>++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|++0#af5f00255&|=|:| +0#0000000&@42|++0#af5f00255&|=|:| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|T|O| +0#0000000&@39|P+0#af5f00255&|L|U|S|T|O| +0#0000000&@20 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@57|6@1|9|,|3| @8|3|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_39.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_39.dump new file mode 100644 index 0000000000..9ea37111d0 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_39.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +>#+0#0000e05&| |S|T|R|I|N|G| |o|p|s|.| |#| +0#0000000&@59 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@57|6|8|7|,|1| @8|3|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_40.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_40.dump new file mode 100644 index 0000000000..46655a2690 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_40.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2>*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|++0#af5f00255&|=|:| +0#0000000&@42|++0#af5f00255&|=|:| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|T|O| +0#0000000&@39|P+0#af5f00255&|L|U|S|T|O| +0#0000000&@20 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|L|E|M| +0#0000000&@41|E+0#af5f00255&|L|E|M| +0#0000000&@22 +@2|r+0#00e0e07&|e|a|l| |p|a|t|h|;+0#0000000&| @35|r+0#00e0e07&|e|a|l|p|a|t|h| +0#0000000&@18 +|#+0#0000e05&| |S|E|M|A| |o|p|s|.| |#| +0#0000000&@61 +@2|L+0#af5f00255&|E|V|E|L| +0#0000000&@40|L+0#af5f00255&|E|V|E|L| +0#0000000&@21 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|D+0#af5f00255&|O|W|N| +0#0000000&@41|D+0#af5f00255&|O|W|N| +0#0000000&@22 +@2|L+0#af5f00255&|E|V|E|L| +0#0000000&@40|L+0#af5f00255&|E|V|E|L| +0#0000000&@21 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|D+0#af5f00255&|O|W|N| +0#0000000&@41|D+0#af5f00255&|O|W|N| +0#0000000&@22 +@57|7|0|5|,|3| @8|3@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_41.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_41.dump new file mode 100644 index 0000000000..f90554b604 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_41.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|D+0#af5f00255&|O|W|N| +0#0000000&@41|D+0#af5f00255&|O|W|N| +0#0000000&@22 +|#+0#0000e05&| |R|O|W|S| |o|p|s|.| |#| +0#0000000&@61 +@2|E+0#af5f00255&|L|E|M|S| +0#0000000&@40|E+0#af5f00255&|L|E|M|S| +0#0000000&@21 +@2|L+0#af5f00255&|W|B| +0#0000000&@42|L+0#af5f00255&|W|B| +0#0000000&@23 +@2|U+0#af5f00255&|P|B| +0#0000000&@42|U+0#af5f00255&|P|B| +0#0000000&@23 +@2>E+0#af5f00255&|L|E|M|S| +0#0000000&@40|E+0#af5f00255&|L|E|M|S| +0#0000000&@21 +@2|L+0#af5f00255&|W|B| +0#0000000&@42|L+0#af5f00255&|W|B| +0#0000000&@23 +@2|U+0#af5f00255&|P|B| +0#0000000&@42|U+0#af5f00255&|P|B| +0#0000000&@23 +@2|S+0#af5f00255&|O|R|T| +0#0000000&@41|S+0#af5f00255&|O|R|T| +0#0000000&@22 +|#+0#0000e05&| |S|o|m|e| |"|t|e|r|m|i|n|a|t|o|r|s| |#| +0#0000000&@53 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +|#+0#0000e05&| |S|O|U|N|D|/|R|I|F@1| |p|r|o|c|s|.| |#| +0#0000000&@53 +@2|n+0#00e0e07&|e|w|s|o|u|n|d|;+0#0000000&| @36|n+0#00e0e07&|e|w|s|o|u|n|d| +0#0000000&@18 +@2|g+0#00e0e07&|e|t|s|o|u|n|d|;+0#0000000&| @36|g+0#00e0e07&|e|t|s|o|u|n|d| +0#0000000&@18 +@2|s+0#00e0e07&|e|t|s|o|u|n|d|;+0#0000000&| @36|s+0#00e0e07&|e|t|s|o|u|n|d| +0#0000000&@18 +@2|R+0#af5f00255&|E|S|O|L|U|T|I|O|N| +0#0000000&@35|R+0#af5f00255&|E|S|O|L|U|T|I|O|N| +0#0000000&@16 +@2|C+0#af5f00255&|H|A|N@1|E|L|S| +0#0000000&@37|C+0#af5f00255&|H|A|N@1|E|L|S| +0#0000000&@18 +@2|R+0#af5f00255&|A|T|E| +0#0000000&@41|R+0#af5f00255&|A|T|E| +0#0000000&@22 +@57|7|2|3|,|3| @8|3|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_42.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_42.dump new file mode 100644 index 0000000000..1fa24a66a8 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_42.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|R+0#af5f00255&|A|T|E| +0#0000000&@41|R+0#af5f00255&|A|T|E| +0#0000000&@22 +@2|S+0#af5f00255&|A|M|P|L|E|S| +0#0000000&@38|S+0#af5f00255&|A|M|P|L|E|S| +0#0000000&@19 +|#+0#0000e05&| |S|e|t| |u|p| |s|t|a|n|d|e|n|v| |-| |t|r|a|n|s|p|u|t|.| |#| +0#0000000&@43 +@2|l+0#00e0e07&|o|n|g| |p|i|;+0#0000000&| @1|d+0#00e0e07&|p|i|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|p|i|;+0#0000000&| @1|d+0#00e0e07&|p|i| +0#0000000&@14 +@2|l+0#00e0e07&|o|n|g| |m|a|x| |b|i|t|s|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|m|a|x|b|i|t|s| +0#0000000&@15 +@2>l+0#00e0e07&|o|n|g| |m|a|x| |i|n|t|;+0#0000000&| @32|l+0#00e0e07&|o|n|g|m|a|x|i|n|t| +0#0000000&@16 +@2|l+0#00e0e07&|o|n|g| |s|m|a|l@1| |r|e|a|l|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|s|m|a|l@1|r|e|a|l| +0#0000000&@13 +@2|l+0#00e0e07&|o|n|g| |m|a|x| |r|e|a|l|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|m|a|x|r|e|a|l| +0#0000000&@15 +@2|l+0#00e0e07&|o|n|g| |m|i|n| |r|e|a|l|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|m|i|n|r|e|a|l| +0#0000000&@15 +@2|l+0#00e0e07&|o|n|g| |i|n|f|i|n|i|t|y|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|i|n|f|i|n|i|t|y| +0#0000000&@14 +@2|l+0#00e0e07&|o|n|g| |m|i|n|u|s| |i|n|f|i|n|i|t|y|;+0#0000000&| @25|l+0#00e0e07&|o|n|g|m|i|n|u|s|i|n|f|i|n|i|t|y| +0#0000000&@9 +@2|l+0#00e0e07&|o|n|g| |i|n|f|;+0#0000000&| @36|l+0#00e0e07&|o|n|g|i|n|f| +0#0000000&@19 +@2|l+0#00e0e07&|o|n|g| |m|i|n| |i|n|f|;+0#0000000&| @32|l+0#00e0e07&|o|n|g|m|i|n|i|n|f| +0#0000000&@16 +|#+0#0000e05&| |L|O|N|G| |I|N|T| |i|n| |s|o|f|t|w|a|r|e| |#| +0#0000000&@50 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@57|7|4|1|,|3| @8|3|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_43.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_43.dump new file mode 100644 index 0000000000..7c1ec22f6d --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_43.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|S+0#af5f00255&|I|G|N| +0#0000000&@41|S+0#af5f00255&|I|G|N| +0#0000000&@22 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2>S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|O+0#af5f00255&|D@1| +0#0000000&@42|O+0#af5f00255&|D@1| +0#0000000&@23 +@2|E+0#af5f00255&|N|T|I|E|R| +0#0000000&@39|E+0#af5f00255&|N|T|I|E|R| +0#0000000&@20 +@2|R+0#af5f00255&|O|U|N|D| +0#0000000&@40|R+0#af5f00255&|O|U|N|D| +0#0000000&@21 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|V|E|R| +0#0000000&@41|O+0#af5f00255&|V|E|R| +0#0000000&@22 +@2|%+0#af5f00255&| +0#0000000&@44|%+0#af5f00255&| +0#0000000&@25 +@2|M+0#af5f00255&|O|D| +0#0000000&@42|M+0#af5f00255&|O|D| +0#0000000&@23 +@2|%+0#af5f00255&|*| +0#0000000&@43|%+0#af5f00255&|*| +0#0000000&@24 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@57|7|5|9|,|3| @8|3|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_44.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_44.dump new file mode 100644 index 0000000000..b661173ada --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_44.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|%+0#af5f00255&|:|=| +0#0000000&@42|%+0#af5f00255&|:|=| +0#0000000&@23 +@2|%+0#af5f00255&|*|:|=| +0#0000000&@41|%+0#af5f00255&|*|:|=| +0#0000000&@22 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2>T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|O+0#af5f00255&|V|E|R|A|B| +0#0000000&@39|O+0#af5f00255&|V|E|R|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|O|D|A|B| +0#0000000&@40|M+0#af5f00255&|O|D|A|B| +0#0000000&@21 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@57|7@2|,|3| @8|3|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_45.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_45.dump new file mode 100644 index 0000000000..a98c063d32 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_45.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2>*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|I+0#af5f00255&| +0#0000000&@44|I+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|*| +0#0000000&@43|++0#af5f00255&|*| +0#0000000&@24 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +|#+0#0000e05&| |L|O|N|G| |R|E|A|L| |i|n| |s|o|f|t|w|a|r|e| |#| +0#0000000&@49 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|s| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c@1|o|s|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|h|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|a|r|c@1|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|h| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|t|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c@1|o|t|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t| +0#0000000&@8 +@57|7|9|5|,|3| @8|3|7|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_46.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_46.dump new file mode 100644 index 0000000000..295222c8e0 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_46.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|t|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c@1|o|t|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|t| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c@1|o|t|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|s|c|;+0#0000000&| @1|d+0#00e0e07&|a|c|s|c|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c@1|s|c|;+0#0000000&| @1|d+0#00e0e07&|a|c|s|c| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|s|c| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|s|c| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c@1|s|c|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|s|c|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |s|e|c|;+0#0000000&| @1|d+0#00e0e07&|a|s|e|c|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c|s|e|c|;+0#0000000&| @1|d+0#00e0e07&|a|s|e|c| +0#0000000&@8 +@2>l+0#00e0e07&|o|n|g| |a|r|c| |s|e|c| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|s|e|c| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c|s|e|c|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|s|e|c|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |s|i|n| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c|s|i|n|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n|h|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|a|r|c|s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n|h| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|h|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|h| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |c|a|s|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|a|s|;+0#0000000&| @1|d+0#00e0e07&|c|a|s| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |c|b|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|b|r|t|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|c|b|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|b|r|t| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |c|o|s| |d|g|;+0#0000000&| @1|d+0#00e0e07&|c|o|s| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|o|s|d|g|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|h|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|h| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |c|o|s| |p|i|;+0#0000000&| @1|d+0#00e0e07&|c|o|s| |p|i|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|o|s|p|i|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|p|i| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|o|t|;+0#0000000&| @1|d+0#00e0e07&|c|o|t|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|o|t|;+0#0000000&| @1|d+0#00e0e07&|c|o|t| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |c|o|t| |d|g|;+0#0000000&| @1|d+0#00e0e07&|c|o|t| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|o|t|d|g|;+0#0000000&| @1|d+0#00e0e07&|c|o|t|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|o|t| |p|i|;+0#0000000&| @1|d+0#00e0e07&|c|o|t| |p|i|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|o|t|p|i|;+0#0000000&| @1|d+0#00e0e07&|c|o|t|p|i| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|s|c|;+0#0000000&| @1|d+0#00e0e07&|c|s|c|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|s|c|;+0#0000000&| @1|d+0#00e0e07&|c|s|c| +0#0000000&@12 +@57|8|1|3|,|3| @8|3|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_47.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_47.dump new file mode 100644 index 0000000000..13c89cbc23 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_47.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |c|s|c|;+0#0000000&| @1|d+0#00e0e07&|c|s|c|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|s|c|;+0#0000000&| @1|d+0#00e0e07&|c|s|c| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |c|s|c| |d|g|;+0#0000000&| @1|d+0#00e0e07&|c|s|c| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|s|c|d|g|;+0#0000000&| @1|d+0#00e0e07&|c|s|c|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|u|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|u|r|t|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|c|u|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|u|r|t| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |e|r|f|c|;+0#0000000&| @1|d+0#00e0e07&|e|r|f|c|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|e|r|f|c|;+0#0000000&| @1|d+0#00e0e07&|e|r|f|c| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |e|r|f|;+0#0000000&| @1|d+0#00e0e07&|e|r|f|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|e|r|f|;+0#0000000&| @1|d+0#00e0e07&|e|r|f| +0#0000000&@12 +@2>l+0#00e0e07&|o|n|g| |g|a|m@1|a|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|;+0#0000000&| @25|l+0#00e0e07&|o|n|g@1|a|m@1|a|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |i|n|v| |e|r|f|c|;+0#0000000&| @1|d+0#00e0e07&|i|n|v|e|r|f|c|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|i|n|v|e|r|f|c|;+0#0000000&| @1|d+0#00e0e07&|i|n|v|e|r|f|c| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |i|n|v| |e|r|f|;+0#0000000&| @1|d+0#00e0e07&|i|n|v|e|r|f|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|i|n|v|e|r|f|;+0#0000000&| @1|d+0#00e0e07&|i|n|v|e|r|f| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |l|n| |g|a|m@1|a|;+0#0000000&| @1|d+0#00e0e07&|l|n|g|a|m@1|a|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|n|g|a|m@1|a|;+0#0000000&| @1|d+0#00e0e07&|l|n|g|a|m@1|a| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |s|e|c| |d|g|;+0#0000000&| @1|d+0#00e0e07&|s|e|c| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|s|e|c|d|g|;+0#0000000&| @1|d+0#00e0e07&|s|e|c|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |s|e|c|;+0#0000000&| @1|d+0#00e0e07&|s|e|c|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|s|e|c|;+0#0000000&| @1|d+0#00e0e07&|s|e|c| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |s|i|n| |d|g|;+0#0000000&| @1|d+0#00e0e07&|s|i|n| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|s|i|n|d|g|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|h|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|h| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |s|i|n| |p|i|;+0#0000000&| @1|d+0#00e0e07&|s|i|n| |p|i|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|s|i|n|p|i|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|p|i| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |t|a|n| |d|g|;+0#0000000&| @1|d+0#00e0e07&|t|a|n| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|t|a|n|d|g|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|h|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|h| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |t|a|n| |p|i|;+0#0000000&| @1|d+0#00e0e07&|t|a|n| |p|i|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|t|a|n|p|i|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|p|i| +0#0000000&@8 +|#+0#0000e05&| |R@1|.| |#| +0#0000000&@67 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c@1|o|s|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s| +0#0000000&@8 +@57|8|3|1|,|3| @8|3|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_48.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_48.dump new file mode 100644 index 0000000000..15a95580a5 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_48.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c@1|o|s|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |s|i|n|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c|s|i|n|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c|o|s| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |e|x|p|;+0#0000000&| @1|d+0#00e0e07&|e|x|p|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|e|x|p|;+0#0000000&| @1|d+0#00e0e07&|e|x|p| +0#0000000&@12 +@2>l+0#00e0e07&|o|n|g| |l|n|;+0#0000000&| @1|d+0#00e0e07&|l|n|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|l|n|;+0#0000000&| @1|d+0#00e0e07&|l|n| +0#0000000&@14 +@2|l+0#00e0e07&|o|n|g| |l|o|g|;+0#0000000&| @1|d+0#00e0e07&|l|o|g|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|l|o|g|;+0#0000000&| @1|d+0#00e0e07&|l|o|g| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |s|i|n|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|s|i|n|;+0#0000000&| @1|d+0#00e0e07&|s|i|n| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |s|q|r|t|;+0#0000000&| @1|d+0#00e0e07&|s|q|r|t|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|s|q|r|t|;+0#0000000&| @1|d+0#00e0e07&|s|q|r|t| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |t|a|n|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|t|a|n|;+0#0000000&| @1|d+0#00e0e07&|t|a|n| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |n|e|x|t| |r|a|n|d|o|m|;+0#0000000&| @28|l+0#00e0e07&|o|n|g|n|e|x|t|r|a|n|d|o|m| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |r|a|n|d|o|m|;+0#0000000&| @33|l+0#00e0e07&|o|n|g|r|a|n|d|o|m| +0#0000000&@16 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n|2|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|2|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|2|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|2| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n|2| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|2|d|g|;+0#0000000&| @17|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|2|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|2|d|g| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |b|e|t|a|;+0#0000000&| @1|d+0#00e0e07&|b|e|t|a|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|b|e|t|a|;+0#0000000&| @1|d+0#00e0e07&|b|e|t|a| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|;+0#0000000&| @18|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|f|;+0#0000000&| @16|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|f| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|g|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g|f|;+0#0000000&| @14|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|g|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g +|f| +0#0000000&@73 +@57|8|4|9|,|3| @8|4|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_49.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_49.dump new file mode 100644 index 0000000000..bba3dc9d41 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_49.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|g|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g|f|;+0#0000000&| @14|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|g|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g +|f| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |l|n| |b|e|t|a|;+0#0000000&| @1|d+0#00e0e07&|l|n|b|e|t|a|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|n|b|e|t|a|;+0#0000000&| @1|d+0#00e0e07&|l|n|b|e|t|a| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |b|e|t|a| |i|n|c|;+0#0000000&| @1|d+0#00e0e07&|b|e|t|a|i|n|c|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|b|e|t|a|i|n|c|;+0#0000000&| @1|d+0#00e0e07&|b|e|t|a|i|n|c| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|g|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g|;+0#0000000&| @16|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|g|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g| +0#0000000& +@2>S+0#af5f00255&|I|G|N| +0#0000000&@41|S+0#af5f00255&|I|G|N| +0#0000000&@22 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@57|8|6|5|,|3| @8|4|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_50.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_50.dump new file mode 100644 index 0000000000..0a628081ee --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_50.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|D+0#af5f00255&|I|V|A|B| +0#0000000&@40|D+0#af5f00255&|I|V|A|B| +0#0000000&@21 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2>/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@57|8@1|3|,|3| @8|4|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_51.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_51.dump new file mode 100644 index 0000000000..f2a3c6ee9e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_51.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|I+0#af5f00255&| +0#0000000&@44|I+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|*| +0#0000000&@43|++0#af5f00255&|*| +0#0000000&@24 +|#+0#0000e05&| |L|O|N|G| |C|O|M|P|L|E|X| |i|n| |s|o|f|t|w|a|r|e| |#| +0#0000000&@46 +@2>L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|R+0#af5f00255&|E| +0#0000000&@43|R+0#af5f00255&|E| +0#0000000&@24 +@2|I+0#af5f00255&|M| +0#0000000&@43|I+0#af5f00255&|M| +0#0000000&@24 +@2|A+0#af5f00255&|R|G| +0#0000000&@42|A+0#af5f00255&|R|G| +0#0000000&@23 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|C+0#af5f00255&|O|N|J| +0#0000000&@41|C+0#af5f00255&|O|N|J| +0#0000000&@22 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@57|9|0|1|,|3| @8|4|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_52.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_52.dump new file mode 100644 index 0000000000..1b005204cb --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_52.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2>=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|D+0#af5f00255&|I|V|A|B| +0#0000000&@40|D+0#af5f00255&|I|V|A|B| +0#0000000&@21 +@57|9|1|9|,|3| @8|4|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_53.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_53.dump new file mode 100644 index 0000000000..e322006764 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_53.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|D+0#af5f00255&|I|V|A|B| +0#0000000&@40|D+0#af5f00255&|I|V|A|B| +0#0000000&@21 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c|a|c|o|s|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c@1|o|s|;+0#0000000&| @1|d+0#00e0e07&|c|a|c|o|s| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|c|o|s|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c@1|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|c|o|s +|h| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |s|i|n|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|i|n|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c|s|i|n|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|i|n| +0#0000000& +@2>l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|i|n|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c|s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|i|n +|h| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |t|a|n|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c|t|a|n|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n +|h| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s|h| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |e|x|p|;+0#0000000&| @1|d+0#00e0e07&|c|e|x|p|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|e|x|p|;+0#0000000&| @1|d+0#00e0e07&|c|e|x|p| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |l|n|;+0#0000000&| @1|d+0#00e0e07&|c|l|n|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|l|n|;+0#0000000&| @1|d+0#00e0e07&|c|l|n| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |s|i|n|;+0#0000000&| @1|d+0#00e0e07&|c|s|i|n|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|s|i|n|;+0#0000000&| @1|d+0#00e0e07&|c|s|i|n| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|s|i|n|;+0#0000000&| @19|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|s|i|n| +0#0000000&@3 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |s|q|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|s|q|r|t|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|s|q|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|s|q|r|t| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |t|a|n|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|t|a|n|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n|h| +0#0000000&@2 +@57|9|3|6|,|3| @8|4@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_54.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_54.dump new file mode 100644 index 0000000000..f36819638f --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_54.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n|h| +0#0000000&@2 +|#+0#0000e05&| |L|O|N|G| |B|I|T|S| |i|n| |s|o|f|t|w|a|r|e| |#| +0#0000000&@49 +@2|l+0#00e0e07&|o|n|g|b|i|t|s|p|a|c|k|;+0#0000000&| @32|l+0#00e0e07&|o|n|g|b|i|t|s|p|a|c|k| +0#0000000&@14 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|B+0#af5f00255&|I|N| +0#0000000&@42|B+0#af5f00255&|I|N| +0#0000000&@23 +@2>S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|N+0#af5f00255&|O|T| +0#0000000&@42|N+0#af5f00255&|O|T| +0#0000000&@23 +@2|~+0#af5f00255&| +0#0000000&@44|~+0#af5f00255&| +0#0000000&@25 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@57|9|5|2|,|3| @8|4|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_55.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_55.dump new file mode 100644 index 0000000000..59635c3d2b --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_55.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|A+0#af5f00255&|N|D| +0#0000000&@42|A+0#af5f00255&|N|D| +0#0000000&@23 +@2|&+0#af5f00255&| +0#0000000&@44|&+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|R| +0#0000000&@43|O+0#af5f00255&|R| +0#0000000&@24 +@2|X+0#af5f00255&|O|R| +0#0000000&@42|X+0#af5f00255&|O|R| +0#0000000&@23 +@2>S+0#af5f00255&|H|L| +0#0000000&@42|S+0#af5f00255&|H|L| +0#0000000&@23 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|S+0#af5f00255&|H|R| +0#0000000&@42|S+0#af5f00255&|H|R| +0#0000000&@23 +@2|D+0#af5f00255&|O|W|N| +0#0000000&@41|D+0#af5f00255&|O|W|N| +0#0000000&@22 +@2|E+0#af5f00255&|L|E|M| +0#0000000&@41|E+0#af5f00255&|L|E|M| +0#0000000&@22 +@2|S+0#af5f00255&|E|T| +0#0000000&@42|S+0#af5f00255&|E|T| +0#0000000&@23 +@2|C+0#af5f00255&|L|E|A|R| +0#0000000&@40|C+0#af5f00255&|L|E|A|R| +0#0000000&@21 +@2|l+0#00e0e07&|o|n|g| |p|i|;+0#0000000&| @1|d+0#00e0e07&|p|i|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|p|i|;+0#0000000&| @1|d+0#00e0e07&|p|i| +0#0000000&@14 +@2|l+0#00e0e07&|o|n|g| |m|a|x| |b|i|t|s|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|m|a|x|b|i|t|s| +0#0000000&@15 +@2|l+0#00e0e07&|o|n|g| |m|a|x| |i|n|t|;+0#0000000&| @32|l+0#00e0e07&|o|n|g|m|a|x|i|n|t| +0#0000000&@16 +@2|l+0#00e0e07&|o|n|g| |s|m|a|l@1| |r|e|a|l|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|s|m|a|l@1|r|e|a|l| +0#0000000&@13 +@2|l+0#00e0e07&|o|n|g| |m|a|x| |r|e|a|l|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|m|a|x|r|e|a|l| +0#0000000&@15 +@2|l+0#00e0e07&|o|n|g| |m|i|n| |r|e|a|l|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|m|i|n|r|e|a|l| +0#0000000&@15 +@2|l+0#00e0e07&|o|n|g| |i|n|f|i|n|i|t|y|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|i|n|f|i|n|i|t|y| +0#0000000&@14 +@57|9|7|0|,|3| @8|4|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_56.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_56.dump new file mode 100644 index 0000000000..0c17cb159a --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_56.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |i|n|f|i|n|i|t|y|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|i|n|f|i|n|i|t|y| +0#0000000&@14 +@2|l+0#00e0e07&|o|n|g| |m|i|n|u|s| |i|n|f|i|n|i|t|y|;+0#0000000&| @25|l+0#00e0e07&|o|n|g|m|i|n|u|s|i|n|f|i|n|i|t|y| +0#0000000&@9 +@2|l+0#00e0e07&|o|n|g| |i|n|f|;+0#0000000&| @36|l+0#00e0e07&|o|n|g|i|n|f| +0#0000000&@19 +@2|l+0#00e0e07&|o|n|g| |m|i|n| |i|n|f|;+0#0000000&| @32|l+0#00e0e07&|o|n|g|m|i|n|i|n|f| +0#0000000&@16 +|#+0#0000e05&| |L|O|N|G| |I|N|T| |a|s| |1|2|8| |b|i|t| |#| +0#0000000&@51 +@2>++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|S+0#af5f00255&|I|G|N| +0#0000000&@41|S+0#af5f00255&|I|G|N| +0#0000000&@22 +@2|O+0#af5f00255&|D@1| +0#0000000&@42|O+0#af5f00255&|D@1| +0#0000000&@23 +@2|E+0#af5f00255&|N|T|I|E|R| +0#0000000&@39|E+0#af5f00255&|N|T|I|E|R| +0#0000000&@20 +@2|R+0#af5f00255&|O|U|N|D| +0#0000000&@40|R+0#af5f00255&|O|U|N|D| +0#0000000&@21 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|V|E|R| +0#0000000&@41|O+0#af5f00255&|V|E|R| +0#0000000&@22 +@2|%+0#af5f00255&| +0#0000000&@44|%+0#af5f00255&| +0#0000000&@25 +@57|9|8@1|,|3| @8|4|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_57.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_57.dump new file mode 100644 index 0000000000..88a059bdb1 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_57.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|%+0#af5f00255&| +0#0000000&@44|%+0#af5f00255&| +0#0000000&@25 +@2|M+0#af5f00255&|O|D| +0#0000000&@42|M+0#af5f00255&|O|D| +0#0000000&@23 +@2|%+0#af5f00255&|*| +0#0000000&@43|%+0#af5f00255&|*| +0#0000000&@24 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2>^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|%+0#af5f00255&|:|=| +0#0000000&@42|%+0#af5f00255&|:|=| +0#0000000&@23 +@2|%+0#af5f00255&|*|:|=| +0#0000000&@41|%+0#af5f00255&|*|:|=| +0#0000000&@22 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|O+0#af5f00255&|V|E|R|A|B| +0#0000000&@39|O+0#af5f00255&|V|E|R|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|O|D|A|B| +0#0000000&@40|M+0#af5f00255&|O|D|A|B| +0#0000000&@21 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@57|1|0@1|6|,|3| @7|4|7|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_58.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_58.dump new file mode 100644 index 0000000000..f7af3b569e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_58.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2><+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +|#+0#0000e05&| |L|O|N|G| |R|E|A|L| |a|s| |1|2|8| |b|i|t| |#| +0#0000000&@50 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|S+0#af5f00255&|I|G|N| +0#0000000&@41|S+0#af5f00255&|I|G|N| +0#0000000&@22 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@57|1|0|2|4|,|3| @7|4|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_59.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_59.dump new file mode 100644 index 0000000000..d49c590f4c --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_59.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2>U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@57|1|0|4|2|,|3| @7|4|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_60.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_60.dump new file mode 100644 index 0000000000..377ed22503 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_60.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|D+0#af5f00255&|I|V|A|B| +0#0000000&@40|D+0#af5f00255&|I|V|A|B| +0#0000000&@21 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2>E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|s| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c@1|o|s|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|d|g| +0#0000000&@4 +@57|1|0|6|0|,|3| @7|5|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_61.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_61.dump new file mode 100644 index 0000000000..69e9524be7 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_61.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|s| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c@1|o|s|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|h|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|a|r|c@1|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|h| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|t| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c@1|o|t|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|t|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c@1|o|t|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|t| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|s|c|;+0#0000000&| @1|d+0#00e0e07&|a|c|s|c|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c@1|s|c|;+0#0000000&| @1|d+0#00e0e07&|a|c|s|c| +0#0000000&@8 +@2>l+0#00e0e07&|o|n|g| |a|r|c| |c|s|c| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|s|c| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c@1|s|c|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|c|s|c|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |s|e|c| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|s|e|c| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c|s|e|c|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|s|e|c|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |s|i|n| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c|s|i|n|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n|h|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|a|r|c|s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n|h| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n| |d|g|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|d|g| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|h|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|h| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |c|b|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|b|r|t|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|c|b|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|b|r|t| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |c|o|s| |d|g|;+0#0000000&| @1|d+0#00e0e07&|c|o|s| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|o|s|d|g|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|h|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|h| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |c|o|s| |p|i|;+0#0000000&| @1|d+0#00e0e07&|c|o|s| |p|i|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|o|s|p|i|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|p|i| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|o|t| |d|g|;+0#0000000&| @1|d+0#00e0e07&|c|o|t| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|o|t|d|g|;+0#0000000&| @1|d+0#00e0e07&|c|o|t|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|o|t|;+0#0000000&| @1|d+0#00e0e07&|c|o|t|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|o|t|;+0#0000000&| @1|d+0#00e0e07&|c|o|t| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |c|o|t| |p|i|;+0#0000000&| @1|d+0#00e0e07&|c|o|t| |p|i|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|o|t|p|i|;+0#0000000&| @1|d+0#00e0e07&|c|o|t|p|i| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|s|c|;+0#0000000&| @1|d+0#00e0e07&|c|s|c|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|s|c|;+0#0000000&| @1|d+0#00e0e07&|c|s|c| +0#0000000&@12 +@57|1|0|7|8|,|3| @7|5|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_62.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_62.dump new file mode 100644 index 0000000000..761ec50b20 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_62.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |c|s|c|;+0#0000000&| @1|d+0#00e0e07&|c|s|c|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|s|c|;+0#0000000&| @1|d+0#00e0e07&|c|s|c| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |c|s|c| |d|g|;+0#0000000&| @1|d+0#00e0e07&|c|s|c| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|c|s|c|d|g|;+0#0000000&| @1|d+0#00e0e07&|c|s|c|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|u|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|u|r|t|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|c|u|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|u|r|t| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |e|r|f|c|;+0#0000000&| @1|d+0#00e0e07&|e|r|f|c|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|e|r|f|c|;+0#0000000&| @1|d+0#00e0e07&|e|r|f|c| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |e|r|f|;+0#0000000&| @1|d+0#00e0e07&|e|r|f|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|e|r|f|;+0#0000000&| @1|d+0#00e0e07&|e|r|f| +0#0000000&@12 +@2>l+0#00e0e07&|o|n|g| |i|n|v| |e|r|f|c|;+0#0000000&| @1|d+0#00e0e07&|i|n|v|e|r|f|c|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|i|n|v|e|r|f|c|;+0#0000000&| @1|d+0#00e0e07&|i|n|v|e|r|f|c| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |i|n|v| |e|r|f|;+0#0000000&| @1|d+0#00e0e07&|i|n|v|e|r|f|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|i|n|v|e|r|f|;+0#0000000&| @1|d+0#00e0e07&|i|n|v|e|r|f| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |g|a|m@1|a|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|;+0#0000000&| @25|l+0#00e0e07&|o|n|g@1|a|m@1|a|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |l|n| |g|a|m@1|a|;+0#0000000&| @1|d+0#00e0e07&|l|n|g|a|m@1|a|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|l|n|g|a|m@1|a|;+0#0000000&| @1|d+0#00e0e07&|l|n|g|a|m@1|a| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |s|e|c|;+0#0000000&| @1|d+0#00e0e07&|s|e|c|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|s|e|c|;+0#0000000&| @1|d+0#00e0e07&|s|e|c| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |s|e|c| |d|g|;+0#0000000&| @1|d+0#00e0e07&|s|e|c| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|s|e|c|d|g|;+0#0000000&| @1|d+0#00e0e07&|s|e|c|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |s|i|n| |d|g|;+0#0000000&| @1|d+0#00e0e07&|s|i|n| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|s|i|n|d|g|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|h|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|h| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |s|i|n| |p|i|;+0#0000000&| @1|d+0#00e0e07&|s|i|n| |p|i|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|s|i|n|p|i|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|p|i| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |t|a|n| |d|g|;+0#0000000&| @1|d+0#00e0e07&|t|a|n| |d|g|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|t|a|n|d|g|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|d|g| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|h|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|h| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |t|a|n| |p|i|;+0#0000000&| @1|d+0#00e0e07&|t|a|n| |p|i|;+0#0000000&| @23|l+0#00e0e07&|o|n|g|t|a|n|p|i|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|p|i| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c@1|o|s|;+0#0000000&| @1|d+0#00e0e07&|a|c|o|s| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |s|i|n|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c|s|i|n|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n| +0#0000000&@8 +@57|1|0|9|6|,|3| @7|5|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_63.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_63.dump new file mode 100644 index 0000000000..9ed00cb1d9 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_63.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |a|r|c| |s|i|n|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c|s|i|n|;+0#0000000&| @1|d+0#00e0e07&|a|s|i|n| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n| +0#0000000&@8 +@2|l+0#00e0e07&|o|n|g| |c|a|s|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|a|s|;+0#0000000&| @1|d+0#00e0e07&|c|a|s| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c|o|s|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c|o|s| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |e|x|p|;+0#0000000&| @1|d+0#00e0e07&|e|x|p|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|e|x|p|;+0#0000000&| @1|d+0#00e0e07&|e|x|p| +0#0000000&@12 +@2>l+0#00e0e07&|o|n|g| |l|n|;+0#0000000&| @1|d+0#00e0e07&|l|n|;+0#0000000&| @31|l+0#00e0e07&|o|n|g|l|n|;+0#0000000&| @1|d+0#00e0e07&|l|n| +0#0000000&@14 +@2|l+0#00e0e07&|o|n|g| |l|o|g|;+0#0000000&| @1|d+0#00e0e07&|l|o|g|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|l|o|g|;+0#0000000&| @1|d+0#00e0e07&|l|o|g| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |s|i|n|;+0#0000000&| @1|d+0#00e0e07&|s|i|n|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|s|i|n|;+0#0000000&| @1|d+0#00e0e07&|s|i|n| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |s|q|r|t|;+0#0000000&| @1|d+0#00e0e07&|s|q|r|t|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|s|q|r|t|;+0#0000000&| @1|d+0#00e0e07&|s|q|r|t| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |t|a|n|;+0#0000000&| @1|d+0#00e0e07&|t|a|n|;+0#0000000&| @29|l+0#00e0e07&|o|n|g|t|a|n|;+0#0000000&| @1|d+0#00e0e07&|t|a|n| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |n|e|x|t| |r|a|n|d|o|m|;+0#0000000&| @28|l+0#00e0e07&|o|n|g|n|e|x|t|r|a|n|d|o|m| +0#0000000&@12 +@2|l+0#00e0e07&|o|n|g| |r|a|n|d|o|m|;+0#0000000&| @33|l+0#00e0e07&|o|n|g|r|a|n|d|o|m| +0#0000000&@16 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n|2| |d|g|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|2|d|g|;+0#0000000&| @17|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|2|d|g|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|2|d|g| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |a|r|c| |t|a|n|2|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|2|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|a|r|c|t|a|n|2|;+0#0000000&| @1|d+0#00e0e07&|a|t|a|n|2| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |b|e|t|a|;+0#0000000&| @1|d+0#00e0e07&|b|e|t|a|;+0#0000000&| @27|l+0#00e0e07&|o|n|g|b|e|t|a|;+0#0000000&| @1|d+0#00e0e07&|b|e|t|a| +0#0000000&@10 +@2|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|g|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g|f|;+0#0000000&| @14|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|g|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g +|f| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|f|;+0#0000000&| @16|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|f|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|f| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|;+0#0000000&| @18|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c| +0#0000000&@2 +@57|1@2|4|,|3| @7|5|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_64.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_64.dump new file mode 100644 index 0000000000..05a0d2c5e2 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_64.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|;+0#0000000&| @18|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |l|n| |b|e|t|a|;+0#0000000&| @1|d+0#00e0e07&|l|n|b|e|t|a|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|l|n|b|e|t|a|;+0#0000000&| @1|d+0#00e0e07&|l|n|b|e|t|a| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |b|e|t|a| |i|n|c|;+0#0000000&| @1|d+0#00e0e07&|b|e|t|a|i|n|c|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|b|e|t|a|i|n|c|;+0#0000000&| @1|d+0#00e0e07&|b|e|t|a|i|n|c| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |g|a|m@1|a| |i|n|c|g|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g|;+0#0000000&| @16|l+0#00e0e07&|o|n|g@1|a|m@1|a|i|n|c|g|;+0#0000000&| @1|d+0#00e0e07&|g|a|m@1|a|i|n|c|g| +0#0000000& +|#+0#0000e05&| |L|O|N|G| |B|I|T|S| |a|s| |1|2|8| |b|i|t| |#| +0#0000000&@50 +@2>l+0#00e0e07&|o|n|g| |b|i|t|s| |p|a|c|k|;+0#0000000&| @30|l+0#00e0e07&|o|n|g|b|i|t|s|p|a|c|k| +0#0000000&@14 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|B+0#af5f00255&|I|N| +0#0000000&@42|B+0#af5f00255&|I|N| +0#0000000&@23 +@2|N+0#af5f00255&|O|T| +0#0000000&@42|N+0#af5f00255&|O|T| +0#0000000&@23 +@2|~+0#af5f00255&| +0#0000000&@44|~+0#af5f00255&| +0#0000000&@25 +@2|A+0#af5f00255&|N|D| +0#0000000&@42|A+0#af5f00255&|N|D| +0#0000000&@23 +@2|&+0#af5f00255&| +0#0000000&@44|&+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|R| +0#0000000&@43|O+0#af5f00255&|R| +0#0000000&@24 +@2|X+0#af5f00255&|O|R| +0#0000000&@42|X+0#af5f00255&|O|R| +0#0000000&@23 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@57|1@1|3|1|,|3| @7|5|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_65.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_65.dump new file mode 100644 index 0000000000..f38588d06b --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_65.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|V|E|R| +0#0000000&@41|O+0#af5f00255&|V|E|R| +0#0000000&@22 +@2|M+0#af5f00255&|O|D| +0#0000000&@42|M+0#af5f00255&|O|D| +0#0000000&@23 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2>~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|<+0#af5f00255&| +0#0000000&@44|<+0#af5f00255&| +0#0000000&@25 +@2|>+0#af5f00255&| +0#0000000&@44|>+0#af5f00255&| +0#0000000&@25 +@2|L+0#af5f00255&|T| +0#0000000&@43|L+0#af5f00255&|T| +0#0000000&@24 +@2|G+0#af5f00255&|T| +0#0000000&@43|G+0#af5f00255&|T| +0#0000000&@24 +@2|E+0#af5f00255&|L|E|M| +0#0000000&@41|E+0#af5f00255&|L|E|M| +0#0000000&@22 +@2|S+0#af5f00255&|E|T| +0#0000000&@42|S+0#af5f00255&|E|T| +0#0000000&@23 +@57|1@1|4|9|,|3| @7|5|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_66.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_66.dump new file mode 100644 index 0000000000..96bcba7283 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_66.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|S+0#af5f00255&|E|T| +0#0000000&@42|S+0#af5f00255&|E|T| +0#0000000&@23 +@2|C+0#af5f00255&|L|E|A|R| +0#0000000&@40|C+0#af5f00255&|L|E|A|R| +0#0000000&@21 +@2|S+0#af5f00255&|H|L| +0#0000000&@42|S+0#af5f00255&|H|L| +0#0000000&@23 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|S+0#af5f00255&|H|R| +0#0000000&@42|S+0#af5f00255&|H|R| +0#0000000&@23 +@2>D+0#af5f00255&|O|W|N| +0#0000000&@41|D+0#af5f00255&|O|W|N| +0#0000000&@22 +@2|R+0#af5f00255&|O|L| +0#0000000&@42|R+0#af5f00255&|O|L| +0#0000000&@23 +@2|R+0#af5f00255&|O|R| +0#0000000&@42|R+0#af5f00255&|O|R| +0#0000000&@23 +|#+0#0000e05&| |L|O|N|G| |C|O|M|P|L|E|X| |a|s| |2| |x| |1|2|8| |b|i|t|.| |#| +0#0000000&@42 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|I+0#af5f00255&| +0#0000000&@44|I+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|*| +0#0000000&@43|++0#af5f00255&|*| +0#0000000&@24 +@2|I+0#af5f00255&| +0#0000000&@44|I+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|*| +0#0000000&@43|++0#af5f00255&|*| +0#0000000&@24 +@2|R+0#af5f00255&|E| +0#0000000&@43|R+0#af5f00255&|E| +0#0000000&@24 +@2|I+0#af5f00255&|M| +0#0000000&@43|I+0#af5f00255&|M| +0#0000000&@24 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|A+0#af5f00255&|R|G| +0#0000000&@42|A+0#af5f00255&|R|G| +0#0000000&@23 +@57|1@1|6|7|,|3| @7|5@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_67.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_67.dump new file mode 100644 index 0000000000..5a5812725b --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_67.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|A+0#af5f00255&|R|G| +0#0000000&@42|A+0#af5f00255&|R|G| +0#0000000&@23 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|C+0#af5f00255&|O|N|J| +0#0000000&@41|C+0#af5f00255&|O|N|J| +0#0000000&@22 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2>/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|++0#af5f00255&| +0#0000000&@44|++0#af5f00255&| +0#0000000&@25 +@2|-+0#af5f00255&| +0#0000000&@44|-+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&| +0#0000000&@44|*+0#af5f00255&| +0#0000000&@25 +@2|/+0#af5f00255&| +0#0000000&@44|/+0#af5f00255&| +0#0000000&@25 +@2|*+0#af5f00255&@1| +0#0000000&@43|*+0#af5f00255&@1| +0#0000000&@24 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2|^+0#af5f00255&| +0#0000000&@44|^+0#af5f00255&| +0#0000000&@25 +@2|++0#af5f00255&|:|=| +0#0000000&@42|++0#af5f00255&|:|=| +0#0000000&@23 +@2|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@57|1@1|8|5|,|3| @7|5|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_68.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_68.dump new file mode 100644 index 0000000000..c01c91ecf7 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_68.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|-+0#af5f00255&|:|=| +0#0000000&@42|-+0#af5f00255&|:|=| +0#0000000&@23 +@2|*+0#af5f00255&|:|=| +0#0000000&@42|*+0#af5f00255&|:|=| +0#0000000&@23 +@2|/+0#af5f00255&|:|=| +0#0000000&@42|/+0#af5f00255&|:|=| +0#0000000&@23 +@2|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@39|P+0#af5f00255&|L|U|S|A|B| +0#0000000&@20 +@2|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@38|M+0#af5f00255&|I|N|U|S|A|B| +0#0000000&@19 +@2>T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@38|T+0#af5f00255&|I|M|E|S|A|B| +0#0000000&@19 +@2|D+0#af5f00255&|I|V|A|B| +0#0000000&@40|D+0#af5f00255&|I|V|A|B| +0#0000000&@21 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|c|o|s|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c@1|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|c|o|s +|h| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c|a|c|o|s|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c@1|o|s|;+0#0000000&| @1|d+0#00e0e07&|c|a|c|o|s| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|i|n|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c|s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|i|n +|h| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |s|i|n|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|i|n|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c|s|i|n|;+0#0000000&| @1|d+0#00e0e07&|c|a|s|i|n| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n|h|;+0#0000000&| @13|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n +|h| +0#0000000&@73 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|r|c| |t|a|n|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n|;+0#0000000&| @15|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|r|c|t|a|n|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |a|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n|h|;+0#0000000&| @16|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|a|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|a|t|a|n|h| +0#0000000& +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|c|o|s|h|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s|h| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s| +0#0000000&@4 +@57|1|2|0|3|,|3| @7|5|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_69.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_69.dump new file mode 100644 index 0000000000..584ca73d5d --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_69.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|c|o|s|;+0#0000000&| @1|d+0#00e0e07&|c@1|o|s| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |e|x|p|;+0#0000000&| @1|d+0#00e0e07&|c|e|x|p|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|e|x|p|;+0#0000000&| @1|d+0#00e0e07&|c|e|x|p| +0#0000000&@4 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |l|n|;+0#0000000&| @1|d+0#00e0e07&|c|l|n|;+0#0000000&| @22|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|l|n|;+0#0000000&| @1|d+0#00e0e07&|c|l|n| +0#0000000&@6 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|s|i|n|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|s|i|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|s|i|n|h| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |s|i|n|;+0#0000000&| @1|d+0#00e0e07&|c|s|i|n|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|s|i|n|;+0#0000000&| @1|d+0#00e0e07&|c|s|i|n| +0#0000000&@4 +@2>l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |s|q|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|s|q|r|t|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|s|q|r|t|;+0#0000000&| @1|d+0#00e0e07&|c|s|q|r|t| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n|h|;+0#0000000&| @18|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|t|a|n|h|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n|h| +0#0000000&@2 +@2|l+0#00e0e07&|o|n|g| |c|o|m|p|l|e|x| |t|a|n|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n|;+0#0000000&| @20|l+0#00e0e07&|o|n|g|c|o|m|p|l|e|x|t|a|n|;+0#0000000&| @1|d+0#00e0e07&|c|t|a|n| +0#0000000&@4 +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |g|a|m@1|a| |i|n|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |d|g|a|m@1|a|i|n|c|;+0#0000000&| @8|m+0#00e0e07&|p|f|r|l|o|n|g@1|a|m@1|a|i|n|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|d|g|a|m +@1|a|i|n|c| +0#0000000&@69 +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |b|e|t|a|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|b|e|t|a|;+0#0000000&| @12|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g|b|e|t|a|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q|b|e|t +|a| +0#0000000&@73 +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |g|a|m@1|a|i|n|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|g|a|m@1|a|i|n|c|;+0#0000000&| @4|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g@1|a|m@1|a|i|n|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r +|q|g|a|m@1|a|i|n|c| +0#0000000&@65 +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |l|n|b|e|t|a|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|l|n|b|e|t|a|;+0#0000000&| @8|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g|l|n|b|e|t|a|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q|l +|n|b|e|t|a| +0#0000000&@69 +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |b|e|t|a|i|n|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|b|e|t|a|i|n|c|;+0#0000000&| @6|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g|b|e|t|a|i|n|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q +|b|e|t|a|i|n|c| +0#0000000&@67 +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |e|r|f|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|e|r|f|c|;+0#0000000&| @12|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g|e|r|f|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q|@+0#4040ff13&@2 +| +0#0000000&@56|1|2|1|8|,|3| @7|5|7|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_70.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_70.dump new file mode 100644 index 0000000000..1e7940f836 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_70.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |e|r|f|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|e|r|f|c|;+0#0000000&| @12|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g|e|r|f|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q|e|r|f +|c| +0#0000000&@73 +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |e|r|f|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|e|r|f|;+0#0000000&| @14|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g|e|r|f|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q|e|r|f| +0#0000000& +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |g|a|m@1|a|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|g|a|m@1|a|;+0#0000000&| @10|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g@1|a|m@1|a|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q|g|a +|m@1|a| +0#0000000&@71 +@2>m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |i|n|v|e|r|f|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|i|n|v|e|r|f|c|;+0#0000000&| @6|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g|i|n|v|e|r|f|c|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q +|i|n|v|e|r|f|c| +0#0000000&@67 +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |i|n|v|e|r|f|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|i|n|v|e|r|f|;+0#0000000&| @8|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g|i|n|v|e|r|f|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q|i +|n|v|e|r|f| +0#0000000&@69 +@2|m+0#00e0e07&|p|f|r| |l|o|n|g| |l|o|n|g| |l|n|g|a|m@1|a|;+0#0000000&| @1|m+0#00e0e07&|p|f|r| |q|l|n|g|a|m@1|a|;+0#0000000&| @6|m+0#00e0e07&|p|f|r|l|o|n|g|l|o|n|g|l|n|g|a|m@1|a|;+0#0000000&| @1|m+0#00e0e07&|p|f|r|q +|l|n|g|a|m@1|a| +0#0000000&@67 +@2|m+0#00e0e07&|p|f|r| |m|p|;+0#0000000&| @37|m+0#00e0e07&|p|f|r|m|p| +0#0000000&@20 +@2|b+0#00e0e07&|l|a|n|k| |c|h|a|r|a|c|t|e|r|;+0#0000000&| @29|b+0#00e0e07&|l|a|n|k|c|h|a|r|a|c|t|e|r| +0#0000000&@12 +@2|f+0#00e0e07&|o|r|m|f|e@1|d| |c|h|a|r|a|c|t|e|r|;+0#0000000&| @26|f+0#00e0e07&|o|r|m|f|e@1|d|c|h|a|r|a|c|t|e|r| +0#0000000&@9 +@2|f+0#00e0e07&|o|r|m|f|e@1|d| |c|h|a|r|;+0#0000000&| @31|f+0#00e0e07&|o|r|m|f|e@1|d|c|h|a|r| +0#0000000&@14 +@2|n+0#00e0e07&|e|w|l|i|n|e| |c|h|a|r|a|c|t|e|r|;+0#0000000&| @27|n+0#00e0e07&|e|w|l|i|n|e|c|h|a|r|a|c|t|e|r| +0#0000000&@10 +@2|n+0#00e0e07&|e|w|l|i|n|e| |c|h|a|r|;+0#0000000&| @32|n+0#00e0e07&|e|w|l|i|n|e|c|h|a|r| +0#0000000&@15 +@2|n+0#00e0e07&|u|l@1| |c|h|a|r|a|c|t|e|r|;+0#0000000&| @30|n+0#00e0e07&|u|l@1|c|h|a|r|a|c|t|e|r| +0#0000000&@13 +@2|t+0#00e0e07&|a|b| |c|h|a|r|a|c|t|e|r|;+0#0000000&| @31|t+0#00e0e07&|a|b|c|h|a|r|a|c|t|e|r| +0#0000000&@14 +@57|1|2@1|9|,|3| @7|5|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_71.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_71.dump new file mode 100644 index 0000000000..0df0079d31 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_71.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|t+0#00e0e07&|a|b| |c|h|a|r|a|c|t|e|r|;+0#0000000&| @31|t+0#00e0e07&|a|b|c|h|a|r|a|c|t|e|r| +0#0000000&@14 +@2|t+0#00e0e07&|a|b| |c|h|a|r|;+0#0000000&| @36|t+0#00e0e07&|a|b|c|h|a|r| +0#0000000&@19 +@2|b+0#00e0e07&|l|a|n|k| |c|h|a|r|;+0#0000000&| @34|b+0#00e0e07&|l|a|n|k|c|h|a|r| +0#0000000&@17 +@2|b+0#00e0e07&|l|a|n|k|;+0#0000000&| @39|b+0#00e0e07&|l|a|n|k| +0#0000000&@21 +@2|e+0#00e0e07&|o|f| |c|h|a|r|a|c|t|e|r|;+0#0000000&| @31|e+0#00e0e07&|o|f|c|h|a|r|a|c|t|e|r| +0#0000000&@14 +@2>e+0#00e0e07&|o|f| |c|h|a|r|;+0#0000000&| @36|e+0#00e0e07&|o|f|c|h|a|r| +0#0000000&@19 +@2|e+0#00e0e07&|r@1|o|r| |c|h|a|r|;+0#0000000&| @34|e+0#00e0e07&|r@1|o|r|c|h|a|r| +0#0000000&@17 +@2|e+0#00e0e07&|x|p| |c|h|a|r|;+0#0000000&| @36|e+0#00e0e07&|x|p|c|h|a|r| +0#0000000&@19 +@2|f+0#00e0e07&|l|i|p|;+0#0000000&| @40|f+0#00e0e07&|l|i|p| +0#0000000&@22 +@2|f+0#00e0e07&|l|o|p|;+0#0000000&| @40|f+0#00e0e07&|l|o|p| +0#0000000&@22 +@2|n+0#00e0e07&|u|l@1| |c|h|a|r|;+0#0000000&| @35|n+0#00e0e07&|u|l@1|c|h|a|r| +0#0000000&@18 +@2|b+0#00e0e07&|i|t|s|;+0#0000000&| @40|b+0#00e0e07&|i|t|s| +0#0000000&@22 +@2|w+0#00e0e07&|h|o|l|e|;+0#0000000&| @39|w+0#00e0e07&|h|o|l|e| +0#0000000&@21 +@2|f+0#00e0e07&|i|x|e|d|;+0#0000000&| @39|f+0#00e0e07&|i|x|e|d| +0#0000000&@21 +@2|f+0#00e0e07&|l|o|a|t|;+0#0000000&| @39|f+0#00e0e07&|l|o|a|t| +0#0000000&@21 +@2|r+0#00e0e07&|e|a|l|;+0#0000000&| @40|r+0#00e0e07&|e|a|l| +0#0000000&@22 +@2|s+0#00e0e07&|t|a|n|d| |i|n|;+0#0000000&| @36|s+0#00e0e07&|t|a|n|d|i|n| +0#0000000&@19 +@2|s+0#00e0e07&|t|a|n|d| |o|u|t|;+0#0000000&| @35|s+0#00e0e07&|t|a|n|d|o|u|t| +0#0000000&@18 +@2|s+0#00e0e07&|t|a|n|d| |b|a|c|k|;+0#0000000&| @34|s+0#00e0e07&|t|a|n|d|b|a|c|k| +0#0000000&@17 +@57|1|2|4@1|,|3| @7|5|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_72.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_72.dump new file mode 100644 index 0000000000..96266e2e6a --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_72.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|s+0#00e0e07&|t|a|n|d| |b|a|c|k|;+0#0000000&| @34|s+0#00e0e07&|t|a|n|d|b|a|c|k| +0#0000000&@17 +@2|s+0#00e0e07&|t|a|n|d| |e|r@1|o|r|;+0#0000000&| @33|s+0#00e0e07&|t|a|n|d|e|r@1|o|r| +0#0000000&@16 +@2|s+0#00e0e07&|t|a|n|d| |i|n| |c|h|a|n@1|e|l|;+0#0000000&| @28|s+0#00e0e07&|t|a|n|d|i|n|c|h|a|n@1|e|l| +0#0000000&@12 +@2|s+0#00e0e07&|t|a|n|d| |o|u|t| |c|h|a|n@1|e|l|;+0#0000000&| @27|s+0#00e0e07&|t|a|n|d|o|u|t|c|h|a|n@1|e|l| +0#0000000&@11 +@2|s+0#00e0e07&|t|a|n|d| |d|r|a|w| |c|h|a|n@1|e|l|;+0#0000000&| @26|s+0#00e0e07&|t|a|n|d@1|r|a|w|c|h|a|n@1|e|l| +0#0000000&@10 +@2>s+0#00e0e07&|t|a|n|d| |b|a|c|k| |c|h|a|n@1|e|l|;+0#0000000&| @26|s+0#00e0e07&|t|a|n|d|b|a|c|k|c|h|a|n@1|e|l| +0#0000000&@10 +@2|s+0#00e0e07&|t|a|n|d| |e|r@1|o|r| |c|h|a|n@1|e|l|;+0#0000000&| @25|s+0#00e0e07&|t|a|n|d|e|r@1|o|r|c|h|a|n@1|e|l| +0#0000000&@9 +@2|m+0#00e0e07&|a|k|e| |t|e|r|m|;+0#0000000&| @35|m+0#00e0e07&|a|k|e|t|e|r|m| +0#0000000&@18 +@2|c+0#00e0e07&|h|a|r| |i|n| |s|t|r|i|n|g|;+0#0000000&| @30|c+0#00e0e07&|h|a|r|i|n|s|t|r|i|n|g| +0#0000000&@14 +@2|l+0#00e0e07&|a|s|t| |c|h|a|r| |i|n| |s|t|r|i|n|g|;+0#0000000&| @25|l+0#00e0e07&|a|s|t|c|h|a|r|i|n|s|t|r|i|n|g| +0#0000000&@10 +@2|s+0#00e0e07&|t|r|i|n|g| |i|n| |s|t|r|i|n|g|;+0#0000000&| @28|s+0#00e0e07&|t|r|i|n|g|i|n|s|t|r|i|n|g| +0#0000000&@12 +@2|i+0#00e0e07&|d|f|;+0#0000000&| @41|i+0#00e0e07&|d|f| +0#0000000&@23 +@2|t+0#00e0e07&|e|r|m|;+0#0000000&| @40|t+0#00e0e07&|e|r|m| +0#0000000&@22 +@2|p+0#00e0e07&|r|o|g|r|a|m| |i|d|f|;+0#0000000&| @33|p+0#00e0e07&|r|o|g|r|a|m|i|d|f| +0#0000000&@16 +|#+0#0000e05&| |E|v|e|n|t| |r|o|u|t|i|n|e|s|.| |#| +0#0000000&@55 +@2|o+0#00e0e07&|n| |f|i|l|e| |e|n|d|;+0#0000000&| @33|o+0#00e0e07&|n|f|i|l|e@1|n|d| +0#0000000&@17 +@2|o+0#00e0e07&|n| |p|a|g|e| |e|n|d|;+0#0000000&| @33|o+0#00e0e07&|n|p|a|g|e@1|n|d| +0#0000000&@17 +@2|o+0#00e0e07&|n| |l|i|n|e| |e|n|d|;+0#0000000&| @33|o+0#00e0e07&|n|l|i|n|e@1|n|d| +0#0000000&@17 +@2|o+0#00e0e07&|n| |l|o|g|i|c|a|l| |f|i|l|e| |e|n|d|;+0#0000000&| @25|o+0#00e0e07&|n|l|o|g|i|c|a|l|f|i|l|e@1|n|d| +0#0000000&@10 +@57|1|2|6|2|,|3| @7|5|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_73.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_73.dump new file mode 100644 index 0000000000..f2391f1307 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_73.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|o+0#00e0e07&|n| |l|o|g|i|c|a|l| |f|i|l|e| |e|n|d|;+0#0000000&| @25|o+0#00e0e07&|n|l|o|g|i|c|a|l|f|i|l|e@1|n|d| +0#0000000&@10 +@2|o+0#00e0e07&|n| |p|h|y|s|i|c|a|l| |f|i|l|e| |e|n|d|;+0#0000000&| @24|o+0#00e0e07&|n|p|h|y|s|i|c|a|l|f|i|l|e@1|n|d| +0#0000000&@9 +@2|o+0#00e0e07&|n| |f|o|r|m|a|t| |e|n|d|;+0#0000000&| @31|o+0#00e0e07&|n|f|o|r|m|a|t|e|n|d| +0#0000000&@15 +@2|o+0#00e0e07&|n| |f|o|r|m|a|t| |e|r@1|o|r|;+0#0000000&| @29|o+0#00e0e07&|n|f|o|r|m|a|t|e|r@1|o|r| +0#0000000&@13 +@2|o+0#00e0e07&|n| |v|a|l|u|e| |e|r@1|o|r|;+0#0000000&| @30|o+0#00e0e07&|n|v|a|l|u|e@1|r@1|o|r| +0#0000000&@14 +@2>o+0#00e0e07&|n| |o|p|e|n| |e|r@1|o|r|;+0#0000000&| @31|o+0#00e0e07&|n|o|p|e|n|e|r@1|o|r| +0#0000000&@15 +@2|o+0#00e0e07&|n| |t|r|a|n|s|p|u|t| |e|r@1|o|r|;+0#0000000&| @27|o+0#00e0e07&|n|t|r|a|n|s|p|u|t|e|r@1|o|r| +0#0000000&@11 +|#+0#0000e05&| |E|n|q|u|i|r|i|e|s| |o|n| |f|i|l|e|s|.| |#| +0#0000000&@51 +@2|d+0#00e0e07&|r|a|w| |p|o|s@1|i|b|l|e|;+0#0000000&| @31|d+0#00e0e07&|r|a|w|p|o|s@1|i|b|l|e| +0#0000000&@14 +@2|e+0#00e0e07&|n|d| |o|f| |f|i|l|e|;+0#0000000&| @33|e+0#00e0e07&|n|d|o|f@1|i|l|e| +0#0000000&@17 +@2|e+0#00e0e07&|n|d| |o|f| |l|i|n|e|;+0#0000000&| @33|e+0#00e0e07&|n|d|o|f|l|i|n|e| +0#0000000&@17 +@2|e+0#00e0e07&|o|f|;+0#0000000&| @41|e+0#00e0e07&|o|f| +0#0000000&@23 +@2|e+0#00e0e07&|o|l|n|;+0#0000000&| @40|e+0#00e0e07&|o|l|n| +0#0000000&@22 +@2|r+0#00e0e07&|e|w|i|n|d| |p|o|s@1|i|b|l|e|;+0#0000000&| @29|r+0#00e0e07&|e|w|i|n|d|p|o|s@1|i|b|l|e| +0#0000000&@12 +@2|b+0#00e0e07&|i|n| |p|o|s@1|i|b|l|e|;+0#0000000&| @32|b+0#00e0e07&|i|n|p|o|s@1|i|b|l|e| +0#0000000&@15 +@2|c+0#00e0e07&|o|m|p|r|e|s@1|i|b|l|e|;+0#0000000&| @32|c+0#00e0e07&|o|m|p|r|e|s@1|i|b|l|e| +0#0000000&@14 +@2|g+0#00e0e07&|e|t| |p|o|s@1|i|b|l|e|;+0#0000000&| @32|g+0#00e0e07&|e|t|p|o|s@1|i|b|l|e| +0#0000000&@15 +@2|p+0#00e0e07&|u|t| |p|o|s@1|i|b|l|e|;+0#0000000&| @32|p+0#00e0e07&|u|t|p|o|s@1|i|b|l|e| +0#0000000&@15 +@2|r+0#00e0e07&|e|i|d|f| |p|o|s@1|i|b|l|e|;+0#0000000&| @30|r+0#00e0e07&|e|i|d|f|p|o|s@1|i|b|l|e| +0#0000000&@13 +@57|1|2|8|0|,|3| @7|6|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_74.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_74.dump new file mode 100644 index 0000000000..d494e9a2f2 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_74.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|r+0#00e0e07&|e|i|d|f| |p|o|s@1|i|b|l|e|;+0#0000000&| @30|r+0#00e0e07&|e|i|d|f|p|o|s@1|i|b|l|e| +0#0000000&@13 +@2|r+0#00e0e07&|e|s|e|t| |p|o|s@1|i|b|l|e|;+0#0000000&| @30|r+0#00e0e07&|e|s|e|t|p|o|s@1|i|b|l|e| +0#0000000&@13 +@2|s+0#00e0e07&|e|t| |p|o|s@1|i|b|l|e|;+0#0000000&| @32|s+0#00e0e07&|e|t|p|o|s@1|i|b|l|e| +0#0000000&@15 +|#+0#0000e05&| |H|a|n|d|l|i|n|g| |o|f| |f|i|l|e|s|.| |#| +0#0000000&@52 +@2|o+0#00e0e07&|p|e|n|;+0#0000000&| @40|o+0#00e0e07&|p|e|n| +0#0000000&@22 +@2>a+0#00e0e07&|p@1|e|n|d|;+0#0000000&| @38|a+0#00e0e07&|p@1|e|n|d| +0#0000000&@20 +@2|e+0#00e0e07&|s|t|a|b|l|i|s|h|;+0#0000000&| @35|e+0#00e0e07&|s|t|a|b|l|i|s|h| +0#0000000&@17 +@2|a+0#00e0e07&|s@1|o|c|i|a|t|e|;+0#0000000&| @35|a+0#00e0e07&|s@1|o|c|i|a|t|e| +0#0000000&@17 +@2|c+0#00e0e07&|o@1|k|e|d|;+0#0000000&| @38|c+0#00e0e07&|o@1|k|e|d| +0#0000000&@20 +@2|r+0#00e0e07&|a|w|;+0#0000000&| @41|r+0#00e0e07&|a|w| +0#0000000&@23 +@2|r+0#00e0e07&|e|w|i|n|d|;+0#0000000&| @38|r+0#00e0e07&|e|w|i|n|d| +0#0000000&@20 +@2|b+0#00e0e07&|a|c|k|s|p|a|c|e|;+0#0000000&| @35|b+0#00e0e07&|a|c|k|s|p|a|c|e| +0#0000000&@17 +@2|c+0#00e0e07&|l|o|s|e|;+0#0000000&| @39|c+0#00e0e07&|l|o|s|e| +0#0000000&@21 +@2|c+0#00e0e07&|r|e|a|t|e|;+0#0000000&| @38|c+0#00e0e07&|r|e|a|t|e| +0#0000000&@20 +@2|e+0#00e0e07&|r|a|s|e|;+0#0000000&| @39|e+0#00e0e07&|r|a|s|e| +0#0000000&@21 +@2|l+0#00e0e07&|o|c|k|;+0#0000000&| @40|l+0#00e0e07&|o|c|k| +0#0000000&@22 +@2|n+0#00e0e07&|e|w|l|i|n|e|;+0#0000000&| @37|n+0#00e0e07&|e|w|l|i|n|e| +0#0000000&@19 +@2|n+0#00e0e07&|e|w|p|a|g|e|;+0#0000000&| @37|n+0#00e0e07&|e|w|p|a|g|e| +0#0000000&@19 +@2|r+0#00e0e07&|e|s|e|t|;+0#0000000&| @39|r+0#00e0e07&|e|s|e|t| +0#0000000&@21 +@57|1|2|9|8|,|3| @7|6|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_75.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_75.dump new file mode 100644 index 0000000000..f2b06f2007 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_75.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|r+0#00e0e07&|e|s|e|t|;+0#0000000&| @39|r+0#00e0e07&|e|s|e|t| +0#0000000&@21 +@2|s+0#00e0e07&|c|r|a|t|c|h|;+0#0000000&| @37|s+0#00e0e07&|c|r|a|t|c|h| +0#0000000&@19 +@2|s+0#00e0e07&|p|a|c|e|;+0#0000000&| @39|s+0#00e0e07&|p|a|c|e| +0#0000000&@21 +@2|s+0#00e0e07&|e|t|;+0#0000000&| @41|s+0#00e0e07&|e|t| +0#0000000&@23 +@2|s+0#00e0e07&|e@1|k|;+0#0000000&| @40|s+0#00e0e07&|e@1|k| +0#0000000&@22 +@2>r+0#00e0e07&|e|a|d|;+0#0000000&| @40|r+0#00e0e07&|e|a|d| +0#0000000&@22 +@2|r+0#00e0e07&|e|a|d| |b|i|n|;+0#0000000&| @36|r+0#00e0e07&|e|a|d|b|i|n| +0#0000000&@19 +@2|r+0#00e0e07&|e|a|d|f|;+0#0000000&| @39|r+0#00e0e07&|e|a|d|f| +0#0000000&@21 +@2|p+0#00e0e07&|r|i|n|t| |b|i|n|;+0#0000000&| @35|p+0#00e0e07&|r|i|n|t|b|i|n| +0#0000000&@18 +@2|p+0#00e0e07&|r|i|n|t|f|;+0#0000000&| @38|p+0#00e0e07&|r|i|n|t|f| +0#0000000&@20 +@2|p+0#00e0e07&|r|i|n|t|;+0#0000000&| @39|p+0#00e0e07&|r|i|n|t| +0#0000000&@21 +@2|w+0#00e0e07&|r|i|t|e| |b|i|n|;+0#0000000&| @35|w+0#00e0e07&|r|i|t|e|b|i|n| +0#0000000&@18 +@2|w+0#00e0e07&|r|i|t|e|f|;+0#0000000&| @38|w+0#00e0e07&|r|i|t|e|f| +0#0000000&@20 +@2|w+0#00e0e07&|r|i|t|e|;+0#0000000&| @39|w+0#00e0e07&|r|i|t|e| +0#0000000&@21 +@2|g+0#00e0e07&|e|t|;+0#0000000&| @41|g+0#00e0e07&|e|t| +0#0000000&@23 +@2|g+0#00e0e07&|e|t|f|;+0#0000000&| @40|g+0#00e0e07&|e|t|f| +0#0000000&@22 +@2|g+0#00e0e07&|e|t| |b|i|n|;+0#0000000&| @37|g+0#00e0e07&|e|t|b|i|n| +0#0000000&@20 +@2|p+0#00e0e07&|u|t|;+0#0000000&| @41|p+0#00e0e07&|u|t| +0#0000000&@23 +@2|p+0#00e0e07&|u|t|f|;+0#0000000&| @40|p+0#00e0e07&|u|t|f| +0#0000000&@22 +@57|1|3|1|6|,|3| @7|6|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_76.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_76.dump new file mode 100644 index 0000000000..a54d6f4d91 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_76.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|p+0#00e0e07&|u|t|f|;+0#0000000&| @40|p+0#00e0e07&|u|t|f| +0#0000000&@22 +@2|p+0#00e0e07&|u|t| |b|i|n|;+0#0000000&| @37|p+0#00e0e07&|u|t|b|i|n| +0#0000000&@20 +@2|g+0#00e0e07&|e|t|s|;+0#0000000&| @40|g+0#00e0e07&|e|t|s| +0#0000000&@22 +@2|g+0#00e0e07&|e|t|s|f|;+0#0000000&| @39|g+0#00e0e07&|e|t|s|f| +0#0000000&@21 +@2|p+0#00e0e07&|u|t|s|;+0#0000000&| @40|p+0#00e0e07&|u|t|s| +0#0000000&@22 +@2>p+0#00e0e07&|u|t|s|f|;+0#0000000&| @39|p+0#00e0e07&|u|t|s|f| +0#0000000&@21 +@2|s+0#00e0e07&|t|r|i|n|g|;+0#0000000&| @38|s+0#00e0e07&|t|r|i|n|g| +0#0000000&@20 +@2|s+0#00e0e07&|t|r|i|n|g|f|;+0#0000000&| @37|s+0#00e0e07&|t|r|i|n|g|f| +0#0000000&@19 +@2|r+0#00e0e07&|e|a|d| |l|i|n|e|;+0#0000000&| @35|r+0#00e0e07&|e|a|d|l|i|n|e| +0#0000000&@18 +|#+0#0000e05&| |S|e|t| |u|p| |s|t|a|n|d|e|n|v| |-| |e|x|t|e|n|s|i|o|n|s|.| |#| +0#0000000&@41 +|#+0#0000e05&| |U|N|I|X| |t|h|i|n|g|s|.| |#| +0#0000000&@58 +@2|r+0#00e0e07&|o|w|s|;+0#0000000&| @40|r+0#00e0e07&|o|w|s| +0#0000000&@22 +@2|c+0#00e0e07&|o|l|u|m|n|s|;+0#0000000&| @37|c+0#00e0e07&|o|l|u|m|n|s| +0#0000000&@19 +@2|a+0#00e0e07&|r|g|c|;+0#0000000&| @40|a+0#00e0e07&|r|g|c| +0#0000000&@22 +@2|a+0#00e0e07&|6|8|g| |a|r|g|c|;+0#0000000&| @35|a+0#00e0e07&|6|8|g|a|r|g|c| +0#0000000&@18 +@2|e+0#00e0e07&|r@1|n|o|;+0#0000000&| @39|e+0#00e0e07&|r@1|n|o| +0#0000000&@21 +@2|f+0#00e0e07&|o|r|k|;+0#0000000&| @40|f+0#00e0e07&|o|r|k| +0#0000000&@22 +@2|g+0#00e0e07&|e|t| |p|w|d|;+0#0000000&| @37|g+0#00e0e07&|e|t|p|w|d| +0#0000000&@20 +@2|s+0#00e0e07&|e|t| |p|w|d|;+0#0000000&| @37|s+0#00e0e07&|e|t|p|w|d| +0#0000000&@20 +@57|1|3@1|4|,|3| @7|6|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_77.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_77.dump new file mode 100644 index 0000000000..fc259bb7d4 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_77.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|s+0#00e0e07&|e|t| |p|w|d|;+0#0000000&| @37|s+0#00e0e07&|e|t|p|w|d| +0#0000000&@20 +@2|f+0#00e0e07&|i|l|e| |i|s| |d|i|r|e|c|t|o|r|y|;+0#0000000&| @27|f+0#00e0e07&|i|l|e|i|s|d|i|r|e|c|t|o|r|y| +0#0000000&@11 +@2|f+0#00e0e07&|i|l|e| |i|s| |b|l|o|c|k| |d|e|v|i|c|e|;+0#0000000&| @24|f+0#00e0e07&|i|l|e|i|s|b|l|o|c|k|d|e|v|i|c|e| +0#0000000&@9 +@2|f+0#00e0e07&|i|l|e| |i|s| |c|h|a|r| |d|e|v|i|c|e|;+0#0000000&| @25|f+0#00e0e07&|i|l|e|i|s|c|h|a|r|d|e|v|i|c|e| +0#0000000&@10 +@2|f+0#00e0e07&|i|l|e| |i|s| |r|e|g|u|l|a|r|;+0#0000000&| @29|f+0#00e0e07&|i|l|e|i|s|r|e|g|u|l|a|r| +0#0000000&@13 +@2>f+0#00e0e07&|i|l|e| |i|s| |f|i|f|o|;+0#0000000&| @32|f+0#00e0e07&|i|l|e|i|s|f|i|f|o| +0#0000000&@16 +@2|f+0#00e0e07&|i|l|e| |i|s| |l|i|n|k|;+0#0000000&| @32|f+0#00e0e07&|i|l|e|i|s|l|i|n|k| +0#0000000&@16 +@2|f+0#00e0e07&|i|l|e| |m|o|d|e|;+0#0000000&| @35|f+0#00e0e07&|i|l|e|m|o|d|e| +0#0000000&@18 +@2|a+0#00e0e07&|r|g|v|;+0#0000000&| @40|a+0#00e0e07&|r|g|v| +0#0000000&@22 +@2|a+0#00e0e07&|6|8|g| |a|r|g|v|;+0#0000000&| @35|a+0#00e0e07&|6|8|g|a|r|g|v| +0#0000000&@18 +@2|r+0#00e0e07&|e|s|e|t| |e|r@1|n|o|;+0#0000000&| @33|r+0#00e0e07&|e|s|e|t|e|r@1|n|o| +0#0000000&@16 +@2|s+0#00e0e07&|t|r| |e|r@1|o|r|;+0#0000000&| @35|s+0#00e0e07&|t|r|e|r@1|o|r| +0#0000000&@18 +@2|e+0#00e0e07&|x|e|c|;+0#0000000&| @40|e+0#00e0e07&|x|e|c| +0#0000000&@22 +@2|e+0#00e0e07&|x|e|c|v|e|;+0#0000000&| @38|e+0#00e0e07&|x|e|c|v|e| +0#0000000&@20 +@2|c+0#00e0e07&|r|e|a|t|e| |p|i|p|e|;+0#0000000&| @33|c+0#00e0e07&|r|e|a|t|e|p|i|p|e| +0#0000000&@16 +@2|e+0#00e0e07&|x|e|c| |s|u|b|;+0#0000000&| @36|e+0#00e0e07&|x|e|c|s|u|b| +0#0000000&@19 +@2|e+0#00e0e07&|x|e|c|v|e| |c|h|i|l|d|;+0#0000000&| @32|e+0#00e0e07&|x|e|c|v|e|c|h|i|l|d| +0#0000000&@15 +@2|e+0#00e0e07&|x|e|c| |s|u|b| |p|i|p|e|l|i|n|e|;+0#0000000&| @27|e+0#00e0e07&|x|e|c|s|u|b|p|i|p|e|l|i|n|e| +0#0000000&@11 +@2|e+0#00e0e07&|x|e|c|v|e| |c|h|i|l|d|p|i|p|e|;+0#0000000&| @28|e+0#00e0e07&|x|e|c|v|e|c|h|i|l|d|p|i|p|e| +0#0000000&@11 +@57|1|3|5|2|,|3| @7|6|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_78.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_78.dump new file mode 100644 index 0000000000..bdc40e0d6b --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_78.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|e+0#00e0e07&|x|e|c|v|e| |c|h|i|l|d|p|i|p|e|;+0#0000000&| @28|e+0#00e0e07&|x|e|c|v|e|c|h|i|l|d|p|i|p|e| +0#0000000&@11 +@2|e+0#00e0e07&|x|e|c| |s|u|b| |o|u|t|p|u|t|;+0#0000000&| @29|e+0#00e0e07&|x|e|c|s|u|b|o|u|t|p|u|t| +0#0000000&@13 +@2|e+0#00e0e07&|x|e|c|v|e| |o|u|t|p|u|t|;+0#0000000&| @31|e+0#00e0e07&|x|e|c|v|e|o|u|t|p|u|t| +0#0000000&@14 +@2|g+0#00e0e07&|e|t| |e|n|v|;+0#0000000&| @37|g+0#00e0e07&|e|t|e|n|v| +0#0000000&@20 +@2|w+0#00e0e07&|a|i|t| |p|i|d|;+0#0000000&| @36|w+0#00e0e07&|a|i|t|p|i|d| +0#0000000&@19 +@2>u+0#00e0e07&|t|c| |t|i|m|e|;+0#0000000&| @36|u+0#00e0e07&|t|c|t|i|m|e| +0#0000000&@19 +@2|l+0#00e0e07&|o|c|a|l| |t|i|m|e|;+0#0000000&| @34|l+0#00e0e07&|o|c|a|l|t|i|m|e| +0#0000000&@17 +@2|g+0#00e0e07&|r|e|p| |i|n| |s|t|r|i|n|g|;+0#0000000&| @30|g+0#00e0e07&|r|e|p|i|n|s|t|r|i|n|g| +0#0000000&@14 +@2|g+0#00e0e07&|r|e|p| |i|n| |s|u|b|s|t|r|i|n|g|;+0#0000000&| @27|g+0#00e0e07&|r|e|p|i|n|s|u|b|s|t|r|i|n|g| +0#0000000&@11 +@2|s+0#00e0e07&|u|b| |i|n| |s|t|r|i|n|g|;+0#0000000&| @31|s+0#00e0e07&|u|b|i|n|s|t|r|i|n|g| +0#0000000&@15 +@2|p+0#00e0e07&|e@1|k| |c|h|a|r|;+0#0000000&| @35|p+0#00e0e07&|e@1|k|c|h|a|r| +0#0000000&@18 +@2|g+0#00e0e07&|e|t| |d|i|r|e|c|t|o|r|y|;+0#0000000&| @31|g+0#00e0e07&|e|t|d|i|r|e|c|t|o|r|y| +0#0000000&@14 +@2|h+0#00e0e07&|t@1|p| |t|i|m|e|o|u|t|;+0#0000000&| @32|h+0#00e0e07&|t@1|p|t|i|m|e|o|u|t| +0#0000000&@15 +@2|h+0#00e0e07&|t@1|p|s| |t|i|m|e|o|u|t|;+0#0000000&| @31|h+0#00e0e07&|t@1|p|s|t|i|m|e|o|u|t| +0#0000000&@14 +@2|h+0#00e0e07&|t@1|p| |c|o|n|t|e|n|t|;+0#0000000&| @32|h+0#00e0e07&|t@1|p|c|o|n|t|e|n|t| +0#0000000&@15 +@2|h+0#00e0e07&|t@1|p|s| |c|o|n|t|e|n|t|;+0#0000000&| @31|h+0#00e0e07&|t@1|p|s|c|o|n|t|e|n|t| +0#0000000&@14 +|#+0#0000e05&| |D|r|a|w|i|n|g|.| |#| +0#0000000&@62 +@2|d+0#00e0e07&|r|a|w| |d|e|v|i|c|e|;+0#0000000&| @33|d+0#00e0e07&|r|a|w|d|e|v|i|c|e| +0#0000000&@16 +@2|m+0#00e0e07&|a|k|e| |d|e|v|i|c|e|;+0#0000000&| @33|m+0#00e0e07&|a|k|e|d|e|v|i|c|e| +0#0000000&@16 +@57|1|3|7|0|,|3| @7|6|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_79.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_79.dump new file mode 100644 index 0000000000..cac7f58c5f --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_79.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|m+0#00e0e07&|a|k|e| |d|e|v|i|c|e|;+0#0000000&| @33|m+0#00e0e07&|a|k|e|d|e|v|i|c|e| +0#0000000&@16 +@2|d+0#00e0e07&|r|a|w| |a|s|p|e|c|t|;+0#0000000&| @33|d+0#00e0e07&|r|a|w|a|s|p|e|c|t| +0#0000000&@16 +@2|d+0#00e0e07&|r|a|w| |c|l|e|a|r|;+0#0000000&| @34|d+0#00e0e07&|r|a|w|c|l|e|a|r| +0#0000000&@17 +@2|d+0#00e0e07&|r|a|w| |e|r|a|s|e|;+0#0000000&| @34|d+0#00e0e07&|r|a|w|e|r|a|s|e| +0#0000000&@17 +@2|d+0#00e0e07&|r|a|w| |f|l|u|s|h|;+0#0000000&| @34|d+0#00e0e07&|r|a|w|f|l|u|s|h| +0#0000000&@17 +@2>d+0#00e0e07&|r|a|w| |s|h|o|w|;+0#0000000&| @35|d+0#00e0e07&|r|a|w|s|h|o|w| +0#0000000&@18 +@2|d+0#00e0e07&|r|a|w| |f|i|l@1| |s|t|y|l|e|;+0#0000000&| @29|d+0#00e0e07&|r|a|w|f|i|l@1|s|t|y|l|e| +0#0000000&@13 +@2|d+0#00e0e07&|r|a|w| |g|e|t| |c|o|l|o|u|r| |n|a|m|e|;+0#0000000&| @24|d+0#00e0e07&|r|a|w|g|e|t|c|o|l|o|u|r|n|a|m|e| +0#0000000&@9 +@2|d+0#00e0e07&|r|a|w| |g|e|t| |c|o|l|o|r| |n|a|m|e|;+0#0000000&| @25|d+0#00e0e07&|r|a|w|g|e|t|c|o|l|o|r|n|a|m|e| +0#0000000&@10 +@2|d+0#00e0e07&|r|a|w| |c|o|l|o|r|;+0#0000000&| @34|d+0#00e0e07&|r|a|w|c|o|l|o|r| +0#0000000&@17 +@2|d+0#00e0e07&|r|a|w| |c|o|l|o|u|r|;+0#0000000&| @33|d+0#00e0e07&|r|a|w|c|o|l|o|u|r| +0#0000000&@16 +@2|d+0#00e0e07&|r|a|w| |b|a|c|k|g|r|o|u|n|d| |c|o|l|o|r|;+0#0000000&| @23|d+0#00e0e07&|r|a|w|b|a|c|k|g|r|o|u|n|d|c|o|l|o|r| +0#0000000&@7 +@2|d+0#00e0e07&|r|a|w| |b|a|c|k|g|r|o|u|n|d| |c|o|l|o|u|r|;+0#0000000&| @22|d+0#00e0e07&|r|a|w|b|a|c|k|g|r|o|u|n|d|c|o|l|o|u|r| +0#0000000&@6 +@2|d+0#00e0e07&|r|a|w| |c|i|r|c|l|e|;+0#0000000&| @33|d+0#00e0e07&|r|a|w|c|i|r|c|l|e| +0#0000000&@16 +@2|d+0#00e0e07&|r|a|w| |b|a|l@1|;+0#0000000&| @35|d+0#00e0e07&|r|a|w|b|a|l@1| +0#0000000&@18 +@2|d+0#00e0e07&|r|a|w| |s|t|a|r|;+0#0000000&| @35|d+0#00e0e07&|r|a|w|s|t|a|r| +0#0000000&@18 +@2|d+0#00e0e07&|r|a|w| |p|o|i|n|t|;+0#0000000&| @34|d+0#00e0e07&|r|a|w|p|o|i|n|t| +0#0000000&@17 +@2|d+0#00e0e07&|r|a|w| |l|i|n|e|;+0#0000000&| @35|d+0#00e0e07&|r|a|w|l|i|n|e| +0#0000000&@18 +@2|d+0#00e0e07&|r|a|w| |m|o|v|e|;+0#0000000&| @35|d+0#00e0e07&|r|a|w|m|o|v|e| +0#0000000&@18 +@57|1|3|8@1|,|3| @7|6|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_80.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_80.dump new file mode 100644 index 0000000000..89b4275e3d --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_80.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|d+0#00e0e07&|r|a|w| |m|o|v|e|;+0#0000000&| @35|d+0#00e0e07&|r|a|w|m|o|v|e| +0#0000000&@18 +@2|d+0#00e0e07&|r|a|w| |r|e|c|t|;+0#0000000&| @35|d+0#00e0e07&|r|a|w|r|e|c|t| +0#0000000&@18 +@2|d+0#00e0e07&|r|a|w| |t|e|x|t|;+0#0000000&| @35|d+0#00e0e07&|r|a|w|t|e|x|t| +0#0000000&@18 +@2|d+0#00e0e07&|r|a|w| |l|i|n|e|s|t|y|l|e|;+0#0000000&| @30|d+0#00e0e07&|r|a|w|l|i|n|e|s|t|y|l|e| +0#0000000&@13 +@2|d+0#00e0e07&|r|a|w| |f|o|n|t| |n|a|m|e|;+0#0000000&| @30|d+0#00e0e07&|r|a|w|f|o|n|t|n|a|m|e| +0#0000000&@14 +@2>d+0#00e0e07&|r|a|w| |l|i|n|e|w|i|d|t|h|;+0#0000000&| @30|d+0#00e0e07&|r|a|w|l|i|n|e|w|i|d|t|h| +0#0000000&@13 +@2|d+0#00e0e07&|r|a|w| |f|o|n|t| |s|i|z|e|;+0#0000000&| @30|d+0#00e0e07&|r|a|w|f|o|n|t|s|i|z|e| +0#0000000&@14 +@2|d+0#00e0e07&|r|a|w| |t|e|x|t| |a|n|g|l|e|;+0#0000000&| @29|d+0#00e0e07&|r|a|w|t|e|x|t|a|n|g|l|e| +0#0000000&@13 +@2|d+0#00e0e07&|r|a|w| |c|o|l|o|r| |n|a|m|e|;+0#0000000&| @29|d+0#00e0e07&|r|a|w|c|o|l|o|r|n|a|m|e| +0#0000000&@13 +@2|d+0#00e0e07&|r|a|w| |c|o|l|o|u|r| |n|a|m|e|;+0#0000000&| @28|d+0#00e0e07&|r|a|w|c|o|l|o|u|r|n|a|m|e| +0#0000000&@12 +@2|d+0#00e0e07&|r|a|w| |b|a|c|k|g|r|o|u|n|d| |c|o|l|o|r| |n|a|m|e|;+0#0000000&| @18|d+0#00e0e07&|r|a|w|b|a|c|k|g|r|o|u|n|d|c|o|l|o|r|n|a|m|e| +0#0000000&@3 +@2|d+0#00e0e07&|r|a|w| |b|a|c|k|g|r|o|u|n|d| |c|o|l|o|u|r| |n|a|m|e|;+0#0000000&| @17|d+0#00e0e07&|r|a|w|b|a|c|k|g|r|o|u|n|d|c|o|l|o|u|r|n|a|m|e| +0#0000000&@2 +@2|c+0#00e0e07&|u|r|s|e|s| |s|t|a|r|t|;+0#0000000&| @32|c+0#00e0e07&|u|r|s|e|s@1|t|a|r|t| +0#0000000&@15 +@2|c+0#00e0e07&|u|r|s|e|s| |e|n|d|;+0#0000000&| @34|c+0#00e0e07&|u|r|s|e|s|e|n|d| +0#0000000&@17 +@2|c+0#00e0e07&|u|r|s|e|s| |c|l|e|a|r|;+0#0000000&| @32|c+0#00e0e07&|u|r|s|e|s|c|l|e|a|r| +0#0000000&@15 +@2|c+0#00e0e07&|u|r|s|e|s| |r|e|f|r|e|s|h|;+0#0000000&| @30|c+0#00e0e07&|u|r|s|e|s|r|e|f|r|e|s|h| +0#0000000&@13 +@2|c+0#00e0e07&|u|r|s|e|s| |g|r|e@1|n|;+0#0000000&| @32|c+0#00e0e07&|u|r|s|e|s|g|r|e@1|n| +0#0000000&@15 +@2|c+0#00e0e07&|u|r|s|e|s| |c|y|a|n|;+0#0000000&| @33|c+0#00e0e07&|u|r|s|e|s|c|y|a|n| +0#0000000&@16 +@2|c+0#00e0e07&|u|r|s|e|s| |r|e|d|;+0#0000000&| @34|c+0#00e0e07&|u|r|s|e|s|r|e|d| +0#0000000&@17 +@57|1|4|0|6|,|3| @7|6@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_81.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_81.dump new file mode 100644 index 0000000000..516b1d0918 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_81.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|u|r|s|e|s| |r|e|d|;+0#0000000&| @34|c+0#00e0e07&|u|r|s|e|s|r|e|d| +0#0000000&@17 +@2|c+0#00e0e07&|u|r|s|e|s| |y|e|l@1|o|w|;+0#0000000&| @31|c+0#00e0e07&|u|r|s|e|s|y|e|l@1|o|w| +0#0000000&@14 +@2|c+0#00e0e07&|u|r|s|e|s| |m|a|g|e|n|t|a|;+0#0000000&| @30|c+0#00e0e07&|u|r|s|e|s|m|a|g|e|n|t|a| +0#0000000&@13 +@2|c+0#00e0e07&|u|r|s|e|s| |b|l|u|e|;+0#0000000&| @33|c+0#00e0e07&|u|r|s|e|s|b|l|u|e| +0#0000000&@16 +@2|c+0#00e0e07&|u|r|s|e|s| |w|h|i|t|e|;+0#0000000&| @32|c+0#00e0e07&|u|r|s|e|s|w|h|i|t|e| +0#0000000&@15 +@2>c+0#00e0e07&|u|r|s|e|s| |g|r|e@1|n| |i|n|v|e|r|s|e|;+0#0000000&| @24|c+0#00e0e07&|u|r|s|e|s|g|r|e@1|n|i|n|v|e|r|s|e| +0#0000000&@8 +@2|c+0#00e0e07&|u|r|s|e|s| |c|y|a|n| |i|n|v|e|r|s|e|;+0#0000000&| @25|c+0#00e0e07&|u|r|s|e|s|c|y|a|n|i|n|v|e|r|s|e| +0#0000000&@9 +@2|c+0#00e0e07&|u|r|s|e|s| |r|e|d| |i|n|v|e|r|s|e|;+0#0000000&| @26|c+0#00e0e07&|u|r|s|e|s|r|e|d|i|n|v|e|r|s|e| +0#0000000&@10 +@2|c+0#00e0e07&|u|r|s|e|s| |y|e|l@1|o|w| |i|n|v|e|r|s|e|;+0#0000000&| @23|c+0#00e0e07&|u|r|s|e|s|y|e|l@1|o|w|i|n|v|e|r|s|e| +0#0000000&@7 +@2|c+0#00e0e07&|u|r|s|e|s| |m|a|g|e|n|t|a| |i|n|v|e|r|s|e|;+0#0000000&| @22|c+0#00e0e07&|u|r|s|e|s|m|a|g|e|n|t|a|i|n|v|e|r|s|e| +0#0000000&@6 +@2|c+0#00e0e07&|u|r|s|e|s| |b|l|u|e| |i|n|v|e|r|s|e|;+0#0000000&| @25|c+0#00e0e07&|u|r|s|e|s|b|l|u|e|i|n|v|e|r|s|e| +0#0000000&@9 +@2|c+0#00e0e07&|u|r|s|e|s| |w|h|i|t|e| |i|n|v|e|r|s|e|;+0#0000000&| @24|c+0#00e0e07&|u|r|s|e|s|w|h|i|t|e|i|n|v|e|r|s|e| +0#0000000&@8 +@2|c+0#00e0e07&|u|r|s|e|s| |g|e|t| |c|h|a|r|;+0#0000000&| @29|c+0#00e0e07&|u|r|s|e|s|g|e|t|c|h|a|r| +0#0000000&@13 +@2|c+0#00e0e07&|u|r|s|e|s| |p|u|t| |c|h|a|r|;+0#0000000&| @29|c+0#00e0e07&|u|r|s|e|s|p|u|t|c|h|a|r| +0#0000000&@13 +@2|c+0#00e0e07&|u|r|s|e|s| |m|o|v|e|;+0#0000000&| @33|c+0#00e0e07&|u|r|s|e|s|m|o|v|e| +0#0000000&@16 +@2|c+0#00e0e07&|u|r|s|e|s| |l|i|n|e|s|;+0#0000000&| @32|c+0#00e0e07&|u|r|s|e|s|l|i|n|e|s| +0#0000000&@15 +@2|c+0#00e0e07&|u|r|s|e|s| |c|o|l|u|m|n|s|;+0#0000000&| @30|c+0#00e0e07&|u|r|s|e|s|c|o|l|u|m|n|s| +0#0000000&@13 +@2|c+0#00e0e07&|u|r|s|e|s| |d|e|l|c|h|a|r|;+0#0000000&| @30|c+0#00e0e07&|u|r|s|e|s|d|e|l|c|h|a|r| +0#0000000&@13 +@2|p+0#00e0e07&|q| |c|o|n@1|e|c|t| |d|b|;+0#0000000&| @31|p+0#00e0e07&|q|c|o|n@1|e|c|t|d|b| +0#0000000&@15 +@57|1|4|2|4|,|3| @7|6|7|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_82.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_82.dump new file mode 100644 index 0000000000..edb3a75698 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_82.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|p+0#00e0e07&|q| |c|o|n@1|e|c|t| |d|b|;+0#0000000&| @31|p+0#00e0e07&|q|c|o|n@1|e|c|t|d|b| +0#0000000&@15 +@2|p+0#00e0e07&|q| |f|i|n|i|s|h|;+0#0000000&| @35|p+0#00e0e07&|q|f|i|n|i|s|h| +0#0000000&@18 +@2|p+0#00e0e07&|q| |r|e|s|e|t|;+0#0000000&| @36|p+0#00e0e07&|q|r|e|s|e|t| +0#0000000&@19 +@2|p+0#00e0e07&|q| |p|a|r|a|m|e|t|e|r| |s|t|a|t|u|s|;+0#0000000&| @25|p+0#00e0e07&|q|p|a|r|a|m|e|t|e|r|s|t|a|t|u|s| +0#0000000&@9 +@2|p+0#00e0e07&|q| |e|x|e|c|;+0#0000000&| @37|p+0#00e0e07&|q|e|x|e|c| +0#0000000&@20 +@2>p+0#00e0e07&|q| |f|n|u|m|b|e|r|;+0#0000000&| @34|p+0#00e0e07&|q|f|n|u|m|b|e|r| +0#0000000&@17 +@2|p+0#00e0e07&|q| |n|t|u|p|l|e|s|;+0#0000000&| @34|p+0#00e0e07&|q|n|t|u|p|l|e|s| +0#0000000&@17 +@2|p+0#00e0e07&|q| |n|f|i|e|l|d|s|;+0#0000000&| @34|p+0#00e0e07&|q|n|f|i|e|l|d|s| +0#0000000&@17 +@2|p+0#00e0e07&|q| |c|m|d| |s|t|a|t|u|s|;+0#0000000&| @31|p+0#00e0e07&|q|c|m|d|s|t|a|t|u|s| +0#0000000&@15 +@2|p+0#00e0e07&|q| |c|m|d| |t|u|p|l|e|s|;+0#0000000&| @31|p+0#00e0e07&|q|c|m|d|t|u|p|l|e|s| +0#0000000&@15 +@2|p+0#00e0e07&|q| |e|r@1|o|r| |m|e|s@1|a|g|e|;+0#0000000&| @28|p+0#00e0e07&|q|e|r@1|o|r|m|e|s@1|a|g|e| +0#0000000&@12 +@2|p+0#00e0e07&|q| |r|e|s|u|l|t| |e|r@1|o|r| |m|e|s@1|a|g|e|;+0#0000000&| @21|p+0#00e0e07&|q|r|e|s|u|l|t|e|r@1|o|r|m|e|s@1|a|g|e| +0#0000000&@6 +@2|p+0#00e0e07&|q| |d|b|;+0#0000000&| @39|p+0#00e0e07&|q|d|b| +0#0000000&@22 +@2|p+0#00e0e07&|q| |u|s|e|r|;+0#0000000&| @37|p+0#00e0e07&|q|u|s|e|r| +0#0000000&@20 +@2|p+0#00e0e07&|q| |p|a|s@1|;+0#0000000&| @37|p+0#00e0e07&|q|p|a|s@1| +0#0000000&@20 +@2|p+0#00e0e07&|q| |h|o|s|t|;+0#0000000&| @37|p+0#00e0e07&|q|h|o|s|t| +0#0000000&@20 +@2|p+0#00e0e07&|q| |p|o|r|t|;+0#0000000&| @37|p+0#00e0e07&|q|p|o|r|t| +0#0000000&@20 +@2|p+0#00e0e07&|q| |t@1|y|;+0#0000000&| @38|p+0#00e0e07&|q|t@1|y| +0#0000000&@21 +@2|p+0#00e0e07&|q| |o|p|t|i|o|n|s|;+0#0000000&| @34|p+0#00e0e07&|q|o|p|t|i|o|n|s| +0#0000000&@17 +@57|1|4@1|2|,|3| @7|6|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_83.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_83.dump new file mode 100644 index 0000000000..06a82b7aa0 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_83.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|p+0#00e0e07&|q| |o|p|t|i|o|n|s|;+0#0000000&| @34|p+0#00e0e07&|q|o|p|t|i|o|n|s| +0#0000000&@17 +@2|p+0#00e0e07&|q| |p|r|o|t|o|c|o|l| |v|e|r|s|i|o|n|;+0#0000000&| @25|p+0#00e0e07&|q|p|r|o|t|o|c|o|l|v|e|r|s|i|o|n| +0#0000000&@9 +@2|p+0#00e0e07&|q| |s|e|r|v|e|r| |v|e|r|s|i|o|n|;+0#0000000&| @27|p+0#00e0e07&|q|s|e|r|v|e|r|v|e|r|s|i|o|n| +0#0000000&@11 +@2|p+0#00e0e07&|q| |s|o|c|k|e|t|;+0#0000000&| @35|p+0#00e0e07&|q|s|o|c|k|e|t| +0#0000000&@18 +@2|p+0#00e0e07&|q| |b|a|c|k|e|n|d| |p|i|d|;+0#0000000&| @30|p+0#00e0e07&|q|b|a|c|k|e|n|d|p|i|d| +0#0000000&@14 +@2>p+0#00e0e07&|q| |f|n|a|m|e|;+0#0000000&| @36|p+0#00e0e07&|q|f|n|a|m|e| +0#0000000&@19 +@2|p+0#00e0e07&|q| |f@1|o|r|m|a|t|;+0#0000000&| @34|p+0#00e0e07&|q|f@1|o|r|m|a|t| +0#0000000&@17 +@2|p+0#00e0e07&|q| |g|e|t| |v|a|l|u|e|;+0#0000000&| @32|p+0#00e0e07&|q|g|e|t|v|a|l|u|e| +0#0000000&@16 +@2|p+0#00e0e07&|q| |g|e|t| |i|s| |n|u|l@1|;+0#0000000&| @30|p+0#00e0e07&|q|g|e|t|i|s|n|u|l@1| +0#0000000&@15 +@2|s+0#00e0e07&|i|g| |s|e|g|v|;+0#0000000&| @36|s+0#00e0e07&|i|g|s|e|g|v| +0#0000000&@19 +@75 +|#+0#0000e05&|:@12| +0#0000000&@60 +|p+0#0000e05&|r|e|l|u|d|e|-|b|i|t|s|.|c| +0#0000000&@60 +|:+0#0000e05&@12|#| +0#0000000&@60 +@75 +|#+0#0000e05&| |M|u|l|t|i|p|l|e| |p|r|e|c|i|s|i|o|n| |B|I|T|S|.| |#| +0#0000000&@46 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |b|i|t|s| |w|i|d|t|h|;+0#0000000&| @24|l+0#00e0e07&|o|n|g|l|o|n|g|b|i|t|s|w|i|d|t|h| +0#0000000&@9 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |m|a|x| |b|i|t|s|;+0#0000000&| @26|l+0#00e0e07&|o|n|g|l|o|n|g|m|a|x|b|i|t|s| +0#0000000&@11 +@2|l+0#00e0e07&|o|n|g| |l|o|n|g| |b|i|t|s| |p|a|c|k|;+0#0000000&| @25|l+0#00e0e07&|o|n|g|l|o|n|g|b|i|t|s|p|a|c|k| +0#0000000&@10 +@57|1|4|6|0|,|3| @7|6|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_84.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_84.dump new file mode 100644 index 0000000000..04bf46954e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_84.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|o|n|g| |l|o|n|g| |b|i|t|s| |p|a|c|k|;+0#0000000&| @25|l+0#00e0e07&|o|n|g|l|o|n|g|b|i|t|s|p|a|c|k| +0#0000000&@10 +@2|L+0#af5f00255&|E|N|G| +0#0000000&@41|L+0#af5f00255&|E|N|G| +0#0000000&@22 +@2|A+0#af5f00255&|B|S| +0#0000000&@42|A+0#af5f00255&|B|S| +0#0000000&@23 +@2|B+0#af5f00255&|I|N| +0#0000000&@42|B+0#af5f00255&|I|N| +0#0000000&@23 +@2|N+0#af5f00255&|O|T| +0#0000000&@42|N+0#af5f00255&|O|T| +0#0000000&@23 +@2>~+0#af5f00255&| +0#0000000&@44|~+0#af5f00255&| +0#0000000&@25 +@2|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@38|S+0#af5f00255&|H|O|R|T|E|N| +0#0000000&@19 +@2|=+0#af5f00255&| +0#0000000&@44|=+0#af5f00255&| +0#0000000&@25 +@2|E+0#af5f00255&|Q| +0#0000000&@43|E+0#af5f00255&|Q| +0#0000000&@24 +@2|/+0#af5f00255&|=| +0#0000000&@43|/+0#af5f00255&|=| +0#0000000&@24 +@2|~+0#af5f00255&|=| +0#0000000&@43|~+0#af5f00255&|=| +0#0000000&@24 +@2|^+0#af5f00255&|=| +0#0000000&@43|^+0#af5f00255&|=| +0#0000000&@24 +@2|N+0#af5f00255&|E| +0#0000000&@43|N+0#af5f00255&|E| +0#0000000&@24 +@2|<+0#af5f00255&|=| +0#0000000&@43|<+0#af5f00255&|=| +0#0000000&@24 +@2|L+0#af5f00255&|E| +0#0000000&@43|L+0#af5f00255&|E| +0#0000000&@24 +@2|>+0#af5f00255&|=| +0#0000000&@43|>+0#af5f00255&|=| +0#0000000&@24 +@2|G+0#af5f00255&|E| +0#0000000&@43|G+0#af5f00255&|E| +0#0000000&@24 +@2|A+0#af5f00255&|N|D| +0#0000000&@42|A+0#af5f00255&|N|D| +0#0000000&@23 +@2|&+0#af5f00255&| +0#0000000&@44|&+0#af5f00255&| +0#0000000&@25 +@57|1|4|7|8|,|3| @7|7|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_85.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_85.dump new file mode 100644 index 0000000000..def0046f40 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_85.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|&+0#af5f00255&| +0#0000000&@44|&+0#af5f00255&| +0#0000000&@25 +@2|O+0#af5f00255&|R| +0#0000000&@43|O+0#af5f00255&|R| +0#0000000&@24 +@2|X+0#af5f00255&|O|R| +0#0000000&@42|X+0#af5f00255&|O|R| +0#0000000&@23 +@2|S+0#af5f00255&|H|L| +0#0000000&@42|S+0#af5f00255&|H|L| +0#0000000&@23 +@2|U+0#af5f00255&|P| +0#0000000&@43|U+0#af5f00255&|P| +0#0000000&@24 +@2>S+0#af5f00255&|H|R| +0#0000000&@42|S+0#af5f00255&|H|R| +0#0000000&@23 +@2|D+0#af5f00255&|O|W|N| +0#0000000&@41|D+0#af5f00255&|O|W|N| +0#0000000&@22 +@2|E+0#af5f00255&|L|E|M| +0#0000000&@41|E+0#af5f00255&|L|E|M| +0#0000000&@22 +@2|S+0#af5f00255&|E|T| +0#0000000&@42|S+0#af5f00255&|E|T| +0#0000000&@23 +@2|C+0#af5f00255&|L|E|A|R| +0#0000000&@40|C+0#af5f00255&|L|E|A|R| +0#0000000&@21 +@75 +|#+0#0000e05&|:@12| +0#0000000&@60 +|p+0#0000e05&|r|e|l|u|d|e|-|m|a|t|h|l|i|b|.|c| +0#0000000&@57 +|:+0#0000e05&@12|#| +0#0000000&@60 +@75 +|#+0#0000e05&| |R| |s|p|e|c|i|f|i|c| |s|p|e|c|i|a|l| |f|u|n|c|t|i|o|n|s| |#| +0#0000000&@42 +@2|r+0#00e0e07&| |d|i| |g|a|m@1|a|;+0#0000000&| @34|r+0#00e0e07&|d|i|g|a|m@1|a| +0#0000000&@18 +@2|r+0#00e0e07&| |t|r|i| |g|a|m@1|a|;+0#0000000&| @33|r+0#00e0e07&|t|r|i|g|a|m@1|a| +0#0000000&@17 +@2|r+0#00e0e07&| |t|e|t|r|a| |g|a|m@1|a|;+0#0000000&| @31|r+0#00e0e07&|t|e|t|r|a|g|a|m@1|a| +0#0000000&@15 +@57|1|4|9|6|,|3| @7|7|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_86.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_86.dump new file mode 100644 index 0000000000..183aeb41af --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_86.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|r+0#00e0e07&| |t|e|t|r|a| |g|a|m@1|a|;+0#0000000&| @31|r+0#00e0e07&|t|e|t|r|a|g|a|m@1|a| +0#0000000&@15 +@2|r+0#00e0e07&| |p|e|n|t|a| |g|a|m@1|a|;+0#0000000&| @31|r+0#00e0e07&|p|e|n|t|a|g|a|m@1|a| +0#0000000&@15 +@2|r+0#00e0e07&| |p|s|i| |g|a|m@1|a|;+0#0000000&| @33|r+0#00e0e07&|p|s|i|g|a|m@1|a| +0#0000000&@17 +|#+0#0000e05&| |R| |d|i|s|t|r|i|b|u|t|i|o|n| |r|e|l|a|t|e|d| |f|u|n|c|t|i|o|n|s| |#| +0#0000000&@38 +@2|r+0#00e0e07&| |r| |t|;+0#0000000&| @39|r+0#00e0e07&@1|t| +0#0000000&@23 +@2>r+0#00e0e07&| |r| |c|h|i|s|q|;+0#0000000&| @35|r+0#00e0e07&@1|c|h|i|s|q| +0#0000000&@19 +@2|r+0#00e0e07&| |r| |e|x|p|;+0#0000000&| @37|r+0#00e0e07&@1|e|x|p| +0#0000000&@21 +@2|r+0#00e0e07&| |r| |g|e|o|m|;+0#0000000&| @36|r+0#00e0e07&@1|g|e|o|m| +0#0000000&@20 +@2|r+0#00e0e07&| |r| |p|o|i|s|;+0#0000000&| @36|r+0#00e0e07&@1|p|o|i|s| +0#0000000&@20 +@2|r+0#00e0e07&| |r| |s|i|g|n|r|a|n|k|;+0#0000000&| @32|r+0#00e0e07&@1|s|i|g|n|r|a|n|k| +0#0000000&@16 +@2|r+0#00e0e07&| |r| |b|e|t|a|;+0#0000000&| @36|r+0#00e0e07&@1|b|e|t|a| +0#0000000&@20 +@2|r+0#00e0e07&| |r| |b|i|n|o|m|;+0#0000000&| @35|r+0#00e0e07&@1|b|i|n|o|m| +0#0000000&@19 +@2|r+0#00e0e07&| |r| |c|a|u|c|h|y|;+0#0000000&| @34|r+0#00e0e07&@1|c|a|u|c|h|y| +0#0000000&@18 +@2|r+0#00e0e07&| |r| |f|;+0#0000000&| @39|r+0#00e0e07&@1|f| +0#0000000&@23 +@2|r+0#00e0e07&| |r| |l|o|g|i|s|;+0#0000000&| @35|r+0#00e0e07&@1|l|o|g|i|s| +0#0000000&@19 +@2|r+0#00e0e07&| |r| |l|n|o|r|m|;+0#0000000&| @35|r+0#00e0e07&@1|l|n|o|r|m| +0#0000000&@19 +@2|r+0#00e0e07&| |r|n| |b|i|n|o|m|;+0#0000000&| @34|r+0#00e0e07&@1|n|b|i|n|o|m| +0#0000000&@18 +@2|r+0#00e0e07&| |r| |n|o|r|m|;+0#0000000&| @36|r+0#00e0e07&@1|n|o|r|m| +0#0000000&@20 +@2|r+0#00e0e07&| |r| |u|n|i|f|;+0#0000000&| @36|r+0#00e0e07&@1|u|n|i|f| +0#0000000&@20 +@57|1|5|1|4|,|3| @7|7|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_87.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_87.dump new file mode 100644 index 0000000000..f4b715057d --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_87.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|r+0#00e0e07&| |r| |u|n|i|f|;+0#0000000&| @36|r+0#00e0e07&@1|u|n|i|f| +0#0000000&@20 +@2|r+0#00e0e07&| |r| |w|e|i|b|u|l@1|;+0#0000000&| @33|r+0#00e0e07&@1|w|e|i|b|u|l@1| +0#0000000&@17 +@2|r+0#00e0e07&| |r| |w|i|l|c|o|x|;+0#0000000&| @34|r+0#00e0e07&@1|w|i|l|c|o|x| +0#0000000&@18 +@2|r+0#00e0e07&| |d| |t|;+0#0000000&| @39|r+0#00e0e07&|d|t| +0#0000000&@23 +@2|r+0#00e0e07&| |d| |c|h|i|s|q|;+0#0000000&| @35|r+0#00e0e07&|d|c|h|i|s|q| +0#0000000&@19 +@2>r+0#00e0e07&| |d| |e|x|p|;+0#0000000&| @37|r+0#00e0e07&|d|e|x|p| +0#0000000&@21 +@2|r+0#00e0e07&| |d| |g|e|o|m|;+0#0000000&| @36|r+0#00e0e07&|d|g|e|o|m| +0#0000000&@20 +@2|r+0#00e0e07&| |d| |p|o|i|s|;+0#0000000&| @36|r+0#00e0e07&|d|p|o|i|s| +0#0000000&@20 +@2|r+0#00e0e07&| |d| |n|o|r|m|;+0#0000000&| @36|r+0#00e0e07&|d|n|o|r|m| +0#0000000&@20 +@2|r+0#00e0e07&| |d| |b|e|t|a|;+0#0000000&| @36|r+0#00e0e07&|d|b|e|t|a| +0#0000000&@20 +@2|r+0#00e0e07&| |d| |b|i|n|o|m|;+0#0000000&| @35|r+0#00e0e07&|d|b|i|n|o|m| +0#0000000&@19 +@2|r+0#00e0e07&| |d|n| |c|h|i|s|q|;+0#0000000&| @34|r+0#00e0e07&|d|n|c|h|i|s|q| +0#0000000&@18 +@2|r+0#00e0e07&| |d| |c|a|u|c|h|y|;+0#0000000&| @34|r+0#00e0e07&|d|c|a|u|c|h|y| +0#0000000&@18 +@2|r+0#00e0e07&| |d| |f|;+0#0000000&| @39|r+0#00e0e07&|d|f| +0#0000000&@23 +@2|r+0#00e0e07&| |d| |l|o|g|i|s|;+0#0000000&| @35|r+0#00e0e07&|d|l|o|g|i|s| +0#0000000&@19 +@2|r+0#00e0e07&| |d| |l|n|o|r|m|;+0#0000000&| @35|r+0#00e0e07&|d|l|n|o|r|m| +0#0000000&@19 +@2|r+0#00e0e07&| |d|n| |b|i|n|o|m|;+0#0000000&| @34|r+0#00e0e07&|d|n|b|i|n|o|m| +0#0000000&@18 +@2|r+0#00e0e07&| |d| |n|t|;+0#0000000&| @38|r+0#00e0e07&|d|n|t| +0#0000000&@22 +@2|r+0#00e0e07&| |d| |u|n|i|f|;+0#0000000&| @36|r+0#00e0e07&|d|u|n|i|f| +0#0000000&@20 +@57|1|5|3|2|,|3| @7|7|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_88.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_88.dump new file mode 100644 index 0000000000..ef271843af --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_88.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|r+0#00e0e07&| |d| |u|n|i|f|;+0#0000000&| @36|r+0#00e0e07&|d|u|n|i|f| +0#0000000&@20 +@2|r+0#00e0e07&| |d| |w|e|i|b|u|l@1|;+0#0000000&| @33|r+0#00e0e07&|d|w|e|i|b|u|l@1| +0#0000000&@17 +@2|r+0#00e0e07&| |d| |n|f|;+0#0000000&| @38|r+0#00e0e07&|d|n|f| +0#0000000&@22 +@2|r+0#00e0e07&| |d| |h|y|p|e|r|;+0#0000000&| @35|r+0#00e0e07&|d|h|y|p|e|r| +0#0000000&@19 +@2|r+0#00e0e07&| |r| |h|y|p|e|r|;+0#0000000&| @35|r+0#00e0e07&@1|h|y|p|e|r| +0#0000000&@19 +@2>r+0#00e0e07&| |p| |t|;+0#0000000&| @39|r+0#00e0e07&|p|t| +0#0000000&@23 +@2|r+0#00e0e07&| |q| |t|;+0#0000000&| @39|r+0#00e0e07&|q|t| +0#0000000&@23 +@2|r+0#00e0e07&| |p| |c|h|i|s|q|;+0#0000000&| @35|r+0#00e0e07&|p|c|h|i|s|q| +0#0000000&@19 +@2|r+0#00e0e07&| |q| |c|h|i|s|q|;+0#0000000&| @35|r+0#00e0e07&|q|c|h|i|s|q| +0#0000000&@19 +@2|r+0#00e0e07&| |p| |e|x|p|;+0#0000000&| @37|r+0#00e0e07&|p|e|x|p| +0#0000000&@21 +@2|r+0#00e0e07&| |q| |e|x|p|;+0#0000000&| @37|r+0#00e0e07&|q|e|x|p| +0#0000000&@21 +@2|r+0#00e0e07&| |p| |g|e|o|m|;+0#0000000&| @36|r+0#00e0e07&|p|g|e|o|m| +0#0000000&@20 +@2|r+0#00e0e07&| |q| |g|e|o|m|;+0#0000000&| @36|r+0#00e0e07&|q|g|e|o|m| +0#0000000&@20 +@2|r+0#00e0e07&| |p| |p|o|i|s|;+0#0000000&| @36|r+0#00e0e07&|p@1|o|i|s| +0#0000000&@20 +@2|r+0#00e0e07&| |q| |p|o|i|s|;+0#0000000&| @36|r+0#00e0e07&|q|p|o|i|s| +0#0000000&@20 +@2|r+0#00e0e07&| |p| |n|o|r|m|;+0#0000000&| @36|r+0#00e0e07&|p|n|o|r|m| +0#0000000&@20 +@2|r+0#00e0e07&| |q| |n|o|r|m|;+0#0000000&| @36|r+0#00e0e07&|q|n|o|r|m| +0#0000000&@20 +@2|r+0#00e0e07&| |p| |b|e|t|a|;+0#0000000&| @36|r+0#00e0e07&|p|b|e|t|a| +0#0000000&@20 +@2|r+0#00e0e07&| |q| |b|e|t|a|;+0#0000000&| @36|r+0#00e0e07&|q|b|e|t|a| +0#0000000&@20 +@57|1|5@1|0|,|3| @7|7|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_89.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_89.dump new file mode 100644 index 0000000000..7da78a8d37 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_89.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|r+0#00e0e07&| |q| |b|e|t|a|;+0#0000000&| @36|r+0#00e0e07&|q|b|e|t|a| +0#0000000&@20 +@2|r+0#00e0e07&| |p| |b|i|n|o|m|;+0#0000000&| @35|r+0#00e0e07&|p|b|i|n|o|m| +0#0000000&@19 +@2|r+0#00e0e07&| |q| |b|i|n|o|m|;+0#0000000&| @35|r+0#00e0e07&|q|b|i|n|o|m| +0#0000000&@19 +@2|r+0#00e0e07&| |p|n| |c|h|i|s|q|;+0#0000000&| @34|r+0#00e0e07&|p|n|c|h|i|s|q| +0#0000000&@18 +@2|r+0#00e0e07&| |q|n| |c|h|i|s|q|;+0#0000000&| @34|r+0#00e0e07&|q|n|c|h|i|s|q| +0#0000000&@18 +@2>r+0#00e0e07&| |p| |c|a|u|c|h|y|;+0#0000000&| @34|r+0#00e0e07&|p|c|a|u|c|h|y| +0#0000000&@18 +@2|r+0#00e0e07&| |q| |c|a|u|c|h|y|;+0#0000000&| @34|r+0#00e0e07&|q|c|a|u|c|h|y| +0#0000000&@18 +@2|r+0#00e0e07&| |p| |f|;+0#0000000&| @39|r+0#00e0e07&|p|f| +0#0000000&@23 +@2|r+0#00e0e07&| |q| |f|;+0#0000000&| @39|r+0#00e0e07&|q|f| +0#0000000&@23 +@2|r+0#00e0e07&| |p| |l|o|g|i|s|;+0#0000000&| @35|r+0#00e0e07&|p|l|o|g|i|s| +0#0000000&@19 +@2|r+0#00e0e07&| |q| |l|o|g|i|s|;+0#0000000&| @35|r+0#00e0e07&|q|l|o|g|i|s| +0#0000000&@19 +@2|r+0#00e0e07&| |p| |l| |n|o|r|m|;+0#0000000&| @34|r+0#00e0e07&|p|l|n|o|r|m| +0#0000000&@19 +@2|r+0#00e0e07&| |q| |l| |n|o|r|m|;+0#0000000&| @34|r+0#00e0e07&|q|l|n|o|r|m| +0#0000000&@19 +@2|r+0#00e0e07&| |p|n| |b|i|n|o|m|;+0#0000000&| @34|r+0#00e0e07&|p|n|b|i|n|o|m| +0#0000000&@18 +@2|r+0#00e0e07&| |q|n| |b|i|n|o|m|;+0#0000000&| @34|r+0#00e0e07&|q|n|b|i|n|o|m| +0#0000000&@18 +@2|r+0#00e0e07&| |p| |n|t|;+0#0000000&| @38|r+0#00e0e07&|p|n|t| +0#0000000&@22 +@2|r+0#00e0e07&| |q| |n|t|;+0#0000000&| @38|r+0#00e0e07&|q|n|t| +0#0000000&@22 +@2|r+0#00e0e07&| |p| |u|n|i|f|;+0#0000000&| @36|r+0#00e0e07&|p|u|n|i|f| +0#0000000&@20 +@2|r+0#00e0e07&| |q| |u|n|i|f|;+0#0000000&| @36|r+0#00e0e07&|q|u|n|i|f| +0#0000000&@20 +@57|1|5|6|8|,|3| @7|7|4|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_90.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_90.dump new file mode 100644 index 0000000000..325b9be56c --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_90.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|r+0#00e0e07&| |q| |u|n|i|f|;+0#0000000&| @36|r+0#00e0e07&|q|u|n|i|f| +0#0000000&@20 +@2|r+0#00e0e07&| |p| |w|e|i|b|u|l@1|;+0#0000000&| @33|r+0#00e0e07&|p|w|e|i|b|u|l@1| +0#0000000&@17 +@2|r+0#00e0e07&| |q| |w|e|i|b|u|l@1|;+0#0000000&| @33|r+0#00e0e07&|q|w|e|i|b|u|l@1| +0#0000000&@17 +@2|r+0#00e0e07&| |p| |t|u|k|e|y|;+0#0000000&| @35|r+0#00e0e07&|p|t|u|k|e|y| +0#0000000&@19 +@2|r+0#00e0e07&| |q| |t|u|k|e|y|;+0#0000000&| @35|r+0#00e0e07&|q|t|u|k|e|y| +0#0000000&@19 +@2>r+0#00e0e07&| |p| |n|f|;+0#0000000&| @38|r+0#00e0e07&|p|n|f| +0#0000000&@22 +@2|r+0#00e0e07&| |q| |n|f|;+0#0000000&| @38|r+0#00e0e07&|q|n|f| +0#0000000&@22 +@2|r+0#00e0e07&| |p| |h|y|p|e|r|;+0#0000000&| @35|r+0#00e0e07&|p|h|y|p|e|r| +0#0000000&@19 +@2|r+0#00e0e07&| |q| |h|y|p|e|r|;+0#0000000&| @35|r+0#00e0e07&|q|h|y|p|e|r| +0#0000000&@19 +@2|r+0#00e0e07&| |d| |s|i|g|n| |r|a|n|k|;+0#0000000&| @31|r+0#00e0e07&|d|s|i|g|n|r|a|n|k| +0#0000000&@16 +@2|r+0#00e0e07&| |d| |w|i|l|c|o|x|;+0#0000000&| @34|r+0#00e0e07&|d|w|i|l|c|o|x| +0#0000000&@18 +@2|r+0#00e0e07&| |p| |s|i|g|n| |r|a|n|k|;+0#0000000&| @31|r+0#00e0e07&|p|s|i|g|n|r|a|n|k| +0#0000000&@16 +@2|r+0#00e0e07&| |q| |s|i|g|n| |r|a|n|k|;+0#0000000&| @31|r+0#00e0e07&|q|s|i|g|n|r|a|n|k| +0#0000000&@16 +@2|r+0#00e0e07&| |p| |w|i|l|c|o|x|;+0#0000000&| @34|r+0#00e0e07&|p|w|i|l|c|o|x| +0#0000000&@18 +@2|r+0#00e0e07&| |q| |w|i|l|c|o|x|;+0#0000000&| @34|r+0#00e0e07&|q|w|i|l|c|o|x| +0#0000000&@18 +@75 +|#+0#0000e05&|:@12| +0#0000000&@60 +|p+0#0000e05&|r|e|l|u|d|e|-|g|s|l|.|c| +0#0000000&@61 +|:+0#0000e05&@12|#| +0#0000000&@60 +@57|1|5|8|6|,|3| @7|7|5|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_91.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_91.dump new file mode 100644 index 0000000000..86366751d5 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_91.dump @@ -0,0 +1,20 @@ +|:+0#0000e05#ffffff0@12|#| +0#0000000&@60 +@75 +|#+0#0000e05&| |S|t|a|n|d|a|r|d| |p|r|e|l|u|d|e| |d|e|f|i|n|i|t|i|o|n|s| |f|r|o|m| |G|S|L|.| |#| +0#0000000&@32 +@2|a+0#00e0e07&|i|r|y| |a|i|;+0#0000000&| @37|a+0#00e0e07&|i|r|y|a|i| +0#0000000&@20 +@2|a+0#00e0e07&|i|r|y| |a|i| |s|c|a|l|e|d|;+0#0000000&| @30|a+0#00e0e07&|i|r|y|a|i|s|c|a|l|e|d| +0#0000000&@14 +@2>a+0#00e0e07&|i|r|y| |b|i|;+0#0000000&| @37|a+0#00e0e07&|i|r|y|b|i| +0#0000000&@20 +@2|a+0#00e0e07&|i|r|y| |b|i| |s|c|a|l|e|d|;+0#0000000&| @30|a+0#00e0e07&|i|r|y|b|i|s|c|a|l|e|d| +0#0000000&@14 +@2|b+0#00e0e07&|e|s@1|e|l| |i|n|0|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|i|n|0| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |i|n|1|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|i|n|1| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |i|n|0| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|i|n|0|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |i|n|1| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|i|n|1|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |j|n|0|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|j|n|0| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |j|n|1|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|j|n|1| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |k|n|0|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|k|n|0| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |k|n|1|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|k|n|1| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |k|n|0| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|k|n|0|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |k|n|1| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|k|n|1|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |y|n|0|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|y|n|0| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |y|n|1|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|y|n|1| +0#0000000&@17 +@57|1|6|0|4|,|3| @7|7|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_92.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_92.dump new file mode 100644 index 0000000000..b3c99b55d6 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_92.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|b+0#00e0e07&|e|s@1|e|l| |y|n|1|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|y|n|1| +0#0000000&@17 +@2|e+0#00e0e07&|x|p|i|n|t|e|1|;+0#0000000&| @36|e+0#00e0e07&|x|p|i|n|t|e|1| +0#0000000&@18 +@2|e+0#00e0e07&|x|p|i|n|t|e|i|;+0#0000000&| @36|e+0#00e0e07&|x|p|i|n|t|e|i| +0#0000000&@18 +@2|d+0#00e0e07&|a|w|s|o|n|;+0#0000000&| @38|d+0#00e0e07&|a|w|s|o|n| +0#0000000&@20 +@2|e+0#00e0e07&|x|p|r|e|l|;+0#0000000&| @38|e+0#00e0e07&|x|p|r|e|l| +0#0000000&@20 +@2>b+0#00e0e07&|e|t|a| |i|n|c| |g|s|l|;+0#0000000&| @32|b+0#00e0e07&|e|t|a|i|n|c|g|s|l| +0#0000000&@16 +@2|p+0#00e0e07&|o|c|h|;+0#0000000&| @40|p+0#00e0e07&|o|c|h| +0#0000000&@22 +@2|d+0#00e0e07&|i|g|a|m@1|a|;+0#0000000&| @37|d+0#00e0e07&|i|g|a|m@1|a| +0#0000000&@19 +@2|a+0#00e0e07&|i|r|y| |a|i| |d|e|r|i|v|a|t|i|v|e|;+0#0000000&| @26|a+0#00e0e07&|i|r|y|a|i|d|e|r|i|v|a|t|i|v|e| +0#0000000&@10 +@2|a+0#00e0e07&|i|r|y| |a|i| |d|e|r|i|v|;+0#0000000&| @31|a+0#00e0e07&|i|r|y|a|i|d|e|r|i|v| +0#0000000&@15 +@2|a+0#00e0e07&|i|r|y| |a|i| |d|e|r|i|v| |s|c|a|l|e|d|;+0#0000000&| @24|a+0#00e0e07&|i|r|y|a|i|d|e|r|i|v|s|c|a|l|e|d| +0#0000000&@9 +@2|a+0#00e0e07&|i|r|y| |b|i| |d|e|r|i|v|a|t|i|v|e|;+0#0000000&| @26|a+0#00e0e07&|i|r|y|b|i|d|e|r|i|v|a|t|i|v|e| +0#0000000&@10 +@2|a+0#00e0e07&|i|r|y| |b|i| |d|e|r|i|v|;+0#0000000&| @31|a+0#00e0e07&|i|r|y|b|i|d|e|r|i|v| +0#0000000&@15 +@2|a+0#00e0e07&|i|r|y| |b|i| |d|e|r|i|v| |s|c|a|l|e|d|;+0#0000000&| @24|a+0#00e0e07&|i|r|y|b|i|d|e|r|i|v|s|c|a|l|e|d| +0#0000000&@9 +@2|a+0#00e0e07&|i|r|y| |z|e|r|o| |a|i| |d|e|r|i|v|;+0#0000000&| @26|a+0#00e0e07&|i|r|y|z|e|r|o|a|i|d|e|r|i|v| +0#0000000&@11 +@2|a+0#00e0e07&|i|r|y| |z|e|r|o| |a|i|;+0#0000000&| @32|a+0#00e0e07&|i|r|y|z|e|r|o|a|i| +0#0000000&@16 +@2|a+0#00e0e07&|i|r|y| |z|e|r|o| |b|i| |d|e|r|i|v|;+0#0000000&| @26|a+0#00e0e07&|i|r|y|z|e|r|o|b|i|d|e|r|i|v| +0#0000000&@11 +@2|a+0#00e0e07&|i|r|y| |z|e|r|o| |b|i|;+0#0000000&| @32|a+0#00e0e07&|i|r|y|z|e|r|o|b|i| +0#0000000&@16 +@2|a+0#00e0e07&|n|g|l|e| |r|e|s|t|r|i|c|t| |p|o|s|;+0#0000000&| @26|a+0#00e0e07&|n|g|l|e|r|e|s|t|r|i|c|t|p|o|s| +0#0000000&@10 +@57|1|6|2@1|,|3| @7|7@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_93.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_93.dump new file mode 100644 index 0000000000..b423308f51 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_93.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|a+0#00e0e07&|n|g|l|e| |r|e|s|t|r|i|c|t| |p|o|s|;+0#0000000&| @26|a+0#00e0e07&|n|g|l|e|r|e|s|t|r|i|c|t|p|o|s| +0#0000000&@10 +@2|a+0#00e0e07&|n|g|l|e| |r|e|s|t|r|i|c|t| |s|y|m@1|;+0#0000000&| @25|a+0#00e0e07&|n|g|l|e|r|e|s|t|r|i|c|t|s|y|m@1| +0#0000000&@9 +@2|a+0#00e0e07&|t|a|n| |i|n|t|;+0#0000000&| @36|a+0#00e0e07&|t|a|n|i|n|t| +0#0000000&@19 +@2|b+0#00e0e07&|e|s@1|e|l| |i|l|0| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|i|l|0|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |i|l|1| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|i|l|1|s|c|a|l|e|d| +0#0000000&@11 +@2>b+0#00e0e07&|e|s@1|e|l| |i|l|2| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|i|l|2|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |i|l| |s|c|a|l|e|d|;+0#0000000&| @28|b+0#00e0e07&|e|s@1|e|l|i|l|s|c|a|l|e|d| +0#0000000&@12 +@2|b+0#00e0e07&|e|s@1|e|l| |i|n|;+0#0000000&| @35|b+0#00e0e07&|e|s@1|e|l|i|n| +0#0000000&@18 +@2|b+0#00e0e07&|e|s@1|e|l| |i|n| |s|c|a|l|e|d|;+0#0000000&| @28|b+0#00e0e07&|e|s@1|e|l|i|n|s|c|a|l|e|d| +0#0000000&@12 +@2|b+0#00e0e07&|e|s@1|e|l| |i|n|u|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|i|n|u| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |i|n|u| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|i|n|u|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |j|l|0|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|j|l|0| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |j|l|1|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|j|l|1| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |j|l|2|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|j|l|2| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |j|l|;+0#0000000&| @35|b+0#00e0e07&|e|s@1|e|l|j|l| +0#0000000&@18 +@2|b+0#00e0e07&|e|s@1|e|l| |j|n|;+0#0000000&| @35|b+0#00e0e07&|e|s@1|e|l|j|n| +0#0000000&@18 +@2|b+0#00e0e07&|e|s@1|e|l| |k|l|0| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|k|l|0|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |k|l|1| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|k|l|1|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |k|l|2| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|k|l|2|s|c|a|l|e|d| +0#0000000&@11 +@57|1|6|4|0|,|3| @7|7@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_94.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_94.dump new file mode 100644 index 0000000000..3b699da72e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_94.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|b+0#00e0e07&|e|s@1|e|l| |k|l|2| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|k|l|2|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |k|l| |s|c|a|l|e|d|;+0#0000000&| @28|b+0#00e0e07&|e|s@1|e|l|k|l|s|c|a|l|e|d| +0#0000000&@12 +@2|b+0#00e0e07&|e|s@1|e|l| |k|n|;+0#0000000&| @35|b+0#00e0e07&|e|s@1|e|l|k|n| +0#0000000&@18 +@2|b+0#00e0e07&|e|s@1|e|l| |k|n| |s|c|a|l|e|d|;+0#0000000&| @28|b+0#00e0e07&|e|s@1|e|l|k|n|s|c|a|l|e|d| +0#0000000&@12 +@2|b+0#00e0e07&|e|s@1|e|l| |k|n|_|s|c|a|l|e|d|;+0#0000000&| @28|b+0#00e0e07&|e|s@1|e|l|k|n|_|s|c|a|l|e|d| +0#0000000&@11 +@2>b+0#00e0e07&|e|s@1|e|l| |k|n|u|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|k|n|u| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |k|n|u| |s|c|a|l|e|d|;+0#0000000&| @27|b+0#00e0e07&|e|s@1|e|l|k|n|u|s|c|a|l|e|d| +0#0000000&@11 +@2|b+0#00e0e07&|e|s@1|e|l| |l|n| |k|n|u|;+0#0000000&| @31|b+0#00e0e07&|e|s@1|e|l@1|n|k|n|u| +0#0000000&@15 +@2|b+0#00e0e07&|e|s@1|e|l| |y|l|0|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|y|l|0| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |y|l|1|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|y|l|1| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |y|l|2|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|y|l|2| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |y|l|;+0#0000000&| @35|b+0#00e0e07&|e|s@1|e|l|y|l| +0#0000000&@18 +@2|b+0#00e0e07&|e|s@1|e|l| |y|n|;+0#0000000&| @35|b+0#00e0e07&|e|s@1|e|l|y|n| +0#0000000&@18 +@2|b+0#00e0e07&|e|s@1|e|l| |y|n|u|;+0#0000000&| @34|b+0#00e0e07&|e|s@1|e|l|y|n|u| +0#0000000&@17 +@2|b+0#00e0e07&|e|s@1|e|l| |z|e|r|o| |j|0|;+0#0000000&| @30|b+0#00e0e07&|e|s@1|e|l|z|e|r|o|j|0| +0#0000000&@14 +@2|b+0#00e0e07&|e|s@1|e|l| |z|e|r|o| |j|1|;+0#0000000&| @30|b+0#00e0e07&|e|s@1|e|l|z|e|r|o|j|1| +0#0000000&@14 +@2|b+0#00e0e07&|e|s@1|e|l| |z|e|r|o| |j|n|u|;+0#0000000&| @29|b+0#00e0e07&|e|s@1|e|l|z|e|r|o|j|n|u| +0#0000000&@13 +@2|c+0#00e0e07&|h|i|;+0#0000000&| @41|c+0#00e0e07&|h|i| +0#0000000&@23 +@2|c+0#00e0e07&|i|;+0#0000000&| @42|c+0#00e0e07&|i| +0#0000000&@24 +@57|1|6|5|8|,|3| @7|7|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_95.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_95.dump new file mode 100644 index 0000000000..69eace6f5e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_95.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|c+0#00e0e07&|i|;+0#0000000&| @42|c+0#00e0e07&|i| +0#0000000&@24 +@2|c+0#00e0e07&|l|a|u|s|e|n|;+0#0000000&| @37|c+0#00e0e07&|l|a|u|s|e|n| +0#0000000&@19 +@2|c+0#00e0e07&|o|n|i|c|a|l| |p|0|;+0#0000000&| @34|c+0#00e0e07&|o|n|i|c|a|l|p|0| +0#0000000&@17 +@2|c+0#00e0e07&|o|n|i|c|a|l| |p|1|;+0#0000000&| @34|c+0#00e0e07&|o|n|i|c|a|l|p|1| +0#0000000&@17 +@2|c+0#00e0e07&|o|n|i|c|a|l| |p|c|y|l|r|e|g|;+0#0000000&| @29|c+0#00e0e07&|o|n|i|c|a|l|p|c|y|l|r|e|g| +0#0000000&@12 +@2>c+0#00e0e07&|o|n|i|c|a|l| |p|h|a|l|f|;+0#0000000&| @31|c+0#00e0e07&|o|n|i|c|a|l|p|h|a|l|f| +0#0000000&@14 +@2|c+0#00e0e07&|o|n|i|c|a|l| |p|m|h|a|l|f|;+0#0000000&| @30|c+0#00e0e07&|o|n|i|c|a|l|p|m|h|a|l|f| +0#0000000&@13 +@2|c+0#00e0e07&|o|n|i|c|a|l| |p|s|p|h|r|e|g|;+0#0000000&| @29|c+0#00e0e07&|o|n|i|c|a|l|p|s|p|h|r|e|g| +0#0000000&@12 +@2|d+0#00e0e07&|e|b|y|e| |1|;+0#0000000&| @37|d+0#00e0e07&|e|b|y|e|1| +0#0000000&@20 +@2|d+0#00e0e07&|e|b|y|e| |2|;+0#0000000&| @37|d+0#00e0e07&|e|b|y|e|2| +0#0000000&@20 +@2|d+0#00e0e07&|e|b|y|e| |3|;+0#0000000&| @37|d+0#00e0e07&|e|b|y|e|3| +0#0000000&@20 +@2|d+0#00e0e07&|e|b|y|e| |4|;+0#0000000&| @37|d+0#00e0e07&|e|b|y|e|4| +0#0000000&@20 +@2|d+0#00e0e07&|e|b|y|e| |5|;+0#0000000&| @37|d+0#00e0e07&|e|b|y|e|5| +0#0000000&@20 +@2|d+0#00e0e07&|e|b|y|e| |6|;+0#0000000&| @37|d+0#00e0e07&|e|b|y|e|6| +0#0000000&@20 +@2|d+0#00e0e07&|i|l|o|g|;+0#0000000&| @39|d+0#00e0e07&|i|l|o|g| +0#0000000&@21 +@2|d+0#00e0e07&|o|u|b|l|e|f|a|c|t|;+0#0000000&| @34|d+0#00e0e07&|o|u|b|l|e|f|a|c|t| +0#0000000&@16 +@2|e+0#00e0e07&|l@1|i|n|t| |d|;+0#0000000&| @36|e+0#00e0e07&|l@1|i|n|t|d| +0#0000000&@19 +@2|e+0#00e0e07&|l@1|i|n|t| |e|c|o|m|p|;+0#0000000&| @32|e+0#00e0e07&|l@1|i|n|t|e|c|o|m|p| +0#0000000&@15 +@2|e+0#00e0e07&|l@1|i|n|t| |e|;+0#0000000&| @36|e+0#00e0e07&|l@1|i|n|t|e| +0#0000000&@19 +@57|1|6|7|6|,|3| @7|7|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_96.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_96.dump new file mode 100644 index 0000000000..693120d631 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_96.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|e+0#00e0e07&|l@1|i|n|t| |e|;+0#0000000&| @36|e+0#00e0e07&|l@1|i|n|t|e| +0#0000000&@19 +@2|e+0#00e0e07&|l@1|i|n|t| |f|;+0#0000000&| @36|e+0#00e0e07&|l@1|i|n|t|f| +0#0000000&@19 +@2|e+0#00e0e07&|l@1|i|n|t| |k|c|o|m|p|;+0#0000000&| @32|e+0#00e0e07&|l@1|i|n|t|k|c|o|m|p| +0#0000000&@15 +@2|e+0#00e0e07&|l@1|i|n|t| |p|c|o|m|p|;+0#0000000&| @32|e+0#00e0e07&|l@1|i|n|t|p|c|o|m|p| +0#0000000&@15 +@2|e+0#00e0e07&|l@1|i|n|t| |p|;+0#0000000&| @36|e+0#00e0e07&|l@1|i|n|t|p| +0#0000000&@19 +@2>e+0#00e0e07&|l@1|i|n|t| |r|c|;+0#0000000&| @35|e+0#00e0e07&|l@1|i|n|t|r|c| +0#0000000&@18 +@2|e+0#00e0e07&|l@1|i|n|t| |r|d|;+0#0000000&| @35|e+0#00e0e07&|l@1|i|n|t|r|d| +0#0000000&@18 +@2|e+0#00e0e07&|l@1|i|n|t| |r|f|;+0#0000000&| @35|e+0#00e0e07&|l@1|i|n|t|r|f| +0#0000000&@18 +@2|e+0#00e0e07&|l@1|i|n|t| |r|j|;+0#0000000&| @35|e+0#00e0e07&|l@1|i|n|t|r|j| +0#0000000&@18 +@2|e+0#00e0e07&|t|a|i|n|t|;+0#0000000&| @38|e+0#00e0e07&|t|a|i|n|t| +0#0000000&@20 +@2|e+0#00e0e07&|t|a|;+0#0000000&| @41|e+0#00e0e07&|t|a| +0#0000000&@23 +@2|e+0#00e0e07&|x|p|i|n|t| |3|;+0#0000000&| @36|e+0#00e0e07&|x|p|i|n|t|3| +0#0000000&@19 +@2|e+0#00e0e07&|x|p|i|n|t| |e|2|;+0#0000000&| @35|e+0#00e0e07&|x|p|i|n|t|e|2| +0#0000000&@18 +@2|e+0#00e0e07&|x|p|i|n|t| |e|n|;+0#0000000&| @35|e+0#00e0e07&|x|p|i|n|t|e|n| +0#0000000&@18 +@2|e+0#00e0e07&|x|p|m|1|;+0#0000000&| @39|e+0#00e0e07&|x|p|m|1| +0#0000000&@21 +@2|e+0#00e0e07&|x|p|r|e|l|2|;+0#0000000&| @37|e+0#00e0e07&|x|p|r|e|l|2| +0#0000000&@19 +@2|e+0#00e0e07&|x|p|r|e|l|n|;+0#0000000&| @37|e+0#00e0e07&|x|p|r|e|l|n| +0#0000000&@19 +@2|f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |0|;+0#0000000&| @31|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|0| +0#0000000&@15 +@2|f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |1|;+0#0000000&| @31|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|1| +0#0000000&@15 +@57|1|6|9|4|,|3| @7|8|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_97.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_97.dump new file mode 100644 index 0000000000..ebed06f482 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_97.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |1|;+0#0000000&| @31|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|1| +0#0000000&@15 +@2|f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |2|;+0#0000000&| @31|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|2| +0#0000000&@15 +@2|f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |3|h|a|l|f|;+0#0000000&| @27|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|3|h|a|l|f| +0#0000000&@11 +@2|f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |h|a|l|f|;+0#0000000&| @28|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|h|a|l|f| +0#0000000&@12 +@2|f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |i|n|c|0|;+0#0000000&| @28|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|i|n|c|0| +0#0000000&@12 +@2>f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |i|n|t|;+0#0000000&| @29|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|i|n|t| +0#0000000&@13 +@2|f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |m|1|;+0#0000000&| @30|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|m|1| +0#0000000&@14 +@2|f+0#00e0e07&|e|r|m|i| |d|i|r|a|c| |m|h|a|l|f|;+0#0000000&| @27|f+0#00e0e07&|e|r|m|i|d|i|r|a|c|m|h|a|l|f| +0#0000000&@11 +@2|g+0#00e0e07&|a|m@1|a| |i|n|c| |g|s|l|;+0#0000000&| @31|g+0#00e0e07&|a|m@1|a|i|n|c|g|s|l| +0#0000000&@15 +@2|g+0#00e0e07&|a|m@1|a| |i|n|c| |p|;+0#0000000&| @33|g+0#00e0e07&|a|m@1|a|i|n|c|p| +0#0000000&@17 +@2|g+0#00e0e07&|a|m@1|a| |i|n|c| |q|;+0#0000000&| @33|g+0#00e0e07&|a|m@1|a|i|n|c|q| +0#0000000&@17 +@2|g+0#00e0e07&|a|m@1|a| |i|n|v|;+0#0000000&| @35|g+0#00e0e07&|a|m@1|a|i|n|v| +0#0000000&@18 +@2|g+0#00e0e07&|a|m@1|a| |s|t|a|r|;+0#0000000&| @34|g+0#00e0e07&|a|m@1|a|s|t|a|r| +0#0000000&@17 +@2|g+0#00e0e07&|e|g|e|n|p|o|l|y| |1| |r|e|a|l|;+0#0000000&| @28|g+0#00e0e07&|e|g|e|n|p|o|l|y|1|r|e|a|l| +0#0000000&@12 +@2|g+0#00e0e07&|e|g|e|n|p|o|l|y| |2| |r|e|a|l|;+0#0000000&| @28|g+0#00e0e07&|e|g|e|n|p|o|l|y|2|r|e|a|l| +0#0000000&@12 +@2|g+0#00e0e07&|e|g|e|n|p|o|l|y| |3| |r|e|a|l|;+0#0000000&| @28|g+0#00e0e07&|e|g|e|n|p|o|l|y|3|r|e|a|l| +0#0000000&@12 +@2|g+0#00e0e07&|e|g|e|n|p|o|l|y| |n| |r|e|a|l|;+0#0000000&| @28|g+0#00e0e07&|e|g|e|n|p|o|l|y|n|r|e|a|l| +0#0000000&@12 +@2|h+0#00e0e07&|e|r|m|i|t|e| |f|u|n|c|;+0#0000000&| @32|h+0#00e0e07&|e|r|m|i|t|e|f|u|n|c| +0#0000000&@15 +@2|h+0#00e0e07&|y|p|o|t|;+0#0000000&| @39|h+0#00e0e07&|y|p|o|t| +0#0000000&@21 +@57|1|7|1|2|,|3| @7|8|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_98.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_98.dump new file mode 100644 index 0000000000..678840fab2 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_98.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|h+0#00e0e07&|y|p|o|t|;+0#0000000&| @39|h+0#00e0e07&|y|p|o|t| +0#0000000&@21 +@2|h+0#00e0e07&|z|e|t|a|;+0#0000000&| @39|h+0#00e0e07&|z|e|t|a| +0#0000000&@21 +@2|l+0#00e0e07&|a|g|u|e|r@1|e| |1| |r|e|a|l|;+0#0000000&| @29|l+0#00e0e07&|a|g|u|e|r@1|e|1|r|e|a|l| +0#0000000&@13 +@2|l+0#00e0e07&|a|g|u|e|r@1|e| |2| |r|e|a|l|;+0#0000000&| @29|l+0#00e0e07&|a|g|u|e|r@1|e|2|r|e|a|l| +0#0000000&@13 +@2|l+0#00e0e07&|a|g|u|e|r@1|e| |3| |r|e|a|l|;+0#0000000&| @29|l+0#00e0e07&|a|g|u|e|r@1|e|3|r|e|a|l| +0#0000000&@13 +@2>l+0#00e0e07&|a|g|u|e|r@1|e| |n| |r|e|a|l|;+0#0000000&| @29|l+0#00e0e07&|a|g|u|e|r@1|e|n|r|e|a|l| +0#0000000&@13 +@2|l+0#00e0e07&|a|m|b|e|r|t| |w|0|;+0#0000000&| @34|l+0#00e0e07&|a|m|b|e|r|t|w|0| +0#0000000&@17 +@2|l+0#00e0e07&|a|m|b|e|r|t| |w|m|1|;+0#0000000&| @33|l+0#00e0e07&|a|m|b|e|r|t|w|m|1| +0#0000000&@16 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |h|3|d|0|;+0#0000000&| @31|l+0#00e0e07&|e|g|e|n|d|r|e|h|3|d|0| +0#0000000&@14 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |h|3|d|1|;+0#0000000&| @31|l+0#00e0e07&|e|g|e|n|d|r|e|h|3|d|1| +0#0000000&@14 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |h|3|d|;+0#0000000&| @32|l+0#00e0e07&|e|g|e|n|d|r|e|h|3|d| +0#0000000&@15 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |p|1|;+0#0000000&| @33|l+0#00e0e07&|e|g|e|n|d|r|e|p|1| +0#0000000&@16 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |p|2|;+0#0000000&| @33|l+0#00e0e07&|e|g|e|n|d|r|e|p|2| +0#0000000&@16 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |p|3|;+0#0000000&| @33|l+0#00e0e07&|e|g|e|n|d|r|e|p|3| +0#0000000&@16 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |p|l|;+0#0000000&| @33|l+0#00e0e07&|e|g|e|n|d|r|e|p|l| +0#0000000&@16 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |q|0|;+0#0000000&| @33|l+0#00e0e07&|e|g|e|n|d|r|e|q|0| +0#0000000&@16 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |q|1|;+0#0000000&| @33|l+0#00e0e07&|e|g|e|n|d|r|e|q|1| +0#0000000&@16 +@2|l+0#00e0e07&|e|g|e|n|d|r|e| |q|l|;+0#0000000&| @33|l+0#00e0e07&|e|g|e|n|d|r|e|q|l| +0#0000000&@16 +@2|l+0#00e0e07&|n| |c|o|s|h|;+0#0000000&| @37|l+0#00e0e07&|n|c|o|s|h| +0#0000000&@20 +@57|1|7|3|0|,|3| @7|8|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_all_preludes_99.dump b/runtime/syntax/testdir/dumps/algol68_all_preludes_99.dump new file mode 100644 index 0000000000..ef7c2adc4e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_all_preludes_99.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@1|l+0#00e0e07&|n| |c|o|s|h|;+0#0000000&| @37|l+0#00e0e07&|n|c|o|s|h| +0#0000000&@20 +@2|l+0#00e0e07&|n| |d|o|u|b|l|e|f|a|c|t|;+0#0000000&| @31|l+0#00e0e07&|n|d|o|u|b|l|e|f|a|c|t| +0#0000000&@14 +@2|l+0#00e0e07&|n| |p|o|c|h|;+0#0000000&| @37|l+0#00e0e07&|n|p|o|c|h| +0#0000000&@20 +@2|l+0#00e0e07&|n| |s|i|n|h|;+0#0000000&| @37|l+0#00e0e07&|n|s|i|n|h| +0#0000000&@20 +@2|l+0#00e0e07&|n|1| |p|l|u|s|x|m|x|;+0#0000000&| @33|l+0#00e0e07&|n|1|p|l|u|s|x|m|x| +0#0000000&@16 +@2>l+0#00e0e07&|n|1| |p|l|u|s|x|;+0#0000000&| @35|l+0#00e0e07&|n|1|p|l|u|s|x| +0#0000000&@18 +@2|l+0#00e0e07&|n| |a|b|s|;+0#0000000&| @38|l+0#00e0e07&|n|a|b|s| +0#0000000&@21 +@2|p+0#00e0e07&|o|c|h| |r|e|l|;+0#0000000&| @36|p+0#00e0e07&|o|c|h|r|e|l| +0#0000000&@19 +@2|p+0#00e0e07&|s|i| |1| |i|n|t|;+0#0000000&| @35|p+0#00e0e07&|s|i|1|i|n|t| +0#0000000&@19 +@2|p+0#00e0e07&|s|i| |1| |p|i|y|;+0#0000000&| @35|p+0#00e0e07&|s|i|1|p|i|y| +0#0000000&@19 +@2|p+0#00e0e07&|s|i| |1|;+0#0000000&| @39|p+0#00e0e07&|s|i|1| +0#0000000&@22 +@2|p+0#00e0e07&|s|i| |i|n|t|;+0#0000000&| @37|p+0#00e0e07&|s|i@1|n|t| +0#0000000&@20 +@2|p+0#00e0e07&|s|i|n|;+0#0000000&| @40|p+0#00e0e07&|s|i|n| +0#0000000&@22 +@2|p+0#00e0e07&|s|i|;+0#0000000&| @41|p+0#00e0e07&|s|i| +0#0000000&@23 +@2|s+0#00e0e07&|h|i|;+0#0000000&| @41|s+0#00e0e07&|h|i| +0#0000000&@23 +@2|s+0#00e0e07&|i|n|c|;+0#0000000&| @40|s+0#00e0e07&|i|n|c| +0#0000000&@22 +@2|s+0#00e0e07&|i|;+0#0000000&| @42|s+0#00e0e07&|i| +0#0000000&@24 +@2|s+0#00e0e07&|y|n|c|h|r|o|t|r|o|n| |1|;+0#0000000&| @31|s+0#00e0e07&|y|n|c|h|r|o|t|r|o|n|1| +0#0000000&@14 +@2|s+0#00e0e07&|y|n|c|h|r|o|t|r|o|n| |2|;+0#0000000&| @31|s+0#00e0e07&|y|n|c|h|r|o|t|r|o|n|2| +0#0000000&@14 +@57|1|7|4|8|,|3| @7|8|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_denotations_00.dump b/runtime/syntax/testdir/dumps/algol68_denotations_00.dump new file mode 100644 index 0000000000..b8a920aa2e --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_denotations_00.dump @@ -0,0 +1,20 @@ +>#+0#0000e05#ffffff0| |A|l|g|o|l| |6|8| |D|e|n|o|t|a|t|i|o|n|s| |#| +0#0000000&@50 +@75 +@75 +|#+0#0000e05&| |B|O@1|L| |#| +0#0000000&@66 +@75 +|B+0#00e0003&|O@1|L| +0#0000000&|x| |=| |T+0#e000002&|R|U|E| +0#0000000&@61 +|B+0#00e0003&|O@1|L| +0#0000000&|x| |=| |F+0#e000002&|A|L|S|E| +0#0000000&@60 +@75 +@75 +|#+0#0000e05&| |I|N|T| |#| +0#0000000&@67 +@75 +|I+0#00e0003&|N|T| +0#0000000&|x| |=| |0+0#e000002&| +0#0000000&@65 +|I+0#00e0003&|N|T| +0#0000000&|x| |=| |4+0#e000002&|2| +0#0000000&@64 +@75 +|#+0#0000e05&| |w|h|i|t|e|s|p|a|c|e| |#| +0#0000000&@60 +|I+0#00e0003&|N|T| +0#0000000&|x| |=| |1+0#e000002&| |0@2| |0@2| +0#0000000&@57 +@75 +|#+0#0000e05&| |m|o|n|a|d|i|c| |o|p|e|r|a|t|o|r|s| |#| +0#0000000&@53 +|I+0#00e0003&|N|T| +0#0000000&|x| |=| |-|4+0#e000002&|2| +0#0000000&@63 +@57|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/algol68_denotations_01.dump b/runtime/syntax/testdir/dumps/algol68_denotations_01.dump new file mode 100644 index 0000000000..2cd06bac49 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_denotations_01.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@74 +|#+0#0000e05&| |w|h|i|t|e|s|p|a|c|e| |#| +0#0000000&@60 +|I+0#00e0003&|N|T| +0#0000000&|x| |=| |1+0#e000002&| |0@2| |0@2| +0#0000000&@57 +@75 +|#+0#0000e05&| |m|o|n|a|d|i|c| |o|p|e|r|a|t|o|r|s| |#| +0#0000000&@53 +>I+0#00e0003&|N|T| +0#0000000&|x| |=| |-|4+0#e000002&|2| +0#0000000&@63 +|I+0#00e0003&|N|T| +0#0000000&|x| |=| |+|4+0#e000002&|2| +0#0000000&@63 +@75 +@75 +|#+0#0000e05&| |R|E|A|L| |#| +0#0000000&@66 +@75 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2| +0#0000000&@62 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2| +0#0000000&@60 +@75 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|e|4|2| +0#0000000&@60 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|E|4|2| +0#0000000&@60 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|\|4|2| +0#0000000&@60 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|⏨|4|2| +0#0000000&@60 +@75 +@57|1|9|,|1| @9|1|3|%| diff --git a/runtime/syntax/testdir/dumps/algol68_denotations_02.dump b/runtime/syntax/testdir/dumps/algol68_denotations_02.dump new file mode 100644 index 0000000000..d8b1fcad00 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_denotations_02.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@74 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|e|-|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|E|-|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|\|-|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|⏨|-|4|2| +0#0000000&@59 +> @74 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|e|+|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|E|+|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|\|+|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|⏨|+|4|2| +0#0000000&@59 +@75 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|e|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|E|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|\|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|⏨|4|2| +0#0000000&@59 +@75 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|e|-|4|2| +0#0000000&@58 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|E|-|4|2| +0#0000000&@58 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|\|-|4|2| +0#0000000&@58 +@57|3|7|,|0|-|1| @7|3|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_denotations_03.dump b/runtime/syntax/testdir/dumps/algol68_denotations_03.dump new file mode 100644 index 0000000000..94a85ab067 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_denotations_03.dump @@ -0,0 +1,20 @@ +|R+0#00e0003#ffffff0|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|\|-|4|2| +0#0000000&@58 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|⏨|-|4|2| +0#0000000&@58 +@75 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|e|+|4|2| +0#0000000&@58 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|E|+|4|2| +0#0000000&@58 +>R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|\|+|4|2| +0#0000000&@58 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |.+0#e000002&|4|2|⏨|+|4|2| +0#0000000&@58 +@75 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|e|4|2| +0#0000000&@57 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|E|4|2| +0#0000000&@57 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|\|4|2| +0#0000000&@57 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|⏨|4|2| +0#0000000&@57 +@75 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|e|-|4|2| +0#0000000&@56 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|E|-|4|2| +0#0000000&@56 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|\|-|4|2| +0#0000000&@56 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|⏨|-|4|2| +0#0000000&@56 +@75 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|e|+|4|2| +0#0000000&@56 +@57|5@1|,|1| @9|5|1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_denotations_04.dump b/runtime/syntax/testdir/dumps/algol68_denotations_04.dump new file mode 100644 index 0000000000..b564a7f216 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_denotations_04.dump @@ -0,0 +1,20 @@ +|R+0#00e0003#ffffff0|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|e|+|4|2| +0#0000000&@56 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|E|+|4|2| +0#0000000&@56 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|\|+|4|2| +0#0000000&@56 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&|2|.|4|2|⏨|+|4|2| +0#0000000&@56 +@75 +>#+0#0000e05&| |w|h|i|t|e|s|p|a|c|e| |#| +0#0000000&@60 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&| |2| |.| |4| |2| |e| |+| |4| |2| +0#0000000&@48 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&| |2| |.| |4| |2| |\| |+| |4| |2| +0#0000000&@48 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |4+0#e000002&| |2| |.| |4| |2| |⏨| |+| |4| |2| +0#0000000&@48 +@75 +|#+0#0000e05&| |m|o|n|a|d|i|c| |o|p|e|r|a|t|o|r|s| |#| +0#0000000&@53 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |-|4+0#e000002&|2|.|4|2| +0#0000000&@59 +|R+0#00e0003&|E|A|L| +0#0000000&|x| |=| |+|4+0#e000002&|2|.|4|2| +0#0000000&@59 +@75 +@75 +|#+0#0000e05&| |B|I|T|S| |#| +0#0000000&@66 +@75 +|B+0#00e0003&|I|T|S| +0#0000000&|x| |=| |2+0#e000002&|r|1|0|1|0|1|0| +0#0000000&@57 +|B+0#00e0003&|I|T|S| +0#0000000&|x| |=| |4+0#e000002&|r|2@2| +0#0000000&@60 +@57|7|3|,|1| @9|6|9|%| diff --git a/runtime/syntax/testdir/dumps/algol68_denotations_05.dump b/runtime/syntax/testdir/dumps/algol68_denotations_05.dump new file mode 100644 index 0000000000..cc19b5b372 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_denotations_05.dump @@ -0,0 +1,20 @@ +|B+0#00e0003#ffffff0|I|T|S| +0#0000000&|x| |=| |4+0#e000002&|r|2@2| +0#0000000&@60 +|B+0#00e0003&|I|T|S| +0#0000000&|x| |=| |8+0#e000002&|r|5|2| +0#0000000&@61 +|B+0#00e0003&|I|T|S| +0#0000000&|x| |=| |1+0#e000002&|6|r|2|a| +0#0000000&@60 +@75 +|#+0#0000e05&| |w|h|i|t|e|s|p|a|c|e| |#| +0#0000000&@60 +>B+0#00e0003&|I|T|S| +0#0000000&|x| |=| |2+0#e000002&|r| |1|0|1|0| |1|0|1|0| +0#0000000&@53 +@75 +|#+0#0000e05&| |u|n|m|a|t|c|h|e|d| |b|y| |b|a|d| |p|a|t@1|e|r|n| |i|n| |l|e|g|a|c|y| |v|e|r|s|i|o|n|s| |o|f| |s|y|n|t|a|x| |f|i|l|e| |#| +0#0000000&@12 +|B+0#00e0003&|I|T|S| +0#0000000&|x| |=| |1+0#e000002&|6|r|9@1| +0#0000000&@60 +@75 +@75 +|#+0#0000e05&| |S|T|R|I|N|G| |#| +0#0000000&@64 +@75 +|S+0#00e0003&|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&|F|o@1| |B|a|r|"| +0#0000000&@54 +@75 +|#+0#0000e05&| |q|u|o|t|e| |e|s|c|a|p|e| |#| +0#0000000&@58 +|S+0#00e0003&|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&|"+0#e000e06&@1|"+0#e000002&| +0#0000000&@59 +|S+0#00e0003&|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&| |"+0#e000e06&@1| +0#e000002&|"| +0#0000000&@57 +|S+0#00e0003&|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&|F|o@1|"+0#e000e06&@1|"+0#e000002&| +0#0000000&@56 +@57|9|1|,|1| @9|8@1|%| diff --git a/runtime/syntax/testdir/dumps/algol68_denotations_06.dump b/runtime/syntax/testdir/dumps/algol68_denotations_06.dump new file mode 100644 index 0000000000..3421d49c36 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_denotations_06.dump @@ -0,0 +1,20 @@ +|S+0#00e0003#ffffff0|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&|F|o@1|"+0#e000e06&@1|"+0#e000002&| +0#0000000&@56 +|S+0#00e0003&|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&|"+0#e000e06&@1|B+0#e000002&|a|r|"| +0#0000000&@56 +|S+0#00e0003&|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&|F|o@1|"+0#e000e06&@1|B+0#e000002&|a|r|"| +0#0000000&@53 +@75 +|#+0#0000e05&| |l|i|n|e| |c|o|n|t|i|n|u|a|t|i|o|n| |#| +0#0000000&@53 +>S+0#00e0003&|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&|F|o@1|\+0#e000e06&| +0#0000000&@58 +| +0#e000002&|B|a|r|"| +0#0000000&@69 +|S+0#00e0003&|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&|F|o@1|"+0#e000e06&@1|\| +0#0000000&@56 +|B+0#e000002&|a|r|"| +0#0000000&@70 +|S+0#00e0003&|T|R|I|N|G| +0#0000000&|s| |=| |"+0#e000002&|F|o@1|\+0#e000e06&| +0#0000000&@58 +|"+0#e000e06&@1|B+0#e000002&|a|r|"| +0#0000000&@68 +@75 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|1|0|9|,|1| @8|B|o|t| diff --git a/runtime/syntax/testdir/input/algol68_all_preludes.a68 b/runtime/syntax/testdir/input/algol68_all_preludes.a68 new file mode 100644 index 0000000000..3182b1f617 --- /dev/null +++ b/runtime/syntax/testdir/input/algol68_all_preludes.a68 @@ -0,0 +1,2117 @@ +# + VIM_TEST_SETUP let g:algol68_symbolic_operators = 1 +# +# + Algol 68 prelude identifier from the Genie source code files. + + Provided as a base to verify the correct syntax highlighting + of the algol68.vim syntax file for the Vim editor. + + There's four sections, each representing an original file, + that are enclosed in the Unix-'more' form to stand out. + + Some informal comments from the source code left intact as + a comment in Algol 68 syntax. + + Depending on the language context there's duplicates in the + list, that are kept for clarity given the associated informal + comment associated with them. + + Some entries deliberately contain two identifier variants that + may be used as alternative form in an Algol 68 Genie program. + + The identifiers are presented in two variants separated by a + tabulator; first with spaces, then in canonical form without + spaces. Variants with spaces are supported by the Vim syntax + file as the Algol 68 language does. + But note that while Algol 68 allows arbitrary spacing within + identifiers - even as extreme as writing one character per + line! - the Vim syntax file for Algol 68 had deliberately + been defined in a way restricting the highlighted options; + spacing can usually be inserted where "natural words" would + allow a separation (say for 'intwidth' you may write, e.g., + 'int width' but not 'int wid th'; the latter would not get + highlighted). + + Modelines are set to simply inspect the correct highlighting + in this file. + + Janis Papanagnou, 2026-04-23 +# + +#::::::::::::: +prelude.c +:::::::::::::# + +# Primitive A68 moids. # + VOID VOID +# Standard precision. # + INT INT + REAL REAL + COMPLEX COMPLEX + COMPL COMPL + BITS BITS + BYTES BYTES +# Multiple precision. # + INT INT + REAL REAL + COMPLEX COMPLEX + COMPL COMPL + BITS BITS + BYTES BYTES + REAL REAL + INT INT + COMPLEX COMPLEX + COMPL COMPL + BITS BITS +# Other. # + BOOL BOOL + CHAR CHAR + STRING STRING + FILE FILE + CHANNEL CHANNEL + PIPE PIPE + FORMAT FORMAT + SEMA SEMA + SOUND SOUND +# Identifiers. # + long long pi; qpi; longlongpi; qpi +# # + bits lengths; bitslengths + bits shorths; bitsshorths + bits width; bitswidth + bytes lengths; byteslengths + bytes shorths; bytesshorths + bytes width; byteswidth + compl lengths; compllengths + compl shorths; complshorths + exp width; expwidth + infinity; infinity + inf; inf + int lengths; intlengths + int shorths; intshorths + int width; intwidth + long bits width; longbitswidth + long bytes width; longbyteswidth + long exp width; longexpwidth + long int width; longintwidth + long long expwidth; longlongexpwidth + long long infinity; longlonginfinity + long long inf; longlonginf + long long intwidth; longlongintwidth + long long maxint; longlongmaxint + long long maxreal; longlongmaxreal + long long mininf; longlongmininf + long long minreal; longlongminreal + long long minus infinity; longlongminusinfinity + long long real width; longlongrealwidth + long long small real; longlongsmallreal + long real width; longrealwidth + max abs char; maxabschar + max bits; maxbits + max int; maxint + max real; maxreal + min inf; mininf + min real; minreal + minus infinity; minusinfinity + mpradix; mpradix + nan; nan + pi; pi + real lengths; reallengths + real shorths; realshorths + real width; realwidth + small real; smallreal + clock; clock + cpu time; cputime + seconds; seconds + wall clock; wallclock + wall seconds; wallseconds + wall time; walltime + blocks; blocks + collections; collections + collect seconds; collectseconds + garbage; garbage + garbage collections; garbagecollections + garbage freed; garbagefreed + garbage refused; garbagerefused + garbage seconds; garbageseconds + on gc event; ongcevent + sweeps; sweeps + sweeps refused; sweepsrefused + stack pointer; stackpointer + system stack pointer; systemstackpointer + system stack size; systemstacksize + actual stack size; actualstacksize + heap pointer; heappointer + system heap pointer; systemheappointer + gc heap; gcheap + sweep heap; sweepheap + preemptive gc; preemptivegc + preemptive sweep; preemptivesweep + preemptive sweepheap; preemptivesweepheap + backtrace; backtrace + break; break + debug; debug + monitor; monitor + abend; abend + evaluate; evaluate + system; system + sleep; sleep +# Machine environ parameters. # + i32mach; i32mach + r64mach; r64mach + i64mach; i64mach + r128mach; r128mach +# BITS procedures. # + bits pack; bitspack +# RNG procedures. # + first random; firstrandom + next random; nextrandom + random; random + rnd; rnd + long long nextrandom; longlongnextrandom + long long random; longlongrandom +# Priorities. # +# INT ops. # + + + + - - + ABS ABS + SIGN SIGN + ODD ODD + = = + /= /= + ~= ~= + ^= ^= + < < + <= <= + > > + >= >= + EQ EQ + NE NE + LT LT + LE LE + GT GT + GE GE + + + + - - + * * + OVER OVER + % % + MOD MOD + %* %* + ** ** + UP UP + ^ ^ + / / + +:= +:= + -:= -:= + *:= *:= + %:= %:= + %*:= %*:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + OVERAB OVERAB + MODAB MODAB +# REAL ops. # + + + + - - + ABS ABS + SIGN SIGN + ROUND ROUND + ENTIER ENTIER + FLOOR FLOOR + CEIL CEIL + NINT NINT + TRUNC TRUNC + FRAC FRAC + FIX FIX + = = + /= /= + ~= ~= + ^= ^= + < < + <= <= + > > + >= >= + EQ EQ + NE NE + LT LT + LE LE + GT GT + GE GE + + + + - - + * * + / / + ** ** + UP UP + ^ ^ + ** ** + UP UP + ^ ^ + +:= +:= + -:= -:= + *:= *:= + /:= /:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + DIVAB DIVAB +# Procedures # + arc cos dg; acos dg; arccosdg; acosdg + arc cosh; acosh; arccosh; acosh + arc cot; acot; arccot; acot + arc cot dg; acot dg; arccotdg; acotdg + arc csc; acsc; arccsc; acsc + arc csc dg; acsc dg; arccscdg; acscdg + arc sec; asec; arcsec; asec + arc sec dg; asec dg; arcsecdg; asecdg + arc sin dg; asin dg; arcsindg; asindg + arc sinh; asinh; arcsinh; asinh + arc tan dg; atan dg; arctandg; atandg + arc tanh; atanh; arctanh; atanh + arc cos; acos; arccos; acos + arc sin; asin; arcsin; asin + arc tan; atan; arctan; atan + cas; cas + cbrt; cbrt + cos dg; cosdg + cosh; cosh + cos pi; cospi + cot dg; cotdg + cot; cot + cot pi; cotpi + csc dg; cscdg + csc; csc + curt; curt + erfc; erfc + erf; erf + gamma; gamma + inv erfc; inverfc + inv erf; inverf + inverse erfc; inverseerfc + inverse erf; inverseerf + ln1p; ln1p + ln gamma; lngamma + sec dg; secdg + sec; sec + sin dg; sindg + sinh; sinh + sin pi; sinpi + tan dg; tandg + tanh; tanh + tan pi; tanpi + cos; cos + exp; exp + ln; ln + log; log + sin; sin + sqrt; sqrt + tan; tan +# Miscellaneous. # + arc tan2; atan2; arctan2; atan2 + arc tan2 dg; atan2 dg; arctan2dg; atan2dg + beta; beta + betainc; betainc + choose; choose + fact; fact + gamma inc; gammainc + gamma incf; gammaincf + gamma incg; gammaincg + gamma incgf; gammaincgf + lje126; lje126 + ljf126; ljf126 + ln beta; lnbeta + ln choose; lnchoose + ln fact; lnfact +# COMPLEX ops. # + I I + +* +* + I I + +* +* + RE RE + IM IM + ABS ABS + ARG ARG + + + + - - + CONJ CONJ + = = + /= /= + ~= ~= + ^= ^= + EQ EQ + NE NE + + + + - - + * * + / / + ** ** + UP UP + ^ ^ + +:= +:= + -:= -:= + *:= *:= + /:= /:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + DIVAB DIVAB + complex arccosh; cacosh; complexarccosh; cacosh + complex arccos; cacos; complexarccos; cacos + complex arcsinh; casinh; complexarcsinh; casinh + complex arcsin; casin; complexarcsin; casin + complex arctanh; catanh; complexarctanh; catanh + complex arctan; catan; complexarctan; catan + complex cosh; ccosh; complexcosh; ccosh + complex cos; ccos; complexcos; ccos + complex exp; cexp; complexexp; cexp + complex ln; cln; complexln; cln + complex sinh; csinh; complexsinh; csinh + complex sin; csin; complexsin; csin + complex sqrt; csqrt; complexsqrt; csqrt + complex tanh; ctanh; complextanh; ctanh + complex tan; ctan; complextan; ctan +# BOOL ops. # + NOT NOT + ~ ~ + ABS ABS + OR OR + AND AND + & & + XOR XOR + = = + /= /= + ~= ~= + ^= ^= + EQ EQ + NE NE +# CHAR ops. # + = = + /= /= + ~= ~= + ^= ^= + < < + <= <= + > > + >= >= + EQ EQ + NE NE + LT LT + LE LE + GT GT + GE GE + ABS ABS + REPR REPR + is alnum; isalnum + is alpha; isalpha + is cntrl; iscntrl + is digit; isdigit + is graph; isgraph + is lower; islower + is print; isprint + is punct; ispunct + is space; isspace + is upper; isupper + is xdigit; isxdigit + to lower; tolower + to upper; toupper +# BITS ops. # + ABS ABS + BIN BIN + NOT NOT + ~ ~ + = = + /= /= + ~= ~= + ^= ^= + <= <= + >= >= + EQ EQ + NE NE + LE LE + GE GE + < < + > > + LT LT + GT GT + AND AND + & & + OR OR + XOR XOR + + + + - - + * * + OVER OVER + MOD MOD + SHL SHL + UP UP + SHR SHR + DOWN DOWN + ROL ROL + ROR ROR + ELEM ELEM + SET SET + CLEAR CLEAR +# LONG LONG INT in software # + + + + - - + ABS ABS + SIGN SIGN + ODD ODD + ENTIER ENTIER + ROUND ROUND + + + + - - + * * + OVER OVER + % % + MOD MOD + %* %* + +:= +:= + -:= -:= + *:= *:= + %:= %:= + %*:= %*:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + OVERAB OVERAB + MODAB MODAB + / / + EQ EQ + NE NE + GE GE + GT GT + LE LE + LT LT + = = + >= >= + > > + <= <= + < < + /= /= + ^= ^= + ~= ~= + ** ** + UP UP + ^ ^ + I I + +* +* +# LONG LONG REAL in software # + ABS ABS + + + + - - + SIGN SIGN + long long arccosdg; qacos dg; longlongarccosdg; qacosdg + long long arccosh; qacosh; longlongarccosh; qacosh + long long arccotdg; qacot dg; longlongarccotdg; qacotdg + long long arccot; qacot; longlongarccot; qacot + long long arccscdg; qacsc dg; longlongarccscdg; qacscdg + long long arccsc; qacsc; longlongarccsc; qacsc + long long arcsecdg; qasec dg; longlongarcsecdg; qasecdg + long long arcsec; qasec; longlongarcsec; qasec + long long arcsindg; qasin dg; longlongarcsindg; qasindg + long long arcsinh; qasinh; longlongarcsinh; qasinh + long long arctandg; qatan dg; longlongarctandg; qatandg + long long arctanh; qatanh; longlongarctanh; qatanh + long long cas; qcas; longlongcas; qcas + long long cbrt; qcbrt; longlongcbrt; qcbrt + long long cosdg; qcos dg; longlongcosdg; qcosdg + long long cosh; qcosh; longlongcosh; qcosh + long long cospi; qcos pi; longlongcospi; qcospi + long long cotdg; qcot dg; longlongcotdg; qcotdg + long long cotpi; qcot pi; longlongcotpi; qcotpi + long long cot; qcot; longlongcot; qcot + long long cscdg; qcsc dg; longlongcscdg; qcscdg + long long csc; qcsc; longlongcsc; qcsc + long long curt; qcurt; longlongcurt; qcurt + long long erfc; qerfc; longlongerfc; qerfc + long long erf; qerf; longlongerf; qerf + long long gamma; qgamma; longlonggamma; qgamma + long long inverfc; qinverfc; longlonginverfc; qinverfc + long long inverf; qinverf; longlonginverf; qinverf + long long lngamma; qlngamma; longlonglngamma; qlngamma + long long secdg; qsec dg; longlongsecdg; qsecdg + long long sec; qsec; longlongsec; qsec + long long sindg; qsin dg; longlongsindg; qsindg + long long sinh; qsinh; longlongsinh; qsinh + long long sinpi; qsin pi; longlongsinpi; qsinpi + long long tandg; qtan dg; longlongtandg; qtandg + long long tanh; qtanh; longlongtanh; qtanh + long long tanpi; qtan pi; longlongtanpi; qtanpi + long long arccos; qacos; longlongarccos; qacos + long long arcsin; qasin; longlongarcsin; qasin + long long arctan; qatan; longlongarctan; qatan + long long cos; qcos; longlongcos; qcos + long long exp; qexp; longlongexp; qexp + long long ln; qln; longlongln; qln + long long log; qlog; longlonglog; qlog + long long sin; qsin; longlongsin; qsin + long long sqrt; qsqrt; longlongsqrt; qsqrt + long long tan; qtan; longlongtan; qtan + long long arctan2dg; qatan2dg; longlongarctan2dg; qatan2dg + long long arctan2; qatan2; longlongarctan2; qatan2 + long long beta; qbeta; longlongbeta; qbeta + long long gamma incf; qgammaincf; longlonggammaincf; qgammaincf + long long gamma incgf; qgammaincgf; longlonggammaincgf; qgammaincgf + long long gamma inc; qgammainc; longlonggammainc; qgammainc + long long ln beta; qlnbeta; longlonglnbeta; qlnbeta + long long beta inc; qbetainc; longlongbetainc; qbetainc + long long gamma incg; qgammaincg; longlonggammaincg; qgammaincg + + + + - - + * * + / / + ** ** + UP UP + ^ ^ + +:= +:= + -:= -:= + *:= *:= + /:= /:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + DIVAB DIVAB + = = + EQ EQ + /= /= + ~= ~= + ^= ^= + NE NE + < < + LT LT + <= <= + LE LE + > > + GT GT + >= >= + GE GE + ** ** + UP UP + ^ ^ + I I + +* +* +# LONG LONG COMPLEX in software # + RE RE + IM IM + ARG ARG + ABS ABS + + + + - - + CONJ CONJ + + + + - - + * * + / / + ** ** + UP UP + ^ ^ + = = + EQ EQ + /= /= + ~= ~= + ^= ^= + NE NE + +:= +:= + -:= -:= + *:= *:= + /:= /:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + DIVAB DIVAB + long long complex arc cosh; qcacosh; longlongcomplexarccosh; qcacosh + long long complex arc cos; qcacos; longlongcomplexarccos; qcacos + long long complex arc sinh; qcasinh; longlongcomplexarcsinh; qcasinh + long long complex arc sin; qcasin; longlongcomplexarcsin; qcasin + long long complex arc tanh; qcatanh; longlongcomplexarctanh; qcatanh + long long complex arc tan; qcatan; longlongcomplexarctan; qcatan + long long complex cosh; qccosh; longlongcomplexcosh; qccosh + long long complex cos; qccos; longlongcomplexcos; qccos + long long complex exp; qcexp; longlongcomplexexp; qcexp + long long complex ln; qcln; longlongcomplexln; qcln + long long complex sinh; qcsinh; longlongcomplexsinh; qcsinh + long long complex sin; qcsin; longlongcomplexsin; qcsin + long long complex sqrt; qcsqrt; longlongcomplexsqrt; qcsqrt + long long complex tanh; qctanh; longlongcomplextanh; qctanh + long long complex tan; qctan; longlongcomplextan; qctan +# BYTES ops. # + bytespack; bytespack + ELEM ELEM + + + + +:= +:= + PLUSAB PLUSAB + +=: +=: + PLUSTO PLUSTO + = = + /= /= + ~= ~= + ^= ^= + < < + <= <= + > > + >= >= + EQ EQ + NE NE + LT LT + LE LE + GT GT + GE GE +# LONG BYTES ops. # + LENG LENG + SHORTEN SHORTEN + long bytes pack; longbytespack + ELEM ELEM + + + + +:= +:= + PLUSAB PLUSAB + +=: +=: + PLUSTO PLUSTO + = = + /= /= + ~= ~= + ^= ^= + < < + <= <= + > > + >= >= + EQ EQ + NE NE + LT LT + LE LE + GT GT + GE GE +# STRING ops. # + = = + /= /= + ~= ~= + ^= ^= + < < + <= <= + >= >= + > > + EQ EQ + NE NE + LT LT + LE LE + GE GE + GT GT + + + + +:= +:= + PLUSAB PLUSAB + *:= *:= + TIMESAB TIMESAB + +=: +=: + PLUSTO PLUSTO + * * + ELEM ELEM + real path; realpath +# SEMA ops. # + LEVEL LEVEL + UP UP + DOWN DOWN + LEVEL LEVEL + UP UP + DOWN DOWN +# ROWS ops. # + ELEMS ELEMS + LWB LWB + UPB UPB + ELEMS ELEMS + LWB LWB + UPB UPB + SORT SORT +# Some "terminators # + LENG LENG + SHORTEN SHORTEN +# SOUND/RIFF procs. # + newsound; newsound + getsound; getsound + setsound; setsound + RESOLUTION RESOLUTION + CHANNELS CHANNELS + RATE RATE + SAMPLES SAMPLES +# Set up standenv - transput. # + long pi; dpi; longpi; dpi + long max bits; longmaxbits + long max int; longmaxint + long small real; longsmallreal + long max real; longmaxreal + long min real; longminreal + long infinity; longinfinity + long minus infinity; longminusinfinity + long inf; longinf + long min inf; longmininf +# LONG INT in software # + + + + - - + ABS ABS + LENG LENG + SHORTEN SHORTEN + SIGN SIGN + LENG LENG + SHORTEN SHORTEN + LENG LENG + SHORTEN SHORTEN + ODD ODD + ENTIER ENTIER + ROUND ROUND + + + + - - + * * + OVER OVER + % % + MOD MOD + %* %* + +:= +:= + -:= -:= + *:= *:= + %:= %:= + %*:= %*:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + OVERAB OVERAB + MODAB MODAB + = = + EQ EQ + /= /= + ~= ~= + ^= ^= + NE NE + < < + LT LT + <= <= + LE LE + > > + GT GT + >= >= + GE GE + / / + ** ** + UP UP + ^ ^ + I I + +* +* + LENG LENG + SHORTEN SHORTEN +# LONG REAL in software # + + + + - - + ABS ABS + long arc cos dg; dacos dg; longarccosdg; dacosdg + long arc cosh; dacosh; longarccosh; dacosh + long arc cot; dacot; longarccot; dacot + long arc cot dg; dacot dg; longarccotdg; dacotdg + long arc csc; dacsc; longarccsc; dacsc + long arc csc dg; dacsc dg; longarccscdg; dacscdg + long arc sec; dasec; longarcsec; dasec + long arc sec dg; dasec dg; longarcsecdg; dasecdg + long arc sin dg; dasin dg; longarcsindg; dasindg + long arc sinh; dasinh; longarcsinh; dasinh + long arc tan dg; datan dg; longarctandg; datandg + long arc tanh; datanh; longarctanh; datanh + long cas; dcas; longcas; dcas + long cbrt; dcbrt; longcbrt; dcbrt + long cos dg; dcos dg; longcosdg; dcosdg + long cosh; dcosh; longcosh; dcosh + long cos pi; dcos pi; longcospi; dcospi + long cot; dcot; longcot; dcot + long cot dg; dcot dg; longcotdg; dcotdg + long cot pi; dcot pi; longcotpi; dcotpi + long csc; dcsc; longcsc; dcsc + long csc dg; dcsc dg; longcscdg; dcscdg + long curt; dcurt; longcurt; dcurt + long erfc; derfc; longerfc; derfc + long erf; derf; longerf; derf + long gamma; dgamma; longgamma; dgamma + long inv erfc; dinverfc; longinverfc; dinverfc + long inv erf; dinverf; longinverf; dinverf + long ln gamma; dlngamma; longlngamma; dlngamma + long sec dg; dsec dg; longsecdg; dsecdg + long sec; dsec; longsec; dsec + long sin dg; dsin dg; longsindg; dsindg + long sinh; dsinh; longsinh; dsinh + long sin pi; dsin pi; longsinpi; dsinpi + long tan dg; dtan dg; longtandg; dtandg + long tanh; dtanh; longtanh; dtanh + long tan pi; dtan pi; longtanpi; dtanpi +# RR. # + long arc cos; dacos; longarccos; dacos + long arc sin; dasin; longarcsin; dasin + long arc tan; datan; longarctan; datan + long cos; dcos; longcos; dcos + long exp; dexp; longexp; dexp + long ln; dln; longln; dln + long log; dlog; longlog; dlog + long sin; dsin; longsin; dsin + long sqrt; dsqrt; longsqrt; dsqrt + long tan; dtan; longtan; dtan + long next random; longnextrandom + long random; longrandom + long arc tan2; datan2; longarctan2; datan2 + long arc tan2 dg; datan2dg; longarctan2dg; datan2dg + long beta; dbeta; longbeta; dbeta + long gamma inc; dgammainc; longgammainc; dgammainc + long gamma incf; dgammaincf; longgammaincf; dgammaincf + long gamma incgf; dgammaincgf; longgammaincgf; dgammaincgf + long ln beta; dlnbeta; longlnbeta; dlnbeta + long beta inc; dbetainc; longbetainc; dbetainc + long gamma incg; dgammaincg; longgammaincg; dgammaincg + SIGN SIGN + + + + - - + * * + / / + ** ** + UP UP + ^ ^ + +:= +:= + -:= -:= + *:= *:= + /:= /:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + DIVAB DIVAB + = = + EQ EQ + /= /= + ~= ~= + ^= ^= + NE NE + < < + LT LT + <= <= + LE LE + > > + GT GT + >= >= + GE GE + ** ** + UP UP + ^ ^ + I I + +* +* +# LONG COMPLEX in software # + LENG LENG + SHORTEN SHORTEN + LENG LENG + SHORTEN SHORTEN + RE RE + IM IM + ARG ARG + ABS ABS + + + + - - + CONJ CONJ + + + + - - + * * + / / + ** ** + UP UP + ^ ^ + = = + EQ EQ + /= /= + ~= ~= + ^= ^= + NE NE + +:= +:= + -:= -:= + *:= *:= + /:= /:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + DIVAB DIVAB + long complex arc cos; dcacos; longcomplexarccos; dcacos + long complex arc cosh; dcacosh; longcomplexarccosh; dcacosh + long complex arc sin; dcasin; longcomplexarcsin; dcasin + long complex arc sinh; dcasinh; longcomplexarcsinh; dcasinh + long complex arc tan; dcatan; longcomplexarctan; dcatan + long complex arc tanh; dcatanh; longcomplexarctanh; dcatanh + long complex cos; dccos; longcomplexcos; dccos + long complex cosh; dccosh; longcomplexcosh; dccosh + long complex exp; dcexp; longcomplexexp; dcexp + long complex ln; dcln; longcomplexln; dcln + long complex sin; dcsin; longcomplexsin; dcsin + long complex sinh; dcsin; longcomplexsinh; dcsin + long complex sqrt; dcsqrt; longcomplexsqrt; dcsqrt + long complex tan; dctan; longcomplextan; dctan + long complex tanh; dctanh; longcomplextanh; dctanh +# LONG BITS in software # + longbitspack; longbitspack + ABS ABS + BIN BIN + SHORTEN SHORTEN + LENG LENG + NOT NOT + ~ ~ + = = + EQ EQ + /= /= + ~= ~= + ^= ^= + NE NE + <= <= + LE LE + >= >= + GE GE + AND AND + & & + OR OR + XOR XOR + SHL SHL + UP UP + SHR SHR + DOWN DOWN + ELEM ELEM + SET SET + CLEAR CLEAR + long pi; dpi; longpi; dpi + long max bits; longmaxbits + long max int; longmaxint + long small real; longsmallreal + long max real; longmaxreal + long min real; longminreal + long infinity; longinfinity + long minus infinity; longminusinfinity + long inf; longinf + long min inf; longmininf +# LONG INT as 128 bit # + + + + - - + ABS ABS + LENG LENG + SHORTEN SHORTEN + SIGN SIGN + ODD ODD + ENTIER ENTIER + ROUND ROUND + + + + - - + * * + OVER OVER + % % + MOD MOD + %* %* + ** ** + UP UP + ^ ^ + +:= +:= + -:= -:= + *:= *:= + %:= %:= + %*:= %*:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + OVERAB OVERAB + MODAB MODAB + / / + = = + EQ EQ + /= /= + ~= ~= + ^= ^= + NE NE + < < + LT LT + <= <= + LE LE + > > + GT GT + >= >= + GE GE +# LONG REAL as 128 bit # + + + + - - + ABS ABS + SIGN SIGN + + + + - - + * * + / / + ** ** + UP UP + ^ ^ + ** ** + UP UP + ^ ^ + LENG LENG + SHORTEN SHORTEN + LENG LENG + SHORTEN SHORTEN + +:= +:= + -:= -:= + *:= *:= + /:= /:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + DIVAB DIVAB + = = + EQ EQ + /= /= + ~= ~= + ^= ^= + NE NE + < < + LT LT + <= <= + LE LE + > > + GT GT + >= >= + GE GE + long arc cos dg; dacos dg; longarccosdg; dacosdg + long arc cosh; dacosh; longarccosh; dacosh + long arc cot dg; dacot dg; longarccotdg; dacotdg + long arc cot; dacot; longarccot; dacot + long arc csc; dacsc; longarccsc; dacsc + long arc csc dg; dacsc dg; longarccscdg; dacscdg + long arc sec dg; dasec dg; longarcsecdg; dasecdg + long arc sin dg; dasin dg; longarcsindg; dasindg + long arc sinh; dasinh; longarcsinh; dasinh + long arc tan dg; datan dg; longarctandg; datandg + long arc tanh; datanh; longarctanh; datanh + long cbrt; dcbrt; longcbrt; dcbrt + long cos dg; dcos dg; longcosdg; dcosdg + long cosh; dcosh; longcosh; dcosh + long cos pi; dcos pi; longcospi; dcospi + long cot dg; dcot dg; longcotdg; dcotdg + long cot; dcot; longcot; dcot + long cot pi; dcot pi; longcotpi; dcotpi + long csc; dcsc; longcsc; dcsc + long csc dg; dcsc dg; longcscdg; dcscdg + long curt; dcurt; longcurt; dcurt + long erfc; derfc; longerfc; derfc + long erf; derf; longerf; derf + long inv erfc; dinverfc; longinverfc; dinverfc + long inv erf; dinverf; longinverf; dinverf + long gamma; dgamma; longgamma; dgamma + long ln gamma; dlngamma; longlngamma; dlngamma + long sec; dsec; longsec; dsec + long sec dg; dsec dg; longsecdg; dsecdg + long sin dg; dsin dg; longsindg; dsindg + long sinh; dsinh; longsinh; dsinh + long sin pi; dsin pi; longsinpi; dsinpi + long tan dg; dtan dg; longtandg; dtandg + long tanh; dtanh; longtanh; dtanh + long tan pi; dtan pi; longtanpi; dtanpi + long arc cos; dacos; longarccos; dacos + long arc sin; dasin; longarcsin; dasin + long arc tan; datan; longarctan; datan + long cas; dcas; longcas; dcas + long cos; dcos; longcos; dcos + long exp; dexp; longexp; dexp + long ln; dln; longln; dln + long log; dlog; longlog; dlog + long sin; dsin; longsin; dsin + long sqrt; dsqrt; longsqrt; dsqrt + long tan; dtan; longtan; dtan + long next random; longnextrandom + long random; longrandom + long arc tan2 dg; datan2dg; longarctan2dg; datan2dg + long arc tan2; datan2; longarctan2; datan2 + long beta; dbeta; longbeta; dbeta + long gamma incgf; dgammaincgf; longgammaincgf; dgammaincgf + long gamma incf; dgammaincf; longgammaincf; dgammaincf + long gamma inc; dgammainc; longgammainc; dgammainc + long ln beta; dlnbeta; longlnbeta; dlnbeta + long beta inc; dbetainc; longbetainc; dbetainc + long gamma incg; dgammaincg; longgammaincg; dgammaincg +# LONG BITS as 128 bit # + long bits pack; longbitspack + SHORTEN SHORTEN + LENG LENG + ABS ABS + BIN BIN + NOT NOT + ~ ~ + AND AND + & & + OR OR + XOR XOR + + + + - - + * * + OVER OVER + MOD MOD + = = + /= /= + ~= ~= + ^= ^= + <= <= + >= >= + EQ EQ + NE NE + LE LE + GE GE + < < + > > + LT LT + GT GT + ELEM ELEM + SET SET + CLEAR CLEAR + SHL SHL + UP UP + SHR SHR + DOWN DOWN + ROL ROL + ROR ROR +# LONG COMPLEX as 2 x 128 bit. # + LENG LENG + SHORTEN SHORTEN + I I + +* +* + I I + +* +* + RE RE + IM IM + ABS ABS + ARG ARG + + + + - - + CONJ CONJ + = = + /= /= + ~= ~= + ^= ^= + EQ EQ + NE NE + + + + - - + * * + / / + ** ** + UP UP + ^ ^ + +:= +:= + -:= -:= + *:= *:= + /:= /:= + PLUSAB PLUSAB + MINUSAB MINUSAB + TIMESAB TIMESAB + DIVAB DIVAB + long complex arc cosh; dcacosh; longcomplexarccosh; dcacosh + long complex arc cos; dcacos; longcomplexarccos; dcacos + long complex arc sinh; dcasinh; longcomplexarcsinh; dcasinh + long complex arc sin; dcasin; longcomplexarcsin; dcasin + long complex arc tanh; dcatanh; longcomplexarctanh; dcatanh + long complex arc tan; dcatan; longcomplexarctan; dcatan + long complex atanh; dcatanh; longcomplexatanh; dcatanh + long complex cosh; dccosh; longcomplexcosh; dccosh + long complex cos; dccos; longcomplexcos; dccos + long complex exp; dcexp; longcomplexexp; dcexp + long complex ln; dcln; longcomplexln; dcln + long complex sinh; dcsinh; longcomplexsinh; dcsinh + long complex sin; dcsin; longcomplexsin; dcsin + long complex sqrt; dcsqrt; longcomplexsqrt; dcsqrt + long complex tanh; dctanh; longcomplextanh; dctanh + long complex tan; dctan; longcomplextan; dctan + mpfr long gamma inc; mpfr dgammainc; mpfrlonggammainc; mpfrdgammainc + mpfr long long beta; mpfr qbeta; mpfrlonglongbeta; mpfrqbeta + mpfr long long gammainc; mpfr qgammainc; mpfrlonglonggammainc; mpfrqgammainc + mpfr long long lnbeta; mpfr qlnbeta; mpfrlonglonglnbeta; mpfrqlnbeta + mpfr long long betainc; mpfr qbetainc; mpfrlonglongbetainc; mpfrqbetainc + mpfr long long erfc; mpfr qerfc; mpfrlonglongerfc; mpfrqerfc + mpfr long long erf; mpfr qerf; mpfrlonglongerf; mpfrqerf + mpfr long long gamma; mpfr qgamma; mpfrlonglonggamma; mpfrqgamma + mpfr long long inverfc; mpfr qinverfc; mpfrlonglonginverfc; mpfrqinverfc + mpfr long long inverf; mpfr qinverf; mpfrlonglonginverf; mpfrqinverf + mpfr long long lngamma; mpfr qlngamma; mpfrlonglonglngamma; mpfrqlngamma + mpfr mp; mpfrmp + blank character; blankcharacter + formfeed character; formfeedcharacter + formfeed char; formfeedchar + newline character; newlinecharacter + newline char; newlinechar + null character; nullcharacter + tab character; tabcharacter + tab char; tabchar + blank char; blankchar + blank; blank + eof character; eofcharacter + eof char; eofchar + error char; errorchar + exp char; expchar + flip; flip + flop; flop + null char; nullchar + bits; bits + whole; whole + fixed; fixed + float; float + real; real + stand in; standin + stand out; standout + stand back; standback + stand error; standerror + stand in channel; standinchannel + stand out channel; standoutchannel + stand draw channel; standdrawchannel + stand back channel; standbackchannel + stand error channel; standerrorchannel + make term; maketerm + char in string; charinstring + last char in string; lastcharinstring + string in string; stringinstring + idf; idf + term; term + program idf; programidf +# Event routines. # + on file end; onfileend + on page end; onpageend + on line end; onlineend + on logical file end; onlogicalfileend + on physical file end; onphysicalfileend + on format end; onformatend + on format error; onformaterror + on value error; onvalueerror + on open error; onopenerror + on transput error; ontransputerror +# Enquiries on files. # + draw possible; drawpossible + end of file; endoffile + end of line; endofline + eof; eof + eoln; eoln + rewind possible; rewindpossible + bin possible; binpossible + compressible; compressible + get possible; getpossible + put possible; putpossible + reidf possible; reidfpossible + reset possible; resetpossible + set possible; setpossible +# Handling of files. # + open; open + append; append + establish; establish + associate; associate + cooked; cooked + raw; raw + rewind; rewind + backspace; backspace + close; close + create; create + erase; erase + lock; lock + newline; newline + newpage; newpage + reset; reset + scratch; scratch + space; space + set; set + seek; seek + read; read + read bin; readbin + readf; readf + print bin; printbin + printf; printf + print; print + write bin; writebin + writef; writef + write; write + get; get + getf; getf + get bin; getbin + put; put + putf; putf + put bin; putbin + gets; gets + getsf; getsf + puts; puts + putsf; putsf + string; string + stringf; stringf + read line; readline +# Set up standenv - extensions. # +# UNIX things. # + rows; rows + columns; columns + argc; argc + a68g argc; a68gargc + errno; errno + fork; fork + get pwd; getpwd + set pwd; setpwd + file is directory; fileisdirectory + file is block device; fileisblockdevice + file is char device; fileischardevice + file is regular; fileisregular + file is fifo; fileisfifo + file is link; fileislink + file mode; filemode + argv; argv + a68g argv; a68gargv + reset errno; reseterrno + str error; strerror + exec; exec + execve; execve + create pipe; createpipe + exec sub; execsub + execve child; execvechild + exec sub pipeline; execsubpipeline + execve childpipe; execvechildpipe + exec sub output; execsuboutput + execve output; execveoutput + get env; getenv + wait pid; waitpid + utc time; utctime + local time; localtime + grep in string; grepinstring + grep in substring; grepinsubstring + sub in string; subinstring + peek char; peekchar + get directory; getdirectory + http timeout; httptimeout + https timeout; httpstimeout + http content; httpcontent + https content; httpscontent +# Drawing. # + draw device; drawdevice + make device; makedevice + draw aspect; drawaspect + draw clear; drawclear + draw erase; drawerase + draw flush; drawflush + draw show; drawshow + draw fill style; drawfillstyle + draw get colour name; drawgetcolourname + draw get color name; drawgetcolorname + draw color; drawcolor + draw colour; drawcolour + draw background color; drawbackgroundcolor + draw background colour; drawbackgroundcolour + draw circle; drawcircle + draw ball; drawball + draw star; drawstar + draw point; drawpoint + draw line; drawline + draw move; drawmove + draw rect; drawrect + draw text; drawtext + draw linestyle; drawlinestyle + draw font name; drawfontname + draw linewidth; drawlinewidth + draw font size; drawfontsize + draw text angle; drawtextangle + draw color name; drawcolorname + draw colour name; drawcolourname + draw background color name; drawbackgroundcolorname + draw background colour name; drawbackgroundcolourname + curses start; cursesstart + curses end; cursesend + curses clear; cursesclear + curses refresh; cursesrefresh + curses green; cursesgreen + curses cyan; cursescyan + curses red; cursesred + curses yellow; cursesyellow + curses magenta; cursesmagenta + curses blue; cursesblue + curses white; curseswhite + curses green inverse; cursesgreeninverse + curses cyan inverse; cursescyaninverse + curses red inverse; cursesredinverse + curses yellow inverse; cursesyellowinverse + curses magenta inverse; cursesmagentainverse + curses blue inverse; cursesblueinverse + curses white inverse; curseswhiteinverse + curses get char; cursesgetchar + curses put char; cursesputchar + curses move; cursesmove + curses lines; curseslines + curses columns; cursescolumns + curses delchar; cursesdelchar + pq connect db; pqconnectdb + pq finish; pqfinish + pq reset; pqreset + pq parameter status; pqparameterstatus + pq exec; pqexec + pq fnumber; pqfnumber + pq ntuples; pqntuples + pq nfields; pqnfields + pq cmd status; pqcmdstatus + pq cmd tuples; pqcmdtuples + pq error message; pqerrormessage + pq result error message; pqresulterrormessage + pq db; pqdb + pq user; pquser + pq pass; pqpass + pq host; pqhost + pq port; pqport + pq tty; pqtty + pq options; pqoptions + pq protocol version; pqprotocolversion + pq server version; pqserverversion + pq socket; pqsocket + pq backend pid; pqbackendpid + pq fname; pqfname + pq fformat; pqfformat + pq get value; pqgetvalue + pq get is null; pqgetisnull + sig segv; sigsegv + +#::::::::::::: +prelude-bits.c +:::::::::::::# + +# Multiple precision BITS. # + long long bits width; longlongbitswidth + long long max bits; longlongmaxbits + long long bits pack; longlongbitspack + LENG LENG + ABS ABS + BIN BIN + NOT NOT + ~ ~ + SHORTEN SHORTEN + = = + EQ EQ + /= /= + ~= ~= + ^= ^= + NE NE + <= <= + LE LE + >= >= + GE GE + AND AND + & & + OR OR + XOR XOR + SHL SHL + UP UP + SHR SHR + DOWN DOWN + ELEM ELEM + SET SET + CLEAR CLEAR + +#::::::::::::: +prelude-mathlib.c +:::::::::::::# + +# R specific special functions # + r di gamma; rdigamma + r tri gamma; rtrigamma + r tetra gamma; rtetragamma + r penta gamma; rpentagamma + r psi gamma; rpsigamma +# R distribution related functions # + r r t; rrt + r r chisq; rrchisq + r r exp; rrexp + r r geom; rrgeom + r r pois; rrpois + r r signrank; rrsignrank + r r beta; rrbeta + r r binom; rrbinom + r r cauchy; rrcauchy + r r f; rrf + r r logis; rrlogis + r r lnorm; rrlnorm + r rn binom; rrnbinom + r r norm; rrnorm + r r unif; rrunif + r r weibull; rrweibull + r r wilcox; rrwilcox + r d t; rdt + r d chisq; rdchisq + r d exp; rdexp + r d geom; rdgeom + r d pois; rdpois + r d norm; rdnorm + r d beta; rdbeta + r d binom; rdbinom + r dn chisq; rdnchisq + r d cauchy; rdcauchy + r d f; rdf + r d logis; rdlogis + r d lnorm; rdlnorm + r dn binom; rdnbinom + r d nt; rdnt + r d unif; rdunif + r d weibull; rdweibull + r d nf; rdnf + r d hyper; rdhyper + r r hyper; rrhyper + r p t; rpt + r q t; rqt + r p chisq; rpchisq + r q chisq; rqchisq + r p exp; rpexp + r q exp; rqexp + r p geom; rpgeom + r q geom; rqgeom + r p pois; rppois + r q pois; rqpois + r p norm; rpnorm + r q norm; rqnorm + r p beta; rpbeta + r q beta; rqbeta + r p binom; rpbinom + r q binom; rqbinom + r pn chisq; rpnchisq + r qn chisq; rqnchisq + r p cauchy; rpcauchy + r q cauchy; rqcauchy + r p f; rpf + r q f; rqf + r p logis; rplogis + r q logis; rqlogis + r p l norm; rplnorm + r q l norm; rqlnorm + r pn binom; rpnbinom + r qn binom; rqnbinom + r p nt; rpnt + r q nt; rqnt + r p unif; rpunif + r q unif; rqunif + r p weibull; rpweibull + r q weibull; rqweibull + r p tukey; rptukey + r q tukey; rqtukey + r p nf; rpnf + r q nf; rqnf + r p hyper; rphyper + r q hyper; rqhyper + r d sign rank; rdsignrank + r d wilcox; rdwilcox + r p sign rank; rpsignrank + r q sign rank; rqsignrank + r p wilcox; rpwilcox + r q wilcox; rqwilcox + +#::::::::::::: +prelude-gsl.c +:::::::::::::# + +# Standard prelude definitions from GSL. # + airy ai; airyai + airy ai scaled; airyaiscaled + airy bi; airybi + airy bi scaled; airybiscaled + bessel in0; besselin0 + bessel in1; besselin1 + bessel in0 scaled; besselin0scaled + bessel in1 scaled; besselin1scaled + bessel jn0; besseljn0 + bessel jn1; besseljn1 + bessel kn0; besselkn0 + bessel kn1; besselkn1 + bessel kn0 scaled; besselkn0scaled + bessel kn1 scaled; besselkn1scaled + bessel yn0; besselyn0 + bessel yn1; besselyn1 + expinte1; expinte1 + expintei; expintei + dawson; dawson + exprel; exprel + beta inc gsl; betaincgsl + poch; poch + digamma; digamma + airy ai derivative; airyaiderivative + airy ai deriv; airyaideriv + airy ai deriv scaled; airyaiderivscaled + airy bi derivative; airybiderivative + airy bi deriv; airybideriv + airy bi deriv scaled; airybiderivscaled + airy zero ai deriv; airyzeroaideriv + airy zero ai; airyzeroai + airy zero bi deriv; airyzerobideriv + airy zero bi; airyzerobi + angle restrict pos; anglerestrictpos + angle restrict symm; anglerestrictsymm + atan int; atanint + bessel il0 scaled; besselil0scaled + bessel il1 scaled; besselil1scaled + bessel il2 scaled; besselil2scaled + bessel il scaled; besselilscaled + bessel in; besselin + bessel in scaled; besselinscaled + bessel inu; besselinu + bessel inu scaled; besselinuscaled + bessel jl0; besseljl0 + bessel jl1; besseljl1 + bessel jl2; besseljl2 + bessel jl; besseljl + bessel jn; besseljn + bessel kl0 scaled; besselkl0scaled + bessel kl1 scaled; besselkl1scaled + bessel kl2 scaled; besselkl2scaled + bessel kl scaled; besselklscaled + bessel kn; besselkn + bessel kn scaled; besselknscaled + bessel kn_scaled; besselkn_scaled + bessel knu; besselknu + bessel knu scaled; besselknuscaled + bessel ln knu; bessellnknu + bessel yl0; besselyl0 + bessel yl1; besselyl1 + bessel yl2; besselyl2 + bessel yl; besselyl + bessel yn; besselyn + bessel ynu; besselynu + bessel zero j0; besselzeroj0 + bessel zero j1; besselzeroj1 + bessel zero jnu; besselzerojnu + chi; chi + ci; ci + clausen; clausen + conical p0; conicalp0 + conical p1; conicalp1 + conical pcylreg; conicalpcylreg + conical phalf; conicalphalf + conical pmhalf; conicalpmhalf + conical psphreg; conicalpsphreg + debye 1; debye1 + debye 2; debye2 + debye 3; debye3 + debye 4; debye4 + debye 5; debye5 + debye 6; debye6 + dilog; dilog + doublefact; doublefact + ellint d; ellintd + ellint ecomp; ellintecomp + ellint e; ellinte + ellint f; ellintf + ellint kcomp; ellintkcomp + ellint pcomp; ellintpcomp + ellint p; ellintp + ellint rc; ellintrc + ellint rd; ellintrd + ellint rf; ellintrf + ellint rj; ellintrj + etaint; etaint + eta; eta + expint 3; expint3 + expint e2; expinte2 + expint en; expinten + expm1; expm1 + exprel2; exprel2 + expreln; expreln + fermi dirac 0; fermidirac0 + fermi dirac 1; fermidirac1 + fermi dirac 2; fermidirac2 + fermi dirac 3half; fermidirac3half + fermi dirac half; fermidirachalf + fermi dirac inc0; fermidiracinc0 + fermi dirac int; fermidiracint + fermi dirac m1; fermidiracm1 + fermi dirac mhalf; fermidiracmhalf + gamma inc gsl; gammaincgsl + gamma inc p; gammaincp + gamma inc q; gammaincq + gamma inv; gammainv + gamma star; gammastar + gegenpoly 1 real; gegenpoly1real + gegenpoly 2 real; gegenpoly2real + gegenpoly 3 real; gegenpoly3real + gegenpoly n real; gegenpolynreal + hermite func; hermitefunc + hypot; hypot + hzeta; hzeta + laguerre 1 real; laguerre1real + laguerre 2 real; laguerre2real + laguerre 3 real; laguerre3real + laguerre n real; laguerrenreal + lambert w0; lambertw0 + lambert wm1; lambertwm1 + legendre h3d0; legendreh3d0 + legendre h3d1; legendreh3d1 + legendre h3d; legendreh3d + legendre p1; legendrep1 + legendre p2; legendrep2 + legendre p3; legendrep3 + legendre pl; legendrepl + legendre q0; legendreq0 + legendre q1; legendreq1 + legendre ql; legendreql + ln cosh; lncosh + ln doublefact; lndoublefact + ln poch; lnpoch + ln sinh; lnsinh + ln1 plusxmx; ln1plusxmx + ln1 plusx; ln1plusx + ln abs; lnabs + poch rel; pochrel + psi 1 int; psi1int + psi 1 piy; psi1piy + psi 1; psi1 + psi int; psiint + psin; psin + psi; psi + shi; shi + sinc; sinc + si; si + synchrotron 1; synchrotron1 + synchrotron 2; synchrotron2 + taylor coeff; taylorcoeff + transport 2; transport2 + transport 3; transport3 + transport 4; transport4 + transport 5; transport5 + zeta int; zetaint + zeta m1 int; zetam1int + zeta m1; zetam1 + zeta; zeta +# Vector and matrix pretty print. # + print vector; printvector + print matrix; printmatrix +# Vector and matrix monadic. # + + + + - - + CV CV + RV RV + + + + - - + T T + INV INV + PINV PINV + MEAN MEAN + DET DET + TRACE TRACE + + + + - - + + + + - - + T T + INV INV + DET DET + TRACE TRACE +# Vector and matrix dyadic. # + = = + /= /= + + + + - - + +:= +:= + PLUSAB PLUSAB + -:= -:= + MINUSAB MINUSAB + = = + /- /- + + + + - - + BEFORE BEFORE + ABOVE ABOVE + +:= +:= + PLUSAB PLUSAB + -:= -:= + MINUSAB MINUSAB + = = + /= /= + + + + - - + +:= +:= + PLUSAB PLUSAB + -:= -:= + MINUSAB MINUSAB + = = + /= /= + + + + - - + +:= +:= + PLUSAB PLUSAB + -:= -:= + MINUSAB MINUSAB +# Vector and matrix scaling. # + * * + / / + * * + / / + * * + / / + * * + / / + *:= *:= + /:= /:= + *:= *:= + /:= /:= + *:= *:= + /:= /:= + *:= *:= + /:= /:= + * * +# Matrix times vector or matrix. # + * * +# Vector and matrix miscellaneous. # + vector echo; vectorecho + matrix echo; matrixecho + compl vector echo; complvectorecho + compl matrix echo; complmatrixecho + * * + NORM NORM + DYAD DYAD +# Principle Component Analysis. # + pcacv; pcacv + pcasvd; pcasvd +# Total Least Square regression. # + ols; ols + tls; tls +# Partial Least Squares regression. # + pcr; pcr + pls1; pls1 + pls2; pls2 +# Routine left columns, a GSL alternative to trimming columns. # + left columns; leftcolumns +# Moore-Penrose pseudo inverse. # + pseudo inv; pseudoinv +# LU decomposition. # + lu decomp; ludecomp + lu det; ludet + lu inv; luinv + lu solve; lusolve + complex lu decomp; complexludecomp + complex lu det; complexludet + complex lu inv; complexluinv + complex lu solve; complexlusolve +# SVD decomposition. # + svd decomp; svddecomp + svd solve; svdsolve +# QR decomposition. # + qr decomp; qrdecomp + qr solve; qrsolve + qr lssolve; qrlssolve +# Cholesky decomposition. # + cholesky decomp; choleskydecomp + cholesky solve; choleskysolve +# Constants ex GSL. # + cgs speed of light; cgsspeedoflight + cgs gravitational constant; cgsgravitationalconstant + cgs planck constant; cgsplanckconstant + cgs planck constant bar; cgsplanckconstantbar + cgs astronomical unit; cgsastronomicalunit + cgs light year; cgslightyear + cgs parsec; cgsparsec + cgs grav accel; cgsgravaccel + cgs electron volt; cgselectronvolt + cgs mass electron; cgsmasselectron + cgs mass muon; cgsmassmuon + cgs mass proton; cgsmassproton + cgs mass neutron; cgsmassneutron + cgs rydberg; cgsrydberg + cgs boltzmann; cgsboltzmann + cgs bohr magneton; cgsbohrmagneton + cgs nuclear magneton; cgsnuclearmagneton + cgs electron magnetic moment; cgselectronmagneticmoment + cgs proton magnetic moment; cgsprotonmagneticmoment + cgs molar gas; cgsmolargas + cgs standard gas volume; cgsstandardgasvolume + cgs minute; cgsminute + cgs hour; cgshour + cgs day; cgsday + cgs week; cgsweek + cgs inch; cgsinch + cgs foot; cgsfoot + cgs yard; cgsyard + cgs mile; cgsmile + cgs nautical mile; cgsnauticalmile + cgs fathom; cgsfathom + cgs mil; cgsmil + cgs point; cgspoint + cgs texpoint; cgstexpoint + cgs micron; cgsmicron + cgs angstrom; cgsangstrom + cgs hectare; cgshectare + cgs acre; cgsacre + cgs barn; cgsbarn + cgs liter; cgsliter + cgs us gallon; cgsusgallon + cgs quart; cgsquart + cgs pint; cgspint + cgs cup; cgscup + cgs fluid ounce; cgsfluidounce + cgs table spoon; cgstablespoon + cgs tea spoon; cgsteaspoon + cgs canadian gallon; cgscanadiangallon + cgs uk gallon; cgsukgallon + cgs miles per hour; cgsmilesperhour + cgs kilometers per hour; cgskilometersperhour + cgs knot; cgsknot + cgs pound mass; cgspoundmass + cgs ounce mass; cgsouncemass + cgs ton; cgston + cgs metricton; cgsmetricton + cgs ukton; cgsukton + cgs troy ounce; cgstroyounce + cgs carat; cgscarat + cgs unified atomic mass; cgsunifiedatomicmass + cgs gram force; cgsgramforce + cgs pound force; cgspoundforce + cgs kilo pound force; cgskilopoundforce + cgs poundal; cgspoundal + cgs calorie; cgscalorie + cgs btu; cgsbtu + cgs therm; cgstherm + cgs horsepower; cgshorsepower + cgs bar; cgsbar + cgs std atmosphere; cgsstdatmosphere + cgs torr; cgstorr + cgs meter of mercury; cgsmeterofmercury + cgs inch of mercury; cgsinchofmercury + cgs inch of water; cgsinchofwater + cgs psi; cgspsi + cgs poise; cgspoise + cgs stokes; cgsstokes + cgs faraday; cgsfaraday + cgs electron charge; cgselectroncharge + cgs gauss; cgsgauss + cgs stilb; cgsstilb + cgs lumen; cgslumen + cgs lux; cgslux + cgs phot; cgsphot + cgs foot candle; cgsfootcandle + cgs lambert; cgslambert + cgs foot lambert; cgsfootlambert + cgs curie; cgscurie + cgs roentgen; cgsroentgen + cgs rad; cgsrad + cgs solar mass; cgssolarmass + cgs bohr radius; cgsbohrradius + cgs newton; cgsnewton + cgs dyne; cgsdyne + cgs joule; cgsjoule + cgs erg; cgserg + mksa speed of light; mksaspeedoflight + mksa gravitational constant; mksagravitationalconstant + mksa planck constant; mksaplanckconstant + mksa planck constant bar; mksaplanckconstantbar + mksa vacuum permeability; mksavacuumpermeability + mksa astronomical unit; mksaastronomicalunit + mksa light year; mksalightyear + mksa parsec; mksaparsec + mksa grav accel; mksagravaccel + mksa electron volt; mksaelectronvolt + mksa mass electron; mksamasselectron + mksa mass muon; mksamassmuon + mksa mass proton; mksamassproton + mksa mass neutron; mksamassneutron + mksa rydberg; mksarydberg + mksa boltzmann; mksaboltzmann + mksa bohr magneton; mksabohrmagneton + mksa nuclear magneton; mksanuclearmagneton + mksa electron magnetic moment; mksaelectronmagneticmoment + mksa proton magnetic moment; mksaprotonmagneticmoment + mksa molar gas; mksamolargas + mksa standard gas volume; mksastandardgasvolume + mksa minute; mksaminute + mksa hour; mksahour + mksa day; mksaday + mksa week; mksaweek + mksa inch; mksainch + mksa foot; mksafoot + mksa yard; mksayard + mksa mile; mksamile + mksa nautical mile; mksanauticalmile + mksa fathom; mksafathom + mksa mil; mksamil + mksa point; mksapoint + mksa texpoint; mksatexpoint + mksa micron; mksamicron + mksa angstrom; mksaangstrom + mksa hectare; mksahectare + mksa acre; mksaacre + mksa barn; mksabarn + mksa liter; mksaliter + mksa us gallon; mksausgallon + mksa quart; mksaquart + mksa pint; mksapint + mksa cup; mksacup + mksa fluid ounce; mksafluidounce + mksa table spoon; mksatablespoon + mksa tea spoon; mksateaspoon + mksa canadian gallon; mksacanadiangallon + mksa uk gallon; mksaukgallon + mksa miles per hour; mksamilesperhour + mksa kilometers per hour; mksakilometersperhour + mksa knot; mksaknot + mksa pound mass; mksapoundmass + mksa ounce mass; mksaouncemass + mksa ton; mksaton + mksa metricton; mksametricton + mksa ukton; mksaukton + mksa troy ounce; mksatroyounce + mksa carat; mksacarat + mksa unified atomic mass; mksaunifiedatomicmass + mksa gram force; mksagramforce + mksa pound force; mksapoundforce + mksa kilo pound force; mksakilopoundforce + mksa poundal; mksapoundal + mksa calorie; mksacalorie + mksa btu; mksabtu + mksa therm; mksatherm + mksa horsepower; mksahorsepower + mksa bar; mksabar + mksa stdatmosphere; mksastdatmosphere + mksa torr; mksatorr + mksa meter of mercury; mksameterofmercury + mksa inch of mercury; mksainchofmercury + mksa inch of water; mksainchofwater + mksa psi; mksapsi + mksa poise; mksapoise + mksa stokes; mksastokes + mksa faraday; mksafaraday + mksa electron charge; mksaelectroncharge + mksa gauss; mksagauss + mksa stilb; mksastilb + mksa lumen; mksalumen + mksa lux; mksalux + mksa phot; mksaphot + mksa foot candle; mksafootcandle + mksa lambert; mksalambert + mksa foot lambert; mksafootlambert + mksa curie; mksacurie + mksa roentgen; mksaroentgen + mksa rad; mksarad + mksa solar mass; mksasolarmass + mksa bohr radius; mksabohrradius + mksa vacuum permittivity; mksavacuumpermittivity + mksa newton; mksanewton + mksa dyne; mksadyne + mksa joule; mksajoule + mksa erg; mksaerg + num fine structure; numfinestructure + num avogadro; numavogadro + num yotta; numyotta + num zetta; numzetta + num exa; numexa + num peta; numpeta + num tera; numtera + num giga; numgiga + num mega; nummega + num kilo; numkilo + num milli; nummilli + num micro; nummicro + num nano; numnano + num pico; numpico + num femto; numfemto + num atto; numatto + num zepto; numzepto + num yocto; numyocto +# FFT. # + primefactors; primefactors + fft complex forward; fftcomplexforward + fft complex backward; fftcomplexbackward + fft complex inverse; fftcomplexinverse + fft forward; fftforward + fft backward; fftbackward + fft inverse; fftinverse +# Laplace. # + laplace; laplace + + +# vim: syntax=algol68 ts=48 +# diff --git a/runtime/syntax/testdir/input/algol68_denotations.a68 b/runtime/syntax/testdir/input/algol68_denotations.a68 new file mode 100644 index 0000000000..cc58007aa9 --- /dev/null +++ b/runtime/syntax/testdir/input/algol68_denotations.a68 @@ -0,0 +1,115 @@ +# Algol 68 Denotations # + + +# BOOL # + +BOOL x = TRUE +BOOL x = FALSE + + +# INT # + +INT x = 0 +INT x = 42 + +# whitespace # +INT x = 1 000 000 + +# monadic operators # +INT x = -42 +INT x = +42 + + +# REAL # + +REAL x = .42 +REAL x = 42.42 + +REAL x = 42e42 +REAL x = 42E42 +REAL x = 42\42 +REAL x = 42⏨42 + +REAL x = 42e-42 +REAL x = 42E-42 +REAL x = 42\-42 +REAL x = 42⏨-42 + +REAL x = 42e+42 +REAL x = 42E+42 +REAL x = 42\+42 +REAL x = 42⏨+42 + +REAL x = .42e42 +REAL x = .42E42 +REAL x = .42\42 +REAL x = .42⏨42 + +REAL x = .42e-42 +REAL x = .42E-42 +REAL x = .42\-42 +REAL x = .42⏨-42 + +REAL x = .42e+42 +REAL x = .42E+42 +REAL x = .42\+42 +REAL x = .42⏨+42 + +REAL x = 42.42e42 +REAL x = 42.42E42 +REAL x = 42.42\42 +REAL x = 42.42⏨42 + +REAL x = 42.42e-42 +REAL x = 42.42E-42 +REAL x = 42.42\-42 +REAL x = 42.42⏨-42 + +REAL x = 42.42e+42 +REAL x = 42.42E+42 +REAL x = 42.42\+42 +REAL x = 42.42⏨+42 + +# whitespace # +REAL x = 4 2 . 4 2 e + 4 2 +REAL x = 4 2 . 4 2 \ + 4 2 +REAL x = 4 2 . 4 2 ⏨ + 4 2 + +# monadic operators # +REAL x = -42.42 +REAL x = +42.42 + + +# BITS # + +BITS x = 2r101010 +BITS x = 4r222 +BITS x = 8r52 +BITS x = 16r2a + +# whitespace # +BITS x = 2r 1010 1010 + +# unmatched by bad pattern in legacy versions of syntax file # +BITS x = 16r99 + + +# STRING # + +STRING s = "Foo Bar" + +# quote escape # +STRING s = """" +STRING s = " "" " +STRING s = "Foo""" +STRING s = """Bar" +STRING s = "Foo""Bar" + +# line continuation # +STRING s = "Foo\ + Bar" +STRING s = "Foo""\ +Bar" +STRING s = "Foo\ +""Bar" + From c23bfd7922dc20d83c96203d9f8840d0673245cf Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 26 Apr 2026 14:36:23 +0000 Subject: [PATCH 117/545] runtime(help): fix wrong check for existing HelpComplete function To check for an existing HelpComplete function, exists('*HelpComplete') should be used, as exists('HelpComplete') still returns 0 after sourcing the ftplugin. closes: #20073 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- runtime/ftplugin/help.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/ftplugin/help.vim b/runtime/ftplugin/help.vim index e77484bffb..2f4de14055 100644 --- a/runtime/ftplugin/help.vim +++ b/runtime/ftplugin/help.vim @@ -24,7 +24,7 @@ if has("conceal") setlocal cole=2 cocu=nc endif -if !exists('HelpComplete') +if !exists('*HelpComplete') func HelpComplete(findstart, base) if a:findstart let colnr = col('.') - 1 " Get the column number before the cursor From 076366bd4e8b89e722efc72a1b62d5b3d2ba62e0 Mon Sep 17 00:00:00 2001 From: Doug Kearns Date: Sun, 26 Apr 2026 14:52:21 +0000 Subject: [PATCH 118/545] runtime(javascript): Fix regex highlighting after `(` - Fix regex highlighting after opening parens, javascriptParens was matching later. Fixes issue #20069. - Add missing regex flags. - Mark the file as unmaintained. Thanks Claudio for all your work. closes: #20076 Signed-off-by: Doug Kearns Signed-off-by: Christian Brabandt --- runtime/syntax/javascript.vim | 31 ++++++++++--------- .../syntax/testdir/dumps/javascript_00.dump | 20 ++++++++++++ runtime/syntax/testdir/input/javascript.js | 5 +++ 3 files changed, 41 insertions(+), 15 deletions(-) create mode 100644 runtime/syntax/testdir/dumps/javascript_00.dump create mode 100644 runtime/syntax/testdir/input/javascript.js diff --git a/runtime/syntax/javascript.vim b/runtime/syntax/javascript.vim index c89fcedce6..c910a668a2 100644 --- a/runtime/syntax/javascript.vim +++ b/runtime/syntax/javascript.vim @@ -1,18 +1,12 @@ " Vim syntax file -" Language: JavaScript -" Maintainer: Claudio Fleiner -" Updaters: Scott Shattuck (ss) -" URL: http://www.fleiner.com/vim/syntax/javascript.vim -" Changes: (ss) added keywords, reserved words, and other identifiers -" (ss) repaired several quoting and grouping glitches -" (ss) fixed regex parsing issue with multiple qualifiers [gi] -" (ss) additional factoring of keywords, globals, and members -" Last Change: 2022 Jun 09 -" 2013 Jun 12: adjusted javaScriptRegexpString (Kevin Locke) -" 2018 Apr 14: adjusted javaScriptRegexpString (LongJohnCoder) -" 2024 Aug 14: fix a few stylistic issues (#15480) -" 2025 Aug 07: as is a reserved keyword (#17912) -" 2025 Sep 24: using is a reserved keyword (Devin Weaver) +" Language: JavaScript +" Maintainer: This runtime file is looking for a maintainer. +" Previous Maintainer: Claudio Fleiner +" Contributors: Scott Shattuck +" Kevin Locke +" LongJohnCoder +" Devin Weaver +" Last Change: 2026 Apr 26 " tuning parameters: " unlet javaScript_fold @@ -52,7 +46,14 @@ syn match javaScriptNumber "\<\d\+\(_\d\+\)*[eE][+-]\?\d\+\>" syn match javaScriptNumber "\<[1-9]\d*\(_\d\+\)*\(\.\(\d\+\(_\d\+\)*\([eE][+-]\?\d\+\)\?\)\?\)\?\>" syn match javaScriptNumber "\<\(\d\+\(_\d\+\)*\)\?\.\d\+\(_\d\+\)*\([eE][+-]\?\d\+\)\?\>" syn match javaScriptNumber "\<\d\+\(_\d\+\)*\.\(\d\+\(_\d\+\)*\([eE][+-]\?\d\+\)\?\)\?\>" -syn region javaScriptRegexpString start=+[,(=+]\s*/[^/*]+ms=e-1,me=e-1 skip=+\\\\\|\\/+ end=+/[gimuys]\{0,2\}\s*$+ end=+/[gimuys]\{0,2\}\s*[+;.,)\]}]+me=e-1 end=+/[gimuys]\{0,2\}\s\+\/+me=e-1 contains=@htmlPreproc,javaScriptComment oneline +syn region javaScriptRegexpString + \ start=+\%([,(=+]\s*\)\@8<=/[^/*]+ + \ skip=+\\\\\|\\/+ + \ end=+/[dgimsuvy]\{0,7\}\ze\s*$+ + \ end=+/[dgimsuvy]\{0,7\}\ze\s*[+;.,)\]}]+ + \ end=+/[dgimsuvy]\{0,7\}\ze\s\+\/+ + \ contains=@htmlPreproc,javaScriptComment + \ oneline syn keyword javaScriptConditional if else switch syn keyword javaScriptRepeat while for do in of diff --git a/runtime/syntax/testdir/dumps/javascript_00.dump b/runtime/syntax/testdir/dumps/javascript_00.dump new file mode 100644 index 0000000000..eca4c4b605 --- /dev/null +++ b/runtime/syntax/testdir/dumps/javascript_00.dump @@ -0,0 +1,20 @@ +>/+0#0000e05#ffffff0@1| |I|s@1|u|e| |#|2|0@1|6|9| |(|J|a|v|a|S|c|r|i|p|t| |s|y|n|t|a|x| |h|i|g|h|l|i|g|h|t|i|n|g| |n|o|t| |w|o|r|k|i|n|g| |p|r|o|p|e|r|l|y| |w|i|t|h| |r +|e|g|e|x| +0#0000000&@70 +|/+0#0000e05&@1| @2|p|a|t@1|e|r|n|s| |c|o|n|t|a|i|n|i|n|g| |q|u|o|t|a|t|i|o|n| |m|a|r|k|s|)| +0#0000000&@33 +@75 +|c+0#af5f00255&|o|n|s|t| +0#0000000&|c|o|n|t|a|i|n|s|S|y|m|b|o|l|s| |=| |p|a|s@1|w|o|r|d|.|m|a|t|c|h|(|/+0#e000002&|[|!|"|.@2|/|g|)+0#0000000&|;| @24 +@75 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|1|,|1| @10|A|l@1| diff --git a/runtime/syntax/testdir/input/javascript.js b/runtime/syntax/testdir/input/javascript.js new file mode 100644 index 0000000000..b0b76eca28 --- /dev/null +++ b/runtime/syntax/testdir/input/javascript.js @@ -0,0 +1,5 @@ +// Issue #20069 (JavaScript syntax highlighting not working properly with regex +// patterns containing quotation marks) + +const containsSymbols = password.match(/[!".../g); + From 22aedc4a902812a2a53b713dbe3c82b77f2f167d Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 26 Apr 2026 16:47:08 +0000 Subject: [PATCH 119/545] patch 9.2.0397: tabpanel: double-click opens a new tab Problem: tabpanel: double-click opens a new tab page Solution: Do not create a new tab page when using a double click (Yasuhiro Matsumoto). The tabpanel click handler inherited the tabline behavior where a double-click opens a new, empty tab page. Unlike the tabline, the tabpanel has no "empty area": every row maps to some tab, so this fires on any double-click in the tabpanel and can generate stray empty tabs. The behavior is also not documented for the tabpanel. Skip the new-tab branch when the click originated in the tabpanel and fall through to the regular tab-switch path instead. The tabline behavior is unchanged. closes: #20044 Signed-off-by: Christian Brabandt --- runtime/doc/tabpage.txt | 12 +++++++++++- runtime/doc/tags | 1 + src/mouse.c | 8 ++++++-- src/version.c | 2 ++ 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index 5083618917..7fc09c5451 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -1,4 +1,4 @@ -*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 25 +*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 26 VIM REFERENCE MANUAL by Bram Moolenaar @@ -513,6 +513,16 @@ The scrollbar uses the |hl-PmenuSbar| highlight group for the track and The scroll offset is remembered across redraws but is reset when "scroll" or "scrollbar" is toggled off and back on. +MOUSE CLICKS IN THE TABPANEL: *tabpanel-mouse* + +A left click on a row in the tabpanel selects the tab page that the row +belongs to. Unlike the tabline, a double click in the tabpanel does not +open a new, empty tab page; it is treated the same as a single click. + +For finer-grained control, the 'tabpanel' value may contain |stl-%[FuncName]| +click regions. Clicks on those regions are dispatched to the callback +function instead of falling through to tab selection. + ============================================================================== 6. Setting 'guitablabel' *setting-guitablabel* diff --git a/runtime/doc/tags b/runtime/doc/tags index 80b1d18bec..65edb2c31c 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -10893,6 +10893,7 @@ tabpagebuflist() builtin.txt /*tabpagebuflist()* tabpagenr() builtin.txt /*tabpagenr()* tabpagewinnr() builtin.txt /*tabpagewinnr()* tabpanel tabpage.txt /*tabpanel* +tabpanel-mouse tabpage.txt /*tabpanel-mouse* tabpanel-scroll tabpage.txt /*tabpanel-scroll* tag tagsrch.txt /*tag* tag-! tagsrch.txt /*tag-!* diff --git a/src/mouse.c b/src/mouse.c index 93fdd31824..ddd94fffcf 100644 --- a/src/mouse.c +++ b/src/mouse.c @@ -591,9 +591,13 @@ do_mouse( c1 = tp_label.nr; if (c1 >= 0) { - if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK) + if ((mod_mask & MOD_MASK_MULTI_CLICK) == MOD_MASK_2CLICK + && !tp_label.is_panel) { - // double click opens new page + // Double-click on the tabline opens a new, empty tab page. + // The tabpanel has no "empty area" (every row maps to a tab) + // and this behavior is not documented for tabpanel, so fall + // through to the regular tab-switch path there. end_visual_mode_keep_button(); tabpage_new(); tabpage_move(c1 == 0 ? 9999 : c1 - 1); diff --git a/src/version.c b/src/version.c index e714ddc2a3..1649c0ff8e 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 397, /**/ 396, /**/ From 036e40e6d80b1d71e57d3715ae20175ce09d7b78 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 26 Apr 2026 16:58:30 +0000 Subject: [PATCH 120/545] patch 9.2.0398: MS-Windows: missing strptime() support Problem: MS-Windows: missing strptime() support Solution: Port NetBSD's strptime fallback to Vim (Yasuhiro Matsumoto). The MSVC and MinGW C runtimes do not provide strptime(), so the strptime() builtin was unavailable on Windows. Port NetBSD's lib/libc/time/strptime.c (rev 1.67) and compile it into the Windows builds. The BSD 2-clause notice from the original is preserved in the file. Windows-specific adjustments: English-only locale tables, the fromzone()/tzalloc() path is stubbed out (no IANA tzfile loader on Windows), and tm_gmtoff / tm_zone stores are elided. Also call tzset() before mktime() in f_strptime() so changes to \$TZ are honored. Skip the POSIX DST TZ assertion in Test_strptime() on MS-Windows since the CRT tzset() does not parse POSIX TZ strings with DST rules. closes: #20054 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- Filelist | 1 + runtime/doc/builtin.txt | 7 +- runtime/doc/version9.txt | 3 +- src/Make_cyg_ming.mak | 1 + src/Make_mvc.mak | 3 + src/os_win32.h | 9 + src/strptime.c | 785 +++++++++++++++++++++++++++++++++ src/testdir/test_codestyle.vim | 2 +- src/testdir/test_functions.vim | 9 +- src/time.c | 4 + src/version.c | 2 + 11 files changed, 820 insertions(+), 6 deletions(-) create mode 100644 src/strptime.c diff --git a/Filelist b/Filelist index 311a1031af..13e0f85f64 100644 --- a/Filelist +++ b/Filelist @@ -153,6 +153,7 @@ SRC_ALL = \ src/spellfile.c \ src/spellsuggest.c \ src/strings.c \ + src/strptime.c \ src/structs.h \ src/syntax.c \ src/tabpanel.c \ diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 9ea0ae82cc..371de4b2c6 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 21 +*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 26 VIM REFERENCE MANUAL by Bram Moolenaar @@ -11317,7 +11317,12 @@ strptime({format}, {timestring}) *strptime()* can try different {format} values until you get a non-zero result. + Note: On MS-Windows, where the C runtime does not provide + strptime(), Vim uses a built-in fallback that always uses + English locale names regardless of the active locale. + See also |strftime()|. + Examples: > :echo strptime("%Y %b %d %X", "1997 Apr 27 11:49:23") < 862156163 > diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 16ed792f99..43106ef30a 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 25 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 26 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52622,6 +52622,7 @@ Other ~ Platform specific ~ ----------------- - support OpenType font features in 'guifont' for DirectWrite (Win32) +- Include strptime() fallback for MS-Windows xxd ~ --- diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index f5b86eeb42..7f3d519bf1 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -881,6 +881,7 @@ OBJ = \ $(OUTDIR)/spellfile.o \ $(OUTDIR)/spellsuggest.o \ $(OUTDIR)/strings.o \ + $(OUTDIR)/strptime.o \ $(OUTDIR)/syntax.o \ $(OUTDIR)/tabpanel.o \ $(OUTDIR)/tag.o \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index a59b083e72..8994143c92 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -785,6 +785,7 @@ OBJ = \ $(OUTDIR)\textobject.obj \ $(OUTDIR)\textprop.obj \ $(OUTDIR)\time.obj \ + $(OUTDIR)\strptime.obj \ $(OUTDIR)\tuple.obj \ $(OUTDIR)\typval.obj \ $(OUTDIR)\ui.obj \ @@ -1792,6 +1793,8 @@ $(OUTDIR)/textprop.obj: $(OUTDIR) textprop.c $(INCL) $(OUTDIR)/time.obj: $(OUTDIR) time.c $(INCL) +$(OUTDIR)/strptime.obj: $(OUTDIR) strptime.c $(INCL) + $(OUTDIR)/tuple.obj: $(OUTDIR) tuple.c $(INCL) $(OUTDIR)/typval.obj: $(OUTDIR) typval.c $(INCL) diff --git a/src/os_win32.h b/src/os_win32.h index 6f395eede7..cfdff674fd 100644 --- a/src/os_win32.h +++ b/src/os_win32.h @@ -29,6 +29,12 @@ #ifndef HAVE_STRFTIME # define HAVE_STRFTIME // guessed #endif +#ifndef HAVE_STRPTIME +# define HAVE_STRPTIME // provided by src/strptime.c +#endif +#ifndef HAVE_TZSET +# define HAVE_TZSET // CRT has tzset() / _tzset() +#endif #define HAVE_MEMSET #ifndef HAVE_LOCALE_H # define HAVE_LOCALE_H 1 @@ -228,3 +234,6 @@ Trace(char *pszFormat, ...); // Windows Version #define MAKE_VER(major, minor, build) \ (((major) << 24) | ((minor) << 16) | (build)) + +// The Windows CRT does not declare strptime(); Vim provides it in strptime.c. +char *strptime(const char *buf, const char *fmt, struct tm *tm); diff --git a/src/strptime.c b/src/strptime.c new file mode 100644 index 0000000000..ca6fcd8a99 --- /dev/null +++ b/src/strptime.c @@ -0,0 +1,785 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + */ + +/* + * strptime.c: portable strptime() fallback for platforms whose C runtime + * does not provide one (currently the MSVC / MinGW CRT on Windows). + * + * Ported from NetBSD's lib/libc/time/strptime.c (rev 1.67, 2024-06-07). + * The original BSD 2-clause licence follows below and must be preserved. + * Windows-specific adjustments: + * - Locale-specific weekday / month / AM-PM tables are hard-coded in + * English; this matches what strftime() emits in the "C" locale on + * Windows and is sufficient for Vim's strptime() test suite. + * - The NetBSD-specific fromzone()/tzalloc() path is stubbed out because + * the Windows CRT does not ship the IANA tzfile loader. + * - The tm_gmtoff / tm_zone BSD extensions are not available in the + * Windows CRT's struct tm, so the %Z / %z conversion parses the input + * without storing the offset (matching NetBSD behavior when those + * fields are not compiled in). + */ + +/*- + * Copyright (c) 1997, 1998, 2005, 2008 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code was contributed to The NetBSD Foundation by Klaus Klein. + * Heavily optimised by David Laight + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "vim.h" + +#ifdef MSWIN + +/* Constants lifted from NetBSD's . */ +#define TM_YEAR_BASE 1900 +#define TM_SUNDAY 0 +#define TM_MONDAY 1 +#define SECSPERHOUR 3600 +#define SECSPERMIN 60 +#define HOURSPERDAY 24 + +#define isleap(y) (((y) % 4 == 0 && (y) % 100 != 0) || (y) % 400 == 0) +/* isleap_sum avoids integer overflow when adding the 1900 base. */ +#define isleap_sum(a, b) \ + isleap(((unsigned)(a) + (unsigned)(b)) % 400) + +#ifdef _MSC_VER +# define timezone _timezone +# define tzname _tzname +#endif + +/* Locale tables (English / "C" locale). */ +static const char *const c_day[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" +}; +static const char *const c_abday[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; +static const char *const c_mon[] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; +static const char *const c_abmon[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +static const char *const c_am_pm[] = { "AM", "PM" }; +static const char c_d_t_fmt[] = "%a %b %e %H:%M:%S %Y"; +static const char c_d_fmt[] = "%m/%d/%y"; +static const char c_t_fmt[] = "%H:%M:%S"; +static const char c_t_fmt_ampm[] = "%I:%M:%S %p"; + +static const unsigned char *conv_num(const unsigned char *, int *, + unsigned int, unsigned int); +static const unsigned char *find_string(const unsigned char *, int *, + const char * const *, const char * const *, int); + +/* + * We do not implement alternate representations. However, we always + * check whether a given modifier is allowed for a certain conversion. + */ +#define ALT_E 0x01 +#define ALT_O 0x02 +#define LEGAL_ALT(x) { if (alt_format & ~(x)) return NULL; } + +#define S_YEAR (1 << 0) +#define S_MON (1 << 1) +#define S_YDAY (1 << 2) +#define S_MDAY (1 << 3) +#define S_WDAY (1 << 4) +#define S_HOUR (1 << 5) + +#define HAVE_MDAY(s) (s & S_MDAY) +#define HAVE_MON(s) (s & S_MON) +#define HAVE_WDAY(s) (s & S_WDAY) +#define HAVE_YDAY(s) (s & S_YDAY) +#define HAVE_YEAR(s) (s & S_YEAR) +#define HAVE_HOUR(s) (s & S_HOUR) + +/* RFC-822/RFC-2822 */ +static const char *const nast[5] = { + "EST", "CST", "MST", "PST", "\0\0\0" +}; +static const char *const nadt[5] = { + "EDT", "CDT", "MDT", "PDT", "\0\0\0" +}; + +/* + * Table to determine the ordinal date for the start of a month. + * Ref: http://en.wikipedia.org/wiki/ISO_week_date + */ +static const int start_of_month[2][13] = { + /* non-leap year */ + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, + /* leap year */ + { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } +}; + +/* + * Calculate the week day of the first day of a year. Valid for + * the Gregorian calendar, which began Sept 14, 1752 in the UK + * and its colonies. Ref: + * http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week + */ + static int +first_wday_of(int yr) +{ + return ((2 * (3 - (yr / 100) % 4)) + (yr % 100) + ((yr % 100) / 4) + + (isleap(yr) ? 6 : 0) + 1) % 7; +} + +#define delim(p) ((p) == '\0' || isspace((unsigned char)(p))) + +/* + * Stub replacing NetBSD's fromzone(): the Windows CRT does not provide + * tzalloc() / tzgetgmtoff() and the associated IANA tzfile loader, so + * we simply decline to recognize arbitrary timezone names. The %Z / %z + * numeric and RFC-822/2822 forms are still handled by the caller. + */ + static int +fromzone(const unsigned char **bp UNUSED, struct tm *tm UNUSED, + int mandatory UNUSED) +{ + return 0; +} + + char * +strptime(const char *buf, const char *fmt, struct tm *tm) +{ + unsigned char c; + const unsigned char *bp, *ep, *zname; + int alt_format, i, split_year = 0, neg = 0, state = 0, + day_offset = -1, week_offset = 0, offs, mandatory; + const char *new_fmt; + + bp = (const unsigned char *)buf; + + while (bp != NULL && (c = *fmt++) != '\0') + { + /* Clear `alternate' modifier prior to new conversion. */ + alt_format = 0; + i = 0; + + /* Eat up white-space. */ + if (isspace(c)) + { + while (isspace(*bp)) + bp++; + continue; + } + + if (c != '%') + goto literal; + + +again: switch (c = *fmt++) + { + case '%': /* "%%" is converted to "%". */ +literal: + if (c != *bp++) + return NULL; + LEGAL_ALT(0); + continue; + + /* + * "Alternative" modifiers. Just set the appropriate flag + * and start over again. + */ + case 'E': /* "%E?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_E; + goto again; + + case 'O': /* "%O?" alternative conversion modifier. */ + LEGAL_ALT(0); + alt_format |= ALT_O; + goto again; + + /* + * "Complex" conversion rules, implemented through recursion. + */ + case 'c': /* Date and time, using the locale's format. */ + new_fmt = c_d_t_fmt; + state |= S_WDAY | S_MON | S_MDAY | S_YEAR; + goto recurse; + + case 'D': /* The date as "%m/%d/%y". */ + new_fmt = "%m/%d/%y"; + LEGAL_ALT(0); + state |= S_MON | S_MDAY | S_YEAR; + goto recurse; + + case 'F': /* The date as "%Y-%m-%d". */ + new_fmt = "%Y-%m-%d"; + LEGAL_ALT(0); + state |= S_MON | S_MDAY | S_YEAR; + goto recurse; + + case 'R': /* The time as "%H:%M". */ + new_fmt = "%H:%M"; + LEGAL_ALT(0); + goto recurse; + + case 'r': /* The time in 12-hour clock representation. */ + new_fmt = c_t_fmt_ampm; + LEGAL_ALT(0); + goto recurse; + + case 'T': /* The time as "%H:%M:%S". */ + new_fmt = "%H:%M:%S"; + LEGAL_ALT(0); + goto recurse; + + case 'X': /* The time, using the locale's format. */ + new_fmt = c_t_fmt; + goto recurse; + + case 'x': /* The date, using the locale's format. */ + new_fmt = c_d_fmt; + state |= S_MON | S_MDAY | S_YEAR; + recurse: + bp = (const unsigned char *)strptime((const char *)bp, + new_fmt, tm); + LEGAL_ALT(ALT_E); + continue; + + /* + * "Elementary" conversion rules. + */ + case 'A': /* The day of week, using the locale's form. */ + case 'a': + bp = find_string(bp, &tm->tm_wday, c_day, c_abday, 7); + LEGAL_ALT(0); + state |= S_WDAY; + continue; + + case 'B': /* The month, using the locale's form. */ + case 'b': + case 'h': + bp = find_string(bp, &tm->tm_mon, c_mon, c_abmon, 12); + LEGAL_ALT(0); + state |= S_MON; + continue; + + case 'C': /* The century number. */ + i = 20; + bp = conv_num(bp, &i, 0, 99); + + i = i * 100 - TM_YEAR_BASE; + if (split_year) + i += tm->tm_year % 100; + split_year = 1; + tm->tm_year = i; + LEGAL_ALT(ALT_E); + state |= S_YEAR; + continue; + + case 'd': /* The day of month. */ + case 'e': + bp = conv_num(bp, &tm->tm_mday, 1, 31); + LEGAL_ALT(ALT_O); + state |= S_MDAY; + continue; + + case 'k': /* The hour (24-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'H': + bp = conv_num(bp, &tm->tm_hour, 0, 23); + LEGAL_ALT(ALT_O); + state |= S_HOUR; + continue; + + case 'l': /* The hour (12-hour clock representation). */ + LEGAL_ALT(0); + /* FALLTHROUGH */ + case 'I': + bp = conv_num(bp, &tm->tm_hour, 1, 12); + if (tm->tm_hour == 12) + tm->tm_hour = 0; + LEGAL_ALT(ALT_O); + state |= S_HOUR; + continue; + + case 'j': /* The day of year. */ + i = 1; + bp = conv_num(bp, &i, 1, 366); + tm->tm_yday = i - 1; + LEGAL_ALT(0); + state |= S_YDAY; + continue; + + case 'M': /* The minute. */ + bp = conv_num(bp, &tm->tm_min, 0, 59); + LEGAL_ALT(ALT_O); + continue; + + case 'm': /* The month. */ + i = 1; + bp = conv_num(bp, &i, 1, 12); + tm->tm_mon = i - 1; + LEGAL_ALT(ALT_O); + state |= S_MON; + continue; + + case 'p': /* The locale's equivalent of AM/PM. */ + bp = find_string(bp, &i, c_am_pm, NULL, 2); + if (HAVE_HOUR(state) && tm->tm_hour > 11) + return NULL; + tm->tm_hour += i * 12; + LEGAL_ALT(0); + continue; + + case 'S': /* The seconds. */ + bp = conv_num(bp, &tm->tm_sec, 0, 61); + LEGAL_ALT(ALT_O); + continue; + + case 's': /* seconds since the epoch */ + { + const time_t TIME_MAX = (time_t)((sizeof(time_t) == 8) + ? LLONG_MAX : INT_MAX); + time_t sse, d; + + if (*bp < '0' || *bp > '9') + { + bp = NULL; + continue; + } + + sse = *bp++ - '0'; + while (*bp >= '0' && *bp <= '9') + { + d = *bp++ - '0'; + if (sse > TIME_MAX / 10) + { + bp = NULL; + break; + } + sse *= 10; + if (sse > TIME_MAX - d) + { + bp = NULL; + break; + } + sse += d; + } + if (bp == NULL) + continue; + + { + struct tm *lt = localtime(&sse); + + if (lt == NULL) + bp = NULL; + else + { + *tm = *lt; + state |= S_YDAY | S_WDAY | + S_MON | S_MDAY | S_YEAR; + } + } + continue; + } + + case 'U': /* The week of year, beginning on sunday. */ + case 'W': /* The week of year, beginning on monday. */ + /* + * This is bogus, as we can not assume any valid + * information present in the tm structure at this + * point to calculate a real value, so save the + * week for now in case it can be used later. + */ + bp = conv_num(bp, &i, 0, 53); + LEGAL_ALT(ALT_O); + if (c == 'U') + day_offset = TM_SUNDAY; + else + day_offset = TM_MONDAY; + week_offset = i; + continue; + + case 'w': /* The day of week, beginning on sunday. */ + bp = conv_num(bp, &tm->tm_wday, 0, 6); + LEGAL_ALT(ALT_O); + state |= S_WDAY; + continue; + + case 'u': /* The day of week, monday = 1. */ + bp = conv_num(bp, &i, 1, 7); + tm->tm_wday = i % 7; + LEGAL_ALT(ALT_O); + state |= S_WDAY; + continue; + + case 'g': /* ISO week year without century (parsed, ignored). */ + bp = conv_num(bp, &i, 0, 99); + continue; + + case 'G': /* ISO week year with century (parsed, ignored). */ + do + bp++; + while (isdigit(*bp)); + continue; + + case 'V': /* ISO 8601:1988 week number (parsed, ignored). */ + bp = conv_num(bp, &i, 1, 53); + continue; + + case 'Y': /* The year. */ + i = TM_YEAR_BASE; /* just for data sanity... */ + bp = conv_num(bp, &i, 0, 9999); + tm->tm_year = i - TM_YEAR_BASE; + LEGAL_ALT(ALT_E); + state |= S_YEAR; + continue; + + case 'y': /* The year within 100 years of the epoch. */ + /* LEGAL_ALT(ALT_E | ALT_O); */ + bp = conv_num(bp, &i, 0, 99); + + if (split_year) + /* preserve century */ + i += (tm->tm_year / 100) * 100; + else + { + split_year = 1; + if (i <= 68) + i = i + 2000 - TM_YEAR_BASE; + else + i = i + 1900 - TM_YEAR_BASE; + } + tm->tm_year = i; + state |= S_YEAR; + continue; + + case 'Z': + case 'z': + tzset(); + mandatory = c == 'z'; + /* + * We recognize all ISO 8601 formats: + * Z = Zulu time/UTC + * [+-]hhmm + * [+-]hh:mm + * [+-]hh + * We recognize all RFC-822/RFC-2822 formats: + * UT|GMT + * North American : UTC offsets + * E[DS]T = Eastern : -4 | -5 + * C[DS]T = Central : -5 | -6 + * M[DS]T = Mountain: -6 | -7 + * P[DS]T = Pacific : -7 | -8 + * Nautical/Military + * [A-IL-M] = -1 ... -9 (J not used) + * [N-Y] = +1 ... +12 + * Note: J maybe used to denote non-nautical + * local time + */ + if (mandatory) + while (isspace(*bp)) + bp++; + + zname = bp; + switch (*bp++) + { + case 'G': + if (*bp++ != 'M') + goto namedzone; + /*FALLTHROUGH*/ + case 'U': + if (*bp++ != 'T') + goto namedzone; + else if (!delim(*bp) && *bp++ != 'C') + goto namedzone; + /*FALLTHROUGH*/ + case 'Z': + if (!delim(*bp)) + goto namedzone; + tm->tm_isdst = 0; + continue; + case '+': + neg = 0; + break; + case '-': + neg = 1; + break; + default: +namedzone: + bp = zname; + + /* Nautical / Military style */ + if (delim(bp[1]) && + ((*bp >= 'A' && *bp <= 'I') || + (*bp >= 'L' && *bp <= 'Y'))) + { + bp++; + continue; + } + /* 'J' is local time */ + if (delim(bp[1]) && *bp == 'J') + { + bp++; + continue; + } + + /* + * From our 3 letter hard-coded table. + */ + if (delim(bp[0]) || delim(bp[1]) || + delim(bp[2]) || !delim(bp[3])) + goto loadzone; + ep = find_string(bp, &i, nast, NULL, 4); + if (ep != NULL) + { + bp = ep; + continue; + } + ep = find_string(bp, &i, nadt, NULL, 4); + if (ep != NULL) + { + tm->tm_isdst = 1; + bp = ep; + continue; + } + /* + * Our current timezone + */ + ep = find_string(bp, &i, + (const char *const *)tzname, NULL, 2); + if (ep != NULL) + { + tm->tm_isdst = i; + bp = ep; + continue; + } +loadzone: + /* + * The hard way, load the zone! + */ + if (fromzone(&bp, tm, mandatory)) + continue; + goto out; + } + offs = 0; + for (i = 0; i < 4; ) + { + if (isdigit(*bp)) + { + offs = offs * 10 + (*bp++ - '0'); + i++; + continue; + } + if (i == 2 && *bp == ':') + { + bp++; + continue; + } + break; + } + if (isdigit(*bp)) + goto out; + switch (i) + { + case 2: + offs *= SECSPERHOUR; + break; + case 4: + i = offs % 100; + offs /= 100; + if (i >= SECSPERMIN) + goto out; + /* Convert minutes into decimal */ + offs = offs * SECSPERHOUR + i * SECSPERMIN; + break; + default: +out: + if (mandatory) + return NULL; + bp = zname; + continue; + } + /* ISO 8601 & RFC 3339 limit to 23:59 max */ + if (offs >= (HOURSPERDAY * SECSPERHOUR)) + goto out; + if (neg) + offs = -offs; + tm->tm_isdst = 0; /* XXX */ + continue; + + /* + * Miscellaneous conversions. + */ + case 'n': /* Any kind of white-space. */ + case 't': + while (isspace(*bp)) + bp++; + LEGAL_ALT(0); + continue; + + + default: /* Unknown/unsupported conversion. */ + return NULL; + } + } + + if (!HAVE_YDAY(state) && HAVE_YEAR(state)) + { + if (HAVE_MON(state) && HAVE_MDAY(state)) + { + /* calculate day of year (ordinal date) */ + tm->tm_yday = start_of_month[isleap_sum(tm->tm_year, + TM_YEAR_BASE)][tm->tm_mon] + (tm->tm_mday - 1); + state |= S_YDAY; + } + else if (day_offset != -1) + { + /* + * Set the date to the first Sunday (or Monday) + * of the specified week of the year. + */ + if (!HAVE_WDAY(state)) + { + tm->tm_wday = day_offset; + state |= S_WDAY; + } + tm->tm_yday = (7 - + first_wday_of(tm->tm_year + TM_YEAR_BASE) + + day_offset) % 7 + (week_offset - 1) * 7 + + tm->tm_wday - day_offset; + state |= S_YDAY; + } + } + + if (HAVE_YDAY(state) && HAVE_YEAR(state)) + { + int leap; + + if (!HAVE_MON(state)) + { + /* calculate month of day of year */ + i = 0; + leap = isleap_sum(tm->tm_year, TM_YEAR_BASE); + while (tm->tm_yday >= start_of_month[leap][i]) + i++; + if (i > 12) + { + i = 1; + tm->tm_yday -= start_of_month[leap][12]; + tm->tm_year++; + } + tm->tm_mon = i - 1; + state |= S_MON; + } + + if (!HAVE_MDAY(state)) + { + /* calculate day of month */ + leap = isleap_sum(tm->tm_year, TM_YEAR_BASE); + tm->tm_mday = tm->tm_yday - + start_of_month[leap][tm->tm_mon] + 1; + state |= S_MDAY; + } + + if (!HAVE_WDAY(state)) + { + /* calculate day of week */ + i = 0; + week_offset = first_wday_of(tm->tm_year); + while (i++ <= tm->tm_yday) + { + if (week_offset++ >= 6) + week_offset = 0; + } + tm->tm_wday = week_offset; + state |= S_WDAY; + } + } + + return (char *)bp; +} + + + static const unsigned char * +conv_num(const unsigned char *buf, int *dest, unsigned int llim, + unsigned int ulim) +{ + unsigned int result = 0; + unsigned char ch; + + /* The limit also determines the number of valid digits. */ + unsigned int rulim = ulim; + + ch = *buf; + if (ch < '0' || ch > '9') + return NULL; + + do + { + result *= 10; + result += ch - '0'; + rulim /= 10; + ch = *++buf; + } while ((result * 10 <= ulim) && rulim && ch >= '0' && ch <= '9'); + + if (result < llim || result > ulim) + return NULL; + + *dest = result; + return buf; +} + + static const unsigned char * +find_string(const unsigned char *bp, int *tgt, const char *const *n1, + const char *const *n2, int c) +{ + int i; + size_t len; + + /* check full name - then abbreviated ones */ + for (; n1 != NULL; n1 = n2, n2 = NULL) + { + for (i = 0; i < c; i++, n1++) + { + len = strlen(*n1); + if (STRNICMP(*n1, bp, len) == 0) + { + *tgt = i; + return bp + len; + } + } + } + + /* Nothing matched */ + return NULL; +} + +#endif // MSWIN diff --git a/src/testdir/test_codestyle.vim b/src/testdir/test_codestyle.vim index ed68e3510e..da08e71ffd 100644 --- a/src/testdir/test_codestyle.vim +++ b/src/testdir/test_codestyle.vim @@ -46,7 +46,7 @@ def Test_source_files() PerformCheck(fname, ';;\+$', 'double semicolon', '') # some files don't stick to the Vim style rules - if fname =~ 'iscygpty.c' + if fname =~ 'iscygpty.c' || fname =~ 'strptime.c' continue endif diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index 0edfbd27fb..9c652101ae 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -328,9 +328,12 @@ func Test_strptime() call assert_equal(1484653763, strptime('%Y-%m-%d %T', '2017-01-17 11:49:23')) - " Force DST and check that it's considered - let $TZ = 'WINTER0SUMMER,J1,J365' - call assert_equal(1484653763 - 3600, strptime('%Y-%m-%d %T', '2017-01-17 11:49:23')) + " Force DST and check that it's considered. + " MS-Windows CRT tzset() does not parse POSIX TZ strings with DST rules. + if !has('win32') + let $TZ = 'WINTER0SUMMER,J1,J365' + call assert_equal(1484653763 - 3600, strptime('%Y-%m-%d %T', '2017-01-17 11:49:23')) + endif call assert_fails('call strptime()', 'E119:') call assert_fails('call strptime("xxx")', 'E119:') diff --git a/src/time.c b/src/time.c index 16112d6eeb..8166b09d1f 100644 --- a/src/time.c +++ b/src/time.c @@ -381,6 +381,10 @@ f_strptime(typval_T *argvars, typval_T *rettv) convert_setup(&conv, p_enc, enc); if (conv.vc_type != CONV_NONE) fmt = string_convert(&conv, fmt, NULL); +# ifdef HAVE_TZSET + // Pick up any change to $TZ so mktime() uses the requested timezone. + tzset(); +# endif if (fmt == NULL || strptime((char *)str, (char *)fmt, &tmval) == NULL || (rettv->vval.v_number = mktime(&tmval)) == -1) diff --git a/src/version.c b/src/version.c index 1649c0ff8e..fc2cb3b8db 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 398, /**/ 397, /**/ From 96be27309c89dcf1a5e48b364cfba57ed126ec96 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 27 Apr 2026 17:06:47 +0000 Subject: [PATCH 121/545] runtime(doc): fix :z command description again Signed-off-by: Christian Brabandt --- runtime/doc/various.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 4403b6c6e4..e60a557dfd 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 9.2. Last change: 2026 Apr 26 +*various.txt* For Vim version 9.2. Last change: 2026 Apr 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -184,8 +184,8 @@ g8 Print the hex values of the bytes used in the If the mark is "=", a line of dashes is printed around the current line. - If the 'number' or 'relativenumber' option is set, - absolute line numbers will be included in the output. + If the 'number' option is set, absolute line numbers + will be included in the output. *:z!* :[range]z![+-^.=][count] From a622dda91597f379bcbddb2e4370ef1ea61fbc47 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 27 Apr 2026 17:10:25 +0000 Subject: [PATCH 122/545] patch 9.2.0399: MS-Windows: compile warning in strptime.c Problem: MS-Windows: compile warning in strptime.c (John Marriott, after v9.2.0398) Solution: Fix the compile warning (Yasuhiro Matsumoto). Use _get_tzname() instead of the deprecated tzname[] global on UCRT and MSVC builds; older MinGW (msvcrt.dll) keeps using tzname[] as a fallback. related: #20054 closes: #20079 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/strptime.c | 29 +++++++++++++++++++++++++---- src/version.c | 2 ++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/src/strptime.c b/src/strptime.c index ca6fcd8a99..0d1663cf03 100644 --- a/src/strptime.c +++ b/src/strptime.c @@ -70,9 +70,13 @@ #define isleap_sum(a, b) \ isleap(((unsigned)(a) + (unsigned)(b)) % 400) -#ifdef _MSC_VER -# define timezone _timezone -# define tzname _tzname +/* + * _get_tzname() is provided by the UCRT and by the MSVC CRT. Older MinGW + * builds linking against msvcrt.dll fall back to the deprecated tzname[] + * global (mingw-w64 already exposes the POSIX-named tzname[]). + */ +#if defined(_UCRT) || defined(_MSC_VER) +# define USE_GET_TZNAME #endif /* Locale tables (English / "C" locale). */ @@ -571,10 +575,27 @@ again: switch (c = *fmt++) continue; } /* - * Our current timezone + * Our current timezone. Prefer _get_tzname() over the + * tzname[] global, which UCRT marks deprecated because it + * may be inaccurate after locale changes. */ +#ifdef USE_GET_TZNAME + { + char tzbuf[2][32]; + const char *tznames[2] = { tzbuf[0], tzbuf[1] }; + size_t tzlen; + + _tzset(); + if (_get_tzname(&tzlen, tzbuf[0], sizeof(tzbuf[0]), 0) != 0) + tzbuf[0][0] = NUL; + if (_get_tzname(&tzlen, tzbuf[1], sizeof(tzbuf[1]), 1) != 0) + tzbuf[1][0] = NUL; + ep = find_string(bp, &i, tznames, NULL, 2); + } +#else ep = find_string(bp, &i, (const char *const *)tzname, NULL, 2); +#endif if (ep != NULL) { tm->tm_isdst = i; diff --git a/src/version.c b/src/version.c index fc2cb3b8db..4541539553 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 399, /**/ 398, /**/ From dd9b31fb62c0003be6cfd18847982b26efc73d34 Mon Sep 17 00:00:00 2001 From: Barrett Ruth Date: Mon, 27 Apr 2026 17:18:17 +0000 Subject: [PATCH 123/545] patch 9.2.0400: sandbox callbacks selected through 'complete' Problem: Modeline-tainted 'complete' values can invoke completion callbacks outside the sandbox. Solution: Enter the sandbox for both 'complete' callback phases and add a regression test (Barrett Ruth) closes: #20078 Signed-off-by: Barrett Ruth Signed-off-by: Christian Brabandt --- src/insexpand.c | 14 +++++++++ src/testdir/test_modeline.vim | 55 +++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 71 insertions(+) diff --git a/src/insexpand.c b/src/insexpand.c index 0019c7eb45..a603ed7b08 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -3631,6 +3631,9 @@ expand_by_function(int type, char_u *base, callback_T *cb) int save_State = State; int retval; int is_cpt_function = (cb != NULL); + int use_sandbox = is_cpt_function + && was_set_insecurely(curwin, + (char_u *)"complete", OPT_LOCAL); if (!is_cpt_function) { @@ -3652,8 +3655,12 @@ expand_by_function(int type, char_u *base, callback_T *cb) // switching to another window, it should not be needed and may end up in // Insert mode in another buffer. ++textlock; + if (use_sandbox) + ++sandbox; retval = call_callback(cb, 0, &rettv, 2, args); + if (use_sandbox) + --sandbox; // Call a function, which returns a list or dict. if (retval == OK) @@ -6760,6 +6767,9 @@ get_userdefined_compl_info( pos_T pos; int save_State = State; int is_cpt_function = (cb != NULL); + int use_sandbox = is_cpt_function + && was_set_insecurely(curwin, + (char_u *)"complete", OPT_LOCAL); if (!is_cpt_function) { @@ -6782,7 +6792,11 @@ get_userdefined_compl_info( args[2].v_type = VAR_UNKNOWN; pos = curwin->w_cursor; ++textlock; + if (use_sandbox) + ++sandbox; col = call_callback_retnr(cb, 2, args); + if (use_sandbox) + --sandbox; --textlock; State = save_State; diff --git a/src/testdir/test_modeline.vim b/src/testdir/test_modeline.vim index b78a4258f0..6884ab473d 100644 --- a/src/testdir/test_modeline.vim +++ b/src/testdir/test_modeline.vim @@ -283,6 +283,61 @@ func Test_modeline_fails_modelineexpr() call s:modeline_fails('titlestring', 'titlestring=Something()', 'E992:') endfunc +func Test_modeline_complete_uses_sandbox() + let modeline = &modeline + let modelineexpr = &modelineexpr + let modelinestrict = &modelinestrict + + func! ModelineCompletePwnFindstart(findstart, base) + if a:findstart + call writefile(['findstart'], 'Xmodeline_complete_proof') + return 0 + endif + return ['match'] + endfunc + + func! ModelineCompletePwnMatches(findstart, base) + if a:findstart + return 0 + endif + call writefile(['matches'], 'Xmodeline_complete_proof') + return ['match'] + endfunc + + try + set modeline modelineexpr nomodelinestrict + + call writefile([ + \ 'vim: set complete=FModelineCompletePwnFindstart :', + \ 'body', + \ ], 'Xmodeline_complete_attack', 'D') + call delete('Xmodeline_complete_proof') + edit Xmodeline_complete_attack + call cursor(2, 1) + call assert_fails('call feedkeys("i\\", "xt")', 'E48:') + call assert_false(filereadable('Xmodeline_complete_proof')) + bwipe! + + call writefile([ + \ 'vim: set complete=FModelineCompletePwnMatches :', + \ 'body', + \ ], 'Xmodeline_complete_attack', 'D') + call delete('Xmodeline_complete_proof') + edit Xmodeline_complete_attack + call cursor(2, 1) + call assert_fails('call feedkeys("i\\", "xt")', 'E48:') + call assert_false(filereadable('Xmodeline_complete_proof')) + bwipe! + finally + let &modeline = modeline + let &modelineexpr = modelineexpr + let &modelinestrict = modelinestrict + call delete('Xmodeline_complete_proof') + delfunc ModelineCompletePwnFindstart + delfunc ModelineCompletePwnMatches + endtry +endfunc + func Test_modeline_setoption_verbose() let modeline = &modeline set modeline diff --git a/src/version.c b/src/version.c index 4541539553..8dfe9b8f78 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 400, /**/ 399, /**/ From 0bc64b19a2e14a51c5a5ee162d7047c53fc30f9c Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 27 Apr 2026 17:41:45 +0000 Subject: [PATCH 124/545] patch 9.2.0401: tests: still a few flaky tests Problem: tests: still a few flaky tests Solution: Add WaitForAssert to test_messages.vim, use a smaller terminal window for test_tabpanel, add TermWait() in test_messages to handle DECQRM messages. closes: #20074 Signed-off-by: Christian Brabandt --- .../dumps/Test_tabpanel_drawing_2_0.dump | 20 +++++++++---------- .../dumps/Test_tabpanel_drawing_2_1.dump | 20 +++++++++---------- src/testdir/test_messages.vim | 4 +++- src/testdir/test_tabpanel.vim | 2 +- src/version.c | 2 ++ 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/testdir/dumps/Test_tabpanel_drawing_2_0.dump b/src/testdir/dumps/Test_tabpanel_drawing_2_0.dump index 23fe3c8e47..c6d034efb7 100644 --- a/src/testdir/dumps/Test_tabpanel_drawing_2_0.dump +++ b/src/testdir/dumps/Test_tabpanel_drawing_2_0.dump @@ -1,10 +1,10 @@ -| +0&#ffffff0@57||+1&&|++2&&| |[|N|o| |N|a|m|e|]| @7 -> +0&&@57||+1&&| @18 -|a+0&&@2| @54||+1&&| @18 -|~+0#4040ff13&| @56||+1#0000000&| @18 -|~+0#4040ff13&| @56||+1#0000000&| @18 -|~+0#4040ff13&| @56||+1#0000000&| @18 -|~+0#4040ff13&| @56||+1#0000000&| @18 -|~+0#4040ff13&| @56||+1#0000000&| @18 -|~+0#4040ff13&| @56||+1#0000000&| @18 -|-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@27|2|,|1| @10|A|l@1| ||+1&&| @18 +| +0&#ffffff0@44||+1&&|++2&&| |[|N|o| |N|a|m|e|]| @7 +> +0&&@44||+1&&| @18 +|a+0&&@2| @41||+1&&| @18 +|~+0#4040ff13&| @43||+1#0000000&| @18 +|~+0#4040ff13&| @43||+1#0000000&| @18 +|~+0#4040ff13&| @43||+1#0000000&| @18 +|~+0#4040ff13&| @43||+1#0000000&| @18 +|~+0#4040ff13&| @43||+1#0000000&| @18 +|~+0#4040ff13&| @43||+1#0000000&| @18 +|-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@14|2|,|1| @10|A|l@1| ||+1&&| @18 diff --git a/src/testdir/dumps/Test_tabpanel_drawing_2_1.dump b/src/testdir/dumps/Test_tabpanel_drawing_2_1.dump index d82adb29a8..92276717e1 100644 --- a/src/testdir/dumps/Test_tabpanel_drawing_2_1.dump +++ b/src/testdir/dumps/Test_tabpanel_drawing_2_1.dump @@ -1,10 +1,10 @@ -|++2&#ffffff0| |[|N|o| |N|a|m|e|]| @7||+1&&| +0&&@57 -| +1&&@18||> +0&&@57 -| +1&&@18|||a+0&&@2| @54 -| +1&&@18|||~+0#4040ff13&| @56 -| +1#0000000&@18|||~+0#4040ff13&| @56 -| +1#0000000&@18|||~+0#4040ff13&| @56 -| +1#0000000&@18|||~+0#4040ff13&| @56 -| +1#0000000&@18|||~+0#4040ff13&| @56 -| +1#0000000&@18|||~+0#4040ff13&| @56 -| +1#0000000&@18|||-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@27|2|,|1| @10|A|l@1| +|++2&#ffffff0| |[|N|o| |N|a|m|e|]| @7||+1&&| +0&&@44 +| +1&&@18||> +0&&@44 +| +1&&@18|||a+0&&@2| @41 +| +1&&@18|||~+0#4040ff13&| @43 +| +1#0000000&@18|||~+0#4040ff13&| @43 +| +1#0000000&@18|||~+0#4040ff13&| @43 +| +1#0000000&@18|||~+0#4040ff13&| @43 +| +1#0000000&@18|||~+0#4040ff13&| @43 +| +1#0000000&@18|||~+0#4040ff13&| @43 +| +1#0000000&@18|||-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@14|2|,|1| @10|A|l@1| diff --git a/src/testdir/test_messages.vim b/src/testdir/test_messages.vim index 5fa84e9542..80996cfad6 100644 --- a/src/testdir/test_messages.vim +++ b/src/testdir/test_messages.vim @@ -439,7 +439,7 @@ func Test_mode_cleared_after_silent_message() let buf = RunVimInTerminal('-S XsilentMessageMode', {'rows': 10}) call term_sendkeys(buf, 'v') - call TermWait(buf) + call WaitForAssert({-> assert_match('VISUAL.*\d\+\s\+\d', term_getline(buf, 10))}, 1000) call VerifyScreenDump(buf, 'Test_mode_cleared_after_silent_message_1', {}) call term_sendkeys(buf, 'd') @@ -458,6 +458,8 @@ func Test_echo_verbose_system() CheckNotMac " the macos TMPDIR is too long for snapshot testing let buf = RunVimInTerminal('', {'rows': 10}) + " give it some time to handle DECRQM response + call TermWait(buf, 50) call term_sendkeys(buf, ":4 verbose echo system('seq 20')\") " Note that the screendump is filtered to remove the name of the temp file call VerifyScreenDump(buf, 'Test_verbose_system_1', {}) diff --git a/src/testdir/test_tabpanel.vim b/src/testdir/test_tabpanel.vim index 0f6aa0b568..c510d078fd 100644 --- a/src/testdir/test_tabpanel.vim +++ b/src/testdir/test_tabpanel.vim @@ -372,7 +372,7 @@ function Test_tabpanel_drawing_2() END call writefile(lines, 'XTest_tabpanel_drawing_2', 'D') - let buf = RunVimInTerminal('-S XTest_tabpanel_drawing_2', {'rows': 10, 'cols': 78}) + let buf = RunVimInTerminal('-S XTest_tabpanel_drawing_2', {'rows': 10, 'cols': 65}) call term_sendkeys(buf, "ggo") call VerifyScreenDump(buf, 'Test_tabpanel_drawing_2_0', {}) diff --git a/src/version.c b/src/version.c index 8dfe9b8f78..df55ec4d40 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 401, /**/ 400, /**/ From 7070a85d94faf90fa3c959ad73b5f9db1b947ee9 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 27 Apr 2026 18:05:12 +0000 Subject: [PATCH 125/545] patch 9.2.0402: pum: opacity not applied to wildmenu pum Problem: pum: opacity not applied to wildmenu pum Solution: Call pum_call_update_screen() in cmdline_pum_display() when opacity is set, fix flicker by checking against expected row (Yasuhiro Matsumoto). closes: #20072 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/cmdexpand.c | 2 ++ src/popupmenu.c | 6 +++++- src/version.c | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/cmdexpand.c b/src/cmdexpand.c index befc18c79c..478cda25a0 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -436,6 +436,8 @@ cmdline_pum_create( void cmdline_pum_display(void) { + if (p_po > 0 && p_po < 100 && !pum_redraw_in_same_position()) + pum_call_update_screen(); pum_display(compl_match_array, compl_match_arraysize, compl_selected); } diff --git a/src/popupmenu.c b/src/popupmenu.c index cadcda7f4d..b7929607d9 100644 --- a/src/popupmenu.c +++ b/src/popupmenu.c @@ -1617,8 +1617,12 @@ pum_visible(void) static int pum_in_same_position(void) { + int row = (State & MODE_CMDLINE) + ? cmdline_row + : curwin->w_wrow + W_WINROW(curwin); + return pum_window != curwin - || (pum_win_row == curwin->w_wrow + W_WINROW(curwin) + || (pum_win_row == row && pum_win_height == curwin->w_height && pum_win_col == curwin->w_wincol && pum_win_width == curwin->w_width); diff --git a/src/version.c b/src/version.c index df55ec4d40..45f5719329 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 402, /**/ 401, /**/ From f1a9449206b019f6443915c4d56ee0cc51492a87 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 27 Apr 2026 18:14:49 +0000 Subject: [PATCH 126/545] patch 9.2.0403: Vim9: def function sandbox bypass Problem: Vim9: def function sandbox bypass (Srinivas Piskala Ganesh Babu) Solution: Check for sandbox flag in call_user_func() and call_dfunc() when executing Vim9 script functions closes: #20071 Supported by AI Signed-off-by: Christian Brabandt --- src/testdir/test_functions.vim | 37 ++++++++++++++++++++++++++++++++++ src/userfunc.c | 7 +++++++ src/version.c | 2 ++ src/vim9execute.c | 6 ++++++ 4 files changed, 52 insertions(+) diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index 9c652101ae..e6142c2df3 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -4629,4 +4629,41 @@ func Test_uriencoding() call v9.CheckLegacyAndVim9Success(lines) endfunc +" Note: legacy func, not vim9 def, to avoid the test file being vim9script +func Test_vim9_def_fc_sandbox() + sandbox def! g:Bad() + system('echo unsafe') + enddef + + call assert_fails('call g:Bad()', 'E48:') + delfunction g:Bad +endfunc + +func Test_vim9_def_call_dfunc_fc_sandbox() + " Inner has FC_SANDBOX, outer does not. + " Calling outer goes through call_dfunc into inner, exercising + " the sandbox handling in call_dfunc and func_return. + sandbox def! g:Inner() + system('echo unsafe') + enddef + + def! g:Outer() + g:Inner() + enddef + + call assert_fails('call g:Outer()', 'E48:') + delfunction g:Inner + delfunction g:Outer +endfunc + +" Deferred body inside a sandboxed def function must still run sandboxed. +func Test_vim9_def_defer_fc_sandbox() + sandbox def! g:BadDefer() + defer system('echo unsafe') + enddef + + call assert_fails('call g:BadDefer()', 'E48:') + delfunction g:BadDefer +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/userfunc.c b/src/userfunc.c index e861a9eacb..2615185386 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3038,6 +3038,11 @@ call_user_func( if (fp->uf_def_status != UF_NOT_COMPILED) { + if (fp->uf_flags & FC_SANDBOX) + { + using_sandbox = TRUE; + ++sandbox; + } #ifdef FEAT_PROFILE ufunc_T *caller = fc->fc_caller == NULL ? NULL : fc->fc_caller->fc_func; #endif @@ -3050,6 +3055,8 @@ call_user_func( if (call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial, funcexe->fe_object, fc, rettv) == FAIL) retval = FCERR_FAILED; + if (using_sandbox) + --sandbox; funcdepth_decrement(); #ifdef FEAT_PROFILE if (do_profiling == PROF_YES && (fp->uf_profiling diff --git a/src/version.c b/src/version.c index 45f5719329..72f88365f5 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 403, /**/ 402, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index d83b7968b2..e1ddb7c1de 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -742,6 +742,9 @@ call_dfunc( else ectx->ec_outer_ref = NULL; + if (ufunc->uf_flags & FC_SANDBOX) + ++sandbox; + ++ufunc->uf_calls; // Set execution state to the start of the called function. @@ -1290,6 +1293,9 @@ func_return(ectx_T *ectx) if (dfunc->df_defer_var_idx > 0) invoke_defer_funcs(ectx); + if (dfunc->df_ufunc->uf_flags & FC_SANDBOX) + --sandbox; + // No check for uf_refcount being zero, cannot think of a way that would // happen. --dfunc->df_ufunc->uf_calls; From 3958188f6aace8f828a36f8886e99c6a64fedcf1 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 27 Apr 2026 18:29:33 +0000 Subject: [PATCH 127/545] patch 9.2.0404: redraw_listener_add() does not check secure flag Problem: redraw_listener_add() does not check secure flag Solution: Check for check_secure() in f_redraw_listener_add() closes: #20070 Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 10 +++++++++- src/drawscreen.c | 3 +++ src/testdir/test_listener.vim | 11 +++++++++++ src/version.c | 2 ++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 371de4b2c6..67ebc70f94 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 26 +*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6863,6 +6863,8 @@ listener_add({callback} [, {buf} [, {unbuffered}]]) *listener_add()* second argument: > GetBuffer()->listener_add(callback) < + This function is not available in the |sandbox|. + Return type: |Number| @@ -6877,6 +6879,8 @@ listener_flush([{buf}]) *listener_flush()* Can also be used as a |method|: > GetBuffer()->listener_flush() < + This function is not available in the |sandbox|. + Return type: void @@ -6888,6 +6892,8 @@ listener_remove({id}) *listener_remove()* Can also be used as a |method|: > GetListenerId()->listener_remove() < + This function is not available in the |sandbox|. + Return type: |Number| @@ -8867,6 +8873,8 @@ redraw_listener_add({opts}) *redraw_listener_add()* Can also be used as a |method|: > GetOpts()->redraw_listener_add() < + This function is not available in the |sandbox|. + Return type: |Number| diff --git a/src/drawscreen.c b/src/drawscreen.c index 2f36f4f5a0..702d686aba 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -3506,6 +3506,9 @@ f_redraw_listener_add(typval_T *argvars, typval_T *rettv) bool got_one = false; static int id; + if (check_secure()) + return; + if (redraw_cb_in_progress) { emsg(_(e_cannot_add_redraw_listener_in_listener_callback)); diff --git a/src/testdir/test_listener.vim b/src/testdir/test_listener.vim index 8cbd15377a..6b91e8d046 100644 --- a/src/testdir/test_listener.vim +++ b/src/testdir/test_listener.vim @@ -801,4 +801,15 @@ func Test_listener_blockwise_paste() bwipe! endfunc +func Test_listener_add_in_sandbox() + call assert_fails( + \ 'sandbox call redraw_listener_add({"on_start": function("tr")})', + \ 'E48:') + call assert_fails( + \ 'sandbox call listener_add({"on_start": function("tr")})', + \ 'E48:') + call assert_fails('sandbox call listener_flush()', 'E48:') + call assert_fails('sandbox call listener_remove(1)', 'E48:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 72f88365f5..ef0fd8afa2 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 404, /**/ 403, /**/ From ae196b2d589562fbb3fcd64cd0cc33f052c6cf56 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 27 Apr 2026 18:40:07 +0000 Subject: [PATCH 128/545] patch 9.2.0405: when jumping to tags, will open URLs Problem: when jumping to tags, will open URLs (Srinivas Piskala Ganesh Babu) Solution: Disallow trying to open remote files. closes: #20068 Supported by AI Signed-off-by: Christian Brabandt --- runtime/doc/tags | 1 + runtime/doc/tagsrch.txt | 3 +++ src/errors.h | 2 ++ src/po/vim.pot | 5 ++++- src/tag.c | 9 +++++++++ src/testdir/test_tagjump.vim | 11 +++++++++++ src/version.c | 2 ++ 7 files changed, 32 insertions(+), 1 deletion(-) diff --git a/runtime/doc/tags b/runtime/doc/tags index 65edb2c31c..6376edad2f 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -4778,6 +4778,7 @@ E1572 options.txt /*E1572* E1573 channel.txt /*E1573* E1574 channel.txt /*E1574* E1575 builtin.txt /*E1575* +E1576 tagsrch.txt /*E1576* E158 sign.txt /*E158* E159 sign.txt /*E159* E16 cmdline.txt /*E16* diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index eb2cb1b3f9..cad4caf1a9 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -579,6 +579,9 @@ ctags). have an absolute or relative path. It may contain environment variables and wildcards (although the use of wildcards is doubtful). It cannot contain a . + *E1576* + Using a remote file via network protocol (e.g. using + http://remote/file.txt) is not allowed. {tagaddress} The Ex command that positions the cursor on the tag. It can be any Ex command, although restrictions apply (see |tag-security|). Posix only allows line numbers and search diff --git a/src/errors.h b/src/errors.h index 8ac627af68..c077118c6d 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3809,3 +3809,5 @@ EXTERN char e_gethostbyname_in_channel_listen[] EXTERN char e_cannot_create_pipes[] INIT(= N_("E1575: Cannot create pipes")); #endif +EXTERN char e_tag_file_entry_must_not_be_url[] + INIT(= N_("E1576: Tag file entry must not be a URL")); diff --git a/src/po/vim.pot b/src/po/vim.pot index e3206e8712..8f43a89f73 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-04-21 19:33+0000\n" +"POT-Creation-Date: 2026-04-27 18:39+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -8856,6 +8856,9 @@ msgstr "" msgid "E1575: Cannot create pipes" msgstr "" +msgid "E1576: Tag file entry must not be a URL" +msgstr "" + #. type of cmdline window or 0 #. result of cmdline window or 0 #. buffer of cmdline window or NULL diff --git a/src/tag.c b/src/tag.c index e10a837601..6861a44cf5 100644 --- a/src/tag.c +++ b/src/tag.c @@ -4135,6 +4135,15 @@ expand_tag_fname(char_u *fname, char_u *tag_fname, int expand) char_u *expanded_fname = NULL; expand_T xpc; + // Refuse to follow URLs from tag files. Tag entries are expected + // to reference local source files; a URL would otherwise be passed + // to netrw and trigger a network request. + if (path_with_url(fname)) + { + emsg(_(e_tag_file_entry_must_not_be_url)); + return NULL; + } + /* * Expand file name (for environment variables) when needed. * Disallow backticks, they could execute arbitrary shell diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index c0fa7b02e6..d0e18002bb 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -1715,4 +1715,15 @@ func Test_tag_backtick_filename_not_expanded() bwipe! endfunc +func Test_tagjump_refuse_url() + call writefile([ + \ "XTagURL\thttp://127.0.0.1:1/$XTAG_SECRET/file.c\t/^int main" + \ ], 'Xtags', 'D') + let save_tags = &tags + set tags=Xtags + + call assert_fails('tag XTagURL', 'E1576:') + let &tags = save_tags +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index ef0fd8afa2..9779964483 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 405, /**/ 404, /**/ From 7e765148964b4758e7c45bbb781a476178e3aaa1 Mon Sep 17 00:00:00 2001 From: Shane Harper Date: Mon, 27 Apr 2026 19:55:03 +0000 Subject: [PATCH 129/545] patch 9.2.0406: VisualNOS not used when Wayland selection ownership lost Problem: VisualNOS not used when Wayland selection ownership lost (lilydjwg) Solution: Don't require X_DISPLAY != NULL to use VisualNOS (Shane Harper). fixes: #19914 related: #19812 related: #19659 closes: #20066 Signed-off-by: Shane Harper Signed-off-by: Christian Brabandt --- runtime/doc/syntax.txt | 5 +++-- src/drawline.c | 9 ++++----- src/version.c | 2 ++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index c18c2b5e3a..34eba55fd1 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 Apr 23 +*syntax.txt* For Vim version 9.2. Last change: 2026 Apr 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6155,7 +6155,8 @@ TitleBarNC Title bar for inactive Gui's window. Visual Visual mode selection. *hl-VisualNOS* VisualNOS Visual mode selection when vim is "Not Owning the Selection". - Only X11 Gui's |gui-x11| and |xterm-clipboard| supports this. + Only X11 Gui's |gui-x11|, |xterm-clipboard| and |wayland-selections| + supports this. *hl-WarningMsg* WarningMsg Warning messages. *hl-WildMenu* diff --git a/src/drawline.c b/src/drawline.c index 93561e689f..3ebb56a173 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -1464,12 +1464,11 @@ win_line( { area_highlighting = TRUE; vi_attr = HL_ATTR(HLF_V); -#if defined(FEAT_CLIPBOARD) && defined(FEAT_X11) - if (X_DISPLAY && - ((clip_star.available && !clip_star.owned +#if defined(FEAT_CLIPBOARD) && (defined(FEAT_X11) || defined(FEAT_WAYLAND_CLIPBOARD)) + if ((clip_star.available && !clip_star.owned && clip_isautosel_star()) - || (clip_plus.available && !clip_plus.owned - && clip_isautosel_plus()))) + || (clip_plus.available && !clip_plus.owned + && clip_isautosel_plus())) vi_attr = HL_ATTR(HLF_VNC); #endif } diff --git a/src/version.c b/src/version.c index 9779964483..0d4d8eac2f 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 406, /**/ 405, /**/ From 2d43240659868e91e7c371017793091082bfda74 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Mon, 27 Apr 2026 21:14:46 +0000 Subject: [PATCH 130/545] patch 9.2.0407: tabpanel: A few issues with the tabpanel Problem: Several issues around the tabpanel scrollbar: 1. :set tabpanelopt= completion did not offer "scroll" and "scrollbar". 2. gt/gT and other tab switches did not update the scrollbar thumb; the current tab could move outside the visible panel range without the view following. 3. When tpl_scroll_offset was at its maximum, the thumb's bottom did not reach the last screen row due to integer truncation in thumb_top (e.g. 31 tabs on 24 rows + :tablast left a one-row gap). 4. For align:right the scrollbar was drawn on the panel's left edge (adjacent to the buffer area), which breaks the common convention that a vertical scrollbar sits on the right. Solution: - Add "scroll" and "scrollbar" to the 'tabpanelopt' expansion list. Cover the completion in test_options.vim and extend util/gen_opt_test.vim with the new valid/invalid values; drop the now-redundant acceptance test from test_tabpanel.vim. - In draw_tabpanel(), remember the last-drawn curtab and, when it changes, adjust tpl_scroll_offset so curtab_row falls inside [offset, offset + Rows). Mouse wheel and drag leave curtab unchanged, so the user's chosen offset is preserved. - In draw_tabpanel_scrollbar(), compute thumb_top as (Rows - thumb_height) * tpl_scroll_offset / (tpl_total_rows - Rows), mirroring the mapping already used by tabpanel_drag_scrollbar(). This guarantees the thumb's bottom reaches the last row at the maximum offset. - In draw_tabpanel(), place the scrollbar at the tabpanel's right edge for both align:left and align:right (previously align:right put it on the panel's left edge next to the vertical separator). For align:right this means the scrollbar now sits at the screen's right edge. - Update :h tabpanel-scroll to describe the new, align- independent placement. - Add Test_tabpanel_scrollbar_follows_curtab() and Test_tabpanel_scrollbar_reaches_bottom() to exercise the regressions fixed by items 2 and 3. closes: #20052 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 28 ++-- runtime/doc/tabpage.txt | 36 ++--- runtime/doc/version9.txt | 6 +- src/optionstr.c | 2 +- src/proto/tabpanel.pro | 1 + src/tabpanel.c | 123 ++++++++++++------ .../dumps/Test_tabpanel_many_tabpages_4.dump | 16 +-- src/testdir/test_options.vim | 6 + src/testdir/test_tabpanel.vim | 119 +++++++++++++---- src/testdir/util/gen_opt_test.vim | 6 +- src/version.c | 2 + src/window.c | 3 + 12 files changed, 228 insertions(+), 120 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index d2611db095..b3edc22e35 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 21 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -9062,25 +9062,17 @@ A jump table for the options with a short description can be found at |Q_op|. columns:{n} Number of columns of the tabpanel. If this value is 0 or less than 'columns', the - tab panel will not be displayed. + tabpanel will not be displayed. (default 20) - scroll Enable mouse wheel scrolling over the tabpanel - area when the tab list exceeds the visible - screen height. The scroll step is controlled - by 'mousescroll'. When disabled (the default), - the tabpanel shows the page containing the - current tab, with no way to view tabs outside - that page. - - scrollbar Reserve a one-column scrollbar in the tabpanel - showing the current scroll position. The - scrollbar uses the |hl-PmenuSbar| and - |hl-PmenuThumb| highlight groups for the track - and thumb respectively. Clicking on the - scrollbar column jumps the thumb to that - position; the thumb can also be dragged. - Implies "scroll". + scrollbar Reserve a one-column scrollbar at the right + edge of the tabpanel showing the current + scroll position. The scrollbar uses the + |hl-PmenuSbar| and |hl-PmenuThumb| highlight + groups for the track and thumb respectively. + Clicking on the scrollbar column jumps the + thumb to that position; the thumb can also be + dragged. See |tabpanel-scroll|. vert Use a vertical separator for tabpanel. The vertical separator character is taken from diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index 7fc09c5451..88aeb4ad34 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -1,4 +1,4 @@ -*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 26 +*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -485,33 +485,33 @@ groups: |hl-TabPanel| |hl-TabPanelSel| |hl-TabPanelFill| SCROLLING IN THE TABPANEL: *tabpanel-scroll* When the total height of the tab page list exceeds the visible screen height, -the tabpanel by default displays the "page" that contains the current tab page -and offers no way to view tab pages outside that page. +mouse wheel events over the tabpanel area scroll the tab page list up or +down. The scroll step follows the 'mousescroll' setting. Wheel events +inside the tabpanel area are consumed by the tabpanel and do not trigger +|| or || mappings. -To make the tabpanel scrollable, add "scroll" to 'tabpanelopt': > - :set tabpanelopt+=scroll +The current tab page is always brought into view: when the selected tab +page changes (by |gt|, |gT|, |:tabnext| etc.), the panel scrolls so the +current entry is visible. -With "scroll" enabled, mouse wheel events over the tabpanel area scroll the -tab page list up or down. The scroll step follows the 'mousescroll' setting. -Wheel events inside the tabpanel area are consumed by the tabpanel and do not -trigger || or || mappings. - -To additionally show a vertical scrollbar indicating the current scroll -position, use "scrollbar": > +To show a vertical scrollbar indicating the current scroll position, add +"scrollbar" to 'tabpanelopt': > :set tabpanelopt+=scrollbar -The "scrollbar" value implies "scroll". A one-column scrollbar is reserved at -the edge of the tabpanel; clicking on the scrollbar column moves the thumb to +A one-column scrollbar is always reserved at the right edge of the +tabpanel, regardless of 'align'. For |'tabpanelopt'|=align:left this is +the edge adjacent to the buffer windows; for align:right it is the right +edge of the screen. Clicking on the scrollbar column moves the thumb to the click position, and the thumb can be dragged to scroll continuously. -When "vert" is combined with "scrollbar", the scrollbar is drawn next to the -vertical separator, on the panel side. +When "vert" is combined with "scrollbar", the vertical separator is drawn +at the tabpanel's boundary with the buffer area and the scrollbar stays at +the tabpanel's right edge. The scrollbar uses the |hl-PmenuSbar| highlight group for the track and |hl-PmenuThumb| for the thumb. -The scroll offset is remembered across redraws but is reset when "scroll" or -"scrollbar" is toggled off and back on. +The scroll offset is remembered across redraws. MOUSE CLICKS IN THE TABPANEL: *tabpanel-mouse* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 43106ef30a..725ba3326d 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 26 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 27 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52616,8 +52616,8 @@ Other ~ - Allow mouse clickable regions in the 'statusline', 'tabline' and the 'tabpanel' using the |stl-%[FuncName]| atom. - Enable reflow support in the |:terminal|. -- Added "scroll" and "scrollbar" sub-options to 'tabpanelopt' so the tabpanel - can scroll when the tab page list exceeds the visible screen height. +- Added "scrollbar" sub-option to 'tabpanelopt' so the tabpanel can scroll + when the tab page list exceeds the visible screen height. Platform specific ~ ----------------- diff --git a/src/optionstr.c b/src/optionstr.c index ab7c5ffff1..72f5dd3ced 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -30,7 +30,7 @@ static char *(p_briopt_values[]) = {"shift:", "min:", "sbr", "list:", "column:", #endif #if defined(FEAT_TABPANEL) // Note: Keep this in sync with tabpanelopt_changed() -static char *(p_tplo_values[]) = {"align:", "columns:", "vert", NULL}; +static char *(p_tplo_values[]) = {"align:", "columns:", "scrollbar", "vert", NULL}; static char *(p_tplo_align_values[]) = {"left", "right", NULL}; #endif #if defined(FEAT_DIFF) diff --git a/src/proto/tabpanel.pro b/src/proto/tabpanel.pro index 72726d89ea..e6328b1774 100644 --- a/src/proto/tabpanel.pro +++ b/src/proto/tabpanel.pro @@ -1,5 +1,6 @@ /* tabpanel.c */ int tabpanelopt_changed(void); +void tabpanel_forget_tabpage(const tabpage_T *tp); int tabpanel_width(void); int tabpanel_leftcol(void); void draw_tabpanel(void); diff --git a/src/tabpanel.c b/src/tabpanel.c index 07be48e44c..34372fe60c 100644 --- a/src/tabpanel.c +++ b/src/tabpanel.c @@ -43,11 +43,11 @@ static int opt_scope = OPT_LOCAL; static int tpl_align = ALIGN_LEFT; static int tpl_columns = 20; static bool tpl_is_vert = false; -static bool tpl_scroll = false; static bool tpl_scrollbar = false; static int tpl_scroll_offset = 0; static int tpl_total_rows = 0; -static int tpl_scrollbar_col = -1; // screen column of scrollbar, -1 if none +static int tpl_scrollbar_col = -1; // screen column of scrollbar, -1 if none +static tabpage_T *tpl_last_curtab = NULL; // last curtab seen by draw_tabpanel typedef struct { win_T *wp; @@ -69,7 +69,6 @@ tabpanelopt_changed(void) int new_align = ALIGN_LEFT; long new_columns = 20; bool new_is_vert = false; - bool new_scroll = false; bool new_scrollbar = false; p = p_tplo; @@ -107,12 +106,6 @@ tabpanelopt_changed(void) { p += 9; new_scrollbar = true; - new_scroll = true; - } - else if (STRNCMP(p, "scroll", 6) == 0) - { - p += 6; - new_scroll = true; } if (*p != ',' && *p != NUL) @@ -124,15 +117,26 @@ tabpanelopt_changed(void) tpl_align = new_align; tpl_columns = new_columns; tpl_is_vert = new_is_vert; - if (tpl_scroll != new_scroll) - tpl_scroll_offset = 0; - tpl_scroll = new_scroll; tpl_scrollbar = new_scrollbar; + // Re-center the current tab on the next redraw. + tpl_last_curtab = NULL; + shell_new_columns(); return OK; } +/* + * Drop any internal reference to "tp", so draw_tabpanel() never compares + * against a dangling pointer after the tabpage has been freed. + */ + void +tabpanel_forget_tabpage(const tabpage_T *tp) +{ + if (tpl_last_curtab == tp) + tpl_last_curtab = NULL; +} + /* * Return the width of tabpanel. */ @@ -264,6 +268,31 @@ tabpanel_append_click_regions( } } +/* + * Ensure the current tab is visible by adjusting tpl_scroll_offset when + * the selected tab has changed since the previous redraw. Mouse wheel or + * scrollbar drag operations leave curtab unchanged, so the user's chosen + * offset is preserved in those cases. + */ + static void +follow_curtab_if_needed(int curtab_row) +{ + if (Rows <= 0 || curtab == tpl_last_curtab) + return; + + if (curtab_row < tpl_scroll_offset) + tpl_scroll_offset = curtab_row; + else if (curtab_row >= tpl_scroll_offset + Rows) + tpl_scroll_offset = curtab_row - Rows + 1; + + int max_offset = tpl_total_rows > Rows ? tpl_total_rows - Rows : 0; + + if (tpl_scroll_offset < 0) + tpl_scroll_offset = 0; + else if (tpl_scroll_offset > max_offset) + tpl_scroll_offset = max_offset; +} + /* * draw the tabpanel. */ @@ -293,30 +322,36 @@ draw_tabpanel(void) int sb_len = tpl_scrollbar ? SCROLL_LEN : 0; int sb_screen_col = -1; + // The scrollbar is always placed at the right edge of the tabpanel, + // regardless of 'align'. The vertical separator sits at the panel's + // boundary with the buffer area (left edge for align:right, right edge + // for align:left). if (tpl_is_vert) { if (is_right) { - // draw main contents in tabpanel - do_by_tplmode(TPLMODE_GET_CURTAB_ROW, VERT_LEN + sb_len, - maxwidth - VERT_LEN, &curtab_row, NULL); - do_by_tplmode(TPLMODE_REDRAW, VERT_LEN + sb_len, maxwidth, + // Panel on the right: vert at panel's left edge, scrollbar at + // panel's right edge (= screen's right edge). + do_by_tplmode(TPLMODE_GET_CURTAB_ROW, VERT_LEN, + maxwidth - sb_len, &curtab_row, NULL); + follow_curtab_if_needed(curtab_row); + do_by_tplmode(TPLMODE_REDRAW, VERT_LEN, maxwidth - sb_len, &curtab_row, NULL); - // draw vert separator in tabpanel for (vsrow = 0; vsrow < Rows; vsrow++) screen_putchar(curwin->w_fill_chars.tpl_vert, vsrow, topframe->fr_width, vs_attr); if (tpl_scrollbar) - sb_screen_col = topframe->fr_width + VERT_LEN; + sb_screen_col = topframe->fr_width + maxwidth - SCROLL_LEN; } else { - // draw main contents in tabpanel + // Panel on the left: scrollbar just left of vert, vert at + // panel's right edge (boundary with buffer). do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth - VERT_LEN - sb_len, &curtab_row, NULL); + follow_curtab_if_needed(curtab_row); do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - VERT_LEN - sb_len, &curtab_row, NULL); - // draw vert separator in tabpanel for (vsrow = 0; vsrow < Rows; vsrow++) screen_putchar(curwin->w_fill_chars.tpl_vert, vsrow, maxwidth - VERT_LEN, vs_attr); @@ -328,16 +363,20 @@ draw_tabpanel(void) { if (is_right) { - do_by_tplmode(TPLMODE_GET_CURTAB_ROW, sb_len, maxwidth, + // Panel on the right, no vert: scrollbar at screen's right edge. + do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth - sb_len, + &curtab_row, NULL); + follow_curtab_if_needed(curtab_row); + do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - sb_len, &curtab_row, NULL); - do_by_tplmode(TPLMODE_REDRAW, sb_len, maxwidth, &curtab_row, NULL); if (tpl_scrollbar) - sb_screen_col = topframe->fr_width; + sb_screen_col = topframe->fr_width + maxwidth - SCROLL_LEN; } else { do_by_tplmode(TPLMODE_GET_CURTAB_ROW, 0, maxwidth - sb_len, &curtab_row, NULL); + follow_curtab_if_needed(curtab_row); do_by_tplmode(TPLMODE_REDRAW, 0, maxwidth - sb_len, &curtab_row, NULL); if (tpl_scrollbar) @@ -354,6 +393,7 @@ draw_tabpanel(void) // A user function may reset KeyTyped, restore it. KeyTyped = saved_KeyTyped; + tpl_last_curtab = curtab; redraw_tabpanel = FALSE; } @@ -606,13 +646,7 @@ do_by_tplmode( args.col_end = col_end; if (tplmode != TPLMODE_GET_CURTAB_ROW && args.maxrow > 0) - { - if (tpl_scroll) - args.offsetrow = tpl_scroll_offset; - else - while (args.offsetrow + args.maxrow <= *pcurtab_row) - args.offsetrow += args.maxrow; - } + args.offsetrow = tpl_scroll_offset; tp = first_tabpage; @@ -632,16 +666,9 @@ do_by_tplmode( { args.attr = attr_tpls; if (tplmode == TPLMODE_GET_CURTAB_ROW) - { + // Capture the row of the current tab and keep iterating so + // tpl_total_rows receives the true content height below. *pcurtab_row = row; - // When scroll mode is active keep iterating so tpl_total_rows - // receives the true content height; otherwise bail out early. - if (!tpl_scroll) - { - do_unlet((char_u *)"g:actual_curtabpage", TRUE); - break; - } - } } else args.attr = attr_tpl; @@ -742,7 +769,7 @@ do_by_tplmode( // Capture the true content height during the GET_CURTAB_ROW pass, which // ignores maxrow and therefore walks every tab. REDRAW stops at the // visible edge so its "row" is clamped and unusable here. - if (tplmode == TPLMODE_GET_CURTAB_ROW && tpl_scroll) + if (tplmode == TPLMODE_GET_CURTAB_ROW) tpl_total_rows = row; } @@ -761,10 +788,21 @@ draw_tabpanel_scrollbar(int screen_col) if (tpl_total_rows > Rows && Rows > 0) { + int max_offset = tpl_total_rows - Rows; + int track_range; + thumb_height = Rows * Rows / tpl_total_rows; if (thumb_height < 1) thumb_height = 1; - thumb_top = Rows * tpl_scroll_offset / tpl_total_rows; + + // Map tpl_scroll_offset onto the track: at offset 0 the thumb's top + // is at row 0, at the maximum offset its bottom reaches the last + // row. This is the exact inverse of tabpanel_drag_scrollbar(). + track_range = Rows - thumb_height; + if (track_range > 0 && max_offset > 0) + thumb_top = track_range * tpl_scroll_offset / max_offset; + else + thumb_top = 0; if (thumb_top + thumb_height > Rows) thumb_top = Rows - thumb_height; if (thumb_top < 0) @@ -848,7 +886,6 @@ tabpanel_drag_scrollbar(int screen_row) /* * Scroll the tabpanel by 'count' rows in direction 'dir' (1 = down, -1 = up). * Returns true if the offset changed and a redraw was scheduled. - * Has no effect unless 'tabpanelopt' contains "scroll". */ bool tabpanel_scroll(int dir, int count) @@ -856,7 +893,7 @@ tabpanel_scroll(int dir, int count) int max_offset; int new_offset; - if (!tpl_scroll || tabpanel_width() == 0) + if (tabpanel_width() == 0) return false; max_offset = tpl_total_rows - Rows; diff --git a/src/testdir/dumps/Test_tabpanel_many_tabpages_4.dump b/src/testdir/dumps/Test_tabpanel_many_tabpages_4.dump index aa1ae3e18b..3f462354c4 100644 --- a/src/testdir/dumps/Test_tabpanel_many_tabpages_4.dump +++ b/src/testdir/dumps/Test_tabpanel_many_tabpages_4.dump @@ -1,10 +1,10 @@ -|1+2&#ffffff0@1|:|t|a|b| @3> +0&&@34 +|5+8#0000001#e0e0e08|:|t|a|b| @4> +0#0000000#ffffff0@34 +|6+8#0000001#e0e0e08|:|t|a|b| @4|~+0#4040ff13#ffffff0| @33 +|7+8#0000001#e0e0e08|:|t|a|b| @4|~+0#4040ff13#ffffff0| @33 +|8+8#0000001#e0e0e08|:|t|a|b| @4|~+0#4040ff13#ffffff0| @33 +|9+8#0000001#e0e0e08|:|t|a|b| @4|~+0#4040ff13#ffffff0| @33 +|1+8#0000001#e0e0e08|0|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33 +|1+2#0000000&@1|:|t|a|b| @3|~+0#4040ff13&| @33 |1+8#0000001#e0e0e08|2|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33 |1+8#0000001#e0e0e08|3|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33 -|1+8#0000001#e0e0e08|4|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33 -|1+8#0000001#e0e0e08|5|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33 -|1+8#0000001#e0e0e08|6|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33 -|1+8#0000001#e0e0e08|7|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33 -|1+8#0000001#e0e0e08|8|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33 -|1+8#0000001#e0e0e08|9|:|t|a|b| @3|~+0#4040ff13#ffffff0| @33 -|2+8#0000001#e0e0e08|0|:|t|a|b| @3|:+0#0000000#ffffff0|t|a|b|n|e|x|t| |-|3| @6|0|,|0|-|1| @7|A|l@1| +|1+8#0000001#e0e0e08|4|:|t|a|b| @3|:+0#0000000#ffffff0|t|a|b|n|e|x|t| |-|3| @6|0|,|0|-|1| @7|A|l@1| diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index b1be224467..91610e1289 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -593,6 +593,12 @@ func Test_set_completion_string_values() if exists('+tabclose') call assert_equal('left uselast', join(sort(getcompletion('set tabclose=', 'cmdline'))), ' ') endif + if has('tabpanel') + call assert_equal(['align:', 'columns:', 'scrollbar', 'vert'], + \ getcompletion('set tabpanelopt=', 'cmdline')) + call assert_equal(['left', 'right'], + \ getcompletion('set tabpanelopt=align:', 'cmdline')) + endif if exists('+termwintype') call assert_equal('conpty', getcompletion('set termwintype=', 'cmdline')[1]) endif diff --git a/src/testdir/test_tabpanel.vim b/src/testdir/test_tabpanel.vim index c510d078fd..f7e6bdb082 100644 --- a/src/testdir/test_tabpanel.vim +++ b/src/testdir/test_tabpanel.vim @@ -963,49 +963,26 @@ func Test_tabpanel_large_columns() call assert_fails(':set tabpanelopt=columns:-1', 'E474:') endfunc -func Test_tabpanel_scrollopt_accepted() - " 'scroll' / 'scrollbar' must be accepted in 'tabpanelopt'. - set tabpanelopt=scroll - call assert_equal('scroll', &tabpanelopt) - set tabpanelopt=scrollbar - call assert_equal('scrollbar', &tabpanelopt) - - " Combination with other values. - set tabpanelopt=align:right,scroll - call assert_equal('align:right,scroll', &tabpanelopt) - set tabpanelopt=columns:15,vert,scrollbar - call assert_equal('columns:15,vert,scrollbar', &tabpanelopt) - set tabpanelopt=align:right,columns:12,vert,scrollbar - call assert_equal('align:right,columns:12,vert,scrollbar', &tabpanelopt) - - " Unknown values must still fail. - call assert_fails(':set tabpanelopt=scrol', 'E474:') - call assert_fails(':set tabpanelopt=scrollbarx', 'E474:') - - call s:reset() -endfunc - func Test_tabpanel_scroll_many_tabs() let save_lines = &lines let save_showtabpanel = &showtabpanel let save_tabpanelopt = &tabpanelopt - " Make the screen short so the tab list exceeds the visible height. + " Make the screen short so the tab page list exceeds the visible height. set lines=8 set showtabpanel=2 - set tabpanelopt=scroll + set tabpanelopt= for i in range(20) tabnew endfor - " Should not crash with many tabs and scroll enabled. + " Should not crash with many tabs (scroll behaviour is always on). redraw! - " Switching to scrollbar resets the offset but must also not crash. + " Toggling scrollbar must also not crash. set tabpanelopt=scrollbar redraw! - " Disabling scroll returns to normal behavior. set tabpanelopt= redraw! @@ -1024,6 +1001,94 @@ func Test_tabpanel_scroll_many_tabs() let &lines = save_lines endfunc +" The scrollbar thumb must follow the current tab when it is moved by +" gt/gT/:tabnext/:tablast, so that the selected tab is always visible. +func Test_tabpanel_scrollbar_follows_curtab() + let save_lines = &lines + let save_columns = &columns + let save_showtabpanel = &showtabpanel + let save_tabpanelopt = &tabpanelopt + + set lines=10 columns=40 + set showtabpanel=2 tabpanelopt=scrollbar,columns:8 + for i in range(49) + tabnew + endfor + let sb_col = 8 + + " With curtab at the top of the list, row 1 shows the thumb and the + " last row shows the track. Record the two attrs for comparison. + tabfirst + redraw + let attr_thumb = screenattr(1, sb_col) + let attr_track = screenattr(&lines, sb_col) + call assert_notequal(attr_thumb, attr_track) + + " Jump to a tab far outside the visible range: thumb must leave the top. + 30tabnext + redraw + call assert_equal(attr_track, screenattr(1, sb_col)) + + " Back to the first tab: thumb returns to the top. + tabfirst + redraw + call assert_equal(attr_thumb, screenattr(1, sb_col)) + call assert_equal(attr_track, screenattr(&lines, sb_col)) + + " gT from the first tab wraps to the last: thumb moves to the bottom. + normal! gT + redraw + call assert_equal(attr_track, screenattr(1, sb_col)) + call assert_equal(attr_thumb, screenattr(&lines, sb_col)) + + " gt from the last tab wraps to the first: thumb returns to the top. + normal! gt + redraw + call assert_equal(attr_thumb, screenattr(1, sb_col)) + call assert_equal(attr_track, screenattr(&lines, sb_col)) + + %bwipeout! + let &tabpanelopt = save_tabpanelopt + let &showtabpanel = save_showtabpanel + let &lines = save_lines + let &columns = save_columns +endfunc + +" With 31 tabs on 24 rows, :tablast must place the scrollbar thumb's +" bottom at the last screen row. Before the fix, integer truncation in +" thumb_top left a one-row gap at the bottom. +func Test_tabpanel_scrollbar_reaches_bottom() + let save_lines = &lines + let save_columns = &columns + let save_showtabpanel = &showtabpanel + let save_tabpanelopt = &tabpanelopt + + set lines=24 columns=40 + set showtabpanel=2 tabpanelopt=scrollbar,columns:8 + for i in range(30) + tabnew + endfor + let sb_col = 8 + + " Identify the thumb attr while the thumb is at the top. + tabfirst + redraw + let attr_thumb = screenattr(1, sb_col) + let attr_track = screenattr(&lines, sb_col) + call assert_notequal(attr_thumb, attr_track) + + " :tablast must push the thumb all the way to the bottom. + tablast + redraw + call assert_equal(attr_thumb, screenattr(&lines, sb_col)) + + %bwipeout! + let &tabpanelopt = save_tabpanelopt + let &showtabpanel = save_showtabpanel + let &lines = save_lines + let &columns = save_columns +endfunc + func Test_tabpanel_variable_height() let save_lines = &lines diff --git a/src/testdir/util/gen_opt_test.vim b/src/testdir/util/gen_opt_test.vim index bc54d272d8..02f7fdf34e 100644 --- a/src/testdir/util/gen_opt_test.vim +++ b/src/testdir/util/gen_opt_test.vim @@ -330,9 +330,11 @@ let test_values = { \ 'tabline': [['', 'xxx'], ['%$', '%{', '%{%', '%{%}', '%(', '%)']], \ 'tabpanel': [['', 'aaa', 'bbb'], []], \ 'tabpanelopt': [['', 'align:left', 'align:right', 'vert', 'columns:0', - \ 'columns:20', 'columns:999'], + \ 'columns:20', 'columns:999', 'scrollbar', + \ 'columns:15,vert,scrollbar', + \ 'align:right,columns:12,vert,scrollbar'], \ ['xxx', 'align:', 'align:middle', 'colomns:', 'cols:10', - \ 'cols:-1']], + \ 'cols:-1', 'scroll', 'scrol', 'scrollbarx']], \ 'tagcase': [['followic', 'followscs', 'ignore', 'match', 'smart'], \ ['', 'xxx', 'smart,match']], \ 'termencoding': [has('gui_gtk') ? [] : ['', 'utf-8'], ['xxx']], diff --git a/src/version.c b/src/version.c index 0d4d8eac2f..b6cbb51aa8 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 407, /**/ 406, /**/ diff --git a/src/window.c b/src/window.c index 478ec03ae1..c3acd7af33 100644 --- a/src/window.c +++ b/src/window.c @@ -4784,6 +4784,9 @@ free_tabpage(tabpage_T *tp) if (tp == lastused_tabpage) lastused_tabpage = NULL; +#ifdef FEAT_TABPANEL + tabpanel_forget_tabpage(tp); +#endif vim_free(tp->tp_localdir); vim_free(tp->tp_prevdir); From e47daed4423182968f64b80c3d7613f0a98a50d4 Mon Sep 17 00:00:00 2001 From: Jaehwang Jung Date: Tue, 28 Apr 2026 19:04:39 +0000 Subject: [PATCH 131/545] patch 9.2.0408: Insert-mode edits can corrupt undo Problem: A command in Insert mode can edit the current buffer, e.g., with setline(). That edit appends to the current undo block, but Insert mode does not know that the cursor line may need to be saved again before the next typed edit. If the next typed edit is a at the start of a line, it can join away the line that was changed by the command before Insert mode saves that updated line. The newest undo entry can then still refer to the joined-away line, so undo sees a range past the end of the buffer and fails with E438. Solution: If a command in Insert mode changes the buffer, set ins_need_undo so stop_arrow() refreshes Insstart. This lets the next edit properly decide whether a new undo entry is needed (Jaehwang Jung) closes: #20087 AI-assisted: Codex Signed-off-by: Jaehwang Jung Signed-off-by: Christian Brabandt --- src/edit.c | 19 +++++++++++++++---- src/testdir/test_undo.vim | 28 ++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/edit.c b/src/edit.c index f15cc55f32..1db9c13071 100644 --- a/src/edit.c +++ b/src/edit.c @@ -1132,6 +1132,10 @@ edit( case K_COMMAND: // command case K_SCRIPT_COMMAND: // command { + bufref_T save_curbuf; + varnumber_T tick = CHANGEDTICK(curbuf); + + set_bufref(&save_curbuf, curbuf); do_cmdkey_command(c, 0); #ifdef FEAT_TERMINAL @@ -1139,10 +1143,15 @@ edit( // Started a terminal that gets the input, exit Insert mode. goto doESCkey; #endif - if (curbuf->b_u_synced) - // The command caused undo to be synced. Need to save the - // line for undo before inserting the next char. + if (curbuf->b_u_synced + || (bufref_valid(&save_curbuf) + && curbuf == save_curbuf.br_buf + && tick != CHANGEDTICK(curbuf))) + { + // The command synced undo or changed this buffer. + // Save the cursor line before the next typed edit. ins_need_undo = TRUE; + } } break; @@ -2503,7 +2512,9 @@ stop_arrow(void) { if (u_save_cursor() == OK) { - // A command or event may have moved the cursor after syncing undo. + // A command or event may have moved the cursor or edited the + // buffer. Update Insstart so that later edits can properly decide + // whether an extra undo entry is needed. Insstart = curwin->w_cursor; Insstart_textlen = (colnr_T)linetabsize_str(ml_get_curline()); ins_need_undo = FALSE; diff --git a/src/testdir/test_undo.vim b/src/testdir/test_undo.vim index 97b77f423d..f03d3ef091 100644 --- a/src/testdir/test_undo.vim +++ b/src/testdir/test_undo.vim @@ -939,4 +939,32 @@ func Test_undo_line_backspace_after_insert_cmd_cursor_movement() bwipe! endfunc +func Test_undo_line_backspace_after_insert_func_edit() + new + setlocal backspace=eol undolevels=100 + + let v:errmsg = '' + call feedkeys("i\" + \ .. "\call setline(2, 'abc')\" + \ .. "\\u", 'xt') + + call assert_equal('', v:errmsg) + call assert_equal([''], getline(1, '$')) + bwipe! +endfunc + +func Test_undo_line_backspace_after_insert_cmd_edit() + new + setlocal backspace=eol undolevels=100 + + let v:errmsg = '' + call feedkeys("i\" + \ .. "\s/.*/abc/\" + \ .. "\\u", 'xt') + + call assert_equal('', v:errmsg) + call assert_equal([''], getline(1, '$')) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index b6cbb51aa8..406bccd54d 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 408, /**/ 407, /**/ From 6cb4173294d0a9612bf105f1c8e492f47a56a568 Mon Sep 17 00:00:00 2001 From: glepnir Date: Tue, 28 Apr 2026 19:14:22 +0000 Subject: [PATCH 132/545] patch 9.2.0409: memory leaks in copy_substring_from_pos() Problem: Memory leak in error path of copy_substring_from_pos(). Solution: Free the garray on OOM in copy_substring_from_pos() (glepnir). closes: #20086 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- src/cmdexpand.c | 10 +++++++--- src/version.c | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 478cda25a0..2a8bd31b23 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -4771,7 +4771,7 @@ copy_substring_from_pos(pos_T *start, pos_T *end, char_u **match, segment_len = is_single_line ? (end->col - start->col) : (int)(ml_get_len(start->lnum) - start->col); if (ga_grow(&ga, segment_len + 2) != OK) - return FAIL; + goto fail; ga_concat_len(&ga, start_ptr, segment_len); if (!is_single_line) @@ -4806,13 +4806,13 @@ copy_substring_from_pos(pos_T *start, pos_T *end, char_u **match, word_end = find_word_end(end_line + end->col); segment_len = (int)(word_end - end_line); if (ga_grow(&ga, segment_len) != OK) - return FAIL; + goto fail; ga_concat_len(&ga, end_line + (is_single_line ? end->col : 0), segment_len - (is_single_line ? end->col : 0)); // Null-terminate if (ga_grow(&ga, 1) != OK) - return FAIL; + goto fail; ga_append(&ga, NUL); *match = (char_u *)ga.ga_data; @@ -4820,6 +4820,10 @@ copy_substring_from_pos(pos_T *start, pos_T *end, char_u **match, match_end->col = segment_len; return OK; + +fail: + ga_clear(&ga); + return FAIL; } /* diff --git a/src/version.c b/src/version.c index 406bccd54d..c1b9f298a8 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 409, /**/ 408, /**/ From 30b42407302e8d5a7d16551ce105648cde8000c5 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Tue, 28 Apr 2026 19:19:53 +0000 Subject: [PATCH 133/545] runtime(doc): Update docs related to tabpanel closes: #20083 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 43 +++++++++++++++------------------------- runtime/doc/tabpage.txt | 35 ++++++++++++++------------------ runtime/doc/version9.txt | 6 +++--- 3 files changed, 34 insertions(+), 50 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index b3edc22e35..a0e450a432 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 27 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 28 VIM REFERENCE MANUAL by Bram Moolenaar @@ -9052,33 +9052,22 @@ A jump table for the options with a short description can be found at |Q_op|. Optional settings for the |tabpanel|, It can consist of the following items. Items must be separated by a comma. - align:{text} Specifies the position of the tabpanel. - Currently supported positions are: - - left left-side + align:{text} Specifies the position of the tabpanel. + Currently supported positions are: + left left-side (default) right right-side - - (default "left") - - columns:{n} Number of columns of the tabpanel. - If this value is 0 or less than 'columns', the - tabpanel will not be displayed. - (default 20) - - scrollbar Reserve a one-column scrollbar at the right - edge of the tabpanel showing the current - scroll position. The scrollbar uses the - |hl-PmenuSbar| and |hl-PmenuThumb| highlight - groups for the track and thumb respectively. - Clicking on the scrollbar column jumps the - thumb to that position; the thumb can also be - dragged. See |tabpanel-scroll|. - - vert Use a vertical separator for tabpanel. - The vertical separator character is taken from - "tpl_vert" in 'fillchars'. - (default off) - + columns:{n} Number of columns of the tabpanel. (default 20) + If this value is 0 or less than 'columns', the + tabpanel will not be displayed. + scrollbar A one-column scrollbar is always displayed at the + right edge of the tabpanel, regardless of the + "align:" setting. (default off) + See |tabpanel-scroll|. + vert The vertical separator is drawn at the boundary + between the tabpanel and the buffer area. + (default off) + The character to be drawn uses "tpl_vert" from + 'fillchars'. Examples: > :set tabpanelopt=columns:16,align:right :set tabpanelopt= diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index 88aeb4ad34..d6db4e8d04 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -1,4 +1,4 @@ -*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 27 +*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 28 VIM REFERENCE MANUAL by Bram Moolenaar @@ -482,42 +482,37 @@ The vertical separator character is taken from "tpl_vert" in 'fillchars'. You can customize the appearance of the tab page labels using the highlight groups: |hl-TabPanel| |hl-TabPanelSel| |hl-TabPanelFill| -SCROLLING IN THE TABPANEL: *tabpanel-scroll* +SCROLLING IN THE TABPANEL *tabpanel-scroll* When the total height of the tab page list exceeds the visible screen height, -mouse wheel events over the tabpanel area scroll the tab page list up or -down. The scroll step follows the 'mousescroll' setting. Wheel events -inside the tabpanel area are consumed by the tabpanel and do not trigger +mouse wheel events over the tabpanel area scroll the tab page list up or down. +The scroll step follows the 'mousescroll' setting. Wheel events inside the +tabpanel area are consumed by the tabpanel and do not trigger || or || mappings. -The current tab page is always brought into view: when the selected tab -page changes (by |gt|, |gT|, |:tabnext| etc.), the panel scrolls so the -current entry is visible. +The current tab page is always brought into view: when the selected tab page +changes (by |gt|, |gT|, |:tabnext| etc.), the panel scrolls so the current +entry is visible. To show a vertical scrollbar indicating the current scroll position, add "scrollbar" to 'tabpanelopt': > :set tabpanelopt+=scrollbar -A one-column scrollbar is always reserved at the right edge of the -tabpanel, regardless of 'align'. For |'tabpanelopt'|=align:left this is -the edge adjacent to the buffer windows; for align:right it is the right -edge of the screen. Clicking on the scrollbar column moves the thumb to -the click position, and the thumb can be dragged to scroll continuously. - -When "vert" is combined with "scrollbar", the vertical separator is drawn -at the tabpanel's boundary with the buffer area and the scrollbar stays at -the tabpanel's right edge. +A one-column scrollbar is always displayed at the right edge of the tabpanel, +regardless of the "align:" setting in 'tabpanelopt'. Clicking on the +scrollbar column moves the thumb to the click position, and the thumb can be +dragged to scroll continuously. The scrollbar uses the |hl-PmenuSbar| highlight group for the track and |hl-PmenuThumb| for the thumb. The scroll offset is remembered across redraws. -MOUSE CLICKS IN THE TABPANEL: *tabpanel-mouse* +MOUSE CLICKS IN THE TABPANEL *tabpanel-mouse* A left click on a row in the tabpanel selects the tab page that the row -belongs to. Unlike the tabline, a double click in the tabpanel does not -open a new, empty tab page; it is treated the same as a single click. +belongs to. Unlike the tabline, a double click in the tabpanel does not open +a new, empty tab page; it is treated the same as a single click. For finer-grained control, the 'tabpanel' value may contain |stl-%[FuncName]| click regions. Clicks on those regions are dispatched to the callback diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 725ba3326d..cc2f5f2dee 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 27 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 28 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52616,8 +52616,8 @@ Other ~ - Allow mouse clickable regions in the 'statusline', 'tabline' and the 'tabpanel' using the |stl-%[FuncName]| atom. - Enable reflow support in the |:terminal|. -- Added "scrollbar" sub-option to 'tabpanelopt' so the tabpanel can scroll - when the tab page list exceeds the visible screen height. +- Enabled scrolling for the tabpanel when the tab page list exceeds the screen + height. Also added the "scrollbar" sub-option to 'tabpanelopt'. Platform specific ~ ----------------- From 6146f3382fcf295bdf7bdfab071e36a6410d14ca Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Tue, 28 Apr 2026 19:23:41 +0000 Subject: [PATCH 134/545] patch 9.2.0410: test suite races when run with parallel make Problem: Running "make test" with -jN causes spurious failures because the old-style tests share filenames (test.ok, test.out, X*, viminfo) in the working directory. Solution: Add .NOTPARALLEL to the testdir Makefile to prevent parallel execution of tests (Jesse Rosenstock). closes: #20082 Co-authored-by: Gemini Signed-off-by: Jesse Rosenstock Signed-off-by: Christian Brabandt --- src/testdir/Makefile | 5 +++++ src/version.c | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/testdir/Makefile b/src/testdir/Makefile index 2e1d900b88..181dee36cb 100644 --- a/src/testdir/Makefile +++ b/src/testdir/Makefile @@ -2,6 +2,11 @@ # Makefile to run all tests for Vim # +# The old-style tests (*.in) use shared filenames in the working directory +# (test.ok, test.out, X*, viminfo), so running them in parallel causes races +# and spurious failures. +.NOTPARALLEL: + # Use console or GUI. VIMPROG = ../vim XXDPROG = ../xxd/xxd diff --git a/src/version.c b/src/version.c index c1b9f298a8..a19d721d7d 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 410, /**/ 409, /**/ From e7745b7cbf8fa4433083d2565af72e9ffca5026d Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 28 Apr 2026 20:25:30 +0000 Subject: [PATCH 135/545] patch 9.2.0411: tabpanel: no Vim script functions for the tabpanel Problem: tabpanel: no Vim script functions for the tabpanel Solution: Add tabpanel_getinfo() and tabpanel_scroll() (Yasuhiro Matsumoto). tabpanel_getinfo() returns a dict describing the tabpanel (align, columns, scrollbar, offset, total, max_offset). tabpanel_scroll(n) scrolls the tabpanel by n rows (positive for down, negative for up). With {absolute: 1} the argument is used as the new absolute offset instead of a delta. The offset is clamped to the valid range; returns true when it actually changes. closes: #20056 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 36 ++++++++++++- runtime/doc/tags | 2 + runtime/doc/usr_41.txt | 4 +- runtime/doc/version9.txt | 2 + runtime/syntax/vim.vim | 8 +-- src/evalfunc.c | 9 ++++ src/proto/tabpanel.pro | 3 ++ src/tabpanel.c | 98 +++++++++++++++++++++++++++++++++++ src/testdir/test_tabpanel.vim | 77 +++++++++++++++++++++++++++ src/version.c | 2 + 10 files changed, 235 insertions(+), 6 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 67ebc70f94..b294b63884 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 27 +*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 28 VIM REFERENCE MANUAL by Bram Moolenaar @@ -705,6 +705,8 @@ tabpagebuflist([{arg}]) List list of buffer numbers in tab page tabpagenr([{arg}]) Number number of current or last tab page tabpagewinnr({tabarg} [, {arg}]) Number number of current window in tab page +tabpanel_getinfo() Dict get current state of the tabpanel +tabpanel_scroll({n} [, {opts}]) Bool scroll the tabpanel tagfiles() List tags files used taglist({expr} [, {filename}]) List list of tags matching {expr} tan({expr}) Float tangent of {expr} @@ -11884,6 +11886,38 @@ tabpagewinnr({tabarg} [, {arg}]) *tabpagewinnr()* Return type: |Number| +tabpanel_getinfo() *tabpanel_getinfo()* + Return a |Dictionary| describing the current state of the + tabpanel (see |tabpanel|). The dictionary has these keys: + align "left" or "right" + columns width in screen columns + scrollbar |TRUE| if a scrollbar is shown + offset current scroll offset in rows + total total number of rows rendered + max_offset largest valid value for "offset" + + The "total" and "max_offset" values are only accurate after + the tabpanel has been drawn at least once. + + Return type: dict + + +tabpanel_scroll({n} [, {opts}]) *tabpanel_scroll()* + Scroll the tabpanel by {n} rows. Positive values scroll down + (later tabs become visible), negative values scroll up. The + new offset is clamped to the valid range. + + When {opts} is a |Dictionary| and its "absolute" entry is + |TRUE|, {n} is used as the new absolute scroll offset + instead of a delta. + + Returns |TRUE| if the scroll offset changed, |FALSE| + otherwise (for example when the tabpanel is not shown, or + the offset is already at the requested value). + + Return type: |vim9-boolean| + + tagfiles() *tagfiles()* Returns a |List| with the file names used to search for tags for the current buffer. This is the 'tags' option expanded. diff --git a/runtime/doc/tags b/runtime/doc/tags index 6376edad2f..a0689193a6 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -10896,6 +10896,8 @@ tabpagewinnr() builtin.txt /*tabpagewinnr()* tabpanel tabpage.txt /*tabpanel* tabpanel-mouse tabpage.txt /*tabpanel-mouse* tabpanel-scroll tabpage.txt /*tabpanel-scroll* +tabpanel_getinfo() builtin.txt /*tabpanel_getinfo()* +tabpanel_scroll() builtin.txt /*tabpanel_scroll()* tag tagsrch.txt /*tag* tag-! tagsrch.txt /*tag-!* tag-binary-search tagsrch.txt /*tag-binary-search* diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index e6ca804572..560f01c704 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1,4 +1,4 @@ -*usr_41.txt* For Vim version 9.2. Last change: 2026 Feb 16 +*usr_41.txt* For Vim version 9.2. Last change: 2026 Apr 28 VIM USER MANUAL by Bram Moolenaar @@ -1081,6 +1081,8 @@ Buffers, windows and the argument list: tabpagebuflist() return List of buffers in a tab page tabpagenr() get the number of a tab page tabpagewinnr() like winnr() for a specified tab page + tabpanel_getinfo() get current state of the tabpanel + tabpanel_scroll() scroll the tabpanel winnr() get the window number for the current window bufwinid() get the window ID of a specific buffer bufwinnr() get the window number of a specific buffer diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index cc2f5f2dee..6b8d97b41a 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52651,6 +52651,8 @@ Various syntax, indent and other plugins were added. Functions: ~ |ch_listen()| listen on {address} +|tabpanel_getinfo()| get current state of the |tabpanel| +|tabpanel_scroll()| scroll the |tabpanel| Autocommands: ~ diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 2906d07cd6..baa5741381 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -2,7 +2,7 @@ " Language: Vim script " Maintainer: Hirohito Higashi " Doug Kearns -" Last Change: 2026 Apr 20 +" Last Change: 2026 Apr 28 " Former Maintainer: Charles E. Campbell " DO NOT CHANGE DIRECTLY. @@ -159,9 +159,9 @@ syn keyword vimFuncName contained getbufline getbufoneline getbufvar getcellpixe syn keyword vimFuncName contained histget histnr hlID hlexists hlget hlset hostname iconv id indent index indexof input inputdialog inputlist inputrestore inputsave inputsecret insert instanceof interrupt invert isabsolutepath isdirectory isinf islocked isnan items job_getchannel job_info job_setoptions job_start job_status job_stop join js_decode js_encode json_decode json_encode keys keytrans len libcall libcallnr line line2byte lispindent list2blob list2str list2tuple listener_add listener_flush listener_remove localtime log log10 luaeval map maparg mapcheck maplist mapnew mapset match matchadd matchaddpos matcharg matchbufline matchdelete matchend matchfuzzy matchfuzzypos matchlist matchstr matchstrlist matchstrpos max menu_info min mkdir mode mzeval nextnonblank syn keyword vimFuncName contained ngettext nr2char or pathshorten perleval popup_atcursor popup_beval popup_clear popup_close popup_create popup_dialog popup_filter_menu popup_filter_yesno popup_findecho popup_findinfo popup_findpreview popup_getoptions popup_getpos popup_hide popup_list popup_locate popup_menu popup_move popup_notification popup_setbuf popup_setoptions popup_settext popup_show pow preinserted prevnonblank printf prompt_getprompt prompt_setcallback prompt_setinterrupt prompt_setprompt prop_add prop_add_list prop_clear prop_find prop_list prop_remove prop_type_add prop_type_change prop_type_delete prop_type_get prop_type_list pum_getpos pumvisible py3eval pyeval pyxeval rand range readblob readdir readdirex readfile redraw_listener_add redraw_listener_remove syn keyword vimFuncName contained reduce reg_executing reg_recording reltime reltimefloat reltimestr remote_expr remote_foreground remote_peek remote_read remote_send remote_startserver remove rename repeat resolve reverse round rubyeval screenattr screenchar screenchars screencol screenpos screenrow screenstring search searchcount searchdecl searchpair searchpairpos searchpos server2client serverlist setbufline setbufvar setcellwidths setcharpos setcharsearch setcmdline setcmdpos setcursorcharpos setenv setfperm setline setloclist setmatches setpos setqflist setreg settabvar settabwinvar settagstack setwinvar sha256 shellescape shiftwidth sign_define sign_getdefined sign_getplaced sign_jump sign_place sign_placelist sign_undefine sign_unplace sign_unplacelist -syn keyword vimFuncName contained simplify sin sinh slice sort sound_clear sound_playevent sound_playfile sound_stop soundfold spellbadword spellsuggest split sqrt srand state str2blob str2float str2list str2nr strcharlen strcharpart strchars strdisplaywidth strftime strgetchar stridx string strlen strpart strptime strridx strtrans strutf16len strwidth submatch substitute swapfilelist swapinfo swapname synID synIDattr synIDtrans synconcealed synstack system systemlist tabpagebuflist tabpagenr tabpagewinnr tagfiles taglist tan tanh tempname term_dumpdiff term_dumpload term_dumpwrite term_getaltscreen term_getansicolors term_getattr term_getcursor term_getjob term_getline term_getscrolled term_getsize term_getstatus term_gettitle term_gettty term_list term_scrape -syn keyword vimFuncName contained term_sendkeys term_setansicolors term_setapi term_setkill term_setrestore term_setsize term_start term_wait terminalprops test_alloc_fail test_autochdir test_feedinput test_garbagecollect_now test_garbagecollect_soon test_getvalue test_gui_event test_ignore_error test_mswin_event test_null_blob test_null_channel test_null_dict test_null_function test_null_job test_null_list test_null_partial test_null_string test_null_tuple test_option_not_set test_override test_refcount test_setmouse test_settime test_srand_seed test_unknown test_void timer_info timer_pause timer_start timer_stop timer_stopall tolower toupper tr trim trunc tuple2list type typename undofile undotree uniq uri_decode uri_encode utf16idx values virtcol virtcol2col -syn keyword vimFuncName contained visualmode wildmenumode wildtrigger win_execute win_findbuf win_getid win_gettype win_gotoid win_id2tabwin win_id2win win_move_separator win_move_statusline win_screenpos win_splitmove winbufnr wincol windowsversion winheight winlayout winline winnr winrestcmd winrestview winsaveview winwidth wordcount writefile xor +syn keyword vimFuncName contained simplify sin sinh slice sort sound_clear sound_playevent sound_playfile sound_stop soundfold spellbadword spellsuggest split sqrt srand state str2blob str2float str2list str2nr strcharlen strcharpart strchars strdisplaywidth strftime strgetchar stridx string strlen strpart strptime strridx strtrans strutf16len strwidth submatch substitute swapfilelist swapinfo swapname synID synIDattr synIDtrans synconcealed synstack system systemlist tabpagebuflist tabpagenr tabpagewinnr tabpanel_getinfo tabpanel_scroll tagfiles taglist tan tanh tempname term_dumpdiff term_dumpload term_dumpwrite term_getaltscreen term_getansicolors term_getattr term_getcursor term_getjob term_getline term_getscrolled term_getsize term_getstatus term_gettitle +syn keyword vimFuncName contained term_gettty term_list term_scrape term_sendkeys term_setansicolors term_setapi term_setkill term_setrestore term_setsize term_start term_wait terminalprops test_alloc_fail test_autochdir test_feedinput test_garbagecollect_now test_garbagecollect_soon test_getvalue test_gui_event test_ignore_error test_mswin_event test_null_blob test_null_channel test_null_dict test_null_function test_null_job test_null_list test_null_partial test_null_string test_null_tuple test_option_not_set test_override test_refcount test_setmouse test_settime test_srand_seed test_unknown test_void timer_info timer_pause timer_start timer_stop timer_stopall tolower toupper tr trim trunc tuple2list type typename undofile undotree uniq uri_decode uri_encode +syn keyword vimFuncName contained utf16idx values virtcol virtcol2col visualmode wildmenumode wildtrigger win_execute win_findbuf win_getid win_gettype win_gotoid win_id2tabwin win_id2win win_move_separator win_move_statusline win_screenpos win_splitmove winbufnr wincol windowsversion winheight winlayout winline winnr winrestcmd winrestview winsaveview winwidth wordcount writefile xor " Predefined variable names {{{2 " GEN_SYN_VIM: vimVarName, START_STR='syn keyword vimVimVarName contained', END_STR='' diff --git a/src/evalfunc.c b/src/evalfunc.c index 5026d1b13b..ed03592635 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -1934,6 +1934,11 @@ typedef struct #else # define TERM_FUNC(name) NULL #endif +#ifdef FEAT_TABPANEL +# define TABPANEL_FUNC(name) name +#else +# define TABPANEL_FUNC(name) NULL +#endif static const funcentry_T global_functions[] = { @@ -2991,6 +2996,10 @@ static const funcentry_T global_functions[] = ret_number, f_tabpagenr}, {"tabpagewinnr", 1, 2, FEARG_1, arg2_number_string, ret_number, f_tabpagewinnr}, + {"tabpanel_getinfo", 0, 0, 0, NULL, + ret_dict_any, TABPANEL_FUNC(f_tabpanel_getinfo)}, + {"tabpanel_scroll", 1, 2, FEARG_1, arg2_number_dict_any, + ret_bool, TABPANEL_FUNC(f_tabpanel_scroll)}, {"tagfiles", 0, 0, 0, NULL, ret_list_string, f_tagfiles}, {"taglist", 1, 2, FEARG_1, arg2_string, diff --git a/src/proto/tabpanel.pro b/src/proto/tabpanel.pro index e6328b1774..42f4dd8b66 100644 --- a/src/proto/tabpanel.pro +++ b/src/proto/tabpanel.pro @@ -9,4 +9,7 @@ bool mouse_on_tabpanel(void); bool mouse_on_tabpanel_scrollbar(void); bool tabpanel_drag_scrollbar(int screen_row); bool tabpanel_scroll(int dir, int count); +bool tabpanel_set_offset(int offset); +void f_tabpanel_getinfo(typval_T *argvars, typval_T *rettv); +void f_tabpanel_scroll(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/tabpanel.c b/src/tabpanel.c index 34372fe60c..1e833c7b65 100644 --- a/src/tabpanel.c +++ b/src/tabpanel.c @@ -913,4 +913,102 @@ tabpanel_scroll(int dir, int count) return true; } +/* + * Set the tabpanel scroll offset to "offset" (clamped to the valid range). + * Returns true if the offset changed and a redraw was scheduled. + */ + bool +tabpanel_set_offset(int offset) +{ + int max_offset; + + if (tabpanel_width() == 0) + return false; + + max_offset = tpl_total_rows - Rows; + if (max_offset < 0) + max_offset = 0; + + if (offset < 0) + offset = 0; + if (offset > max_offset) + offset = max_offset; + if (offset == tpl_scroll_offset) + return false; + + tpl_scroll_offset = offset; + redraw_tabpanel = TRUE; + return true; +} + +/* + * "tabpanel_getinfo()" function + */ + void +f_tabpanel_getinfo(typval_T *argvars UNUSED, typval_T *rettv) +{ + dict_T *d; + int max_offset; + + if (rettv_dict_alloc(rettv) == FAIL) + return; + d = rettv->vval.v_dict; + + max_offset = tpl_total_rows - Rows; + if (max_offset < 0) + max_offset = 0; + + dict_add_string(d, "align", + (char_u *)(tpl_align == ALIGN_RIGHT ? "right" : "left")); + dict_add_number(d, "columns", tabpanel_width()); + dict_add_bool(d, "scrollbar", tpl_scrollbar); + dict_add_number(d, "offset", tpl_scroll_offset); + dict_add_number(d, "total", tpl_total_rows); + dict_add_number(d, "max_offset", max_offset); +} + +/* + * "tabpanel_scroll()" function + */ + void +f_tabpanel_scroll(typval_T *argvars, typval_T *rettv) +{ + varnumber_T n; + int absolute = 0; + bool changed; + + rettv->v_type = VAR_BOOL; + rettv->vval.v_number = VVAL_FALSE; + + if (in_vim9script() + && (check_for_number_arg(argvars, 0) == FAIL + || check_for_opt_dict_arg(argvars, 1) == FAIL)) + return; + + n = tv_get_number_chk(&argvars[0], NULL); + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[1].v_type != VAR_DICT || argvars[1].vval.v_dict == NULL) + { + emsg(_(e_dictionary_required)); + return; + } + absolute = dict_get_bool(argvars[1].vval.v_dict, "absolute", FALSE); + } + + // Clamp to int range to avoid signed overflow when casting and negating. + if (n > INT_MAX) + n = INT_MAX; + else if (n < -INT_MAX) + n = -INT_MAX; + + if (absolute) + changed = tabpanel_set_offset((int)n); + else + changed = tabpanel_scroll(n >= 0 ? 1 : -1, + (int)(n >= 0 ? n : -n)); + + rettv->vval.v_number = changed ? VVAL_TRUE : VVAL_FALSE; +} + #endif // FEAT_TABPANEL diff --git a/src/testdir/test_tabpanel.vim b/src/testdir/test_tabpanel.vim index f7e6bdb082..b5bf678a10 100644 --- a/src/testdir/test_tabpanel.vim +++ b/src/testdir/test_tabpanel.vim @@ -1129,4 +1129,81 @@ func Test_tabpanel_empty() set tabpanel& endfunc +func Test_tabpanel_getinfo_and_scroll() + CheckScreendump + + let lines =<< trim END + set showtabpanel=2 tabpanelopt=columns:20 + set showtabline=0 + for i in range(40) + tabnew + endfor + tabfirst + redraw + func WriteResult(label) + call writefile([a:label, string(tabpanel_getinfo())], 'Xresult', 'a') + endfunc + call delete('Xresult') + call WriteResult('initial') + let g:r1 = tabpanel_scroll(1) + redraw + call WriteResult('after_scroll_1') + call tabpanel_scroll(-10) + redraw + call WriteResult('after_scroll_minus10') + let g:max = tabpanel_getinfo().max_offset + call tabpanel_scroll(g:max, #{absolute: 1}) + redraw + call WriteResult('after_abs_max') + call tabpanel_scroll(99999, #{absolute: 1}) + redraw + call WriteResult('after_abs_overflow') + call tabpanel_scroll(-5, #{absolute: 1}) + redraw + call WriteResult('after_abs_negative') + let g:r_zero = tabpanel_scroll(0, #{absolute: 1}) + let g:r_neg_at_zero = tabpanel_scroll(-1) + call writefile([ + \ 'r1=' .. g:r1, + \ 'max=' .. g:max, + \ 'r_zero=' .. g:r_zero, + \ 'r_neg_at_zero=' .. g:r_neg_at_zero, + \ ], 'Xflags') + END + call writefile(lines, 'XTest_tabpanel_getinfo', 'D') + + let buf = RunVimInTerminal('-S XTest_tabpanel_getinfo', {'rows': 10, 'cols': 45}) + call WaitForAssert({-> assert_true(filereadable('Xflags'))}) + call StopVimInTerminal(buf) + + let results = readfile('Xresult') + let flags = readfile('Xflags') + call delete('Xresult') + call delete('Xflags') + + " Parse [label, dict] pairs. + let info = {} + for i in range(0, len(results) - 1, 2) + let info[results[i]] = eval(results[i + 1]) + endfor + + call assert_equal('left', info.initial.align) + call assert_equal(20, info.initial.columns) + call assert_false(has_key(info.initial, 'scroll')) + call assert_equal(0, info.initial.offset) + call assert_true(info.initial.total > 0) + call assert_true(info.initial.max_offset > 0) + + call assert_equal(1, info.after_scroll_1.offset) + call assert_equal(0, info.after_scroll_minus10.offset) + + let max = info.initial.max_offset + call assert_equal(max, info.after_abs_max.offset) + call assert_equal(max, info.after_abs_overflow.offset) + call assert_equal(0, info.after_abs_negative.offset) + + call assert_equal(['r1=v:true', 'max=' .. max, + \ 'r_zero=v:false', 'r_neg_at_zero=v:false'], flags) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index a19d721d7d..d3ed299226 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 411, /**/ 410, /**/ From 41c3379bdf944dcee7f64b8e95094c03e2dce968 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Tue, 28 Apr 2026 21:03:12 +0000 Subject: [PATCH 136/545] patch 9.2.0412: channel: term_start() out_cb/err_cb no longer deliver raw chunks Problem: channel: term_start() out_cb/err_cb no longer deliver raw chunks (regression from patch 9.2.0224, breaks callers like vim-fugitive that parse multi-line output) (D. Ben Knoble, after v9.2.0224) Solution: Remove the PTY-specific per-line splitting in may_invoke_callback() so RAW callbacks again receive the raw chunk as returned by read(), preserving embedded NL. If per-line handling is desired, the callback must split "msg" on NL and strip the trailing CR itself; document this behavior in term_start(). Replace Test_term_start_cb_per_line() with Test_term_start_cb_raw_chunk() to verify the raw-chunk contract. fixes: #20041 closes: #20045 Co-Authored-By: Claude Opus 4.7 (1M context) Co-Authored-By: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/channel.txt | 2 +- runtime/doc/terminal.txt | 14 ++++++++++++ src/channel.c | 41 +----------------------------------- src/testdir/test_channel.vim | 22 +++++++++++++------ src/version.c | 2 ++ 5 files changed, 33 insertions(+), 48 deletions(-) diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index ce0ceb45be..26cdc6a006 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -1,4 +1,4 @@ -*channel.txt* For Vim version 9.2. Last change: 2026 Apr 15 +*channel.txt* For Vim version 9.2. Last change: 2026 Apr 28 VIM REFERENCE MANUAL by Bram Moolenaar diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index 6c9430dc81..01fd88ca66 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -965,6 +965,20 @@ term_start({cmd} [, {options}]) *term_start()* input and one output handle, with no separate handle for stderr. + Note: term_start() always uses RAW mode for its callbacks. + "out_cb" and "err_cb" receive the raw chunk of data as read + from the OS. A single callback invocation may contain + multiple lines separated by NL, and (for stdout via a pty) + each line may have a trailing CR from the line discipline + (ONLCR). If per-line handling is desired, the callback must + split "msg" on NL and strip the trailing CR itself. + Example: > + func Handle(ch, msg) + for line in split(a:msg, "\n") + echom substitute(line, '\r$', '', '') + endfor + endfunc +< There are extra options: "term_name" name to use for the buffer name, instead of the command name. diff --git a/src/channel.c b/src/channel.c index dc8645b060..a0b7b953e2 100644 --- a/src/channel.c +++ b/src/channel.c @@ -3510,46 +3510,7 @@ may_invoke_callback(channel_T *channel, ch_part_T part) // invoke the channel callback ch_log(channel, "Invoking channel callback %s", (char *)callback->cb_name); -#ifdef FEAT_TERMINAL - // For a terminal job in RAW mode (term_start()), split msg on - // NL and invoke the callback once per line with trailing CR - // stripped. This ensures out_cb/err_cb receive one line at a - // time regardless of how much data arrives in a single read. - if (ch_mode == CH_MODE_RAW && msg != NULL - && channel->ch_job != NULL - && channel->ch_job->jv_tty_out != NULL) - { - char_u *cp = msg; - char_u *nl; - - while ((nl = vim_strchr(cp, NL)) != NULL) - { - long_u len = (long_u)(nl - cp); - - if (len > 0 && cp[len - 1] == CAR) - --len; - argv[1].vval.v_string = vim_strnsave(cp, len); - if (argv[1].vval.v_string != NULL) - invoke_callback(channel, callback, argv); - vim_free(argv[1].vval.v_string); - cp = nl + 1; - } - if (*cp != NUL) - { - long_u len = STRLEN(cp); - - if (len > 0 && cp[len - 1] == CAR) - --len; - argv[1].vval.v_string = vim_strnsave(cp, len); - if (argv[1].vval.v_string != NULL) - invoke_callback(channel, callback, argv); - vim_free(argv[1].vval.v_string); - } - argv[1].vval.v_string = msg; - } - else -#endif - invoke_callback(channel, callback, argv); + invoke_callback(channel, callback, argv); } } } diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index ceb5532637..abdaed0dc1 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -2933,13 +2933,15 @@ func Test_error_callback_terminal() unlet! g:out g:error endfunc -" Verify that term_start() with out_cb/err_cb delivers one line per callback -" call (no embedded newlines, no trailing CR), matching the user's expectation. -func Test_term_start_cb_per_line() +" Verify that term_start() with out_cb/err_cb delivers data in RAW mode, +" preserving embedded newlines in the raw chunk received from read(). If +" per-line handling is desired, it is the callback's responsibility to split +" on NL and strip the trailing CR. +func Test_term_start_cb_raw_chunk() CheckUnix CheckFeature terminal let g:Ch_msgs = [] - let script_file = 'Xterm_cb_per_line.sh' + let script_file = 'Xterm_cb_raw_chunk.sh' call writefile(["#!/bin/sh", \ "printf 'err:1\\nerr:2\\n' >&2", \ "printf 'out:3\\n'"], script_file, 'D') @@ -2947,10 +2949,16 @@ func Test_term_start_cb_per_line() let ptybuf = term_start('./' .. script_file, { \ 'out_cb': {ch, msg -> add(g:Ch_msgs, msg)}, \ 'err_cb': {ch, msg -> add(g:Ch_msgs, msg)}}) - call WaitForAssert({-> assert_equal(3, len(g:Ch_msgs))}, 5000) - " Each line must arrive as a separate callback call with no embedded CR/NL. - call assert_equal(['err:1', 'err:2', 'out:3'], g:Ch_msgs) + " Wait until both the raw stderr chunk and a stdout chunk have arrived. + call WaitForAssert({-> assert_true( + \ index(g:Ch_msgs, "err:1\nerr:2\n") >= 0 + \ && match(g:Ch_msgs, 'out:3') >= 0)}, 5000) + " stderr (via pipe) arrives as a single raw chunk with embedded NL, + " not split per line. stdout (via PTY) is delivered, but its exact + " CR/LF shape depends on the PTY line discipline, so we only check that + " 'out:3' appears somewhere in the received chunks. call job_stop(term_getjob(ptybuf)) + exe 'bwipe! ' .. ptybuf unlet g:Ch_msgs endfunc diff --git a/src/version.c b/src/version.c index d3ed299226..778a227b7a 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 412, /**/ 411, /**/ From bd0f3e6da5df70ab2250264d0a7efc7db83c3dc8 Mon Sep 17 00:00:00 2001 From: Luuk van Baal Date: Tue, 28 Apr 2026 21:09:45 +0000 Subject: [PATCH 137/545] patch 9.2.0413: Scrolling wrong with 'splitkeep' when changing 'cmdheight' Problem: Cursor is not adjusted when 'cmdheight' is changed to cover the cursor with 'splitkeep' ~= "cursor". Solution: Handle window resize for 'splitkeep' after changing 'cmdheight'. Ensure previous window height is set when changing 'splitkeep' (Luuk van Baal). closes: #20043 Signed-off-by: Luuk van Baal Signed-off-by: Christian Brabandt --- src/optionstr.c | 4 ++++ src/testdir/test_window_cmd.vim | 10 ++++++++++ src/version.c | 2 ++ src/window.c | 1 + 4 files changed, 17 insertions(+) diff --git a/src/optionstr.c b/src/optionstr.c index 72f5dd3ced..0390d2dac7 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -4384,6 +4384,10 @@ expand_set_spellsuggest(optexpand_T *args, int *numMatches, char_u ***matches) char * did_set_splitkeep(optset_T *args UNUSED) { + win_T *wp; + tabpage_T *tp; + FOR_ALL_TAB_WINDOWS(tp, wp) + wp->w_prev_height = wp->w_height; return did_set_opt_strings(p_spk, p_spk_values, FALSE); } diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index 98e9ba83db..b1305c77e1 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -1992,6 +1992,16 @@ func Test_splitkeep_screen_cursor_pos() set splitkeep& endfunc +func Test_splitkeep_cmdheight() + set splitkeep=screen + call setline(1, range(&lines)) + norm! G + set cmdheight=2 + call assert_equal(&lines - 1, line('.')) + %bwipeout! + set splitkeep& cmdheight& +endfunc + func Test_splitkeep_cursor() CheckScreendump let lines =<< trim END diff --git a/src/version.c b/src/version.c index 778a227b7a..824a80de9c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 413, /**/ 412, /**/ diff --git a/src/window.c b/src/window.c index c3acd7af33..8813ffa808 100644 --- a/src/window.c +++ b/src/window.c @@ -7591,6 +7591,7 @@ command_height(void) } if (p_ch < old_p_ch && command_frame_height && frp != NULL) frame_add_height(frp, (int)(old_p_ch - p_ch)); + win_fix_scroll(true); // Recompute window positions. win_comp_pos(); From c5de8231f436d47ca5daf4f4d5d91c55632f52ce Mon Sep 17 00:00:00 2001 From: ii14 Date: Tue, 28 Apr 2026 21:20:41 +0000 Subject: [PATCH 138/545] runtime(qml): Add optional chaining to QML syntax "obj?.prop" was wrongly parsed as ternary operator. closes: #19988 Signed-off-by: ii14 Signed-off-by: Christian Brabandt --- runtime/syntax/qml.vim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runtime/syntax/qml.vim b/runtime/syntax/qml.vim index d6f2abec37..e395d83c2a 100644 --- a/runtime/syntax/qml.vim +++ b/runtime/syntax/qml.vim @@ -4,6 +4,7 @@ " Maintainer: Chase Knowlden " Changes: `git log` is your friend " Last Change: 2023 Aug 16 +" 2026 Apr 16 by Vim project: handle ?. optional chaining #19988 " " This file is bassed on the original work done by Warwick Allison " whose did about 99% of the work here. @@ -44,6 +45,7 @@ syn match qmlObjectLiteralType "[A-Za-z][_A-Za-z0-9]*\s*\({\)\@=" syn region qmlTernaryColon start="?" end=":" contains=@qmlExpr,qmlBraces,qmlParens,qmlLineComment syn match qmlBindingProperty "\<[A-Za-z][_A-Za-z.0-9]*\s*:" syn match qmlNullishCoalescing "??" +syn match qmlOptionalChaining "?\." syn keyword qmlConditional if else switch syn keyword qmlRepeat while for do in From 458fed4f65668b0c6d97b46669bb3aceb8b6422f Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 29 Apr 2026 15:58:42 +0000 Subject: [PATCH 139/545] patch 9.2.0414: Flicker when drawing window separator and pum is shown Problem: In a vertical split where the pum overlaps the windows vsep column, background draws (vsep at cursor row, status line, redraw_vseps, idle ins_redraw) can write into cells that are covered by the pum, because skip_for_popup() only protects those cells while pum_will_redraw is set. Solution: In skip_for_popup(), also skip cells under a visible pum when the current draw is not the pum itself (screen_zindex POPUPMENU_ZINDEX). Exclude the wildmenu pum (MODE_CMDLINE): while the cmdline grows, pum_row is briefly stale and protecting those cells would blank a cell of the wrapped cmdline row (Yasuhiro Matsumoto). closes: #20093 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/screen.c | 15 +++++++++++++++ src/version.c | 2 ++ 2 files changed, 17 insertions(+) diff --git a/src/screen.c b/src/screen.c index 3798de88fd..5dfaed93c1 100644 --- a/src/screen.c +++ b/src/screen.c @@ -445,6 +445,21 @@ skip_for_popup(int row, int col) #endif ) return TRUE; + // Protect cells under the pum from background draws (vsep, status line). + // Excluded for wildmenu pum (MODE_CMDLINE): pum_row can be stale while + // the cmdline grows. +#ifdef FEAT_PROP_POPUP + if (screen_zindex < POPUPMENU_ZINDEX + && pum_visible() + && (State & MODE_CMDLINE) == 0 + && pum_under_menu(row, col, FALSE)) + return TRUE; +#else + if (pum_visible() + && (State & MODE_CMDLINE) == 0 + && pum_under_menu(row, col, FALSE)) + return TRUE; +#endif #ifdef FEAT_PROP_POPUP if (blocked_by_popup(row, col)) return TRUE; diff --git a/src/version.c b/src/version.c index 824a80de9c..08ebffbb59 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 414, /**/ 413, /**/ From 6453a7c440648f938a9bde83213ffcf3903f44be Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 29 Apr 2026 16:10:04 +0000 Subject: [PATCH 140/545] patch 9.2.0415: Wrong behavior when executing register that ends in Insert mode Problem: Wrong behavior when executing register that ends in Insert mode from Ctrl-O (Emilien Breton) Solution: Use :startinsert etc. to restore Insert mode after executing the register contents (zeertzjq). fixes: #20085 closes: #20091 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/register.c | 22 +++++++++------------- src/testdir/test_registers.vim | 32 ++++++++++++++++++++++++++------ src/version.c | 2 ++ 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/src/register.c b/src/register.c index 9eeb88f442..9f0e06b546 100644 --- a/src/register.c +++ b/src/register.c @@ -787,22 +787,18 @@ do_execreg( static void put_reedit_in_typebuf(int silent) { - char_u buf[3]; - if (restart_edit == NUL) return; - if (restart_edit == 'V') - { - buf[0] = 'g'; - buf[1] = 'R'; - buf[2] = NUL; - } - else - { - buf[0] = restart_edit == 'I' ? 'i' : restart_edit; - buf[1] = NUL; - } + char_u buf[] = { K_SPECIAL, KS_EXTRA, KE_COMMAND, + // :startinsert + 's', 't', 'a', 'r', 't', 'i', CAR, NUL }; + if (restart_edit == 'R') + buf[8] = 'r'; // :startreplace + else if (restart_edit == 'V') + buf[8] = 'g'; // :startgreplace + else if (restart_edit == 'A') + buf[8] = '!'; // :startinsert! if (ins_typebuf(buf, REMAP_NONE, 0, TRUE, silent) == OK) restart_edit = NUL; } diff --git a/src/testdir/test_registers.vim b/src/testdir/test_registers.vim index 245340c82a..bb1dd31f3e 100644 --- a/src/testdir/test_registers.vim +++ b/src/testdir/test_registers.vim @@ -582,21 +582,41 @@ func Test_clipboard_regs_both_unnamed() bwipe! endfunc -" Test for restarting the current mode (insert or virtual replace) after +" Test for restarting the current mode (insert or (virtual) replace) after " executing the contents of a register -func Test_put_reg_restart_mode() +func Test_exec_reg_restart_mode() new - call append(0, 'editor') - normal gg + let @r = "ivim \" - call feedkeys("i\@r\=mode()\", 'xt') + + call setline(1, 'editor') + normal gg + call feedkeys("i\@r\=mode(1)\", 'xt') call assert_equal('vimi editor', getline(1)) call setline(1, 'editor') normal gg - call feedkeys("gR\@r\=mode()\", 'xt') + call feedkeys("R\@r\=mode(1)\", 'xt') call assert_equal('vimReditor', getline(1)) + call setline(1, 'editor') + normal gg + call feedkeys("gR\@r\=mode(1)\", 'xt') + call assert_equal('vimRvditor', getline(1)) + + " If the register doesn't return to Normal mode, Vim should stay in whatever + " mode the register ends up with, and should not insert extra text. #20085 + for [s0, expected] in + \ [['i', 'vim editor,i'], ['R', 'vim or,R'], ['gR', 'vim or,Rv']] + let @r = $"{s0}vim " + for s1 in ['i', 'R', 'gR'] + call setline(1, 'editor') + normal gg + call feedkeys($"{s1}\@r\,\=mode(1)\", 'xt') + call assert_equal(expected, getline(1)) + endfor + endfor + bwipe! endfunc diff --git a/src/version.c b/src/version.c index 08ebffbb59..c54177a4a9 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 415, /**/ 414, /**/ From c2bda0add96a18328299e9fe6d75925049a5146a Mon Sep 17 00:00:00 2001 From: Maxim Kim Date: Wed, 29 Apr 2026 17:14:11 +0000 Subject: [PATCH 141/545] patch 9.2.0416: Unix: filename completion splits at space for single-file Ex commands Problem: SPACE_IN_FILENAME is defined on most platforms but not on Unix. As a result, set_context_for_wildcard_arg() on Unix always resets the completion pattern at white space for Ex commands that take a single file argument. Solution: Drop the SPACE_IN_FILENAME ifdef (Maxim Kim) fixes: #18411 closes: #20090 Signed-off-by: Maxim Kim Signed-off-by: Christian Brabandt --- runtime/doc/todo.txt | 4 +--- runtime/doc/version9.txt | 3 +++ src/cmdexpand.c | 2 -- src/os_amiga.h | 1 - src/os_dos.h | 1 - src/os_mac.h | 1 - src/os_vms_conf.h | 1 - src/testdir/test_cmdline.vim | 16 ++++++++++++++++ src/version.c | 2 ++ 9 files changed, 22 insertions(+), 9 deletions(-) diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index beddbea4c2..34dacefd6e 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -1,4 +1,4 @@ -*todo.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*todo.txt* For Vim version 9.2. Last change: 2026 Apr 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -421,8 +421,6 @@ more information. Add an option to restrict 'incsearch' to not scroll the view. (Tavis Ormandy) -Remove SPACE_IN_FILENAME ? It is only used for completion. - When 'term' starts with "foot" then default t_TI and t_TE to the values used for the builtin xterm termcap. diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 6b8d97b41a..f3bea36e1f 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52642,6 +52642,9 @@ Changed ~ - Removed the Wayland focus steal feature 'wlsteal', since it causes too many issues and can now be re-implemted using the |clipboard-providers| feature if needed, see |wayland-primary-selection| for an example. +- On Unix, filename completion for single-file Ex commands now treats embedded + whitespace as part of the filename, like on other platforms. + *added-9.3* Added ~ diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 2a8bd31b23..9dfb6979c0 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -1832,9 +1832,7 @@ set_context_for_wildcard_arg( // An argument can contain just about everything, except // characters that end the command and white space. else if (c == '|' || c == '\n' || c == '"' || (VIM_ISWHITE(c) -#ifdef SPACE_IN_FILENAME && (!(eap != NULL && (eap->argt & EX_NOSPC)) || usefilter) -#endif )) { len = 0; // avoid getting stuck when space is in 'isfname' diff --git a/src/os_amiga.h b/src/os_amiga.h index 1407613744..257972012b 100644 --- a/src/os_amiga.h +++ b/src/os_amiga.h @@ -11,7 +11,6 @@ */ #define CASE_INSENSITIVE_FILENAME // ignore case when comparing file names -#define SPACE_IN_FILENAME #define USE_FNAME_CASE // adjust case of file names #define USE_TERM_CONSOLE #define HAVE_AVAIL_MEM diff --git a/src/os_dos.h b/src/os_dos.h index f53434824e..72a876755f 100644 --- a/src/os_dos.h +++ b/src/os_dos.h @@ -109,7 +109,6 @@ #define CLEAN_RUNTIMEPATH "$VIM/vimfiles,$VIMRUNTIME,$VIM/vimfiles/after" #define CASE_INSENSITIVE_FILENAME // ignore case when comparing file names -#define SPACE_IN_FILENAME #define BACKSLASH_IN_FILENAME #define USE_CRNL // lines end in CR-NL instead of NL #define HAVE_DUP // have dup() diff --git a/src/os_mac.h b/src/os_mac.h index f171d7ccf6..7716e63aeb 100644 --- a/src/os_mac.h +++ b/src/os_mac.h @@ -74,7 +74,6 @@ #define USE_EXE_NAME // to find $VIM #define CASE_INSENSITIVE_FILENAME // ignore case when comparing file names -#define SPACE_IN_FILENAME #define USE_FNAME_CASE // make ":e os_Mac.c" open the file in its // original case, as "os_mac.c" diff --git a/src/os_vms_conf.h b/src/os_vms_conf.h index 5831cb1f74..37be6ddfc6 100644 --- a/src/os_vms_conf.h +++ b/src/os_vms_conf.h @@ -12,7 +12,6 @@ #include // Required early for large-file support #define CASE_INSENSITIVE_FILENAME // Open VMS is case insensitive -#define SPACE_IN_FILENAME // There could be space between user and passwd #define FNAME_ILLEGAL "|*#?%" // Illegal characters in a file name #define BINARY_FILE_IO // Use binary fileio #define USE_GETCWD diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 270f1ddbcb..8a170894c7 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -5400,4 +5400,20 @@ func Test_rulerformat_empty() set rulerformat& endfunc +func Test_cmdline_complete_with_space() + call mkdir('Xspc', 'R') + let save_cwd = getcwd() + cd Xspc + call writefile([], 'foo bar') + call writefile([], 'baz') + call writefile([], 'bz') + + " This should expand to foo\ bar, not add 3 space separated + " files: foo baz bz + call feedkeys(":badd foo b\\\"\", 'xt') + call assert_equal('"badd foo\ bar', @:) + + call chdir(save_cwd) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index c54177a4a9..e4607baba5 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 416, /**/ 415, /**/ From af494af5ff188d976073cbc049249614edffa4bf Mon Sep 17 00:00:00 2001 From: glepnir Date: Wed, 29 Apr 2026 18:35:55 +0000 Subject: [PATCH 142/545] patch 9.2.0417: completion: no support for "noinsert" with 'wildmode' Problem: completion: no support for "noinsert" with 'wildmode' and commandline completion Solution: Add "noinsert" value to the 'wildmode' option, mirroring 'completeopt' "noinsert" behaviour (glepnir). fixes: #16551 closes: #20080 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 10 ++-- runtime/doc/version9.txt | 4 +- src/cmdexpand.c | 16 ++++-- src/ex_getln.c | 30 +++++++---- src/option.h | 1 + src/optionstr.c | 2 +- src/proto/cmdexpand.pro | 2 +- src/testdir/test_cmdline.vim | 90 +++++++++++++++++++++++++++++++ src/testdir/util/gen_opt_test.vim | 1 + src/version.c | 2 + src/vim.h | 1 + 11 files changed, 139 insertions(+), 20 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index a0e450a432..e994d2901e 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 28 +*options.txt* For Vim version 9.2. Last change: 2026 Apr 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -10398,8 +10398,12 @@ A jump table for the options with a short description can be found at |Q_op|. applies to buffer name completion. "noselect" If 'wildmenu' is enabled, show the menu but do not preselect the first item. - If only one match exists, it is completed fully, unless "noselect" is - specified. + "noinsert" If 'wildmenu' is enabled, show the menu and preselect + the first match, but do not insert it in the + command line. If both "noinsert" and "noselect" are + present, "noselect" takes precedence. + If only one match exists, it is completed fully, unless "noselect" or + "noinsert" is specified. Some useful combinations of colon-separated values: "longest:full" Start with the longest common string and show diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index f3bea36e1f..7dbc447a9c 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 28 +*version9.txt* For Vim version 9.2. Last change: 2026 Apr 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52618,6 +52618,8 @@ Other ~ - Enable reflow support in the |:terminal|. - Enabled scrolling for the tabpanel when the tab page list exceeds the screen height. Also added the "scrollbar" sub-option to 'tabpanelopt'. +- Added the "noinsert" value to the 'wildmode' option for symmetry with the + 'completeopt' option Platform specific ~ ----------------- diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 9dfb6979c0..58a59bef67 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -346,7 +346,7 @@ nextwild( cmdline_orig.length = ccline->cmdlen; } - if (p != NULL && !got_int && !(options & WILD_NOSELECT)) + if (p != NULL && !got_int && !(options & (WILD_NOSELECT | WILD_NOINSERT))) { size_t plen = STRLEN(p); int difflen; @@ -380,7 +380,8 @@ nextwild( if (xp->xp_numfiles <= 0 && p == NULL) beep_flush(); - else if (xp->xp_numfiles == 1 && !(options & WILD_NOSELECT) + else if (xp->xp_numfiles == 1 + && !(options & (WILD_NOSELECT | WILD_NOINSERT)) && !wild_navigate) // free expanded pattern (void)ExpandOne(xp, NULL, NULL, 0, WILD_FREE); @@ -1295,7 +1296,11 @@ showmatches_oneline( * inserted as a normal character. */ int -showmatches(expand_T *xp, int display_wildmenu, int display_list, int noselect) +showmatches( + expand_T *xp, + int display_wildmenu, + int display_list, + int wim_flags_arg) { cmdline_info_T *ccline = get_cmdline_info(); int numMatches; @@ -1306,6 +1311,9 @@ showmatches(expand_T *xp, int display_wildmenu, int display_list, int noselect) int columns; int attr; int showtail; + int noselect = (wim_flags_arg & WIM_NOSELECT); + int noinsert = (wim_flags_arg & WIM_NOINSERT); + int cmdline_unchanged = noselect || noinsert; if (xp->xp_numfiles == -1) { @@ -1328,7 +1336,7 @@ showmatches(expand_T *xp, int display_wildmenu, int display_list, int noselect) && vim_strchr(p_wop, WOP_PUM) != NULL) { int retval = cmdline_pum_create(ccline, xp, matches, numMatches, - showtail && !noselect); + showtail && !cmdline_unchanged); if (retval == EXPAND_OK) { compl_selected = noselect ? -1 : 0; diff --git a/src/ex_getln.c b/src/ex_getln.c index 3cec79f809..1124f45865 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -958,6 +958,7 @@ cmdline_wildchar_complete( int cmdpos_before; int options = WILD_NO_BEEP; int wim_noselect = p_wmnu && (wim_flags[0] & WIM_NOSELECT); + int wim_noinsert = p_wmnu && (wim_flags[0] & WIM_NOINSERT); if (wim_flags[wim_index] & WIM_BUFLASTUSED) options |= WILD_BUFLASTUSED; @@ -968,7 +969,8 @@ cmdline_wildchar_complete( && !*did_wild_list && (wim_flags[wim_index] & WIM_LIST)) { - (void)showmatches(xp, FALSE, TRUE, wim_noselect); + (void)showmatches(xp, FALSE, TRUE, + p_wmnu ? wim_flags[wim_index] : 0); redrawcmd(); *did_wild_list = TRUE; } @@ -1006,6 +1008,8 @@ cmdline_wildchar_complete( { if (wim_noselect || wim_list) options |= WILD_NOSELECT; + if (wim_noinsert) + options |= WILD_NOINSERT; res = nextwild(xp, WILD_EXPAND_KEEP, options, escape); } @@ -1028,26 +1032,28 @@ cmdline_wildchar_complete( } // Display matches - if (res == OK && xp->xp_numfiles > (wim_noselect ? 0 : 1)) + if (res == OK && xp->xp_numfiles > ((wim_noselect || wim_noinsert) ? 0 : 1)) { if (wim_longest) { int found_longest_prefix = (ccline.cmdpos != cmdpos_before); if (wim_list || (p_wmnu && wim_full)) - (void)showmatches(xp, p_wmnu, wim_list, TRUE); + (void)showmatches(xp, p_wmnu, wim_list, WIM_NOSELECT); else if (!found_longest_prefix) { int wim_list_next = (wim_flags[1] & WIM_LIST); int wim_full_next = (wim_flags[1] & WIM_FULL); int wim_noselect_next = (wim_flags[1] & WIM_NOSELECT); + int wim_noinsert_next = (wim_flags[1] & WIM_NOINSERT); if (wim_list_next || (p_wmnu && (wim_full_next - || wim_noselect_next))) + || wim_noselect_next || wim_noinsert_next))) { - if (wim_full_next && !wim_noselect_next) + if (wim_full_next && !wim_noselect_next && !wim_noinsert_next) nextwild(xp, WILD_NEXT, options, escape); else (void)showmatches(xp, p_wmnu, wim_list_next, - wim_noselect_next); + p_wmnu ? wim_flags[1] : 0); + if (wim_list_next) *did_wild_list = TRUE; } @@ -1055,8 +1061,10 @@ cmdline_wildchar_complete( } else { - if (wim_list || (p_wmnu && (wim_full || wim_noselect))) - (void)showmatches(xp, p_wmnu, wim_list, wim_noselect); + if (wim_list || (p_wmnu && (wim_full || wim_noselect + || wim_noinsert))) + (void)showmatches(xp, p_wmnu, wim_list, + p_wmnu ? wim_flags[0] : 0); else vim_beep(BO_WILD); } @@ -2181,7 +2189,7 @@ getcmdline_int( { // Trigger the popup menu when wildoptions=pum showmatches(&xpc, p_wmnu, wim_flags[wim_index] & WIM_LIST, - wim_flags[0] & WIM_NOSELECT); + p_wmnu ? wim_flags[0] : 0); } if (nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK && nextwild(&xpc, WILD_PREV, 0, firstc != '@') == OK) @@ -2296,7 +2304,7 @@ getcmdline_int( goto cmdline_not_changed; case Ctrl_D: - if (showmatches(&xpc, FALSE, TRUE, wim_flags[0] & WIM_NOSELECT) + if (showmatches(&xpc, FALSE, TRUE, p_wmnu ? wim_flags[0] : 0) == EXPAND_NOTHING) break; // Use ^D as normal char instead @@ -2900,6 +2908,8 @@ check_opt_wim(void) new_wim_flags[idx] |= WIM_BUFLASTUSED; else if (i == 8 && STRNCMP(p, "noselect", 8) == 0) new_wim_flags[idx] |= WIM_NOSELECT; + else if (i == 8 && STRNCMP(p, "noinsert", 8) == 0) + new_wim_flags[idx] |= WIM_NOINSERT; else return FAIL; p += i; diff --git a/src/option.h b/src/option.h index c6607a6262..cda49a4af5 100644 --- a/src/option.h +++ b/src/option.h @@ -375,6 +375,7 @@ typedef enum { #define WIM_LIST 0x04 #define WIM_BUFLASTUSED 0x08 #define WIM_NOSELECT 0x10 +#define WIM_NOINSERT 0x20 // flags for the 'wildoptions' option // each defined char should be unique over all values. diff --git a/src/optionstr.c b/src/optionstr.c index 0390d2dac7..128e5a9469 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -116,7 +116,7 @@ static char *(p_ttym_values[]) = {"xterm", "xterm2", "dec", "netterm", "jsbterm" #endif static char *(p_ve_values[]) = {"block", "insert", "all", "onemore", "none", "NONE", NULL}; // Note: Keep this in sync with check_opt_wim() -static char *(p_wim_values[]) = {"full", "longest", "list", "lastused", "noselect", NULL}; +static char *(p_wim_values[]) = {"full", "longest", "list", "lastused", "noselect", "noinsert", NULL}; static char *(p_wop_values[]) = {"fuzzy", "tagfile", "pum", "exacttext", NULL}; #ifdef FEAT_WAK static char *(p_wak_values[]) = {"yes", "menu", "no", NULL}; diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro index 388523d6f0..50383b98cc 100644 --- a/src/proto/cmdexpand.pro +++ b/src/proto/cmdexpand.pro @@ -12,7 +12,7 @@ char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode void ExpandInit(expand_T *xp); void ExpandCleanup(expand_T *xp); void clear_cmdline_orig(void); -int showmatches(expand_T *xp, int display_wildmenu, int display_list, int noselect); +int showmatches(expand_T *xp, int display_wildmenu, int display_list, int wim_flags_arg); char_u *addstar(char_u *fname, int len, int context); void set_expand_context(expand_T *xp); void set_cmd_context(expand_T *xp, char_u *str, int len, int col, int use_ccline); diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 8a170894c7..80a023d2c1 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -5416,4 +5416,94 @@ func Test_cmdline_complete_with_space() call chdir(save_cwd) endfunc +func Test_wildmode_noinsert() + command! -nargs=1 -complete=custom,T MyCmd echo + func T(a, c, p) + return "oneA\noneB\noneC" + endfunc + + set wildmenu wildoptions=pum wildmode=noinsert,full wildchar= + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneB', @:) + call feedkeys(":MyCmd o\\\\\"\", 'xt') + call assert_equal('"MyCmd oneC', @:) + + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneA', @:) + + " CTRL-P from highlighted first item returns to original text + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + " Another CTRL-P wraps to the last match + call feedkeys(":MyCmd o\\\\\"\", 'xt') + call assert_equal('"MyCmd oneC', @:) + + set wildoptions= + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneB', @:) + + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneA', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + + " 'nowildmenu' should make 'noinsert' ineffective + set nowildmenu + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd oneA', @:) + + " 'noselect' takes precedence over 'noinsert' + set wildmenu wildoptions=pum wildmode=noselect:noinsert,full + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneA', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + + set wildmode=noinsert + call feedkeys(":MyCmd o\\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + + set wildmode=noinsert,full + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd oneB', @:) + call feedkeys(":MyCmd o\\\\"\", 'xt') + call assert_equal('"MyCmd o', @:) + + " 'longest' takes precedence over 'noinsert' + set wildmode=noinsert:longest + call feedkeys(":MyCmd o\\\"\", 'xt') + call assert_equal('"MyCmd one', @:) + + set wildmode& + call feedkeys(":set wildmode=noi\\\"\", 'xt') + call assert_equal('"set wildmode=noinsert', @:) + + set wildmode=noinsert:lastused,full + call assert_equal('noinsert:lastused,full', &wildmode) + call assert_fails('set wildmode=noinser', 'E474:') + + " Single match with 'noinsert': item shown highlighted, C-Y commits + command! -nargs=1 -complete=custom,T1 MyCmd1 echo + func T1(a, c, p) + return "oneA" + endfunc + set wildmenu wildoptions=pum wildmode=noinsert,full + call feedkeys(":MyCmd1 o\\\"\", 'xt') + call assert_equal('"MyCmd1 o', @:) + call feedkeys(":MyCmd1 o\\\\"\", 'xt') + call assert_equal('"MyCmd1 oneA', @:) + delcommand MyCmd1 + delfunc T1 + + set wildmenu& wildoptions& wildmode& wildchar& + delcommand MyCmd + delfunc T +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/util/gen_opt_test.vim b/src/testdir/util/gen_opt_test.vim index 02f7fdf34e..91ed2c80cd 100644 --- a/src/testdir/util/gen_opt_test.vim +++ b/src/testdir/util/gen_opt_test.vim @@ -365,6 +365,7 @@ let test_values = { \ ['xxx']], \ 'wildmode': [['', 'full', 'longest', 'list', 'lastused', 'list:full', \ 'noselect', 'noselect,full', 'noselect:lastused,full', + \ 'noinsert', 'noinsert,full', 'noinsert:lastused,full', \ 'full,longest', 'full,full,full,full'], \ ['xxx', 'a4', 'full,full,full,full,full']], \ 'wildoptions': [['', 'tagfile', 'pum', 'fuzzy'], ['xxx']], diff --git a/src/version.c b/src/version.c index e4607baba5..86f3c4ffd1 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 417, /**/ 416, /**/ diff --git a/src/vim.h b/src/vim.h index 143642d044..c0c811d1d4 100644 --- a/src/vim.h +++ b/src/vim.h @@ -904,6 +904,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring); #define WILD_NOSELECT 0x4000 #define WILD_MAY_EXPAND_PATTERN 0x8000 #define WILD_FUNC_TRIGGER 0x10000 // called from wildtrigger() +#define WILD_NOINSERT 0x20000 // Flags for expand_wildcards() #define EW_DIR 0x01 // include directory names From 20e98ff1cc3ffc13d42579d2bf9b9567f98595a2 Mon Sep 17 00:00:00 2001 From: glepnir Date: Wed, 29 Apr 2026 19:10:43 +0000 Subject: [PATCH 143/545] patch 9.2.0418: wildcards in expanded env vars reinterpreted by glob Problem: With $d='[dir]', `:e $d/file.txt` opens the wrong file, `:e $d/` fails to complete, and `glob('$d/*')` returns nothing. Wildcard characters inside expanded environment variables get picked up by globbing again. Solution: Turn the 4th parameter of expand_env_esc() from a bool into a string of characters to escape in each expanded value. Callers that pass the result to wildcard expansion should include PATH_ESC_WILDCARDS in addition to " \t" (glepnir). closes: #20053 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- runtime/doc/todo.txt | 1 - src/cmdexpand.c | 2 +- src/ex_docmd.c | 3 ++- src/filepath.c | 2 +- src/findfile.c | 2 +- src/misc1.c | 20 +++++++++++--------- src/option.c | 3 ++- src/profiler.c | 2 +- src/proto/misc1.pro | 4 ++-- src/testdir/test_cmdline.vim | 27 +++++++++++++++++++++++++++ src/version.c | 2 ++ src/vim.h | 13 +++++++++++++ 12 files changed, 63 insertions(+), 18 deletions(-) diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt index 34dacefd6e..40a961f328 100644 --- a/runtime/doc/todo.txt +++ b/runtime/doc/todo.txt @@ -2113,7 +2113,6 @@ es_ES.utf-8" gives an error and doesn't switch messages. (Dominique Pelle, When $HOME contains special characters, such as a comma, escape them when used in an option. (Michael Hordijk, 2009 May 5) -Turn "esc" argument of expand_env_esc() into string of chars to be escaped. Should make 'ignorecase' global-local, so that it makes sense setting it from a modeline. diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 58a59bef67..b7a17bbdda 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -1254,7 +1254,7 @@ showmatches_oneline( // Expansion was done before and special characters // were escaped, need to halve backslashes. Also // $HOME has been replaced with ~/. - exp_path = expand_env_save_opt(matches[j], TRUE); + exp_path = expand_env_save_opt(matches[j], TRUE, NULL); path = exp_path != NULL ? exp_path : matches[j]; halved_slash = backslash_halve_save(path); isdir = mch_isdir(halved_slash != NULL ? halved_slash diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 74e32a74ed..7ea20b2628 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -5246,7 +5246,8 @@ expand_filename( || vim_strchr(eap->arg, '~') != NULL) { expand_env_esc(eap->arg, NameBuff, MAXPATHL, - TRUE, TRUE, NULL); + (char_u *)(" \t" PATH_ESC_WILDCARDS), + TRUE, NULL); has_wildcards = mch_has_wildcard(NameBuff); p = NameBuff; } diff --git a/src/filepath.c b/src/filepath.c index 492e0ad3ad..a41f8c27fc 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -4158,7 +4158,7 @@ gen_expand_wildcards( */ if ((has_env_var(p) && !(flags & EW_NOTENV)) || *p == '~') { - p = expand_env_save_opt(p, TRUE); + p = expand_env_save_opt(p, TRUE, (char_u *)PATH_ESC_WILDCARDS); if (p == NULL) p = pat[i]; #ifdef UNIX diff --git a/src/findfile.c b/src/findfile.c index 0c5d1cf252..ed0ee76c49 100644 --- a/src/findfile.c +++ b/src/findfile.c @@ -1808,7 +1808,7 @@ find_file_in_path_option( // copy file name into NameBuff, expanding environment variables save_char = ptr[len]; ptr[len] = NUL; - file_to_findlen = expand_env_esc(ptr, NameBuff, MAXPATHL, FALSE, TRUE, NULL); + file_to_findlen = expand_env_esc(ptr, NameBuff, MAXPATHL, NULL, TRUE, NULL); ptr[len] = save_char; vim_free(*file_to_find); diff --git a/src/misc1.c b/src/misc1.c index a0f9f799e2..88e872d766 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -1398,7 +1398,7 @@ init_vimdir(void) char_u * expand_env_save(char_u *src) { - return expand_env_save_opt(src, FALSE); + return expand_env_save_opt(src, FALSE, NULL); } /* @@ -1406,13 +1406,13 @@ expand_env_save(char_u *src) * expand "~" at the start. */ char_u * -expand_env_save_opt(char_u *src, int one) +expand_env_save_opt(char_u *src, int one, char_u *esc_chars) { char_u *p; p = alloc(MAXPATHL); if (p != NULL) - expand_env_esc(src, p, MAXPATHL, FALSE, one, NULL); + expand_env_esc(src, p, MAXPATHL, esc_chars, one, NULL); return p; } @@ -1428,7 +1428,7 @@ expand_env( char_u *dst, // where to put the result int dstlen) // maximum length of the result { - return expand_env_esc(src, dst, dstlen, FALSE, FALSE, NULL); + return expand_env_esc(src, dst, dstlen, NULL, FALSE, NULL); } size_t @@ -1436,7 +1436,7 @@ expand_env_esc( char_u *srcp, // input string e.g. "$HOME/vim.hlp" char_u *dst, // where to put the result int dstlen, // maximum length of the result - int esc, // escape spaces in expanded variables + char_u *esc_chars, // chars to escape in expanded vars int one, // "srcp" is one file name char_u *startstr) // start again after this (can be NULL) { @@ -1655,11 +1655,13 @@ expand_env_esc( } #endif - // If "var" contains white space, escape it with a backslash. - // Required for ":e ~/tt" when $HOME includes a space. - if (esc && var != NULL && vim_strpbrk(var, (char_u *)" \t") != NULL) + // If "var" contains any character from "esc_chars", escape it + // with a backslash. The historical use is escaping spaces so + // that ":e ~/tt" works when $HOME contains a space. + if (esc_chars != NULL && var != NULL + && vim_strpbrk(var, esc_chars) != NULL) { - char_u *p = vim_strsave_escaped(var, (char_u *)" \t"); + char_u *p = vim_strsave_escaped(var, esc_chars); if (p != NULL) { diff --git a/src/option.c b/src/option.c index a1138a0e17..b44a3990ad 100644 --- a/src/option.c +++ b/src/option.c @@ -3264,7 +3264,8 @@ option_expand(int opt_idx, char_u *val) char_u ** var = (char_u **)options[opt_idx].var; int esc = var == &p_tags || var == &p_path; - expand_env_esc(val, NameBuff, MAXPATHL, esc, FALSE, + expand_env_esc(val, NameBuff, MAXPATHL, + esc ? (char_u *)" \t" : NULL, FALSE, #ifdef FEAT_SPELL var == &p_sps ? (char_u *)"file:" : #endif diff --git a/src/profiler.c b/src/profiler.c index aa127a419f..2aa5aa50ac 100644 --- a/src/profiler.c +++ b/src/profiler.c @@ -392,7 +392,7 @@ ex_profile(exarg_T *eap) if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) { VIM_CLEAR(profile_fname); - profile_fname = expand_env_save_opt(e, TRUE); + profile_fname = expand_env_save_opt(e, TRUE, NULL); do_profiling = PROF_YES; profile_zero(&prof_wait_time); set_vim_var_nr(VV_PROFILING, 1L); diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro index d24a61d151..706629490d 100644 --- a/src/proto/misc1.pro +++ b/src/proto/misc1.pro @@ -29,9 +29,9 @@ void free_homedir(void); void free_users(void); void init_vimdir(void); char_u *expand_env_save(char_u *src); -char_u *expand_env_save_opt(char_u *src, int one); +char_u *expand_env_save_opt(char_u *src, int one, char_u *esc_chars); size_t expand_env(char_u *src, char_u *dst, int dstlen); -size_t expand_env_esc(char_u *srcp, char_u *dst, int dstlen, int esc, int one, char_u *startstr); +size_t expand_env_esc(char_u *srcp, char_u *dst, int dstlen, char_u *esc_chars, int one, char_u *startstr); char_u *vim_getenv(char_u *name, int *mustfree); void vim_unsetenv(char_u *var); void vim_unsetenv_ext(char_u *var); diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 80a023d2c1..ee5f668bb2 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -5506,4 +5506,31 @@ func Test_wildmode_noinsert() delfunc T endfunc +func Test_cmdline_compl_env_var_wildcard() + CheckUnix + + let d = tempname() + call mkdir(d .. '/[x]', 'pR') + call writefile(['hello'], d .. '/[x]/file.txt') + let $XWILD = d .. '/[x]' + + call feedkeys(":e $XWILD/fi\\\"\", 'xt') + call assert_match('\[x\]/file\.txt$', @:) + call assert_equal([d .. '/[x]/file.txt'], glob('$XWILD/*', 0, 1)) + + edit $XWILD/file.txt + call assert_equal('hello', getline(1)) + bwipe! + + if has('profile') + let prof = d .. '/[x]/prof.out' + profile start $XWILD/prof.out + profile stop + call assert_true(filereadable(prof)) + call delete(prof) + endif + + unlet $XWILD +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 86f3c4ffd1..23de968780 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 418, /**/ 417, /**/ diff --git a/src/vim.h b/src/vim.h index c0c811d1d4..37183f130a 100644 --- a/src/vim.h +++ b/src/vim.h @@ -3098,4 +3098,17 @@ long elapsed(DWORD start_tick); // Flags used by getvcol() #define GETVCOL_END_EXCL_LBR 1 +// Used by expand_env_esc() callers that feed the result to +// wildcard expansion, so that such characters embedded in +// environment variable values are treated as literal. +#ifdef VMS +# define PATH_ESC_WILDCARDS "*?%" +#else +# ifdef MSWIN +# define PATH_ESC_WILDCARDS "*?[" +# else +# define PATH_ESC_WILDCARDS "*?[{" +# endif +#endif + #endif // VIM__H From bd8716178e4f410ba5b0a0c2bcffa5cc860a2137 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 29 Apr 2026 19:23:47 +0000 Subject: [PATCH 144/545] patch 9.2.0419: popup: rendering issues Problem: popup: rendering issues Solution: Fix popup bottom edge overflow, stabilize popup width across scrolling, fix popup right edge overflow (Yasuhiro Matsumoto) closes: #20042 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/popupwin.c | 77 ++++++++++++++++--- src/testdir/dumps/Test_popupwin_20.dump | 4 +- src/testdir/dumps/Test_popupwin_21.dump | 4 +- .../dumps/Test_popupwin_drag_minwidth_1.dump | 18 ++--- .../dumps/Test_popupwin_drag_minwidth_2.dump | 12 +-- .../dumps/Test_popupwin_drag_minwidth_3.dump | 16 ++-- .../dumps/Test_popupwin_firstline_1.dump | 8 +- src/testdir/dumps/Test_popupwin_mask_3.dump | 8 +- src/testdir/dumps/Test_popupwin_mask_5.dump | 10 +-- .../dumps/Test_popupwin_previewpopup_10.dump | 12 +-- .../dumps/Test_popupwin_previewpopup_4.dump | 12 +-- .../dumps/Test_popupwin_previewpopup_5.dump | 12 +-- .../dumps/Test_popupwin_previewpopup_7.dump | 12 +-- .../dumps/Test_popupwin_previewpopup_8.dump | 12 +-- .../dumps/Test_popupwin_previewpopup_9.dump | 12 +-- src/testdir/dumps/Test_popupwin_wrap_1.dump | 12 +-- src/testdir/dumps/Test_popupwin_wrap_2.dump | 12 +-- src/testdir/test_ins_complete.vim | 2 +- src/testdir/test_popupwin.vim | 2 +- src/version.c | 2 + 20 files changed, 159 insertions(+), 100 deletions(-) diff --git a/src/popupwin.c b/src/popupwin.c index 7016605115..f920816338 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -1410,16 +1410,26 @@ popup_adjust_position(win_T *wp) || wp->w_popup_pos == POPPOS_BOTLEFT)) { wp->w_wincol = wantcol - 1; - // Need to see at least one character after the decoration. - if (wp->w_wincol > firstwin->w_wincol + topframe->fr_width - left_extra - 1) - wp->w_wincol = firstwin->w_wincol + topframe->fr_width - left_extra - 1; + // Need to see at least one character of content plus the right + // border/padding/shadow after the decoration. + if (wp->w_wincol > firstwin->w_wincol + topframe->fr_width + - left_extra - right_extra - 1) + wp->w_wincol = firstwin->w_wincol + topframe->fr_width + - left_extra - right_extra - 1; } } + // Keep the popup out of the tabpanel area so the available width is + // computed correctly below. + if (wp->w_wincol < firstwin->w_wincol) + wp->w_wincol = firstwin->w_wincol; + // When centering or right aligned, use maximum width. // When left aligned use the space available, but shift to the left when we // hit the right of the screen. - maxspace = firstwin->w_wincol + topframe->fr_width - wp->w_wincol - left_extra; + // Reserve room for the right border/padding/shadow so the popup fits. + maxspace = firstwin->w_wincol + topframe->fr_width + - wp->w_wincol - left_extra - right_extra; maxwidth = maxspace; if (wp->w_maxwidth > 0 && maxwidth > wp->w_maxwidth) { @@ -1481,6 +1491,27 @@ popup_adjust_position(win_T *wp) // backwards. // TODO: more accurate wrapping wp->w_width = 1; + // Pre-scan every buffer line to find the widest one, so the popup width + // stays stable when scrolling changes which lines are visible. + { + linenr_T ln; + int saved_w_width = wp->w_width; + + if (wp->w_width < maxwidth) + wp->w_width = maxwidth; + for (ln = 1; ln <= wp->w_buffer->b_ml.ml_line_count; ++ln) + { + int len = linetabsize(wp, ln) + margin_width; + + if (wp->w_maxwidth > 0 && len > wp->w_maxwidth) + len = wp->w_maxwidth; + if (saved_w_width < len) + saved_w_width = len; + if (wp->w_maxwidth > 0 && saved_w_width >= wp->w_maxwidth) + break; + } + wp->w_width = saved_w_width; + } if (wp->w_firstline < 0) lnum = wp->w_buffer->b_ml.ml_line_count; else @@ -1615,9 +1646,10 @@ popup_adjust_position(win_T *wp) } if (center_hor) { - wp->w_wincol = (firstwin->w_wincol + topframe->fr_width - wp->w_width - extra_width) / 2; - if (wp->w_wincol < 0) - wp->w_wincol = 0; + wp->w_wincol = firstwin->w_wincol + + (topframe->fr_width - wp->w_width - extra_width) / 2; + if (wp->w_wincol < firstwin->w_wincol) + wp->w_wincol = firstwin->w_wincol; } else if (wp->w_popup_pos == POPPOS_BOTRIGHT || wp->w_popup_pos == POPPOS_TOPRIGHT) @@ -1748,12 +1780,37 @@ popup_adjust_position(win_T *wp) else if (wp->w_winrow < 0) wp->w_winrow = 0; - if (wp->w_wincol + wp->w_width > firstwin->w_wincol + topframe->fr_width) - wp->w_wincol = firstwin->w_wincol + topframe->fr_width - wp->w_width; - else if (wp->w_wincol < firstwin->w_wincol) + if (wp->w_wincol + wp->w_width + extra_width + > firstwin->w_wincol + topframe->fr_width) + wp->w_wincol = firstwin->w_wincol + topframe->fr_width + - wp->w_width - extra_width; + if (wp->w_wincol < firstwin->w_wincol) wp->w_wincol = firstwin->w_wincol; if (wp->w_wincol < 0) wp->w_wincol = 0; + // If the popup is wider than the available area (e.g. minwidth larger than + // the work area between tabpanels), clip the content width so the right + // border/padding/shadow stays visible instead of being pushed off the + // screen or into the tabpanel. + if (wp->w_wincol + wp->w_width + extra_width + > firstwin->w_wincol + topframe->fr_width) + { + int avail = firstwin->w_wincol + topframe->fr_width + - wp->w_wincol - extra_width; + wp->w_width = avail > 0 ? avail : 0; + } + + // Same for the bottom edge: shift up so the border/padding/shadow stays + // on screen, and clip the height if the popup is taller than the screen. + if (wp->w_winrow + wp->w_height + extra_height > Rows) + wp->w_winrow = Rows - wp->w_height - extra_height; + if (wp->w_winrow < 0) + wp->w_winrow = 0; + if (wp->w_winrow + wp->w_height + extra_height > Rows) + { + int avail = Rows - wp->w_winrow - extra_height; + wp->w_height = avail > 0 ? avail : 0; + } if (wp->w_height != org_height) win_comp_scroll(wp); diff --git a/src/testdir/dumps/Test_popupwin_20.dump b/src/testdir/dumps/Test_popupwin_20.dump index 692708beb0..e6c8421786 100644 --- a/src/testdir/dumps/Test_popupwin_20.dump +++ b/src/testdir/dumps/Test_popupwin_20.dump @@ -5,8 +5,8 @@ |5+0#0000000#ffffff0| @40||| @11||| @17|X+0#0000001#ffd7ff255 |6+0#0000000#ffffff0| |++0#0000001#ffd7ff255|-@8| +0#0000000#ffffff0@9| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@5|+|-@11|+| @18 |7| ||+0#0000001#ffd7ff255|b|o|r|d|e|r| |T|L| +0#0000000#ffffff0@9| +0#0000001#ffd7ff255@3|p|a|d@1|i|n|g|s| @1| +0#0000000#ffffff0@38 -|8| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@13||+0#0000001#ffd7ff255| @2|w|r|a|p@1|e|d| |l|o|n|g|e|r| |t|e| @2|| -|9+0#0000000#ffffff0| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@13||+0#0000001#ffd7ff255| @2|x|t| @17|| +|8| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@17||+0#0000001#ffd7ff255| @2|w|r|a|p@1|e|d| |l|o|n|g|e| @2|| +|9+0#0000000#ffffff0| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@17||+0#0000001#ffd7ff255| @2|r| |t|e|x|t| @9|| |1+0#0000000#ffffff0|0| @19| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@38 |1@1| @46||+0#0000001#ffd7ff255| @2|r|i|g|h|t| |a|l|i|g|n|e|d| |t|e|x|t| @2|| |1+0#0000000#ffffff0|2| @72 diff --git a/src/testdir/dumps/Test_popupwin_21.dump b/src/testdir/dumps/Test_popupwin_21.dump index 6398549db8..d4f92f359b 100644 --- a/src/testdir/dumps/Test_popupwin_21.dump +++ b/src/testdir/dumps/Test_popupwin_21.dump @@ -5,8 +5,8 @@ |5+0#0000000#ffffff0| @40|║| @11|║| @17|X+0#0000001#ffd7ff255 |6+0#0000000#ffffff0| |╔+0#0000001#ffd7ff255|═@8| +0#0000000#ffffff0@9| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@5|╚|═@11|╝| @18 |7| |║+0#0000001#ffd7ff255|b|o|r|d|e|r| |T|L| +0#0000000#ffffff0@9| +0#0000001#ffd7ff255@3|p|a|d@1|i|n|g|s| @1| +0#0000000#ffffff0@38 -|8| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@13|║+0#0000001#ffd7ff255| @2|w|r|a|p@1|e|d| |l|o|n|g|e|r| |t|e| @2|║ -|9+0#0000000#ffffff0| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@13|║+0#0000001#ffd7ff255| @2|x|t| @17|║ +|8| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@17|║+0#0000001#ffd7ff255| @2|w|r|a|p@1|e|d| |l|o|n|g|e| @2|║ +|9+0#0000000#ffffff0| @20| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@17|║+0#0000001#ffd7ff255| @2|r| |t|e|x|t| @9|║ |1+0#0000000#ffffff0|0| @19| +0#0000001#ffd7ff255@13| +0#0000000#ffffff0@38 |1@1| @46|║+0#0000001#ffd7ff255| @2|r|i|g|h|t| |a|l|i|g|n|e|d| |t|e|x|t| @2|║ |1+0#0000000#ffffff0|2| @72 diff --git a/src/testdir/dumps/Test_popupwin_drag_minwidth_1.dump b/src/testdir/dumps/Test_popupwin_drag_minwidth_1.dump index fbeec7a901..5e20f29281 100644 --- a/src/testdir/dumps/Test_popupwin_drag_minwidth_1.dump +++ b/src/testdir/dumps/Test_popupwin_drag_minwidth_1.dump @@ -1,10 +1,10 @@ >╔+0#0000001#ffd7ff255|═@73 -|║|0| @72 -|║|1| @72 -|║|2| @72 -|║|3| @72 -|║|4| @72 -|║|5| @72 -|║|6| @72 -|║|7| @72 -|║|8| @72 +|║|0| @71| +0#0000000#0000001 +|║+0#0000001#ffd7ff255|1| @71| +0#0000000#0000001 +|║+0#0000001#ffd7ff255|2| @71| +0#0000000#a8a8a8255 +|║+0#0000001#ffd7ff255|3| @71| +0#0000000#a8a8a8255 +|║+0#0000001#ffd7ff255|4| @71| +0#0000000#a8a8a8255 +|║+0#0000001#ffd7ff255|5| @71| +0#0000000#a8a8a8255 +|║+0#0000001#ffd7ff255|6| @71| +0#0000000#a8a8a8255 +|║+0#0000001#ffd7ff255|7| @71| +0#0000000#a8a8a8255 +|╚+0#0000001#ffd7ff255|═@73 diff --git a/src/testdir/dumps/Test_popupwin_drag_minwidth_2.dump b/src/testdir/dumps/Test_popupwin_drag_minwidth_2.dump index b4edcfea03..8d8a9983b4 100644 --- a/src/testdir/dumps/Test_popupwin_drag_minwidth_2.dump +++ b/src/testdir/dumps/Test_popupwin_drag_minwidth_2.dump @@ -2,9 +2,9 @@ |~+0#4040ff13&| @73 |~| @73 |~| @73 -|~| @26|╔+0#0000001#ffd7ff255|═@44|╗ -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|0| @42| +0#0000000#0000001|║+0#0000001#ffd7ff255 -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|1| @42| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|2| @42| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|3| @42| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -| +0#0000000#ffffff0@27|╚+0#0000001#ffd7ff255|═@44|╝ +|~| @27|╔+0#0000001#ffd7ff255|═@43|╗ +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|0| @41| +0#0000000#0000001|║+0#0000001#ffd7ff255 +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|1| @41| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|2| @41| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|3| @41| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +| +0#0000000#ffffff0@28|╚+0#0000001#ffd7ff255|═@43|╝ diff --git a/src/testdir/dumps/Test_popupwin_drag_minwidth_3.dump b/src/testdir/dumps/Test_popupwin_drag_minwidth_3.dump index e429d3809e..d714ac2760 100644 --- a/src/testdir/dumps/Test_popupwin_drag_minwidth_3.dump +++ b/src/testdir/dumps/Test_popupwin_drag_minwidth_3.dump @@ -1,10 +1,10 @@ > +0&#ffffff0@74 |~+0#4040ff13&| @73 -|~| @26|╔+0#0000001#ffd7ff255|═@44|╗ -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|0| @42| +0#0000000#0000001|║+0#0000001#ffd7ff255 -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|1| @42| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|2| @42| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|3| @42| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|4| @42| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|~+0#4040ff13#ffffff0| @26|║+0#0000001#ffd7ff255|5| @42| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -| +0#0000000#ffffff0@27|╚+0#0000001#ffd7ff255|═@44|╝ +|~| @27|╔+0#0000001#ffd7ff255|═@43|╗ +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|0| @41| +0#0000000#0000001|║+0#0000001#ffd7ff255 +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|1| @41| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|2| @41| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|3| @41| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|4| @41| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|~+0#4040ff13#ffffff0| @27|║+0#0000001#ffd7ff255|5| @41| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +| +0#0000000#ffffff0@28|╚+0#0000001#ffd7ff255|═@43|╝ diff --git a/src/testdir/dumps/Test_popupwin_firstline_1.dump b/src/testdir/dumps/Test_popupwin_firstline_1.dump index b3b0349305..5e79f57b54 100644 --- a/src/testdir/dumps/Test_popupwin_firstline_1.dump +++ b/src/testdir/dumps/Test_popupwin_firstline_1.dump @@ -1,10 +1,10 @@ >1+0&#ffffff0| @73 |2| @73 |3| @73 -|4| @32|3+0#0000001#ffd7ff255@4| | +0#0000000#a8a8a8255| +0&#ffffff0@33 -|5| @32|4+0#0000001#ffd7ff255@1| @3| +0#0000000#0000001| +0&#ffffff0@33 -|6| @32|5+0#0000001#ffd7ff255| @4| +0#0000000#0000001| +0&#ffffff0@33 -|7| @32|6+0#0000001#ffd7ff255@5| +0#0000000#a8a8a8255| +0&#ffffff0@33 +|4| @27|3+0#0000001#ffd7ff255@4| @10| +0#0000000#a8a8a8255| +0&#ffffff0@28 +|5| @27|4+0#0000001#ffd7ff255@1| @13| +0#0000000#0000001| +0&#ffffff0@28 +|6| @27|5+0#0000001#ffd7ff255| @14| +0#0000000#0000001| +0&#ffffff0@28 +|7| @27|6+0#0000001#ffd7ff255@5| @9| +0#0000000#a8a8a8255| +0&#ffffff0@28 |8| @73 |9| @73 @57|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_popupwin_mask_3.dump b/src/testdir/dumps/Test_popupwin_mask_3.dump index 40681fb4ca..8bd605af9b 100644 --- a/src/testdir/dumps/Test_popupwin_mask_3.dump +++ b/src/testdir/dumps/Test_popupwin_mask_3.dump @@ -1,12 +1,12 @@ >1+0&#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 |1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|7| +0&#e0e0e08@9 |1+0&#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|x+0#0000001#ffd7ff255@8|8+0#0000000#ffffff0|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3| +0&#e0e0e08|s|o|m|e| |0+0&#ffffff0|4|1|t+0&#e0e0e08| -|1+0&#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|y+0#0000001#ffd7ff255@8|8+0#0000000#ffffff0|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3| +0&#e0e0e08|3+0&#ffffff0|8|3|t+0&#e0e0e08|h|0+0&#ffffff0|4|1|l+0&#e0e0e08|i +|1+0&#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|y+0#0000001#ffd7ff255@8|8+0#0000000#ffffff0|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3| +0&#e0e0e08|3+0&#ffffff0|8|3|t+0&#e0e0e08|h|0+0&#ffffff0|4|1|l+0&#e0e0e08| |1+0&#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3| +0&#e0e0e08@8|4+0&#ffffff0|2 |1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 -|1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|═+0#0000001#ffd7ff255@10 -|1+0#0000000#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|║+0#0000001#ffd7ff255| @4|9+0#0000000#ffffff0|4|0| +0#0000001#ffd7ff255@3 -|1+0#0000000#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|║+0#0000001#ffd7ff255| |j|u|s|t|9+0#0000000#ffffff0|4|0|e+0#0000001#ffd7ff255| |l|i +|1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|═+0#0000001#ffd7ff255@9|X +|1+0#0000000#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|║+0#0000001#ffd7ff255| @4|9+0#0000000#ffffff0|4|0| +0#0000001#ffd7ff255@2|║ +|1+0#0000000#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|║+0#0000001#ffd7ff255| |j|u|s|t|9+0#0000000#ffffff0|4|0|e+0#0000001#ffd7ff255| @1|║ |1+0#0000000#ffffff0|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|║+0#0000001#ffd7ff255| @10|2+0#0000000#ffffff0 |1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|╚+0#0000001#ffd7ff255|═|7+0#0000000#ffffff0|3|8|═+0#0000001#ffd7ff255@4|1+0#0000000#ffffff0|4|2 |1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 diff --git a/src/testdir/dumps/Test_popupwin_mask_5.dump b/src/testdir/dumps/Test_popupwin_mask_5.dump index 78cc6f0858..63ba30dea6 100644 --- a/src/testdir/dumps/Test_popupwin_mask_5.dump +++ b/src/testdir/dumps/Test_popupwin_mask_5.dump @@ -6,8 +6,8 @@ |1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 |1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 |1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 -|1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 -|1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|2|7|2|8|2|9|3|0|3|1|3|2|3@2|4|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 -| +0&#e0e0e08@11|1+0&#ffffff0@1|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|═+0#0000001#ffd7ff255@13|X|4+0#0000000#ffffff0|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 -|o+0&#e0e0e08|m|e| |5+0&#ffffff0|6|7|t+0&#e0e0e08| @3|1+0&#ffffff0@1|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|║+0#0000001#ffd7ff255| @4|2+0#0000000#ffffff0|9|3| +0#0000001#ffd7ff255@6|║|4+0#0000000#ffffff0|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 -|:| |t+0&#e0e0e08|h| +0&#ffffff0@2|l+0&#e0e0e08|i|n|e| | +0&#ffffff0@28|║+0#0000001#ffd7ff255| |j|u|s|t| +0#0000000#ffffff0@2|e+0#0000001#ffd7ff255| |l|i|n|e| |║|,+0#0000000#ffffff0|1| @10|T|o|p| +|1|2|3|4|5|6|7|8|9|1|0|1@2|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|2|6|═+0#0000001#ffd7ff255@13|X|4+0#0000000#ffffff0|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 +| +0&#e0e0e08@11|1+0&#ffffff0@1|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|║+0#0000001#ffd7ff255| @4|2+0#0000000#ffffff0|9|3| +0#0000001#ffd7ff255@6|║|4+0#0000000#ffffff0|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 +|o+0&#e0e0e08|m|e| |5+0&#ffffff0|6|7|t+0&#e0e0e08| @3|1+0&#ffffff0@1|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|║+0#0000001#ffd7ff255| |j|u|s|t|2+0#0000000#ffffff0|9|3|e+0#0000001#ffd7ff255| |l|i|n|e| |║|4+0#0000000#ffffff0|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 +|1|2|t+0&#e0e0e08|h|5+0&#ffffff0|6|7|l+0&#e0e0e08|i|n|e| |1+0&#ffffff0@1|2|1|3|1|4|1|5|1|6|1|7|1|8|1|9|2|0|2|1|2@2|3|2|4|2|5|║+0#0000001#ffd7ff255| @10|3+0#0000000#ffffff0|2|3@2|4|3|5|3|6|3|7|3|8|3|9|4|0|4|1|4|2 +| +0&#e0e0e08@6| +0&#ffffff0@33|╚+0#0000001#ffd7ff255|═| +0#0000000#ffffff0@2|═+0#0000001#ffd7ff255@4| +0#0000000#ffffff0@3|═+0#0000001#ffd7ff255@1|╝|,+0#0000000#ffffff0|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_popupwin_previewpopup_10.dump b/src/testdir/dumps/Test_popupwin_previewpopup_10.dump index fdf16b4906..7003b43fd9 100644 --- a/src/testdir/dumps/Test_popupwin_previewpopup_10.dump +++ b/src/testdir/dumps/Test_popupwin_previewpopup_10.dump @@ -2,12 +2,12 @@ |#|i|n|c|l|u|d|e| |"|X|h|e|a|d|e|r|.|h|"| @54 |t|h|r|e@1| @69 |f|o|u|r| @70 -|f|i|v|e| @27|╔+0#0000001#ffd7ff255| |X|h|e|a|d|e|r|.|h| |═@29|X -|s+0#0000000#ffffff0|i|x| @28|║+0#0000001#ffd7ff255|1+0#e000002&|0| +0#0000001&@37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|s+0#0000000#ffffff0|e|v|e|n| @26|║+0#0000001#ffd7ff255|s|e|a|r|c|h|e|d| |w|o|r|d| |i|s| |h|e|r|e| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|f+0#0000000#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0#0000001#ffd7ff255|1+0#e000002&|2| +0#0000001&@37| +0#0000000#0000001|║+0#0000001#ffd7ff255 -|n+0#0000000#ffffff0|i|n|e| @27|║+0#0000001#ffd7ff255|1+0#e000002&|3| +0#0000001&@37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|t+0#0000000#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @11|╚+0#0000001#ffd7ff255|═@40|⇲ +|f|i|v|e| @28|╔+0#0000001#ffd7ff255| |X|h|e|a|d|e|r|.|h| |═@28|X +|s+0#0000000#ffffff0|i|x| @29|║+0#0000001#ffd7ff255|1+0#e000002&|0| +0#0000001&@36| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|s+0#0000000#ffffff0|e|v|e|n| @27|║+0#0000001#ffd7ff255|s|e|a|r|c|h|e|d| |w|o|r|d| |i|s| |h|e|r|e| @17| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|f+0#0000000#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @10|║+0#0000001#ffd7ff255|1+0#e000002&|2| +0#0000001&@36| +0#0000000#0000001|║+0#0000001#ffd7ff255 +|n+0#0000000#ffffff0|i|n|e| @28|║+0#0000001#ffd7ff255|1+0#e000002&|3| +0#0000001&@36| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|t+0#0000000#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @12|╚+0#0000001#ffd7ff255|═@39|⇲ |v+0#0000000#ffffff0|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| >a|n|o|t|h|e|r| @29 |~+0#4040ff13&| @73 |~| @73 diff --git a/src/testdir/dumps/Test_popupwin_previewpopup_4.dump b/src/testdir/dumps/Test_popupwin_previewpopup_4.dump index f8d411dadb..d6a90df432 100644 --- a/src/testdir/dumps/Test_popupwin_previewpopup_4.dump +++ b/src/testdir/dumps/Test_popupwin_previewpopup_4.dump @@ -2,12 +2,12 @@ |#|i|n|c|l|u|d|e| |"|X|h|e|a|d|e|r|.|h|"| @54 |t|h|r|e@1| @69 |f|o|u|r| @70 -|f|i|v|e| @27|╔+0&#afffff255| |X|t|a|g|f|i|l|e| |═@30|X -|s+0&#ffffff0|i|x| @28|║+0&#afffff255|2|6| @37| +0&#a8a8a8255|║+0&#afffff255 -|s+0&#ffffff0|e|v|e|n| @26|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255 -|f+0&#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0|║+0&#afffff255 -|n+0&#ffffff0|i|n|e| @27|║+0&#afffff255|2|9| @37| +0&#a8a8a8255|║+0&#afffff255 -|t+0&#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @11|╚+0&#afffff255|═@40|⇲ +|f|i|v|e| @28|╔+0&#afffff255| |X|t|a|g|f|i|l|e| |═@29|X +|s+0&#ffffff0|i|x| @29|║+0&#afffff255|2|6| @36| +0&#a8a8a8255|║+0&#afffff255 +|s+0&#ffffff0|e|v|e|n| @27|║+0&#afffff255|2|7| @36| +0&#a8a8a8255|║+0&#afffff255 +|f+0&#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @10|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @17| +0|║+0&#afffff255 +|n+0&#ffffff0|i|n|e| @28|║+0&#afffff255|2|9| @36| +0&#a8a8a8255|║+0&#afffff255 +|t+0&#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @12|╚+0&#afffff255|═@39|⇲ |v+0&#ffffff0|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| >a|n|o|t|h|e|r| @29 |~+0#4040ff13&| @73 |~| @73 diff --git a/src/testdir/dumps/Test_popupwin_previewpopup_5.dump b/src/testdir/dumps/Test_popupwin_previewpopup_5.dump index 8f1b98f489..b0815fde77 100644 --- a/src/testdir/dumps/Test_popupwin_previewpopup_5.dump +++ b/src/testdir/dumps/Test_popupwin_previewpopup_5.dump @@ -2,12 +2,12 @@ |#|i|n|c|l|u|d|e| |"|X|h|e|a|d|e|r|.|h|"| @54 |t|h|r|e@1| @69 |f|o|u|r| @70 -|f|i|v|e| @27|╔+0&#afffff255| |t|e|s|t|d|i|r|/|X|t|a|g|f|i|l|e| |═@22|X -|s+0&#ffffff0|i|x| @28|║+0&#afffff255|2|6| @37| +0&#a8a8a8255|║+0&#afffff255 -|s+0&#ffffff0|e|v|e|n| @26|║+0&#afffff255|2|7| @37| +0&#a8a8a8255|║+0&#afffff255 -|f+0&#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @18| +0|║+0&#afffff255 -|n+0&#ffffff0|i|n|e| @27|║+0&#afffff255|2|9| @37| +0&#a8a8a8255|║+0&#afffff255 -|t+0&#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @11|╚+0&#afffff255|═@40|⇲ +|f|i|v|e| @28|╔+0&#afffff255| |t|e|s|t|d|i|r|/|X|t|a|g|f|i|l|e| |═@21|X +|s+0&#ffffff0|i|x| @29|║+0&#afffff255|2|6| @36| +0&#a8a8a8255|║+0&#afffff255 +|s+0&#ffffff0|e|v|e|n| @27|║+0&#afffff255|2|7| @36| +0&#a8a8a8255|║+0&#afffff255 +|f+0&#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @10|║+0&#afffff255|t|h|i|s| |i|s| |a|n|o|t|h|e|r| |p|l|a|c|e| @17| +0|║+0&#afffff255 +|n+0&#ffffff0|i|n|e| @28|║+0&#afffff255|2|9| @36| +0&#a8a8a8255|║+0&#afffff255 +|t+0&#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @12|╚+0&#afffff255|═@39|⇲ |v+0&#ffffff0|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| >a|n|o|t|h|e|r| @29 |~+0#4040ff13&| @73 |~| @73 diff --git a/src/testdir/dumps/Test_popupwin_previewpopup_7.dump b/src/testdir/dumps/Test_popupwin_previewpopup_7.dump index f510b8b778..add98bfcfe 100644 --- a/src/testdir/dumps/Test_popupwin_previewpopup_7.dump +++ b/src/testdir/dumps/Test_popupwin_previewpopup_7.dump @@ -2,12 +2,12 @@ |#|i|n|c|l|u|d|e| |"|X|h|e|a|d|e|r|.|h|"| @54 |t|h|r|e@1| @69 |f|o|u|r| @70 -|f|i|v|e| @27|╔+0#0000001#ffd7ff255| |X|t|a|g|f|i|l|e| |═@30|X -|s+0#0000000#ffffff0|i|x| @28|║+0#0000001#ffd7ff255|2|0| @37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|s+0#0000000#ffffff0|e|v|e|n| @26|║+0#0000001#ffd7ff255|t|h|e|w|o|r|d| |i|s| |h|e|r|e| @24| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|f+0#0000000#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0#0000001#ffd7ff255|2@1| @37| +0#0000000#0000001|║+0#0000001#ffd7ff255 -|n+0#0000000#ffffff0|i|n|e| @27|║+0#0000001#ffd7ff255|2|3| @37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|t+0#0000000#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @11|╚+0#0000001#ffd7ff255|═@40|⇲ +|f|i|v|e| @28|╔+0#0000001#ffd7ff255| |X|t|a|g|f|i|l|e| |═@29|X +|s+0#0000000#ffffff0|i|x| @29|║+0#0000001#ffd7ff255|2|0| @36| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|s+0#0000000#ffffff0|e|v|e|n| @27|║+0#0000001#ffd7ff255|t|h|e|w|o|r|d| |i|s| |h|e|r|e| @23| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|f+0#0000000#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @10|║+0#0000001#ffd7ff255|2@1| @36| +0#0000000#0000001|║+0#0000001#ffd7ff255 +|n+0#0000000#ffffff0|i|n|e| @28|║+0#0000001#ffd7ff255|2|3| @36| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|t+0#0000000#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @12|╚+0#0000001#ffd7ff255|═@39|⇲ |v+0#0000000#ffffff0|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| >a|n|o|t|h|e|r| @29 |~+0#4040ff13&| @73 |~| @73 diff --git a/src/testdir/dumps/Test_popupwin_previewpopup_8.dump b/src/testdir/dumps/Test_popupwin_previewpopup_8.dump index 6e88fffcae..c5e75e19b9 100644 --- a/src/testdir/dumps/Test_popupwin_previewpopup_8.dump +++ b/src/testdir/dumps/Test_popupwin_previewpopup_8.dump @@ -2,12 +2,12 @@ |#|i|n|c|l|u|d|e| |"|X|h|e|a|d|e|r|.|h|"| @54 |t|h|r|e@1| @69 |f|o|u|r| @70 -|f|i|v|e| @27|╔+0#0000001#ffd7ff255| |X|h|e|a|d|e|r|.|h| |═@29|X -|s+0#0000000#ffffff0|i|x| @28|║+0#0000001#ffd7ff255|1+0#e000002&|0| +0#0000001&@37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|s+0#0000000#ffffff0|e|v|e|n| @26|║+0#0000001#ffd7ff255|s|e|a|r|c|h|e|d| |w|o|r|d| |i|s| |h|e|r|e| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|f+0#0000000#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0#0000001#ffd7ff255|1+0#e000002&|2| +0#0000001&@37| +0#0000000#0000001|║+0#0000001#ffd7ff255 -|n+0#0000000#ffffff0|i|n|e| @27|║+0#0000001#ffd7ff255|1+0#e000002&|3| +0#0000001&@37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|t+0#0000000#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @11|╚+0#0000001#ffd7ff255|═@40|⇲ +|f|i|v|e| @28|╔+0#0000001#ffd7ff255| |X|h|e|a|d|e|r|.|h| |═@28|X +|s+0#0000000#ffffff0|i|x| @29|║+0#0000001#ffd7ff255|1+0#e000002&|0| +0#0000001&@36| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|s+0#0000000#ffffff0|e|v|e|n| @27|║+0#0000001#ffd7ff255|s|e|a|r|c|h|e|d| |w|o|r|d| |i|s| |h|e|r|e| @17| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|f+0#0000000#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @10|║+0#0000001#ffd7ff255|1+0#e000002&|2| +0#0000001&@36| +0#0000000#0000001|║+0#0000001#ffd7ff255 +|n+0#0000000#ffffff0|i|n|e| @28|║+0#0000001#ffd7ff255|1+0#e000002&|3| +0#0000001&@36| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|t+0#0000000#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @12|╚+0#0000001#ffd7ff255|═@39|⇲ |v+0#0000000#ffffff0|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| >a|n|o|t|h|e|r| @29 |~+0#4040ff13&| @73 |~| @73 diff --git a/src/testdir/dumps/Test_popupwin_previewpopup_9.dump b/src/testdir/dumps/Test_popupwin_previewpopup_9.dump index 09f8a45a14..f555a00dcd 100644 --- a/src/testdir/dumps/Test_popupwin_previewpopup_9.dump +++ b/src/testdir/dumps/Test_popupwin_previewpopup_9.dump @@ -2,12 +2,12 @@ |#|i|n|c|l|u|d|e| |"|X|h|e|a|d|e|r|.|h|"| @54 |t|h|r|e@1| @69 |f|o|u|r| @70 -|f|i|v|e| @27|╔+0#0000001#ffd7ff255| |X|h|e|a|d|e|r|.|h| |═@29|X -|s+0#0000000#ffffff0|i|x| @28|║+0#0000001#ffd7ff255|1+0#e000002&|0| +0#0000001&@37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|s+0#0000000#ffffff0|e|v|e|n| @26|║+0#0000001#ffd7ff255|s|e|a|r|c|h|e|d| |w|o|r|d| |i|s| |h|e|r|e| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|f+0#0000000#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @9|║+0#0000001#ffd7ff255|1+0#e000002&|2| +0#0000001&@37| +0#0000000#0000001|║+0#0000001#ffd7ff255 -|n+0#0000000#ffffff0|i|n|e| @27|║+0#0000001#ffd7ff255|1+0#e000002&|3| +0#0000001&@37| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 -|t+0#0000000#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @11|╚+0#0000001#ffd7ff255|═@40|⇲ +|f|i|v|e| @28|╔+0#0000001#ffd7ff255| |X|h|e|a|d|e|r|.|h| |═@28|X +|s+0#0000000#ffffff0|i|x| @29|║+0#0000001#ffd7ff255|1+0#e000002&|0| +0#0000001&@36| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|s+0#0000000#ffffff0|e|v|e|n| @27|║+0#0000001#ffd7ff255|s|e|a|r|c|h|e|d| |w|o|r|d| |i|s| |h|e|r|e| @17| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|f+0#0000000#ffffff0|i|n|d| |t|h|e|w|o|r|d| |s|o|m|e|w|h|e|r|e| @10|║+0#0000001#ffd7ff255|1+0#e000002&|2| +0#0000001&@36| +0#0000000#0000001|║+0#0000001#ffd7ff255 +|n+0#0000000#ffffff0|i|n|e| @28|║+0#0000001#ffd7ff255|1+0#e000002&|3| +0#0000001&@36| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255 +|t+0#0000000#ffffff0|h|i|s| |i|s| |a|n|o|t|h|e|r| |w|o|r|d| @12|╚+0#0000001#ffd7ff255|═@39|⇲ |v+0#0000000#ffffff0|e|r|y| |l|o|n|g| |l|i|n|e| |w|h|e|r|e| |t|h|e| |w|o|r|d| |i|s| |a|l|s|o| >a|n|o|t|h|e|r| @29 |~+0#4040ff13&| @73 |~| @73 diff --git a/src/testdir/dumps/Test_popupwin_wrap_1.dump b/src/testdir/dumps/Test_popupwin_wrap_1.dump index 5643dc75c6..0a04f26849 100644 --- a/src/testdir/dumps/Test_popupwin_wrap_1.dump +++ b/src/testdir/dumps/Test_popupwin_wrap_1.dump @@ -1,10 +1,10 @@ >1+0&#ffffff0| @73 |2| @73 -|╔+0#0000001#ffd7ff255|═@73 -|║| |o+0&#e0e0e08|n|e| @67| +0&#ffd7ff255| +0#0000000#0000001 -|║+0#0000001#ffd7ff255| |a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d| | +0#0000000#0000001 -|║+0#0000001#ffd7ff255| |f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s| @52| +0#0000000#a8a8a8255 -|╚+0#0000001#ffd7ff255|═@73 -|8+0#0000000#ffffff0| @73 +|╔+0#0000001#ffd7ff255|═@71|╗| +0#0000000#ffffff0 +|║+0#0000001#ffd7ff255| |o+0&#e0e0e08|n|e| @65| +0&#ffd7ff255| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#0000000#ffffff0 +|║+0#0000001#ffd7ff255| |a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a| | +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#0000000#ffffff0 +|║+0#0000001#ffd7ff255| |s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s| @48| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#0000000#ffffff0 +|╚+0#0000001#ffd7ff255|═@71|╝| +0#0000000#ffffff0 +|8| @73 |9| @73 @57|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_popupwin_wrap_2.dump b/src/testdir/dumps/Test_popupwin_wrap_2.dump index 3a02fc5c95..e9b35f648e 100644 --- a/src/testdir/dumps/Test_popupwin_wrap_2.dump +++ b/src/testdir/dumps/Test_popupwin_wrap_2.dump @@ -1,10 +1,10 @@ >1+0&#ffffff0| @73 |2| @73 -|╔+0#0000001#ffd7ff255|═@73 -|║| |a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d| | +0#0000000#a8a8a8255 -|║+0#0000001#ffd7ff255| |f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s| @52| +0#0000000#0000001 -|║+0#0000001#ffd7ff255| |t+0&#e0e0e08|h|r|e@1| @65| +0&#ffd7ff255| +0#0000000#0000001 -|╚+0#0000001#ffd7ff255|═@73 -|8+0#0000000#ffffff0| @73 +|╔+0#0000001#ffd7ff255|═@71|╗| +0#0000000#ffffff0 +|║+0#0000001#ffd7ff255| |a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a| | +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#0000000#ffffff0 +|║+0#0000001#ffd7ff255| |s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s|d|f|a|s| @48| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#0000000#ffffff0 +|║+0#0000001#ffd7ff255| |t+0&#e0e0e08|h|r|e@1| @63| +0&#ffd7ff255| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#0000000#ffffff0 +|╚+0#0000001#ffd7ff255|═@71|╝| +0#0000000#ffffff0 +|8| @73 |9| @73 @57|1|,|1| @10|T|o|p| diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index ac1b5678ce..fbfb45e2f3 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -674,7 +674,7 @@ func Test_scroll_info_window() call ScrollInfoWindowTest("", 0, 1) call ScrollInfoWindowTest("pagedown", 1, 4) call ScrollInfoWindowTest("pagedown", 2, 7) - call ScrollInfoWindowTest("pagedown", 3, 11) + call ScrollInfoWindowTest("pagedown", 3, 10) call ScrollInfoWindowTest("pageup", 3, 1) endfunc diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 030e9e6598..4ce6a7158a 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -3115,7 +3115,7 @@ func Test_popupwin_with_buffer_and_filter() call assert_equal(1, popup_getpos(winid).firstline) redraw call feedkeys("G", 'xt') - call assert_equal(99, popup_getpos(winid).firstline) + call assert_equal(96, popup_getpos(winid).firstline) call popup_close(winid) exe 'bwipe! ' .. bufnr diff --git a/src/version.c b/src/version.c index 23de968780..9873a0236f 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 419, /**/ 418, /**/ From 3ac7b974391c40ba61c7abec8c02ea3bacf818a4 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 29 Apr 2026 19:48:05 +0000 Subject: [PATCH 145/545] patch 9.2.0420: channel: cannot handle binary data via channel callbacks Problem: channel: cannot handle binary data via channel callbacks Solution: Add a blob channel mode that passes callback data as a Blob (Yasuhiro Matsumoto). closes: #20084 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/channel.txt | 5 ++- runtime/doc/version9.txt | 1 + src/channel.c | 60 +++++++++++++++++++++++++++++------- src/errors.h | 4 +-- src/job.c | 2 ++ src/po/vim.pot | 5 +-- src/structs.h | 1 + src/testdir/test_channel.vim | 58 ++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 9 files changed, 122 insertions(+), 16 deletions(-) diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 26cdc6a006..839dcc296e 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -1,4 +1,4 @@ -*channel.txt* For Vim version 9.2. Last change: 2026 Apr 28 +*channel.txt* For Vim version 9.2. Last change: 2026 Apr 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -170,6 +170,7 @@ unreachable on the network. "js" - Use JS (JavaScript) encoding, more efficient than JSON. "nl" - Use messages that end in a NL character "raw" - Use raw messages + "blob" - Use raw messages and pass callback data as a |Blob| "lsp" - Use language server protocol encoding "dap" - Use debug adapter protocol encoding *channel-callback* *E921* @@ -189,6 +190,8 @@ unreachable on the network. excluding the NL. When "mode" is "raw" the "msg" argument is the whole message as a string. + When "mode" is "blob" the "msg" argument is the whole message + as a |Blob|. For all callbacks: Use |function()| to bind it to arguments and/or a Dictionary. Or use the form "dict.function" to bind diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 7dbc447a9c..9516ecd125 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52620,6 +52620,7 @@ Other ~ height. Also added the "scrollbar" sub-option to 'tabpanelopt'. - Added the "noinsert" value to the 'wildmode' option for symmetry with the 'completeopt' option +- Channel can handle |Blob| messages |channel-open-options|. Platform specific ~ ----------------- diff --git a/src/channel.c b/src/channel.c index a0b7b953e2..e703a82089 100644 --- a/src/channel.c +++ b/src/channel.c @@ -3213,6 +3213,7 @@ channel_use_json_head(channel_T *channel, ch_part_T part) may_invoke_callback(channel_T *channel, ch_part_T part) { char_u *msg = NULL; + blob_T *blob = NULL; typval_T *listtv = NULL; typval_T argv[CH_JSON_MAX_ARGS]; int seq_nr = -1; @@ -3224,6 +3225,7 @@ may_invoke_callback(channel_T *channel, ch_part_T part) buf_T *buffer = NULL; char_u *p; int called_otc; // one time callbackup + int raw_len = 0; if (channel->ch_nb_close_cb != NULL) // this channel is handled elsewhere (netbeans) @@ -3384,15 +3386,42 @@ may_invoke_callback(channel_T *channel, ch_part_T part) { // For a raw channel we don't know where the message ends, just // get everything we have. - // Convert NUL to NL, the internal representation. - msg = channel_get_all(channel, part, NULL); + raw_len = 0; + msg = channel_get_all(channel, part, + ch_mode == CH_MODE_BLOB ? &raw_len : NULL); } if (msg == NULL) return FALSE; // out of memory (and avoids Coverity warning) - argv[1].v_type = VAR_STRING; - argv[1].vval.v_string = msg; + if (ch_mode == CH_MODE_BLOB) + { + blob = blob_alloc(); + if (blob == NULL) + { + vim_free(msg); + return FALSE; + } + if (ga_grow(&blob->bv_ga, raw_len) == FAIL) + { + blob_free(blob); + vim_free(msg); + return FALSE; + } + if (raw_len > 0) + { + mch_memmove(blob->bv_ga.ga_data, msg, (size_t)raw_len); + blob->bv_ga.ga_len = raw_len; + } + argv[1].v_type = VAR_BLOB; + argv[1].vval.v_blob = blob; + ++blob->bv_refcount; + } + else + { + argv[1].v_type = VAR_STRING; + argv[1].vval.v_string = msg; + } } called_otc = FALSE; @@ -3519,6 +3548,8 @@ may_invoke_callback(channel_T *channel, ch_part_T part) if (listtv != NULL) free_tv(listtv); + if (blob != NULL) + blob_unref(blob); vim_free(msg); return TRUE; @@ -3654,6 +3685,9 @@ channel_part_info(channel_T *channel, dict_T *dict, char *name, ch_part_T part) case CH_MODE_RAW: STR_LITERAL_SET(s, "RAW"); break; + case CH_MODE_BLOB: + STR_LITERAL_SET(s, "BLOB"); + break; case CH_MODE_JSON: STR_LITERAL_SET(s, "JSON"); break; @@ -4317,14 +4351,16 @@ channel_read_block( readq_T *node; ch_log(channel, "Blocking %s read, timeout: %d msec", - mode == CH_MODE_RAW ? "RAW" : "NL", timeout); + mode == CH_MODE_RAW ? "RAW" + : mode == CH_MODE_BLOB ? "BLOB" : "NL", timeout); while (TRUE) { node = channel_peek(channel, part); if (node != NULL) { - if (mode == CH_MODE_RAW || (mode == CH_MODE_NL + if (mode == CH_MODE_RAW || mode == CH_MODE_BLOB + || (mode == CH_MODE_NL && channel_first_nl(node) != NULL)) // got a complete message break; @@ -4348,7 +4384,7 @@ channel_read_block( } // We have a complete message now. - if (mode == CH_MODE_RAW || outlen != NULL) + if (mode == CH_MODE_RAW || mode == CH_MODE_BLOB || outlen != NULL) { msg = channel_get_all(channel, part, outlen); } @@ -4384,7 +4420,8 @@ channel_read_block( } } if (ch_log_active()) - ch_log(channel, "Returning %d bytes", (int)STRLEN(msg)); + ch_log(channel, "Returning %d bytes", + outlen != NULL ? *outlen : (int)STRLEN(msg)); return msg; } @@ -4595,7 +4632,7 @@ common_channel_read(typval_T *argvars, typval_T *rettv, int raw, int blob) if (opt.jo_set & JO_TIMEOUT) timeout = opt.jo_timeout; - if (blob) + if (blob || mode == CH_MODE_BLOB) { int outlen = 0; char_u *p = channel_read_block(channel, part, @@ -5000,9 +5037,10 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval) part_send = channel_part_send(channel); ch_mode = channel_get_mode(channel, part_send); - if (ch_mode == CH_MODE_RAW || ch_mode == CH_MODE_NL) + if (ch_mode == CH_MODE_RAW || ch_mode == CH_MODE_BLOB + || ch_mode == CH_MODE_NL) { - emsg(_(e_cannot_use_evalexpr_sendexpr_with_raw_or_nl_channel)); + emsg(_(e_cannot_use_evalexpr_sendexpr_with_raw_nl_or_blob_channel)); return; } diff --git a/src/errors.h b/src/errors.h index c077118c6d..384dfaab9c 100644 --- a/src/errors.h +++ b/src/errors.h @@ -2377,8 +2377,8 @@ EXTERN char e_using_job_as_number[] INIT(= N_("E910: Using a Job as a Number")); EXTERN char e_using_job_as_float[] INIT(= N_("E911: Using a Job as a Float")); -EXTERN char e_cannot_use_evalexpr_sendexpr_with_raw_or_nl_channel[] - INIT(= N_("E912: Cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel")); +EXTERN char e_cannot_use_evalexpr_sendexpr_with_raw_nl_or_blob_channel[] + INIT(= N_("E912: Cannot use ch_evalexpr()/ch_sendexpr() with a raw, nl or blob channel")); EXTERN char e_using_channel_as_number[] INIT(= N_("E913: Using a Channel as a Number")); EXTERN char e_using_channel_as_float[] diff --git a/src/job.c b/src/job.c index 041a8e9b93..937d55f6be 100644 --- a/src/job.c +++ b/src/job.c @@ -27,6 +27,8 @@ handle_mode(typval_T *item, jobopt_T *opt, ch_mode_T *modep, int jo) *modep = CH_MODE_NL; else if (STRCMP(val, "raw") == 0) *modep = CH_MODE_RAW; + else if (STRCMP(val, "blob") == 0) + *modep = CH_MODE_BLOB; else if (STRCMP(val, "js") == 0) *modep = CH_MODE_JS; else if (STRCMP(val, "json") == 0) diff --git a/src/po/vim.pot b/src/po/vim.pot index 8f43a89f73..4dd6ba9093 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-04-27 18:39+0000\n" +"POT-Creation-Date: 2026-04-29 19:49+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -6766,7 +6766,8 @@ msgstr "" msgid "E911: Using a Job as a Float" msgstr "" -msgid "E912: Cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" +msgid "" +"E912: Cannot use ch_evalexpr()/ch_sendexpr() with a raw, nl or blob channel" msgstr "" msgid "E913: Using a Channel as a Number" diff --git a/src/structs.h b/src/structs.h index 14978739f5..8429ebe29e 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2671,6 +2671,7 @@ typedef enum { CH_MODE_NL = 0, CH_MODE_RAW, + CH_MODE_BLOB, CH_MODE_JSON, CH_MODE_JS, CH_MODE_LSP, // Language Server Protocol (http + json) diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim index abdaed0dc1..f35a1b8556 100644 --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -1390,6 +1390,64 @@ func Test_out_cb_lambda() endtry endfunc +func Test_out_cb_blob_mode() + let g:Ch_blob_bytes = [] + func OutBlobCb(chan, msg) + call assert_equal(v:t_blob, type(a:msg)) + let g:Ch_blob_bytes += blob2list(a:msg) + endfunc + + let cmd = [s:python, '-c', + \ 'import sys,time;' + \ .. 'sys.stdout.buffer.write(bytes([0, 1, 2, 10, 255]));' + \ .. 'sys.stdout.flush();' + \ .. 'time.sleep(0.1)'] + let job = job_start(cmd, #{ + \ out_mode: 'blob', + \ out_cb: 'OutBlobCb', + \ }) + try + call WaitForAssert({-> assert_equal([0, 1, 2, 10, 255], g:Ch_blob_bytes)}) + finally + call job_stop(job) + delfunc OutBlobCb + unlet g:Ch_blob_bytes + endtry +endfunc + +func Test_pty_out_cb_blob_mode() + CheckUnix + + let g:Ch_blob_bytes = [] + func PtyBlobCb(chan, msg) + call assert_equal(v:t_blob, type(a:msg)) + let g:Ch_blob_bytes += blob2list(a:msg) + endfunc + + " Put the pty in raw mode so the line discipline does not translate LF + " to CRLF or strip NUL bytes, then write bytes that include NULs on + " both sides of an embedded LF. + let cmd = [s:python, '-c', + \ 'import os,sys,time;' + \ .. 'os.system("stty raw -echo");' + \ .. 'sys.stdout.buffer.write(bytes([65, 0, 66, 10, 67, 0, 68]));' + \ .. 'sys.stdout.flush();' + \ .. 'time.sleep(0.1)'] + let job = job_start(cmd, #{ + \ pty: 1, + \ out_mode: 'blob', + \ out_cb: 'PtyBlobCb', + \ }) + try + call WaitForAssert({-> assert_equal( + \ [65, 0, 66, 10, 67, 0, 68], g:Ch_blob_bytes)}) + finally + call job_stop(job) + delfunc PtyBlobCb + unlet g:Ch_blob_bytes + endtry +endfunc + func Test_close_and_exit_cb() let g:test_is_flaky = 1 let g:retdict = {'ret': {}} diff --git a/src/version.c b/src/version.c index 9873a0236f..699db3c560 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 420, /**/ 419, /**/ From 77499e009af677720e747debebedb78d12a77cb6 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 29 Apr 2026 20:36:14 +0000 Subject: [PATCH 146/545] patch 9.2.0421: vimball: can smuggle Vimscript into VimballRecord file Problem: vimball: can smuggle Vimscript into VimballRecord file (Mayank Jangid and Kushal Khemka) Solution: Disallow strange file names Signed-off-by: Christian Brabandt --- runtime/autoload/vimball.vim | 25 +++++++++++++++++-------- src/testdir/test_plugin_vimball.vim | 13 +++++++++++++ src/version.c | 2 ++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/runtime/autoload/vimball.vim b/runtime/autoload/vimball.vim index 8cc206f1d4..3afd8e6c08 100644 --- a/runtime/autoload/vimball.vim +++ b/runtime/autoload/vimball.vim @@ -103,14 +103,14 @@ fun! vimball#MkVimball(line1,line2,writelevel,...) range while linenr <= a:line2 let svfile = getline(linenr) - + if !filereadable(svfile) call vimball#ShowMesg(s:ERROR,"unable to read file<".svfile.">") call s:ChgDir(curdir) call vimball#RestoreSettings() return endif - + " create/switch to mkvimball tab if !exists("vbtabnr") tabnew @@ -119,7 +119,7 @@ fun! vimball#MkVimball(line1,line2,writelevel,...) range else exe "tabn ".vbtabnr endif - + let lastline= line("$") + 1 if lastline == 2 && getline("$") == "" call setline(1,'" Vimball Archiver by Charles E. Campbell') @@ -163,7 +163,7 @@ endfun " --------------------------------------------------------------------- " vimball#Vimball: extract and distribute contents from a vimball {{{2 -" (invoked the the UseVimball command embedded in +" (invoked the the UseVimball command embedded in " vimballs' prologue) fun! vimball#Vimball(really,...) @@ -213,7 +213,7 @@ fun! vimball#Vimball(really,...) " give title to listing of (extracted) files from Vimball Archive if a:really echohl Title | echomsg "Vimball Archive" | echohl None - else + else echohl Title | echomsg "Vimball Archive Listing" | echohl None echohl Statement | echomsg "files would be placed under: ".home | echohl None endif @@ -237,6 +237,15 @@ fun! vimball#Vimball(really,...) bw! Vimball call s:ChgDir(curdir) return + " Also, disallow strange paths, that could lead to code execution from + " .VimballRecord + " Disallow: pipe, quotes and closing paren + elseif fname =~ '[|'')"]' + echomsg printf("(Vimball) Forbidding strange filename: '%s', aborting...", fname) + exe "tabn ".curtabnr + bw! Vimball + call s:ChgDir(curdir) + return endif if a:really @@ -295,7 +304,7 @@ fun! vimball#Vimball(really,...) exe "silent w! ".fnameescape(fnamepath) endif echo "wrote ".fnameescape(fnamepath) - call s:RecordInVar(home,"call delete('".fnamepath."')") + call s:RecordInVar(home,"call delete('".escape(fnamepath, '"''|')."')") endif " return to tab with vimball @@ -370,7 +379,7 @@ fun! vimball#RmVimball(...) call s:ChgDir(home) if filereadable(".VimballRecord") - keepalt keepjumps 1split + keepalt keepjumps 1split sil! keepalt keepjumps e .VimballRecord let keepsrch= @/ if search('^\M'.curfile."\m: ".'cw') @@ -558,7 +567,7 @@ fun! s:RecordInFile(home) if exists("s:recordfile") || exists("s:recorddir") let curdir= getcwd() call s:ChgDir(a:home) - keepalt keepjumps 1split + keepalt keepjumps 1split let cmd= expand("%:tr").": " diff --git a/src/testdir/test_plugin_vimball.vim b/src/testdir/test_plugin_vimball.vim index ab740d1d2b..30093ef719 100644 --- a/src/testdir/test_plugin_vimball.vim +++ b/src/testdir/test_plugin_vimball.vim @@ -97,3 +97,16 @@ func Test_vimball_path_traversal_drive_letter() call assert_match('(Vimball) Path Traversal Attack detected, aborting\.\.\.', mess) call s:teardown() endfunc + +func Test_vimball_evil_filenames() + call s:Mkvimball() + call delete('XVimball', 'rf') + sp Xtest.vmb + 4s#XVimball#pwn')# + so % + call feedkeys("\", "it") + + let mess = execute(':mess')->split('\n')[-1] + call assert_match('(Vimball) Forbidding strange filename:.* aborting\.\.\.', mess) + call s:teardown() +endfunc diff --git a/src/version.c b/src/version.c index 699db3c560..11ae9897aa 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 421, /**/ 420, /**/ From e7e35b9e3866abcbb33eec789c85636671c86440 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 29 Apr 2026 21:17:11 +0000 Subject: [PATCH 147/545] runtime(doc): clarify that viminfo file should be trusted Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 8 ++++++++ runtime/doc/starting.txt | 20 +++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e994d2901e..3576e174b7 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -10041,6 +10041,14 @@ A jump table for the options with a short description can be found at |Q_op|. letter. Thus "KEEPTHIS and "K_L_M" are stored, but "KeepThis" and "_K_L_M" are not. Nested List and Dict items may not be read back correctly, you end up with an empty item. + + Restoring |List|, |Dictionary| and |Tuple| variables is done by + parsing their textual form with the Vim expression evaluator. + Reading a viminfo file with "!" enabled is therefore only safe + when the file is trusted. In particular, do not enable "!" + before reading a viminfo file you obtained from another user, + downloaded, or that is writable by another account. See + also |viminfo-security|. *viminfo-quote* " Maximum number of lines saved for each register. Old name of the '<' item, with the disadvantage that you need to put a diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 46a47b7e2b..816849fb9c 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1,4 +1,4 @@ -*starting.txt* For Vim version 9.2. Last change: 2026 Mar 17 +*starting.txt* For Vim version 9.2. Last change: 2026 Apr 29 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1573,6 +1573,24 @@ Session. You could have several Session files, one for each project you are working on. Viminfo and Session files together can be used to effectively enter Vim and directly start working in your desired setup. |session-file| + *viminfo-security* +A viminfo file written by Vim is plain text and contains Vim expressions for +|List|, |Dictionary| and |Tuple| values. When "!" is in 'viminfo' at the time +Vim processes the file as a viminfo file, those expressions are evaluated. + +The default value of 'viminfo' does not include "!", so by default no +expression evaluation happens. + +Opening a viminfo file in a buffer (e.g. with |:edit|) is harmless; Vim only +displays the file contents. The risk is letting Vim process an untrusted file +through the viminfo machinery, which happens when: +- |:rviminfo| is used on the file, or +- 'viminfofile' is set to point at it, or +- the file is placed at the path Vim already reads as viminfo + |viminfo-file-name| + +In any of those cases, do not have "!" in 'viminfo' if you do not trust it. + *viminfo-read* When Vim is started and the 'viminfo' option is non-empty, the contents of the viminfo file are read and the info can be used in the appropriate places. From 620557bd48865fa3d927901764d2747bf68597b5 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 29 Apr 2026 21:22:48 +0000 Subject: [PATCH 148/545] runtime(doc): Update help tags file forgotten from Commit e7e35b9e3866abcbb33e Signed-off-by: Christian Brabandt --- runtime/doc/tags | 1 + 1 file changed, 1 insertion(+) diff --git a/runtime/doc/tags b/runtime/doc/tags index a0689193a6..3bc6f71554 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -11761,6 +11761,7 @@ viminfo-r options.txt /*viminfo-r* viminfo-read starting.txt /*viminfo-read* viminfo-read-write starting.txt /*viminfo-read-write* viminfo-s options.txt /*viminfo-s* +viminfo-security starting.txt /*viminfo-security* viminfo-timestamp starting.txt /*viminfo-timestamp* viminfo-write starting.txt /*viminfo-write* vimrc starting.txt /*vimrc* From f793e98068a3de540caf539ff0ecb35f06aa2a08 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Wed, 29 Apr 2026 21:44:12 +0000 Subject: [PATCH 149/545] runtime(doc): clarify separator cell on status line rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Expand hl-VertSplit / hl-VertSplitNC in syntax.txt to spell out which character (space vs 'fillchars' "vert") and which highlight group (StatusLine / StatusLineNC / VertSplit / VertSplitNC) are used at the separator cell on each kind of screen row. - Add cross references from hl-StatusLine and hl-StatusLineNC to hl-VertSplit / hl-VertSplitNC. The behavior itself is unchanged — see v9.2.0349 (c72196529) — but the asymmetry reported in #20089 surprised users, so this aims to make the spec discoverable from the highlight group docs. closes: #20101 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/syntax.txt | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 34eba55fd1..efebe56674 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 Apr 27 +*syntax.txt* For Vim version 9.2. Last change: 2026 Apr 30 VIM REFERENCE MANUAL by Bram Moolenaar @@ -5993,13 +5993,28 @@ EndOfBuffer Filler lines (~) after the last line in the buffer. ErrorMsg Error messages on the command line. *hl-VertSplit* VertSplit Column separating vertically split windows that are adjacent - to the current window. On the current window's status line - rows, the separator cell is drawn as a space with the - |hl-StatusLine| highlight so it blends into the status line - without a stray fillchar glyph. + to the current window. The cell character and highlight at + each screen row depend on what is at that row on both sides + of the separator: + - On a status line row of the current window: the cell is + drawn as a space with the |hl-StatusLine| highlight, so it + visually merges into the current window's status line. + - On any other row (including a row that is a status line + row of the adjacent non-current window but not of the + current window): the cell is drawn with the "vert" item of + 'fillchars' and the VertSplit highlight. *hl-VertSplitNC* -VertSplitNC Column separating vertically split windows of non-current - windows. By default, highlighted like |hl-VertSplit|. +VertSplitNC Column separating vertically split windows where neither + adjacent window is the current window. The cell character + and highlight at each screen row depend on what is at that + row on both sides of the separator: + - On a status line row of either adjacent (non-current) + window: the cell is drawn as a space with the + |hl-StatusLineNC| highlight of that window, so it visually + merges into that window's status line. + - On any other row: the cell is drawn with the "vert" item + of 'fillchars' and the VertSplitNC highlight. + By default, highlighted like |hl-VertSplit|. *hl-Folded* Folded Line used for closed folds. *hl-FoldColumn* @@ -6116,8 +6131,12 @@ SpellRare Word that is recognized by the spellchecker as one that is This will be combined with the highlighting used otherwise. *hl-StatusLine* StatusLine Status line of current window. + Also used for the vertical separator cell adjacent to the + current window's status line. see |hl-VertSplit|. *hl-StatusLineNC* -StatusLineNC status lines of not-current windows +StatusLineNC status lines of not-current windows. + Also used for the vertical separator cell adjacent to a + non-current window's status line. see |hl-VertSplitNC|. Note: If this is equal to "StatusLine", Vim will use "^^^" in the status line of the current window. *hl-StatusLineTerm* From 7da90de1cb6df8f2dcbab97d3c7b8b83fa0de13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9ana=20=E6=B1=9F?= Date: Thu, 30 Apr 2026 18:00:28 +0000 Subject: [PATCH 150/545] runtime(cabal): add missing haskell language editions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes: #20097 Signed-off-by: Léana 江 Signed-off-by: Christian Brabandt --- runtime/syntax/cabal.vim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/syntax/cabal.vim b/runtime/syntax/cabal.vim index a1db25034a..ddc905615b 100644 --- a/runtime/syntax/cabal.vim +++ b/runtime/syntax/cabal.vim @@ -5,6 +5,8 @@ " Previous Maintainer: Vincent Berthoux " File Types: .cabal " Last Change: 22 Oct 2022 +" +" 2026 Apr 29 by Léana: add missing haskell language editions " 2026 Apr 20 by Vim project: remove wrong oneline keyword #20018 " " v1.6: Added support for foreign-libraries @@ -193,7 +195,9 @@ syn match cabalVersionRegionB \ contains=cabalStatementRegion,cabalVersionOperator,cabalVersion \ /^\s*\%(cabal-\)\?version\s*:.*$/ -syn keyword cabalLanguage Haskell98 Haskell2010 +" See the following link for all Haskell language editions supported by Cabal. +" https://cabal.readthedocs.io/en/stable/cabal-package-description-file.html#pkg-field-default-language +syn keyword cabalLanguage Haskell98 Haskell2010 GHC2021 GHC2024 " title region syn match cabalName contained /:\@<=.*/ From 7f3243e3a8285badb0b2e34458cecaa9bfaadf49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Pe=C5=A1i=C4=87?= <27575106+eevan78@users.noreply.github.com> Date: Thu, 30 Apr 2026 18:06:46 +0000 Subject: [PATCH 151/545] translation(sr): Update of Serbian translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit closes: #20105 Signed-off-by: Ivan Pešić <27575106+eevan78@users.noreply.github.com> Signed-off-by: Christian Brabandt --- src/po/sr.po | 93 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 32 deletions(-) diff --git a/src/po/sr.po b/src/po/sr.po index c8c1537898..5187a3f3f0 100644 --- a/src/po/sr.po +++ b/src/po/sr.po @@ -10,8 +10,8 @@ msgid "" msgstr "" "Project-Id-Version: Vim(Serbian)\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-03-30 11:05+0900\n" -"PO-Revision-Date: 2026-02-20 09:34+0400\n" +"POT-Creation-Date: 2026-04-29 19:49+0000\n" +"PO-Revision-Date: 2026-05-01 10:09+0400\n" "Last-Translator: Ivan Pešić \n" "Language-Team: Serbian\n" "Language: sr\n" @@ -2038,6 +2038,9 @@ msgstr "???ПРАЗАН БЛОК" msgid "???LINES MISSING" msgstr "???НЕДОСТАЈУ ЛИНИЈЕ" +msgid "???ILLEGAL BLOCK NUMBER" +msgstr "???НЕИСПРАВАН БРОЈ БЛОКА" + msgid "???BLOCK MISSING" msgstr "???НЕДОСТАЈЕ БЛОК" @@ -2386,6 +2389,10 @@ msgstr " (Прекинуто)" msgid "Beep!" msgstr "Биип!" +#, c-format +msgid "Executing directly: \"%s\"" +msgstr "Директно се извршава: „%s”" + #, c-format msgid "Calling shell to execute: \"%s\"" msgstr "Позива се командно окружење да изврши: „%s”" @@ -2600,7 +2607,7 @@ msgid "" "Cannot execute shell sh\n" msgstr "" "\n" -"Командно окружење sh не може да се изврши\n" +"Не може да се изврши командно окружење sh\n" msgid "" "\n" @@ -2614,7 +2621,7 @@ msgid "" "Cannot create pipes\n" msgstr "" "\n" -"Токови података не могу да се креирају\n" +"Не могу да се креирају токови података\n" msgid "" "\n" @@ -2628,7 +2635,7 @@ msgid "" "Cannot execute shell " msgstr "" "\n" -"Командно окружење не може да се изврши " +"Не може да се изврши командно окружење " msgid "" "\n" @@ -2642,14 +2649,14 @@ msgstr "XSMP је изгубио ICE везу" #, c-format msgid "Could not load gpm library: %s" -msgstr "Библиотека gpm није могла да се учита: %s" +msgstr "Није могла да се учита gpm библиотека: %s" #, c-format msgid "dlerror = \"%s\"" msgstr "dlerror = „%s”" msgid "Opening the X display failed" -msgstr "Отварање X приказа није успело" +msgstr "Није успело отварање X приказа" msgid "XSMP handling save-yourself request" msgstr "XSMP опслужује сачувај-се захтев" @@ -2669,7 +2676,7 @@ msgid "Failed creating socket directory: %s" msgstr "Не може да се креира сокет директоријум: %s" msgid "At line" -msgstr "Код линије" +msgstr "На линији" #, c-format msgid "Vim: Caught %s event\n" @@ -5465,10 +5472,6 @@ msgstr "E456: PostScript resource фајл „%s.ps” не може да се msgid "E457: Can't read PostScript resource file \"%s\"" msgstr "E457: PostScript resource фајл „%s” не може да се чита" -msgid "E458: Cannot allocate colormap entry, some colors may be incorrect" -msgstr "" -"E458: colormap ставка не може да се алоцира, неке боје су можда неисправне" - msgid "E459: Cannot go back to previous directory" msgstr "E459: Не може да се оде назад на претходни директоријум" @@ -6123,6 +6126,9 @@ msgstr "E647: Неисправни идентификатор бафера у se msgid "E648: Invalid buffer identifier in close" msgstr "E648: Неисправни идентификатор бафера у close" +msgid "E649: Invalid identifier name in defineAnnoType" +msgstr "E649: Неисправно име идентификатора у defineAnnoType" + msgid "E650: Invalid buffer identifier in defineAnnoType" msgstr "E650: Неисправни идентификатор бафера у defineAnnoType" @@ -7046,10 +7052,11 @@ msgstr "E910: Посао се користи као Број" msgid "E911: Using a Job as a Float" msgstr "E911: Посао се користи као Покретни" -msgid "E912: Cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" +msgid "" +"E912: Cannot use ch_evalexpr()/ch_sendexpr() with a raw, nl or blob channel" msgstr "" -"E912: Функција ch_evalexpr()/ch_sendexpr() не може да се користи са raw или " -"nl каналом" +"E912: Функција ch_evalexpr()/ch_sendexpr() не може да се користи са raw, nl " +"или blob каналом" msgid "E913: Using a Channel as a Number" msgstr "E913: Канал се користи као Број" @@ -8411,10 +8418,8 @@ msgstr "E1291: Неважећи аргумент: %ld" msgid "E1292: Command-line window is already open" msgstr "E1292: Прозор командне линије је већ отворен" -msgid "E1293: Cannot use a negative id after adding a textprop with text" -msgstr "" -"E1293: Након додавања текст особине са текстом не може да се употреби " -"негативан id" +msgid "E1293: Cannot use a negative id for a text property" +msgstr "E1293: За текст особину може да се употреби негативан id" msgid "E1294: Can only use text_align when column is zero" msgstr "E1294: Када је колона нула, може да се користи само text_align" @@ -8559,13 +8564,6 @@ msgstr "E1335: У променљиву „%s” у класи „%s” не мо msgid "E1337: Class variable \"%s\" not found in class \"%s\"" msgstr "E1337: Променљива класе „%s” није пронађена у класи „%s”" -msgid "" -"E1339: Cannot add a textprop with text after using a textprop with a " -"negative id" -msgstr "" -"E1339: Текст особина са текстом не може да се дода након употребе текст " -"особине са негативним id" - #, c-format msgid "E1340: Argument already declared in the class: %s" msgstr "E1340: Аргумент је већ дефинисан у класи: %s" @@ -9100,7 +9098,7 @@ msgstr "E1545: Стек Quickfix листе није доступан" msgid "E1546: Cannot switch to a closing buffer" msgstr "E1546: Не може да се пређе на бафер који се затвара" -msgid "E1547: This version of Vim does support :redrawtabpanel" +msgid "E1547: This version of Vim does not support :redrawtabpanel" msgstr "E1547: Ова верзија Vim едитора не подржава :redrawtabpanel" msgid "E1548: Wayland connection is unavailable" @@ -9193,6 +9191,21 @@ msgstr "" "E1571: Мора да се наведе барем једна функција повратног позива за " "redraw_listener_add" +msgid "E1572: 'listchars' field \"leadtab\" requires \"tab\" to be specified" +msgstr "E1572: 'listchars' поље „leadtab” захтева да се наведе „tab”" + +msgid "E1573: Cannot listen on port" +msgstr "E1573: Не може да се слуша на порту" + +msgid "E1574: gethostbyname(): cannot resolve hostname in channel_listen()" +msgstr "E1574: gethostbyname(): не може да се разреши име хоста у channel_listen()" + +msgid "E1575: Cannot create pipes" +msgstr "E1575: Не могу да се креирају токови података" + +msgid "E1576: Tag file entry must not be a URL" +msgstr "E1576: Ставка у фајлу ознака не сме да буде URL" + msgid "--No lines in buffer--" msgstr "--У баферу нема линија--" @@ -9750,6 +9763,9 @@ msgstr "скроловање по екранској линији" msgid "number of screen lines to show around the cursor" msgstr "број екранских линија које се приказују око курсора" +msgid "vertically center cursor even at end of file" +msgstr "центрирај курсор по вертикали чак и на крају фајла" + msgid "long lines wrap" msgstr "дуге линије се обавијају" @@ -9805,6 +9821,12 @@ msgstr "број линија који скролују CTRL-F и CTRL-B" msgid "don't redraw while executing macros" msgstr "екран се не исцртава током извршавања макроа" +msgid "configure method of receiving terminal size changes" +msgstr "конфигурисање методе пријема промена величине терминала" + +msgid "enable terminal sync mode" +msgstr "укључивање режима синхронизације терминала" + msgid "timeout for 'hlsearch' and :match highlighting in msec" msgstr "тајмаут за 'hlsearch' и :match истицање у милисекундама" @@ -9866,6 +9888,9 @@ msgstr "истичу се сва подударања последње кори msgid "highlight group to use for the window" msgstr "група истицања која се користи за прозор" +msgid "highlight group mappings for the window" +msgstr "мапирања група истицања за прозор" + msgid "use GUI colors for the terminal" msgstr "за терминал се користе ГКИ боје" @@ -9911,6 +9936,9 @@ msgstr "0, 1 или 2; када се користи статусна линиј msgid "alternate format to be used for a status line" msgstr "алтернативни формат који ће се користити за статусну линију" +msgid "optional settings for the status line" +msgstr "необавезна подешавања за статусну линију" + msgid "make all windows the same size when adding/removing windows" msgstr "" "поставља све прозоре на исту величину када се прозори\n" @@ -10315,11 +10343,6 @@ msgstr "" msgid "Wayland seat to use" msgstr "Wayland седиште које се користи" -msgid "" -"Enable wayland focus stealing functionality in order to access the clipboard" -msgstr "" -"Укључује wayland функционалност крађе фокуса да би се приступило клипборду" - msgid "\"startsel\" and/or \"stopsel\"; what special keys can do" msgstr "„startsel” и/или „stopsel”; шта могу да ураде специјални тастери" @@ -10425,6 +10448,9 @@ msgstr "максимална ширина искачућег менија" msgid "popup border style" msgstr "стил оквира искачућег прозора" +msgid "additional options for the popup menu" +msgstr "додатне опције за искачући мени" + msgid "user defined function for Insert mode completion" msgstr "кориснички дефинисана функција за довршавање у режиму Уметање" @@ -10640,6 +10666,9 @@ msgstr "" msgid "allow setting expression options from a modeline" msgstr "омогућавање постављања опција израза из режимске линије" +msgid "only allow safe options to be set from a modeline" +msgstr "из режимске линије могу да се поставе само безбедне опције" + msgid "number of lines to check for modelines" msgstr "број линија које се проверавају на постојање режимске линије" From ba85f88fe95db2daf3ea2542042af9e65308e2b8 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 1 May 2026 13:12:11 +0000 Subject: [PATCH 152/545] patch 9.2.0422: popup: leave stray char when scrollbar changes Problem: popup: leave stray char when scrollbar changes (Maxim Kim, after v9.2.0112) Solution: refresh popup mask when scrollbar visibility changes (Yasuhiro Matsumoto) popup_adjust_position() set popup_mask_refresh only on geometry changes, missing the case where w_has_scrollbar flips. After popup_settext() shrinks the buffer enough that the scrollbar disappears, the cell that held the old border / scrollbar was never repainted, leaving stray characters. fixes: #20092 closes: #20098 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/popupwin.c | 4 ++- ...t_popup_settext_scrollbar_disappear_1.dump | 15 +++++++++++ ...t_popup_settext_scrollbar_disappear_2.dump | 15 +++++++++++ src/testdir/test_popupwin.vim | 26 +++++++++++++++++++ src/version.c | 2 ++ 5 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 src/testdir/dumps/Test_popup_settext_scrollbar_disappear_1.dump create mode 100644 src/testdir/dumps/Test_popup_settext_scrollbar_disappear_2.dump diff --git a/src/popupwin.c b/src/popupwin.c index f920816338..103daccd29 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -1288,6 +1288,7 @@ popup_adjust_position(win_T *wp) int org_height = wp->w_height; int org_leftcol = wp->w_leftcol; int org_leftoff = wp->w_popup_leftoff; + int org_has_scrollbar = wp->w_has_scrollbar; int minwidth, minheight; int maxheight = Rows; int wantline = wp->w_wantline; // adjusted for textprop @@ -1830,7 +1831,8 @@ popup_adjust_position(win_T *wp) || org_leftcol != wp->w_leftcol || org_leftoff != wp->w_popup_leftoff || org_width != wp->w_width - || org_height != wp->w_height) + || org_height != wp->w_height + || org_has_scrollbar != wp->w_has_scrollbar) { redraw_win_later(wp, UPD_NOT_VALID); if (wp->w_popup_flags & POPF_ON_CMDLINE) diff --git a/src/testdir/dumps/Test_popup_settext_scrollbar_disappear_1.dump b/src/testdir/dumps/Test_popup_settext_scrollbar_disappear_1.dump new file mode 100644 index 0000000000..d11662fe75 --- /dev/null +++ b/src/testdir/dumps/Test_popup_settext_scrollbar_disappear_1.dump @@ -0,0 +1,15 @@ +> +0&#ffffff0@49 +|~+0#4040ff13&| @48 +|~| @2|╔+0#0000001#ffd7ff255|═@30|╗| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#0000001|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|║+0#0000001#ffd7ff255|h|e|l@1|o| |w|o|r|l|d| @18| +0#0000000#a8a8a8255|║+0#0000001#ffd7ff255| +0#4040ff13#ffffff0@12 +|~| @2|╚+0#0000001#ffd7ff255|═@30|╝| +0#4040ff13#ffffff0@12 +| +0#0000000&@31|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_popup_settext_scrollbar_disappear_2.dump b/src/testdir/dumps/Test_popup_settext_scrollbar_disappear_2.dump new file mode 100644 index 0000000000..52dc32b8d4 --- /dev/null +++ b/src/testdir/dumps/Test_popup_settext_scrollbar_disappear_2.dump @@ -0,0 +1,15 @@ +> +0&#ffffff0@49 +|~+0#4040ff13&| @48 +|~| @2|╔+0#0000001#ffd7ff255|═@29|╗| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255|s|h|o|r|t| @24|║| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255| +0#4040ff13&@29|║+0#0000001&| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255| +0#4040ff13&@29|║+0#0000001&| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255| +0#4040ff13&@29|║+0#0000001&| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255| +0#4040ff13&@29|║+0#0000001&| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255| +0#4040ff13&@29|║+0#0000001&| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255| +0#4040ff13&@29|║+0#0000001&| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255| +0#4040ff13&@29|║+0#0000001&| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255| +0#4040ff13&@29|║+0#0000001&| +0#4040ff13#ffffff0@13 +|~| @2|║+0#0000001#ffd7ff255| +0#4040ff13&@29|║+0#0000001&| +0#4040ff13#ffffff0@13 +|~| @2|╚+0#0000001#ffd7ff255|═@29|╝| +0#4040ff13#ffffff0@13 +|:+0#0000000&|c|a|l@1| |p|o|p|u|p|_|s|e|t@1|e|x|t|(|g|:|p|,| |[|'|s|h|o|r|t|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 4ce6a7158a..1bc9344df3 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -2563,6 +2563,32 @@ func Test_popup_settext() call StopVimInTerminal(buf) endfunc +func Test_popup_settext_scrollbar_disappear() + CheckScreendump + + let lines =<< trim END + let g:p = popup_create(repeat(['hello world'], 30), #{ + \ line: 3, + \ col: 5, + \ pos: 'topleft', + \ minheight: 10, + \ maxheight: 10, + \ minwidth: 30, + \ border: [1, 1, 1, 1], + \ }) + END + call writefile(lines, 'XtestPopupScrollDisappear', 'D') + let buf = RunVimInTerminal('-S XtestPopupScrollDisappear', #{rows: 15, cols: 50}) + call VerifyScreenDump(buf, 'Test_popup_settext_scrollbar_disappear_1', {}) + + " Shrinking the buffer makes the scrollbar disappear. The right border + " column must not leave stray characters where the scrollbar used to be. + call term_sendkeys(buf, ":call popup_settext(g:p, ['short'])\") + call VerifyScreenDump(buf, 'Test_popup_settext_scrollbar_disappear_2', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_popup_settext_getline() let id = popup_create('', #{ tabpage: 0 }) call popup_settext(id, ['a','b']) diff --git a/src/version.c b/src/version.c index 11ae9897aa..9a28bf20e1 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 422, /**/ 421, /**/ From 587447ec64ffd9e6eaba329ffd9d778159ea6e32 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 1 May 2026 13:25:31 +0000 Subject: [PATCH 153/545] patch 9.2.0423: popup: wrapped cmdline truncated with wildoptions=pum Problem: popup: wrapped cmdline truncated with wildoptions=pum Solution: Call msg_starthere() in redrawcmd() to reset lines_left before each redraw (Yasuhiro Matsumoto). redrawcmd() leaves lines_left at its previous value, which decrements across successive redraws (e.g. when wildtrigger() refreshes the popup on every keystroke) until 0, after which msg_no_more aborts drawing the wrapped cmdline. Call msg_starthere() to reset it. related: #20081 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/ex_getln.c | 2 ++ .../Test_wildtrigger_wrapped_cmdline_1.dump | 8 ++++++ src/testdir/test_cmdline.vim | 26 +++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 38 insertions(+) create mode 100644 src/testdir/dumps/Test_wildtrigger_wrapped_cmdline_1.dump diff --git a/src/ex_getln.c b/src/ex_getln.c index 1124f45865..e5a9ce776f 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -4122,6 +4122,8 @@ redrawcmd(void) sb_text_restart_cmdline(); msg_start(); + // Reset lines_left so a wrapped cmdline isn't truncated by msg_no_more. + msg_starthere(); redrawcmdprompt(); // Don't use more prompt, truncate the cmdline if it doesn't fit. diff --git a/src/testdir/dumps/Test_wildtrigger_wrapped_cmdline_1.dump b/src/testdir/dumps/Test_wildtrigger_wrapped_cmdline_1.dump new file mode 100644 index 0000000000..409cf54fa4 --- /dev/null +++ b/src/testdir/dumps/Test_wildtrigger_wrapped_cmdline_1.dump @@ -0,0 +1,8 @@ +|~+0#4040ff13#ffffff0| @28 +|~| @28 +|~| @28 +|~| @28 +|~| @28 +|~| @28 +|:+0#0000000&|e| |x@26 +@13> @16 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index ee5f668bb2..e793292ff7 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -5249,6 +5249,32 @@ func Test_wildtrigger_update_screen() call StopVimInTerminal(buf) endfunc +" Wrapped cmdline must not be truncated when wildtrigger() redraws on every +" keystroke. +func Test_wildtrigger_wrapped_cmdline() + CheckScreendump + + let lines =<< trim [SCRIPT] + set wildmenu wildmode=noselect:lastused,full wildoptions=pum + cnoremap =wildtrigger()[-1] + [SCRIPT] + call writefile(lines, 'XTest_wildtrigger_wrap', 'D') + let rows = 8 + let cols = 30 + let buf = RunVimInTerminal('-S XTest_wildtrigger_wrap', {'rows': rows, 'cols': cols}) + + call term_sendkeys(buf, ":e ") + for i in range(40) + call term_sendkeys(buf, "x\") + endfor + + call WaitForTermCurPosAndLinesToMatch(buf, [rows, ((3 + 40) - cols + 1)]) + call VerifyScreenDump(buf, 'Test_wildtrigger_wrapped_cmdline_1', {}) + + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) +endfunc + " Issue #17969: With 'noselect', the popup menu should appear next to the " environment variable being expanded. Disable 'showtail' when completing " file paths when 'noselect' is present. diff --git a/src/version.c b/src/version.c index 9a28bf20e1..dca5176357 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 423, /**/ 422, /**/ From ec8b8bd82a905f0faf1a73bf57381597f0e25654 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 1 May 2026 13:29:01 +0000 Subject: [PATCH 154/545] patch 9.2.0424: popup: flicker when wildtrigger() refreshes the popup menu Problem: popup: flicker when wildtrigger() refreshes the popup menu Solution: Wrap the pum teardown and cmdline redraw in synchronized terminal output (Yasuhiro Matsumoto). Reduces flicker when wildtrigger() refreshes the popup on every keystroke and the cmdline is wrapped: the un-scroll inside update_screen() and the re-scroll inside redrawcmd() are emitted as one atomic terminal update. closes: #20081 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/cmdexpand.c | 2 ++ src/version.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/cmdexpand.c b/src/cmdexpand.c index b7a17bbdda..a4891871f6 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -465,6 +465,7 @@ cmdline_pum_remove(cmdline_info_T *cclp UNUSED, int defer_redraw) RedrawingDisabled = 0; #endif + term_set_sync_output(TERM_SYNC_OUTPUT_ENABLE); pum_undisplay(); VIM_CLEAR(compl_match_array); compl_match_arraysize = 0; @@ -478,6 +479,7 @@ cmdline_pum_remove(cmdline_info_T *cclp UNUSED, int defer_redraw) else pum_call_update_screen(); redrawcmd(); + term_set_sync_output(TERM_SYNC_OUTPUT_DISABLE); // When a function is called (e.g. for 'foldtext') KeyTyped might be reset // as a side effect. diff --git a/src/version.c b/src/version.c index dca5176357..837b453d10 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 424, /**/ 423, /**/ From d25f8d1b2c6e0cbd390a36884c95edc88f1b3d69 Mon Sep 17 00:00:00 2001 From: Shougo Matsushita Date: Fri, 1 May 2026 14:54:56 +0000 Subject: [PATCH 155/545] patch 9.2.0425: Cannot silence undo/redo messages Problem: Cannot silence undo/redo messages Solution: Add "u" flag to 'shortmess' option (Shougo Matsushita). fixes: #20049 closes: #20107 Signed-off-by: Shougo Matsushita Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 5 ++- runtime/doc/tags | 1 + runtime/doc/version9.txt | 3 +- src/option.h | 3 +- src/testdir/test_messages.vim | 65 +++++++++++++++++++++++++++++++++++ src/undo.c | 20 +++++++---- src/version.c | 2 ++ 7 files changed, 89 insertions(+), 10 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 3576e174b7..d9dbcd8336 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 Apr 29 +*options.txt* For Vim version 9.2. Last change: 2026 May 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -8044,6 +8044,9 @@ A jump table for the options with a short description can be found at |Q_op|. search count statistics. The maximum limit can be set with the 'maxsearchcount' option, see also |searchcount()| function. + u don't give undo and redo messages like *shm-u* + "1 line less; before #1 1 second ago", "Already at oldest + change" or "Already at newest change" This gives you the opportunity to avoid that a change between buffers requires you to hit , but still gives as useful a message as diff --git a/runtime/doc/tags b/runtime/doc/tags index 3bc6f71554..4ad699ed55 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -10331,6 +10331,7 @@ shm-q options.txt /*shm-q* shm-r options.txt /*shm-r* shm-s options.txt /*shm-s* shm-t options.txt /*shm-t* +shm-u options.txt /*shm-u* shm-w options.txt /*shm-w* shm-x options.txt /*shm-x* short-name-changed version4.txt /*short-name-changed* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 9516ecd125..ca69850e05 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 Apr 29 +*version9.txt* For Vim version 9.2. Last change: 2026 May 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52621,6 +52621,7 @@ Other ~ - Added the "noinsert" value to the 'wildmode' option for symmetry with the 'completeopt' option - Channel can handle |Blob| messages |channel-open-options|. +- Added the "u" flag to 'shortmess' to silence undo/redo messages: |shm-u| Platform specific ~ ----------------- diff --git a/src/option.h b/src/option.h index cda49a4af5..a4359a2590 100644 --- a/src/option.h +++ b/src/option.h @@ -277,8 +277,9 @@ typedef enum { #define SHM_RECORDING 'q' // short recording message #define SHM_FILEINFO 'F' // no file info messages #define SHM_SEARCHCOUNT 'S' // no search stats: '[1/10]' +#define SHM_UNDO 'u' // undo messages #define SHM_POSIX "AS" // POSIX value -#define SHM_ALL "rmfixlnwaWtToOsAIcCqFS" // all possible flags for 'shm' +#define SHM_ALL "rmfixlnwaWtToOsAIcCqFSu" // all possible flags for 'shm' #define SHM_LEN 30 // max length of all flags together // plus a NUL character diff --git a/src/testdir/test_messages.vim b/src/testdir/test_messages.vim index 80996cfad6..2e5a16e4b8 100644 --- a/src/testdir/test_messages.vim +++ b/src/testdir/test_messages.vim @@ -837,4 +837,69 @@ func Test_fileinfo_after_last_bd() call StopVimInTerminal(buf) endfunc +func Test_undo_messages() + new + + " Normal undo/redo messages + redir => result + call setline(1, 'foo') + undo + undo + redo + redo + redir END + let msg_list = split(result, "\n") + call assert_match("^1 line less; before #1", msg_list[0]) + call assert_equal("Already at oldest change", msg_list[1]) + call assert_match("^1 more line; after #1", msg_list[2]) + call assert_equal("Already at newest change", msg_list[3]) + + " Ignore undo/redo messages + redir => result + set shortmess+=u + call setline(1, 'foo') + undo + undo + redo + redo + redir END + let msg_list = split(result, "\n") + call assert_equal([], msg_list) + set shortmess& + + " undo_time() path: :earlier and :later go through a separate + " message site than u_doit(); make sure SHM_UNDO suppresses it too. + enew! + call setline(1, 'a') + call setline(1, 'b') + call setline(1, 'c') + + redir => result + earlier 1 + earlier 999 + earlier 999 + later 1 + later 999 + redir END + let msg_list = split(result, "\n") + call assert_match('^1 line less; before #', msg_list[0]) + call assert_match('^1 changes; before #', msg_list[1]) + call assert_match('^1 changes; before #', msg_list[2]) + call assert_match('^1 more line; after #', msg_list[3]) + call assert_equal('Already at newest change', msg_list[4]) + + set shortmess+=u + redir => result + earlier 1 + earlier 999 + later 1 + later 999 + later 999 + redir END + call assert_equal([], split(result, "\n")) + + set shortmess& + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/undo.c b/src/undo.c index 170eaac476..ac38a8bf21 100644 --- a/src/undo.c +++ b/src/undo.c @@ -2262,7 +2262,8 @@ u_doit(int startcount) beep_flush(); if (count == startcount - 1) { - msg(_("Already at oldest change")); + if (!shortmess(SHM_UNDO)) + msg(_("Already at oldest change")); return; } break; @@ -2277,7 +2278,8 @@ u_doit(int startcount) beep_flush(); // nothing to redo if (count == startcount - 1) { - msg(_("Already at newest change")); + if (!shortmess(SHM_UNDO)) + msg(_("Already at newest change")); return; } break; @@ -2530,10 +2532,13 @@ undo_time( if (closest == closest_start) { - if (step < 0) - msg(_("Already at oldest change")); - else - msg(_("Already at newest change")); + if (!shortmess(SHM_UNDO)) + { + if (step < 0) + msg(_("Already at oldest change")); + else + msg(_("Already at newest change")); + } return; } @@ -2996,7 +3001,8 @@ u_undo_end( #endif if (global_busy // no messages now, wait until global is finished - || !messaging()) // 'lazyredraw' set, don't do messages now + || !messaging() // 'lazyredraw' set, don't do messages now + || shortmess(SHM_UNDO)) return; if (curbuf->b_ml.ml_flags & ML_EMPTY) diff --git a/src/version.c b/src/version.c index 837b453d10..4108665794 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 425, /**/ 424, /**/ From cf5d7102b9624f1bf230b6efad22f9bd0b39bd53 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 1 May 2026 16:01:03 +0000 Subject: [PATCH 156/545] patch 9.2.0426: tests: still some flaky screendump tests Problem: tests: still some flaky screendump tests (James McCoy) Solution: Replace flaky VerifyScreenDump checks with assert_* assertions for Test_visual_block_scroll and Test_scrolloffpad_with_folds, and remove the now-unused dump files, mark those tests as flaky (which happened previously for screendump tests automatically) (Yasuhiro Matsumoto). fixes: #20096 related: #20095 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- .../Test_display_visual_block_scroll.dump | 7 --- .../dumps/Test_scrolloffpad_folds_1.dump | 20 --------- .../dumps/Test_scrolloffpad_folds_2.dump | 20 --------- .../dumps/Test_scrolloffpad_folds_3.dump | 20 --------- src/testdir/test_plugin_matchparen.vim | 13 ++++-- src/testdir/test_scroll_opt.vim | 43 ++++++++++++------- src/version.c | 2 + 7 files changed, 39 insertions(+), 86 deletions(-) delete mode 100644 src/testdir/dumps/Test_display_visual_block_scroll.dump delete mode 100644 src/testdir/dumps/Test_scrolloffpad_folds_1.dump delete mode 100644 src/testdir/dumps/Test_scrolloffpad_folds_2.dump delete mode 100644 src/testdir/dumps/Test_scrolloffpad_folds_3.dump diff --git a/src/testdir/dumps/Test_display_visual_block_scroll.dump b/src/testdir/dumps/Test_display_visual_block_scroll.dump deleted file mode 100644 index 36eae0c024..0000000000 --- a/src/testdir/dumps/Test_display_visual_block_scroll.dump +++ /dev/null @@ -1,7 +0,0 @@ -|{+0#0000001#a8a8a8255| | +0#0000000#ffffff0@72 -|}+0#0000001#a8a8a8255| | +0#0000000#ffffff0@72 -|{+0#0000001#a8a8a8255| | +0#0000000#ffffff0@72 -|f+0#0000001#a8a8a8255| | +0#0000000#ffffff0@72 ->g| +0#0000001#a8a8a8255| +0#0000000#ffffff0@72 -|}| @73 -|-+2&&@1| |V|I|S|U|A|L| |L|I|N|E| |-@1| +0&&@29|7| @8|1@1|,|1| @9|B|o|t| diff --git a/src/testdir/dumps/Test_scrolloffpad_folds_1.dump b/src/testdir/dumps/Test_scrolloffpad_folds_1.dump deleted file mode 100644 index 1c64333006..0000000000 --- a/src/testdir/dumps/Test_scrolloffpad_folds_1.dump +++ /dev/null @@ -1,20 +0,0 @@ -|l+0&#ffffff0|i|n|e| |1@2| @69 -|l|i|n|e| |1@1|2| @69 -|l|i|n|e| |1@1|3| @69 -|l|i|n|e| |1@1|4| @69 -|l|i|n|e| |1@1|5| @69 -|l|i|n|e| |1@1|6| @69 -|l|i|n|e| |1@1|7| @69 -|l|i|n|e| |1@1|8| @69 -|l|i|n|e| |1@1|9| @69 ->l|i|n|e| |1|2|0| @69 -|~+0#4040ff13&| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -| +0#0000000&@59|1|2|0|,|1| @8|B|o|t| diff --git a/src/testdir/dumps/Test_scrolloffpad_folds_2.dump b/src/testdir/dumps/Test_scrolloffpad_folds_2.dump deleted file mode 100644 index 63ac801b4a..0000000000 --- a/src/testdir/dumps/Test_scrolloffpad_folds_2.dump +++ /dev/null @@ -1,20 +0,0 @@ -|l+0&#ffffff0|i|n|e| |5|1| @70 -|l|i|n|e| |5|2| @70 -|l|i|n|e| |5|3| @70 -|l|i|n|e| |5|4| @70 -|l|i|n|e| |5@1| @70 -|l|i|n|e| |5|6| @70 -|l|i|n|e| |5|7| @70 -|l|i|n|e| |5|8| @70 -|l|i|n|e| |5|9| @70 ->++0#0000e05#a8a8a8255|-@1| |5|1| |l|i|n|e|s|:| |l|i|n|e| |6|0|-@56 -|l+0#0000000#ffffff0|i|n|e| |1@2| @69 -|l|i|n|e| |1@1|2| @69 -|l|i|n|e| |1@1|3| @69 -|l|i|n|e| |1@1|4| @69 -|l|i|n|e| |1@1|5| @69 -|l|i|n|e| |1@1|6| @69 -|l|i|n|e| |1@1|7| @69 -|l|i|n|e| |1@1|8| @69 -|l|i|n|e| |1@1|9| @69 -@60|6|0|,|1| @9|9|8|%| diff --git a/src/testdir/dumps/Test_scrolloffpad_folds_3.dump b/src/testdir/dumps/Test_scrolloffpad_folds_3.dump deleted file mode 100644 index 1c64333006..0000000000 --- a/src/testdir/dumps/Test_scrolloffpad_folds_3.dump +++ /dev/null @@ -1,20 +0,0 @@ -|l+0&#ffffff0|i|n|e| |1@2| @69 -|l|i|n|e| |1@1|2| @69 -|l|i|n|e| |1@1|3| @69 -|l|i|n|e| |1@1|4| @69 -|l|i|n|e| |1@1|5| @69 -|l|i|n|e| |1@1|6| @69 -|l|i|n|e| |1@1|7| @69 -|l|i|n|e| |1@1|8| @69 -|l|i|n|e| |1@1|9| @69 ->l|i|n|e| |1|2|0| @69 -|~+0#4040ff13&| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -|~| @76 -| +0#0000000&@59|1|2|0|,|1| @8|B|o|t| diff --git a/src/testdir/test_plugin_matchparen.vim b/src/testdir/test_plugin_matchparen.vim index cc6513bb6f..136d55a31b 100644 --- a/src/testdir/test_plugin_matchparen.vim +++ b/src/testdir/test_plugin_matchparen.vim @@ -8,8 +8,8 @@ source util/screendump.vim " Test for scrolling that modifies buffer during visual block func Test_visual_block_scroll() - CheckScreendump - + CheckRunVimInTerminal + let g:test_is_flaky = 1 let lines =<< trim END source $VIMRUNTIME/plugin/matchparen.vim set scrolloff=1 @@ -23,8 +23,13 @@ func Test_visual_block_scroll() let buf = RunVimInTerminal('-S '.filename, #{rows: 7}) call term_sendkeys(buf, "V\\") - call WaitForAssert({-> assert_match('VISUAL.*\d\+\s\+\d', term_getline(buf, 7))}, 1000) - call VerifyScreenDump(buf, 'Test_display_visual_block_scroll', {}) + call WaitForAssert({-> assert_equal('{', trim(term_getline(buf, 1)))}, 1000) + call assert_equal('}', trim(term_getline(buf, 2))) + call assert_equal('{', trim(term_getline(buf, 3))) + call assert_equal('f', trim(term_getline(buf, 4))) + call assert_equal('g', trim(term_getline(buf, 5))) + call assert_equal('}', trim(term_getline(buf, 6))) + call assert_match('VISUAL LINE .*1,1\s\+Bot', term_getline(buf, 7)) call StopVimInTerminal(buf) endfunc diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim index 4d0e44c2ea..7ee9fa2d27 100644 --- a/src/testdir/test_scroll_opt.vim +++ b/src/testdir/test_scroll_opt.vim @@ -2038,9 +2038,9 @@ func Test_scrolloffpad_diff_eof_filler_behavior() endfunc func Test_scrolloffpad_with_folds() - CheckScreendump CheckRunVimInTerminal CheckFeature folding + let g:test_is_flaky = 1 let save_termwinsize = &termwinsize set termwinsize= @@ -2065,27 +2065,40 @@ func Test_scrolloffpad_with_folds() let buf = RunVimInTerminal('-S XScrolloffpadFolds', #{rows: 20, cols: 78}) - " Case 1: Jump to end-of-file - " With folds present, scrolloffpad should still - " keep the cursor positioned with padding below EOF + " Case 1: Jump to end-of-file. + " With folds present, scrolloffpad should still keep the cursor positioned + " with padding below EOF. call term_sendkeys(buf, "\:\normal! G\") call term_sendkeys(buf, "\") - call TermWait(buf) - call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_1', {}) - - " Case 2: Move to the folded line to ensure the fold is actually in view + call WaitForAssert({-> assert_equal('line 120', trim(term_getline(buf, 10)))}, + \ 1000) + call assert_equal('line 111', trim(term_getline(buf, 1))) + call assert_equal('line 119', trim(term_getline(buf, 9))) + call assert_equal('~', trim(term_getline(buf, 11))) + call assert_match('120,1\s\+Bot', term_getline(buf, 20)) + + " Case 2: Move to the folded line to ensure the fold is actually in view. call term_sendkeys(buf, "\:\normal! 60G\") call term_sendkeys(buf, "\") - call TermWait(buf) - call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_2', {}) - - " Case 3: Close the fold explicitly and go to EOF again - " Behavior should remain stable with closed folds + call WaitForAssert({-> assert_match('^\s*+-- 51 lines: line 60--', + \ term_getline(buf, 10))}, 1000) + call assert_equal('line 51', trim(term_getline(buf, 1))) + call assert_equal('line 59', trim(term_getline(buf, 9))) + call assert_equal('line 111', trim(term_getline(buf, 11))) + call assert_equal('line 119', trim(term_getline(buf, 19))) + call assert_match('60,1\s\+98%', term_getline(buf, 20)) + + " Case 3: Close the fold explicitly and go to EOF again. + " Behavior should remain stable with closed folds. call term_sendkeys(buf, "\:\normal! zc\") call term_sendkeys(buf, "\:\normal! G\") call term_sendkeys(buf, "\") - call TermWait(buf) - call VerifyScreenDump(buf, 'Test_scrolloffpad_folds_3', {}) + call WaitForAssert({-> assert_equal('line 120', trim(term_getline(buf, 10)))}, + \ 1000) + call assert_equal('line 111', trim(term_getline(buf, 1))) + call assert_equal('line 119', trim(term_getline(buf, 9))) + call assert_equal('~', trim(term_getline(buf, 11))) + call assert_match('120,1\s\+Bot', term_getline(buf, 20)) call StopVimInTerminal(buf) let &termwinsize = save_termwinsize diff --git a/src/version.c b/src/version.c index 4108665794..d79705029e 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 426, /**/ 425, /**/ From 7b218ae98c0f9a26ade91f0950e5c43d3c3c5b2b Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 1 May 2026 16:10:21 +0000 Subject: [PATCH 157/545] patch 9.2.0427: popup: opacity blend may leaks white bg color Problem: popup: opacity blend may leaks white bg color Solution: Add cterm color blending for 256 color terminals, use COLOR_INVALID() macro to check for invalid color (Yasuhiro Matsumoto) When a textprop highlight only set gui=undercurl/guisp (no fg/bg), the CTERMCOLOR sentinel was treated by hl_blend_attr() as a real near-white color, leaking white bg onto textprop-covered cells under an opacity popup or pum. Add a cterm color blending path that approximates blends in the xterm 256-color palette using the gui RGB when available, so opacity now has a visible effect even without 'termguicolors' (in 256-color terminals). Below 256 colors the blend is skipped. Also document the requirement (GUI, 'termguicolors', or 256-color terminal) and update existing pumopt/popupwin opacity screendumps to reflect the new blended output. closes: #20095 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 3 +- runtime/doc/popup.txt | 7 +- src/highlight.c | 321 +++++++++++++++--- .../dumps/Test_popupwin_opacity_hl_80.dump | 4 +- ...t_popupwin_opacity_textprop_undercurl.dump | 8 + .../dumps/Test_popupwin_opacity_zero_01.dump | 4 +- .../dumps/Test_popupwin_opacity_zero_02.dump | 4 +- src/testdir/dumps/Test_pumopt_opacity_50.dump | 4 +- .../dumps/Test_pumopt_opacity_text_attrs.dump | 6 +- ...est_pumopt_opacity_textprop_undercurl.dump | 20 ++ .../dumps/Test_pumopt_opacity_wide_bg.dump | 6 +- .../Test_pumopt_opacity_wide_bg_shifted.dump | 6 +- src/testdir/test_popup.vim | 34 ++ src/testdir/test_popupwin.vim | 34 ++ src/version.c | 2 + 15 files changed, 398 insertions(+), 65 deletions(-) create mode 100644 src/testdir/dumps/Test_popupwin_opacity_textprop_undercurl.dump create mode 100644 src/testdir/dumps/Test_pumopt_opacity_textprop_undercurl.dump diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index d9dbcd8336..b0f3f1c72b 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -7007,7 +7007,8 @@ A jump table for the options with a short description can be found at |Q_op|. of 'fillchars' option. opacity:{n} opacity percentage 0-100 (default 100). When less than 100, background content shows - through the popup menu. + through the popup menu. Requires the GUI, + 'termguicolors', or a 256-color terminal. Flags (no value): margin adds one-cell spacing inside the left and diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt index 6b99ac47ee..250127b9b6 100644 --- a/runtime/doc/popup.txt +++ b/runtime/doc/popup.txt @@ -1,4 +1,4 @@ -*popup.txt* For Vim version 9.2. Last change: 2026 Apr 06 +*popup.txt* For Vim version 9.2. Last change: 2026 May 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1067,8 +1067,9 @@ The opacity value ranges from 0 to 100: 1-99 Partially transparent - the popup background is blended with the underlying text, making both partially visible. -The transparency effect requires using the GUI or having 'termguicolors' -enabled in the terminal. Without it, the opacity setting has no effect. +The transparency effect requires using the GUI, having 'termguicolors' +enabled, or running in a 256-color terminal. On terminals with fewer +than 256 colors the opacity setting has no effect. When a popup is transparent: - The popup's background color is blended with the background text diff --git a/src/highlight.c b/src/highlight.c index 67b551c67e..b21e489e61 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -3126,9 +3126,19 @@ hl_combine_attr(int char_attr, int prim_attr) return get_attr_entry(&term_attr_table, &new_en); } -#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) +// ANSI color order: Black, Red, Green, Yellow, Blue, Magenta, +// Cyan, White, then bright variants. Approximate RGB values used when +// only a cterm color number is known (no guifg/guibg). Real terminal +// palettes may differ if the user reconfigured their emulator, but +// these are reasonable xterm-ish defaults. +static const long_u cterm_color_16[16] = { + 0x000000, 0xc00000, 0x008000, 0x808000, + 0x0000c0, 0xc000c0, 0x004080, 0xc0c0c0, + 0x808080, 0xff8080, 0x00ff00, 0xffff00, + 0x6060ff, 0xff40ff, 0x00ffff, 0xffffff +}; -# ifdef FEAT_TERMGUICOLORS +#ifdef FEAT_TERMGUICOLORS /* * Convert a cterm color number (1-16) to an RGB value. * Used as a fallback when 'termguicolors' is set but only cterm colors are @@ -3138,21 +3148,156 @@ hl_combine_attr(int char_attr, int prim_attr) static guicolor_T cterm_color_to_rgb(int color_nr) { - // ANSI color order: Black, Red, Green, Yellow, Blue, Magenta, - // Cyan, White, then bright variants. - static const guicolor_T cterm_color_16[16] = { - 0x000000, 0xc00000, 0x008000, 0x808000, - 0x0000c0, 0xc000c0, 0x004080, 0xc0c0c0, - 0x808080, 0xff8080, 0x00ff00, 0xffff00, - 0x6060ff, 0xff40ff, 0x00ffff, 0xffffff - }; - if (color_nr < 1 || color_nr > 16) return INVALCOLOR; - return cterm_color_16[color_nr - 1]; + return (guicolor_T)cterm_color_16[color_nr - 1]; +} +#endif + +/* + * Convert an xterm 256-color index (0-255) to an approximate RGB triple. + * Uses the standard xterm palette: 0-15 ANSI (cterm_color_16), 16-231 + * 6x6x6 cube, 232-255 grayscale ramp. + */ + static void +cterm_idx_to_rgb(int idx, int *r, int *g, int *b) +{ + static const int cube[] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF }; + + if (idx < 0 || idx > 255) + { + *r = *g = *b = 0; + return; + } + if (idx < 16) + { + long_u rgb = cterm_color_16[idx]; + *r = (rgb >> 16) & 0xFF; + *g = (rgb >> 8) & 0xFF; + *b = rgb & 0xFF; + } + else if (idx < 232) + { + int n = idx - 16; + *r = cube[n / 36 % 6]; + *g = cube[n / 6 % 6]; + *b = cube[n % 6]; + } + else + { + int v = 8 + (idx - 232) * 10; + *r = *g = *b = v; + } } -# endif +/* + * Approximate an RGB triple to the nearest xterm 256-color index. + * Searches the 6x6x6 cube and the grayscale ramp; returns 0-255. + */ + static int +rgb_to_cterm_idx(int r, int g, int b) +{ + static const int cube[] = { 0x00, 0x5F, 0x87, 0xAF, 0xD7, 0xFF }; + int best = 0; + long best_d = -1; + int i; + + // Search the 6x6x6 cube. + for (i = 16; i < 232; ++i) + { + int n = i - 16; + int cr = cube[n / 36 % 6]; + int cg = cube[n / 6 % 6]; + int cb = cube[n % 6]; + long d = (cr - r) * (cr - r) + (cg - g) * (cg - g) + (cb - b) * (cb - b); + if (best_d < 0 || d < best_d) + { + best_d = d; + best = i; + } + } + // Search the grayscale ramp. + for (i = 232; i < 256; ++i) + { + int v = 8 + (i - 232) * 10; + long d = (v - r) * (v - r) + (v - g) * (v - g) + (v - b) * (v - b); + if (d < best_d) + { + best_d = d; + best = i; + } + } + return best; +} + +/* + * Resolve a color to an RGB triple. Prefer the gui RGB value (set by + * guifg/guibg) when valid, otherwise convert the cterm 256-color index. + * Returns true on success; false when no color is defined. + * cterm_c is 1-based (0 means "no color"). + */ + static bool +resolve_color_to_rgb(int cterm_c, guicolor_T rgb UNUSED, int *r, int *g, int *b) +{ +#ifdef FEAT_TERMGUICOLORS + if (!COLOR_INVALID(rgb)) + { + *r = (rgb >> 16) & 0xFF; + *g = (rgb >> 8) & 0xFF; + *b = rgb & 0xFF; + return true; + } +#endif + if (cterm_c > 0) + { + cterm_idx_to_rgb(cterm_c - 1, r, g, b); + return true; + } + return false; +} + +/* + * Blend two colors expressed as (cterm 256 index, gui RGB) pairs and + * return the nearest 1-based cterm 256-color index. Prefers the gui + * RGB so highlight definitions like "guibg=#2D2A3D" without ctermbg + * still produce a meaningful blend in 256-color terminals. + * + * "default_rgb" is used as the underlying color when neither under_c + * nor under_rgb is set; pass 0xFFFFFF for fg-style blends (so text + * fades toward white) or 0x000000 for bg-style blends (so the popup + * fades toward terminal default dark). + * + * Requires t_colors >= 256; otherwise returns popup_c unchanged. + */ + static int +blend_cterm_colors(int popup_c, guicolor_T popup_rgb, + int under_c, guicolor_T under_rgb, + int default_rgb, + int blend_val) +{ + int pr, pg, pb, ur, ug, ub, r, g, b; + + if (t_colors < 256) + return popup_c; + if (!resolve_color_to_rgb(popup_c, popup_rgb, &pr, &pg, &pb)) + return under_c; + if (blend_val <= 0) + return rgb_to_cterm_idx(pr, pg, pb) + 1; + if (!resolve_color_to_rgb(under_c, under_rgb, &ur, &ug, &ub)) + { + ur = (default_rgb >> 16) & 0xFF; + ug = (default_rgb >> 8) & 0xFF; + ub = default_rgb & 0xFF; + } + if (blend_val >= 100) + return rgb_to_cterm_idx(ur, ug, ub) + 1; + r = pr + (ur - pr) * blend_val / 100; + g = pg + (ug - pg) * blend_val / 100; + b = pb + (ub - pb) * blend_val / 100; + return rgb_to_cterm_idx(r, g, b) + 1; +} + +#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) /* * Blend two RGB colors based on blend value (0-100). * blend: 0=use popup color, 100=use background color @@ -3163,7 +3308,9 @@ blend_colors(guicolor_T popup_color, guicolor_T bg_color, int blend_val) { int r1, g1, b1, r2, g2, b2, r, g, b; - if (popup_color == INVALCOLOR) + // CTERMCOLOR is a sentinel meaning "use the cterm color"; for blending + // it has no real RGB so treat it like INVALCOLOR. + if (COLOR_INVALID(popup_color)) return INVALCOLOR; // Fully transparent: use underlying color as-is. @@ -3174,7 +3321,7 @@ blend_colors(guicolor_T popup_color, guicolor_T bg_color, int blend_val) g1 = (popup_color >> 8) & 0xFF; b1 = popup_color & 0xFF; - if (bg_color == INVALCOLOR) + if (COLOR_INVALID(bg_color)) { // Background color unknown: fade popup color to black as blend increases // This makes background text more visible at high blend values @@ -3253,11 +3400,13 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED) else { // blend_fg=FALSE: popup text is opaque. Replace the - // underlying cell's attribute flags and fg with the - // popup's, so the underlying syntax highlighting does - // not bleed through. + // underlying cell's attribute flags, fg and special + // color with the popup's, so the underlying syntax + // highlighting and any decoration (textprop undercurl, + // ...) do not bleed through. new_en.ae_attr = popup_aep->ae_attr; new_en.ae_u.gui.fg_color = popup_aep->ae_u.gui.fg_color; + new_en.ae_u.gui.sp_color = popup_aep->ae_u.gui.sp_color; } // Blend background color: blend popup bg toward underlying bg if (popup_aep->ae_u.gui.bg_color != INVALCOLOR) @@ -3301,18 +3450,60 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED) if (!blend_fg) { // blend_fg=FALSE: popup text is opaque. Replace the - // underlying cell's attribute flags and fg with the - // popup's, so the underlying syntax highlighting does - // not bleed through. + // underlying cell's attribute flags, fg and underline + // color with the popup's, so the underlying syntax + // highlighting and any decoration (textprop undercurl, + // ...) do not bleed through. When the popup has no fg + // (e.g. "guifg=NONE") fall back to Normal's fg so the + // text is still readable instead of taking on whatever + // the underlying cell happened to have. new_en.ae_attr = popup_aep->ae_attr; - new_en.ae_u.cterm.fg_color = popup_aep->ae_u.cterm.fg_color; + if (popup_aep->ae_u.cterm.fg_color > 0) + new_en.ae_u.cterm.fg_color = + popup_aep->ae_u.cterm.fg_color; + else if (cterm_normal_fg_color > 0) + new_en.ae_u.cterm.fg_color = cterm_normal_fg_color; + else + new_en.ae_u.cterm.fg_color = 16; // white-ish + new_en.ae_u.cterm.ul_color = popup_aep->ae_u.cterm.ul_color; +#ifdef FEAT_TERMGUICOLORS + new_en.ae_u.cterm.ul_rgb = popup_aep->ae_u.cterm.ul_rgb; +#endif + } + else + { + // blend_fg=TRUE: fade underlying fg toward popup bg in + // the 256-color palette. Used when the popup is over a + // cell rendered with cterm colors (no termguicolors RGB). + int under_fg = (char_aep != NULL) + ? char_aep->ae_u.cterm.fg_color : 0; + guicolor_T under_fg_rgb = INVALCOLOR; + guicolor_T popup_bg_rgb = INVALCOLOR; +#ifdef FEAT_TERMGUICOLORS + if (char_aep != NULL) + under_fg_rgb = char_aep->ae_u.cterm.fg_rgb; + popup_bg_rgb = popup_aep->ae_u.cterm.bg_rgb; +#endif + new_en.ae_u.cterm.fg_color = blend_cterm_colors( + popup_aep->ae_u.cterm.bg_color, popup_bg_rgb, + under_fg, under_fg_rgb, 0xFFFFFF, blend); + } + // Approximate cterm bg by blending with the underlying bg + // in the 256-color palette and mapping to the nearest entry. + { + int under_bg = (char_aep != NULL) + ? char_aep->ae_u.cterm.bg_color : 0; + guicolor_T under_bg_rgb = INVALCOLOR; + guicolor_T popup_bg_rgb = INVALCOLOR; +#ifdef FEAT_TERMGUICOLORS + if (char_aep != NULL) + under_bg_rgb = char_aep->ae_u.cterm.bg_rgb; + popup_bg_rgb = popup_aep->ae_u.cterm.bg_rgb; +#endif + new_en.ae_u.cterm.bg_color = blend_cterm_colors( + popup_aep->ae_u.cterm.bg_color, popup_bg_rgb, + under_bg, under_bg_rgb, 0x000000, blend); } - else if (popup_aep->ae_u.cterm.fg_color > 0) - // Blend foreground color - new_en.ae_u.cterm.fg_color = popup_aep->ae_u.cterm.fg_color; - // Use popup background color (cterm colors don't support blending) - if (popup_aep->ae_u.cterm.bg_color > 0) - new_en.ae_u.cterm.bg_color = popup_aep->ae_u.cterm.bg_color; #ifdef FEAT_TERMGUICOLORS // Blend RGB colors for termguicolors mode. // Fall back to cterm color converted to RGB when @@ -3336,24 +3527,38 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED) if (popup_bg != INVALCOLOR) { int base_fg = 0xFFFFFF; + // CTERMCOLOR is a sentinel meaning "use the cterm + // color"; treat it as no underlying color so it is + // not blended in as a real near-white pixel. if (char_aep != NULL - && char_aep->ae_u.cterm.fg_rgb != INVALCOLOR) + && !COLOR_INVALID(char_aep->ae_u.cterm.fg_rgb)) base_fg = char_aep->ae_u.cterm.fg_rgb; new_en.ae_u.cterm.fg_rgb = blend_colors( base_fg, popup_bg, blend); } } else + { // blend_fg=FALSE: popup text is opaque. Replace fg - // with popup's (even INVALCOLOR) so the underlying - // syntax highlighting fg does not bleed. ae_attr - // was already set above for this branch. - new_en.ae_u.cterm.fg_rgb = popup_fg; + // with popup's so the underlying syntax highlighting + // fg does not bleed. ae_attr was already set above + // for this branch. When the popup has no fg fall + // back to Normal's fg, then to white, so the text + // stays readable instead of rendering as default + // (which can be black on dark themes). + if (!COLOR_INVALID(popup_fg)) + new_en.ae_u.cterm.fg_rgb = popup_fg; + else if (!COLOR_INVALID(cterm_normal_fg_gui_color)) + new_en.ae_u.cterm.fg_rgb = cterm_normal_fg_gui_color; + else + new_en.ae_u.cterm.fg_rgb = 0xFFFFFF; + } if (popup_bg != INVALCOLOR) { // Blend popup bg toward underlying bg guicolor_T underlying_bg = INVALCOLOR; - if (char_aep != NULL) + if (char_aep != NULL + && !COLOR_INVALID(char_aep->ae_u.cterm.bg_rgb)) underlying_bg = char_aep->ae_u.cterm.bg_rgb; new_en.ae_u.cterm.bg_rgb = blend_colors( popup_bg, underlying_bg, blend); @@ -3463,21 +3668,48 @@ hl_pum_blend_attr(int char_attr, int popup_attr, int blend UNUSED) popup_aep = syn_cterm_attr2entry(popup_attr); if (popup_aep != NULL) { - // Blend cterm fg: use popup bg (hides text when opaque) - if (popup_aep->ae_u.cterm.fg_color > 0) - new_en.ae_u.cterm.fg_color = - popup_aep->ae_u.cterm.fg_color; - // Use popup cterm bg. - if (popup_aep->ae_u.cterm.bg_color > 0) - new_en.ae_u.cterm.bg_color = - popup_aep->ae_u.cterm.bg_color; + // Blend cterm fg: pum_bg toward underlying_fg in the + // 256-color palette (mirrors the fg_rgb blend below). + { + int under_fg = (char_aep != NULL) + ? char_aep->ae_u.cterm.fg_color : 0; + guicolor_T under_fg_rgb = INVALCOLOR; + guicolor_T popup_bg_rgb = INVALCOLOR; +#ifdef FEAT_TERMGUICOLORS + if (char_aep != NULL) + under_fg_rgb = char_aep->ae_u.cterm.fg_rgb; + popup_bg_rgb = popup_aep->ae_u.cterm.bg_rgb; +#endif + new_en.ae_u.cterm.fg_color = blend_cterm_colors( + popup_aep->ae_u.cterm.bg_color, popup_bg_rgb, + under_fg, under_fg_rgb, 0xFFFFFF, blend); + } + // Approximate cterm bg by blending with the underlying bg + // in the 256-color palette and mapping to the nearest entry. + { + int under_bg = (char_aep != NULL) + ? char_aep->ae_u.cterm.bg_color : 0; + guicolor_T under_bg_rgb = INVALCOLOR; + guicolor_T popup_bg_rgb = INVALCOLOR; +#ifdef FEAT_TERMGUICOLORS + if (char_aep != NULL) + under_bg_rgb = char_aep->ae_u.cterm.bg_rgb; + popup_bg_rgb = popup_aep->ae_u.cterm.bg_rgb; +#endif + new_en.ae_u.cterm.bg_color = blend_cterm_colors( + popup_aep->ae_u.cterm.bg_color, popup_bg_rgb, + under_bg, under_bg_rgb, 0x000000, blend); + } #ifdef FEAT_TERMGUICOLORS // Blend fg_rgb: pum_bg toward underlying_fg. + // CTERMCOLOR is a sentinel meaning "use the cterm color"; + // treat it as no underlying color so it is not blended in + // as a real near-white pixel. if (popup_aep->ae_u.cterm.bg_rgb != INVALCOLOR) { int base_fg = 0xFFFFFF; if (char_aep != NULL - && char_aep->ae_u.cterm.fg_rgb != INVALCOLOR) + && !COLOR_INVALID(char_aep->ae_u.cterm.fg_rgb)) base_fg = char_aep->ae_u.cterm.fg_rgb; new_en.ae_u.cterm.fg_rgb = blend_colors( popup_aep->ae_u.cterm.bg_rgb, base_fg, blend); @@ -3486,7 +3718,8 @@ hl_pum_blend_attr(int char_attr, int popup_attr, int blend UNUSED) if (popup_aep->ae_u.cterm.bg_rgb != INVALCOLOR) { guicolor_T underlying_bg = INVALCOLOR; - if (char_aep != NULL) + if (char_aep != NULL + && !COLOR_INVALID(char_aep->ae_u.cterm.bg_rgb)) underlying_bg = char_aep->ae_u.cterm.bg_rgb; new_en.ae_u.cterm.bg_rgb = blend_colors( popup_aep->ae_u.cterm.bg_rgb, diff --git a/src/testdir/dumps/Test_popupwin_opacity_hl_80.dump b/src/testdir/dumps/Test_popupwin_opacity_hl_80.dump index 89700f9c40..d197c8dacf 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_hl_80.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_hl_80.dump @@ -1,7 +1,7 @@ >1+0&#ffffff0| @73 |2| @73 -|3| @7|f+0#ff404010#5fd7ff255|o@1| @1|b+0#0000000&|a|r| +0&#ffffff0@57 -|4| @7|b+0fd7ff255|a|z| @4| +0&#ffffff0@57 +|3| @7|f+0#ff404010#5fafd7255|o@1| +0#87d7ff255&@1|b+0#ffffff16&|a|r| +0#0000000#ffffff0@57 +|4| @7|b+0#ffffff16#5fafd7255|a|z| +0#87d7ff255&@4| +0#0000000#ffffff0@57 |5| @73 |6| @73 |7| @73 diff --git a/src/testdir/dumps/Test_popupwin_opacity_textprop_undercurl.dump b/src/testdir/dumps/Test_popupwin_opacity_textprop_undercurl.dump new file mode 100644 index 0000000000..2d29cfdeaf --- /dev/null +++ b/src/testdir/dumps/Test_popupwin_opacity_textprop_undercurl.dump @@ -0,0 +1,8 @@ +>a+0&#ffffff0@2| |b@2| |c|P+0#e5e9f0255#03050b255|O|P|U|P|d+0#61646f255&|C+0#e5e9f0255&|O|N|T|E|N|T|f+0#61646f255&| |g@2| |h@2| @7| +0#0000000#ffffff0@10 +|i@2| |j@2| |k|k+0#61646f255#03050b255@1| |l@2| |m@2| |n@2| |o@2| |p@2| @7| +0#0000000#ffffff0@10 +|q@2| |r@2| |s|s+0#61646f255#03050b255@1| |m+0#e5e9f0255&|i|d@1|l|e|u+0#61646f255&| |v@2| |w@2| |x@2| @7| +0#0000000#ffffff0@10 +|y@2| |z@2| |1@2| |2@2| |3@2| |4@2| |5@2| |6@2| @18 +|~+0#0000ff255&| @48 +|~| @48 +|~| @48 +| +0#0000000&@31|1|,|1| @10|A|l@1| diff --git a/src/testdir/dumps/Test_popupwin_opacity_zero_01.dump b/src/testdir/dumps/Test_popupwin_opacity_zero_01.dump index 17aa447f86..1209af25c5 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_zero_01.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_zero_01.dump @@ -1,7 +1,7 @@ >b+0&#ffffff0|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 |b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 -|b|a|c|k|b|l|u|e|n|p|o|p|u|p|t| |h|e|r|e| @54 -|b|a|c|k|g|r|o|r|e|d| |p|o|p|u|p|h|e|r|e| @54 +|b|a|c|k|b+0#ffffff16#00005f255|l|u|e|n+0#8787d7255&|p+0#ffffff16&|o|p|u|p|t+0#0000000#ffffff0| |h|e|r|e| @54 +|b|a|c|k|g|r|o|r+0#ffffff16#000000255|e|d| +0#0000000#ffffff0|p+0#ffffff16#000000255|o|p|u|p|h+0#0000000#ffffff0|e|r|e| @54 |b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 |b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 |b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 diff --git a/src/testdir/dumps/Test_popupwin_opacity_zero_02.dump b/src/testdir/dumps/Test_popupwin_opacity_zero_02.dump index 03b9f4a59f..c724edb7fe 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_zero_02.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_zero_02.dump @@ -1,7 +1,7 @@ >b+0&#ffffff0|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 |b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 -|b|a|c|k|b|l|u|e|n|p|o|p|u|p|t| |h|e|r|e| @54 -|b|a|c|k|g|r|o|r|e|d| |p|o|p|u|p|h|e|r|e| @54 +|b|a|c|k|b+0#ffffff16#00005f255|l|u|e|n+0#8787d7255&|p+0#ffffff16&|o|p|u|p|t+0#0000000#ffffff0| |h|e|r|e| @54 +|b|a|c|k|g|r|o|r+0#ffffff16#000000255|e|d| +0#0000000#ffffff0|p+0#ffffff16#000000255|o|p|u|p|h+0#0000000#ffffff0|e|r|e| @54 |b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 |b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 |b|a|c|k|g|r|o|u|n|d| |t|e|x|t| |h|e|r|e| @54 diff --git a/src/testdir/dumps/Test_pumopt_opacity_50.dump b/src/testdir/dumps/Test_pumopt_opacity_50.dump index d9e44da0f7..4490b1ef1d 100644 --- a/src/testdir/dumps/Test_pumopt_opacity_50.dump +++ b/src/testdir/dumps/Test_pumopt_opacity_50.dump @@ -13,8 +13,8 @@ |B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G|R|O|U|N|D|B|A|C|K|G |R|O|U|N|D| @69 |h|e|l@1|o> @69 -|h+0#0000001#e0e0e08|e|l@1|o| @9| +0#4040ff13#ffffff0@59 -|h+0#0000001#ffd7ff255|e|l|p| @10| +0#4040ff13#ffffff0@59 +|h+0#0000001#5f5f5f255|e|l@1|o| +0#5f5fd7255&@9| +0#4040ff13#ffffff0@59 +|h+0#0000001#875f87255|e|l|p| +0#875fff255&@10| +0#4040ff13#ffffff0@59 |~| @73 |~| @73 |-+2#0000000&@1| |K|e|y|w|o|r|d| |c|o|m|p|l|e|t|i|o|n| |(|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |2| +0#0000000&@33 diff --git a/src/testdir/dumps/Test_pumopt_opacity_text_attrs.dump b/src/testdir/dumps/Test_pumopt_opacity_text_attrs.dump index 93ea3a5a4a..0911a881c9 100644 --- a/src/testdir/dumps/Test_pumopt_opacity_text_attrs.dump +++ b/src/testdir/dumps/Test_pumopt_opacity_text_attrs.dump @@ -1,7 +1,7 @@ |ほ*0&#ffffff0|げ> +&@70 -|ほ*0#0000001#ffff4012|げ|ほ|げ|ほ|げ|漢*4&&| +&| +4#e000e06#ffffff0|テ*&|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 -|ふ*0#ffffff16#0000e05|が|漢|字|ほ|げ|漢*4&&| +&| +4#e000e06#ffffff0|テ*&|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 -|カ*0#ffffff16#0000e05|タ|カ|ナ|候|補|漢*4&&| +&| +4#e000e06#ffffff0|テ*&|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ほ*0#0000001#875f00255|げ|ほ*0#ffd787255&|げ|ほ|げ|漢*4#ff875f255&| +&| +4#e000e06#ffffff0|テ*&|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|ふ*0#ffffff16#00005f255|が|漢|字|ほ*0#87afaf255&|げ|漢*4#875faf255&| +&| +4#e000e06#ffffff0|テ*&|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 +|カ*0#ffffff16#00005f255|タ|カ|ナ|候|補|漢*4#875faf255&| +&| +4#e000e06#ffffff0|テ*&|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 |ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 |ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 |ほ*&|げ|ほ|げ|ほ|げ|漢*4#e000e06&|字|テ|ス|ト|あ*0#0000000&|い|う|え|お|カ|タ|カ|ナ| +&@34 diff --git a/src/testdir/dumps/Test_pumopt_opacity_textprop_undercurl.dump b/src/testdir/dumps/Test_pumopt_opacity_textprop_undercurl.dump new file mode 100644 index 0000000000..8b9b0a0913 --- /dev/null +++ b/src/testdir/dumps/Test_pumopt_opacity_textprop_undercurl.dump @@ -0,0 +1,20 @@ +|p+0&#ffffff0|o|p|u|p|-|i|t|e|m|-|1> @62 +|p+0#000000255#5f5f5f255|o|p|u|p|-|i|t|e|m|-|1|d+0#dedede255&@2| +0#0000000#ffffff0|e@2| |f@2| |g@2| |h@2| @43 +|p+0#000000255#7f457f255|o|p|u|p|-|i|t|e|m|-|2|d+0#ffc5ff255&@2| +0#0000000#ffffff0|e@2| |f@2| |g@2| |h@2| @43 +|p+0#000000255#7f457f255|o|p|u|p|-|i|t|e|m|-|3|d+0#ffc5ff255&@2| +0#0000000#ffffff0|e@2| |f@2| |g@2| |h@2| @43 +|a@2| |b@2| |c@2| |d@2| |e@2| |f@2| |g@2| |h@2| @43 +|a@2| |b@2| |c@2| |d@2| |e@2| |f@2| |g@2| |h@2| @43 +|a@2| |b@2| |c@2| |d@2| |e@2| |f@2| |g@2| |h@2| @43 +|a@2| |b@2| |c@2| |d@2| |e@2| |f@2| |g@2| |h@2| @43 +|a@2| |b@2| |c@2| |d@2| |e@2| |f@2| |g@2| |h@2| @43 +|~+0#0000ff255&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|-+2#0000000&@1| |I|N|S|E|R|T| |-@1| +0&&@44|1|,|1| @10|A|l@1| diff --git a/src/testdir/dumps/Test_pumopt_opacity_wide_bg.dump b/src/testdir/dumps/Test_pumopt_opacity_wide_bg.dump index ed52522f01..87c7bd6710 100644 --- a/src/testdir/dumps/Test_pumopt_opacity_wide_bg.dump +++ b/src/testdir/dumps/Test_pumopt_opacity_wide_bg.dump @@ -1,8 +1,8 @@ |ほ*0&#ffffff0|げ> +&@70 |╭+0#0000001#ffd7ff255|─@15|╮|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 -|│+0#0000001#ffd7ff255| +0&#e0e0e08|ほ*&|げ@1|ほ|げ|漢|字| +&|│+0&#ffd7ff255|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 -|│+0#0000001#ffd7ff255| |ふ*&|が|漢|字|げ|漢|字| +&|│|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 -|│+0#0000001#ffd7ff255| |カ*&|タ|カ|ナ|候|補|字| +&|│|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|│+0#0000001#ffd7ff255| +0f5f5f255|ほ*&|げ|げ*0#dadada255&|ほ|げ|漢|字| +&|│+0#0000001#ffd7ff255|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|│+0#0000001#ffd7ff255| +0ͫf87255|ふ*&|が|漢|字|げ*0#ffd7ff255&|漢|字| +&|│+0#0000001#ffd7ff255|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|│+0#0000001#ffd7ff255| +0ͫf87255|カ*&|タ|カ|ナ|候|補|字*0#ffd7ff255&| +&|│+0#0000001#ffd7ff255|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 |╰+0#0000001#ffd7ff255|─@15|╯|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 |ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 |ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 diff --git a/src/testdir/dumps/Test_pumopt_opacity_wide_bg_shifted.dump b/src/testdir/dumps/Test_pumopt_opacity_wide_bg_shifted.dump index c145bf0800..ef1b9acf82 100644 --- a/src/testdir/dumps/Test_pumopt_opacity_wide_bg_shifted.dump +++ b/src/testdir/dumps/Test_pumopt_opacity_wide_bg_shifted.dump @@ -1,8 +1,8 @@ |ほ*0&#ffffff0|げ> +&@70 |╭+0#0000001#ffd7ff255|─@15|╮|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 -|│+0#0000001#ffd7ff255| +0&#e0e0e08|ほ*&|げ| +&|げ*&|ほ|げ|漢|字|│+0&#ffd7ff255| +0#0000000#ffffff0|ス*&|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 -|│+0#0000001#ffd7ff255| |ふ*&|が|漢|字|げ|漢|字| +&|│|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 -|│+0#0000001#ffd7ff255| |カ*&|タ|カ|ナ|候|補| +&|字*&|│+&| +0#0000000#ffffff0|ス*&|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|│+0#0000001#ffd7ff255| +0f5f5f255|ほ*&|げ| +0#dadada255&|げ*&|ほ|げ|漢|字|│+0#0000001#ffd7ff255| +0#0000000#ffffff0|ス*&|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 +|│+0#0000001#ffd7ff255| +0ͫf87255|ふ*&|が|漢|字|げ*0#ffd7ff255&|漢|字| +&|│+0#0000001#ffd7ff255|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 +|│+0#0000001#ffd7ff255| +0ͫf87255|カ*&|タ|カ|ナ|候|補| +0#ffd7ff255&|字*&|│+0#0000001#ffd7ff255| +0#0000000#ffffff0|ス*&|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 |╰+0#0000001#ffd7ff255|─@15|╯|ス*0#0000000#ffffff0|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 |a|ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@33 |ほ*&|げ|ほ|げ|ほ|げ|漢|字|テ|ス|ト|あ|い|う|え|お|カ|タ|カ|ナ| +&@34 diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim index 3362cd14f0..2ceaec945a 100644 --- a/src/testdir/test_popup.vim +++ b/src/testdir/test_popup.vim @@ -2474,6 +2474,40 @@ func Test_pumopt_opacity_wide_bg() call StopVimInTerminal(buf) endfunc +" hl_pum_blend_attr() treated the CTERMCOLOR sentinel as a real near-white +" color, leaking white bg onto textprop-covered cells under pum opacity. +" Triggered by a textprop hl that only sets guisp. +func Test_pumopt_opacity_textprop_undercurl() + CheckScreendump + let lines =<< trim END + set termguicolors + set t_Cs= t_Ce= + set pumopt=opacity:50 + set completeopt=menu + call setline(1, '') + for i in range(8) + call append(line('$'), 'aaa bbb ccc ddd eee fff ggg hhh') + endfor + hi MyError guisp=#ec7279 + call prop_type_add('mytype', #{highlight: 'MyError', combine: 1}) + for s:l in range(2, 8) + call prop_add(s:l, 5, #{type: 'mytype', length: 20}) + endfor + normal gg + inoremap call complete(col('.'), + \ ['popup-item-1', 'popup-item-2', 'popup-item-3']) + END + call writefile(lines, 'Xpumoptopacitytextprop', 'D') + let buf = RunVimInTerminal('-S Xpumoptopacitytextprop', {}) + call TermWait(buf) + call term_sendkeys(buf, "i\") + call TermWait(buf, 100) + call VerifyScreenDump(buf, 'Test_pumopt_opacity_textprop_undercurl', {}) + call term_sendkeys(buf, "\\u") + call TermWait(buf) + call StopVimInTerminal(buf) +endfunc + " Test pumopt opacity when every other background line is shifted by one " narrow cell, so the background's wide-character boundaries do not align " with the popup's wide-character grid. Exercises the blend path when: diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 1bc9344df3..d6aa634c72 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -5120,6 +5120,40 @@ func Test_popup_opacity_vsplit() call StopVimInTerminal(buf) endfunc +func Test_popup_opacity_textprop_undercurl() + CheckScreendump + + " hl_blend_attr() treated the CTERMCOLOR sentinel as a real near-white + " color, leaking white bg onto textprop-covered cells under an opacity + " popup. Triggered by a textprop hl that only sets guisp. + let lines =<< trim END + set termguicolors + set t_Cs= t_Ce= + call setline(1, ['aaa bbb ccc ddd eee fff ggg hhh', + \ 'iii jjj kkk lll mmm nnn ooo ppp', + \ 'qqq rrr sss ttt uuu vvv www xxx', + \ 'yyy zzz 111 222 333 444 555 666']) + hi MyError guisp=#ec7279 + call prop_type_add('mytype', #{highlight: 'MyError', combine: 1}) + for s:l in range(1, line('$')) + call prop_add(s:l, 5, #{type: 'mytype', length: 20}) + endfor + hi PanelBg guibg=#0b1021 guifg=#e5e9f0 + call popup_create(['POPUP CONTENT', ' ', ' middle '], #{ + \ line: 1, col: 10, + \ minwidth: 30, minheight: 3, + \ opacity: 35, + \ highlight: 'PanelBg', + \ zindex: 200, + \ }) + END + call writefile(lines, 'XtestPopupOpacityTextprop', 'D') + let buf = RunVimInTerminal('-S XtestPopupOpacityTextprop', #{rows: 8, cols: 50}) + call VerifyScreenDump(buf, 'Test_popupwin_opacity_textprop_undercurl', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_popup_close_b_nwindows() edit Xfoo setlocal bufhidden=wipe diff --git a/src/version.c b/src/version.c index d79705029e..d77ebcc1f4 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 427, /**/ 426, /**/ From 59e59a62b417c6cee7384718120a9378ee347064 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 1 May 2026 16:28:51 +0000 Subject: [PATCH 158/545] patch 9.2.0428: popup: no opacity support for completepopup/previewpopup Problem: popup: no opacity support for completepopup/previewpopup Solution: Add support opacity: suboption for the 'completeopt'. Accepts opacity:0-100 with the same semantics as popup_create()'s opacity option, allowing the info / preview popup to blend with the background. closes: #20099 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 4 ++++ runtime/doc/version9.txt | 1 + runtime/doc/windows.txt | 6 +++++- src/optionstr.c | 8 ++++---- src/popupwin.c | 13 +++++++++++++ src/testdir/test_options.vim | 16 ++++++++++++++++ src/version.c | 2 ++ 7 files changed, 45 insertions(+), 5 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index b0f3f1c72b..7e1d4cb67e 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2393,6 +2393,9 @@ A jump table for the options with a short description can be found at |Q_op|. close show close button: "on" (default) or "off" height maximum height of the popup highlight popup highlight group (default: PmenuSel) + opacity opacity percentage 0-100 (default 100, fully + opaque). When less than 100, content beneath + the popup shows through. resize show resize handle: "on" (default) or "off" shadow "off" (default) or "on" using |hl-PmenuShadow| width maximum width of the popup @@ -2400,6 +2403,7 @@ A jump table for the options with a short description can be found at |Q_op|. Example: > :set completepopup=height:10,border:single,highlight:InfoPopup :set completepopup=width:60,border:custom:─;│;─;│;┌;┐;┘;└ + :set completepopup=border:round,opacity:80 < When "align" is set to "item", the popup is positioned near the selected item and moves as the selection changes. diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index ca69850e05..c9ede397b8 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52587,6 +52587,7 @@ Popups ~ - Support for transparency, see |popup-opacity|. - 'previewpopup' supports the same values as 'completepopup' (except for "align"). +- Support "opacity" setting for 'completepopup' option. Diff mode ~ --------- diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index f27cc6b056..f0d9afaf1a 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -1,4 +1,4 @@ -*windows.txt* For Vim version 9.2. Last change: 2026 Mar 01 +*windows.txt* For Vim version 9.2. Last change: 2026 May 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -960,6 +960,9 @@ settings. The option is a comma-separated list of values: the value is "on", it must be set after border. height maximum height of the popup highlight highlight group of the popup (default is Pmenu) + opacity opacity percentage 0-100 (default 100, fully opaque). + When less than 100, content beneath the popup shows + through. resize show resize handle: "on" (default) or "off" shadow "off" (default) or "on" using |hl-PmenuShadow| width maximum width of the popup @@ -968,6 +971,7 @@ Example: > :set previewpopup=height:10,width:60 :set previewpopup=border:single,borderhilight:PmenuBorder :set previewpopup=border:custom:─;│;─;│;┌;┐;┘;└ + :set previewpopup=border:round,opacity:80 A few peculiarities: - If the file is in a buffer already, it will be re-used. This will allow for diff --git a/src/optionstr.c b/src/optionstr.c index 128e5a9469..59d4909130 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -73,11 +73,11 @@ static char *(p_kpc_protocol_values[]) = {"none", "mok2", "kitty", NULL}; #ifdef FEAT_PROP_POPUP // Note: Keep this in sync with parse_popup_option() static char *(p_popup_cpp_option_values[]) = {"align:", "border:", - "borderhighlight:", "close:", "height:", "highlight:", "resize:", - "shadow:", "width:", NULL}; + "borderhighlight:", "close:", "height:", "highlight:", "opacity:", + "resize:", "shadow:", "width:", NULL}; static char *(p_popup_pvp_option_values[]) = {"border:", - "borderhighlight:", "close:", "height:", "highlight:", "resize:", - "shadow:", "width:", NULL}; + "borderhighlight:", "close:", "height:", "highlight:", "opacity:", + "resize:", "shadow:", "width:", NULL}; static char *(p_popup_option_on_off_values[]) = {"on", "off", NULL}; static char *(p_popup_cpp_border_values[]) = {"single", "double", "round", "ascii", "on", "off", "custom:", NULL}; diff --git a/src/popupwin.c b/src/popupwin.c index 103daccd29..fb6e17d058 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -2163,6 +2163,19 @@ parse_popup_option(win_T *wp, int is_preview) if (wp != NULL && menu) wp->w_popup_flags |= POPF_INFO_MENU; } + else if (STRNCMP(s, "opacity:", 8) == 0) + { + if (dig != p || x < 0 || x > 100) + return FAIL; + if (wp != NULL) + { + if (x < 100) + wp->w_popup_flags |= POPF_OPACITY; + else + wp->w_popup_flags &= ~POPF_OPACITY; + wp->w_popup_blend = 100 - x; + } + } else return FAIL; } diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index 91610e1289..bbca7fe46f 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -663,6 +663,22 @@ func Test_set_completion_string_values() call feedkeys(":set completepopup=height:10,align:\\\"\", 'xt') call assert_equal('"set completepopup=height:10,align:item', @:) call assert_equal([], getcompletion('set completepopup=bogusname:', 'cmdline')) + + " opacity: numeric, 0..100 only + call assert_true(index(getcompletion('set completepopup=', 'cmdline'), + \ 'opacity:') >= 0) + call assert_true(index(getcompletion('set previewpopup=', 'cmdline'), + \ 'opacity:') >= 0) + set completepopup=border:on,opacity:0 + set completepopup=border:on,opacity:50 + set completepopup=border:on,opacity:100 + call assert_fails('set completepopup=opacity:101', 'E474:') + call assert_fails('set completepopup=opacity:abc', 'E474:') + call assert_fails('set completepopup=opacity:-10', 'E474:') + set previewpopup=opacity:30 + call assert_fails('set previewpopup=opacity:200', 'E474:') + call assert_fails('set previewpopup=opacity:-10', 'E474:') + set previewpopup& completepopup& " diffopt: special handling of algorithm: and inline: diff --git a/src/version.c b/src/version.c index d77ebcc1f4..376f278c64 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 428, /**/ 427, /**/ From e4413c5df76d353251be53edfbd7718ff18dc36b Mon Sep 17 00:00:00 2001 From: Doug Kearns Date: Fri, 1 May 2026 16:35:57 +0000 Subject: [PATCH 159/545] runtime(algol68): Update syntax file, match symbolic identity relators closes: #20109 Signed-off-by: Doug Kearns Signed-off-by: Christian Brabandt --- runtime/syntax/algol68.vim | 3 +- .../testdir/dumps/algol68_operators_00.dump | 20 ++++ .../testdir/dumps/algol68_operators_01.dump | 20 ++++ .../testdir/dumps/algol68_operators_02.dump | 20 ++++ .../testdir/dumps/algol68_operators_03.dump | 20 ++++ .../testdir/dumps/algol68_operators_04.dump | 20 ++++ .../testdir/dumps/algol68_operators_05.dump | 20 ++++ .../testdir/input/algol68_operators.a68 | 100 ++++++++++++++++++ 8 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 runtime/syntax/testdir/dumps/algol68_operators_00.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_operators_01.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_operators_02.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_operators_03.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_operators_04.dump create mode 100644 runtime/syntax/testdir/dumps/algol68_operators_05.dump create mode 100644 runtime/syntax/testdir/input/algol68_operators.a68 diff --git a/runtime/syntax/algol68.vim b/runtime/syntax/algol68.vim index 129e938074..5ea87e77bd 100644 --- a/runtime/syntax/algol68.vim +++ b/runtime/syntax/algol68.vim @@ -3,7 +3,7 @@ " Version: 0.4 " Maintainer: Janis Papanagnou " Previous Maintainer: NevilleD.ALGOL_68@sgr-a.net -" Last Change: 2026 Apr 23 +" Last Change: 2026 May 02 if exists("b:current_syntax") finish @@ -71,6 +71,7 @@ if exists("algol68_symbolic_operators") syn match algol68SymbolOperator "\%([-+*%/]\|%\*\):=" syn match algol68SymbolOperator "+=:" syn match algol68SymbolOperator "*\*\|&" + syn match algol68SymbolOperator ":/\==:" endif syn match algol68Number "\<\d\+\%(\s\+\d\+\)*\>" diff --git a/runtime/syntax/testdir/dumps/algol68_operators_00.dump b/runtime/syntax/testdir/dumps/algol68_operators_00.dump new file mode 100644 index 0000000000..7733bcfc66 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_operators_00.dump @@ -0,0 +1,20 @@ +>#+0#0000e05#ffffff0| +0#0000000&@73 +| +0#0000e05&@3|V|I|M|_|T|E|S|T|_|S|E|T|U|P| |l|e|t| |g|:|a|l|g|o|l|6|8|_|s|y|m|b|o|l|i|c|_|o|p|e|r|a|t|o|r|s| |=| |1| +0#0000000&@19 +|#+0#0000e05&| +0#0000000&@73 +@75 +@75 +|#+0#0000e05&| |s|y|m|b|o|l|i|c| |o|p|e|r|a|t|o|r|s| |#| +0#0000000&@52 +@75 +|#+0#0000e05&| |I|D|E|N|T|I|T|Y| |#| +0#0000000&@62 +@75 +|x| |:+0#af5f00255&|=|:| +0#0000000&@1|y| @66 +|x| |:+0#af5f00255&|/|=|:| +0#0000000&|y| @66 +@75 +|#+0#0000e05&| |B|O@1|L| |#| +0#0000000&@66 +@75 +|~+0#af5f00255&| +0#0000000&|T+0#e000002&|R|U|E| +0#0000000&@68 +|T+0#e000002&|R|U|E| +0#0000000&|&+0#af5f00255&| +0#0000000&|F+0#e000002&|A|L|S|E| +0#0000000&@62 +|T+0#e000002&|R|U|E| +0#0000000&|=+0#af5f00255&| +0#0000000&|F+0#e000002&|A|L|S|E| +0#0000000&@62 +|T+0#e000002&|R|U|E| +0#0000000&|/+0#af5f00255&|=| +0#0000000&|F+0#e000002&|A|L|S|E| +0#0000000&@61 +@75 +@57|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/algol68_operators_01.dump b/runtime/syntax/testdir/dumps/algol68_operators_01.dump new file mode 100644 index 0000000000..4e28102713 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_operators_01.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@74 +|~+0#af5f00255&| +0#0000000&|T+0#e000002&|R|U|E| +0#0000000&@68 +|T+0#e000002&|R|U|E| +0#0000000&|&+0#af5f00255&| +0#0000000&|F+0#e000002&|A|L|S|E| +0#0000000&@62 +|T+0#e000002&|R|U|E| +0#0000000&|=+0#af5f00255&| +0#0000000&|F+0#e000002&|A|L|S|E| +0#0000000&@62 +|T+0#e000002&|R|U|E| +0#0000000&|/+0#af5f00255&|=| +0#0000000&|F+0#e000002&|A|L|S|E| +0#0000000&@61 +> @74 +|#+0#0000e05&| |I|N|T| |#| +0#0000000&@67 +@75 +|++0#af5f00255&|4+0#e000002&|2| +0#0000000&@71 +|-+0#af5f00255&|4+0#e000002&|2| +0#0000000&@71 +@75 +|4+0#e000002&|2| +0#0000000&|++0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|-+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|*+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|%+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|%+0#af5f00255&|*| +0#0000000&|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|/+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|*+0#af5f00255&@1| +0#0000000&|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|^+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +@57|1|9|,|0|-|1| @7|1|6|%| diff --git a/runtime/syntax/testdir/dumps/algol68_operators_02.dump b/runtime/syntax/testdir/dumps/algol68_operators_02.dump new file mode 100644 index 0000000000..5fe3c487f3 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_operators_02.dump @@ -0,0 +1,20 @@ +|4+0#e000002#ffffff0|2| +0#0000000&|^+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +@75 +|i| |++0#af5f00255&|:|=| +0#0000000&@1|4+0#e000002&|2| +0#0000000&@65 +|i| |-+0#af5f00255&|:|=| +0#0000000&@1|4+0#e000002&|2| +0#0000000&@65 +|i| |*+0#af5f00255&|:|=| +0#0000000&@1|4+0#e000002&|2| +0#0000000&@65 +>i| |%+0#af5f00255&|:|=| +0#0000000&@1|4+0#e000002&|2| +0#0000000&@65 +|i| |%+0#af5f00255&|*|:|=| +0#0000000&|4+0#e000002&|2| +0#0000000&@65 +@75 +|4+0#e000002&|2| +0#0000000&|=+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|/+0#af5f00255&|=| +0#0000000&|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|<+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|<+0#af5f00255&|=| +0#0000000&|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|>+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@66 +|4+0#e000002&|2| +0#0000000&|>+0#af5f00255&|=| +0#0000000&|8+0#e000002&|7| +0#0000000&@66 +@75 +|#+0#0000e05&| |R|E|A|L| |#| +0#0000000&@66 +@75 +|++0#af5f00255&|4+0#e000002&|.|2| +0#0000000&@70 +|-+0#af5f00255&|4+0#e000002&|.|2| +0#0000000&@70 +@57|3|7|,|1| @9|3|8|%| diff --git a/runtime/syntax/testdir/dumps/algol68_operators_03.dump b/runtime/syntax/testdir/dumps/algol68_operators_03.dump new file mode 100644 index 0000000000..0dd531a406 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_operators_03.dump @@ -0,0 +1,20 @@ +|-+0#af5f00255#ffffff0|4+0#e000002&|.|2| +0#0000000&@70 +@75 +|4+0#e000002&|.|2| +0#0000000&|++0#af5f00255&| +0#0000000&|8+0#e000002&|.|7| +0#0000000&@65 +|4+0#e000002&|.|2| +0#0000000&|-+0#af5f00255&| +0#0000000&|8+0#e000002&|.|7| +0#0000000&@65 +|4+0#e000002&|.|2| +0#0000000&|*+0#af5f00255&| +0#0000000&|8+0#e000002&|.|7| +0#0000000&@65 +>4+0#e000002&|.|2| +0#0000000&|/+0#af5f00255&| +0#0000000&|8+0#e000002&|.|7| +0#0000000&@65 +@75 +|4+0#e000002&|.|2| +0#0000000&|*+0#af5f00255&@1| +0#0000000&|8+0#e000002&|.|7| +0#0000000&@64 +|4+0#e000002&|.|2| +0#0000000&|^+0#af5f00255&| +0#0000000&@1|8+0#e000002&|.|7| +0#0000000&@64 +|4+0#e000002&|.|2| +0#0000000&|*+0#af5f00255&@1| +0#0000000&|8+0#e000002&|7| +0#0000000&@65 +|4+0#e000002&|.|2| +0#0000000&|^+0#af5f00255&| +0#0000000&@1|8+0#e000002&|7| +0#0000000&@65 +@75 +|r| |++0#af5f00255&|:|=| +0#0000000&@1|4+0#e000002&|2| +0#0000000&@65 +|r| |-+0#af5f00255&|:|=| +0#0000000&@1|4+0#e000002&|2| +0#0000000&@65 +|r| |*+0#af5f00255&|:|=| +0#0000000&@1|4+0#e000002&|2| +0#0000000&@65 +|r| |/+0#af5f00255&|:|=| +0#0000000&@1|4+0#e000002&|2| +0#0000000&@65 +@75 +|4+0#e000002&|.|2| +0#0000000&|=+0#af5f00255&| +0#0000000&@1|8+0#e000002&|.|7| +0#0000000&@64 +|4+0#e000002&|.|2| +0#0000000&|/+0#af5f00255&|=| +0#0000000&|8+0#e000002&|.|7| +0#0000000&@64 +@57|5@1|,|1| @9|6|0|%| diff --git a/runtime/syntax/testdir/dumps/algol68_operators_04.dump b/runtime/syntax/testdir/dumps/algol68_operators_04.dump new file mode 100644 index 0000000000..774f49e6ef --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_operators_04.dump @@ -0,0 +1,20 @@ +|4+0#e000002#ffffff0|.|2| +0#0000000&|/+0#af5f00255&|=| +0#0000000&|8+0#e000002&|.|7| +0#0000000&@64 +|4+0#e000002&|.|2| +0#0000000&|<+0#af5f00255&| +0#0000000&@1|8+0#e000002&|.|7| +0#0000000&@64 +|4+0#e000002&|.|2| +0#0000000&|<+0#af5f00255&|=| +0#0000000&|8+0#e000002&|.|7| +0#0000000&@64 +|4+0#e000002&|.|2| +0#0000000&|>+0#af5f00255&| +0#0000000&@1|8+0#e000002&|.|7| +0#0000000&@64 +|4+0#e000002&|.|2| +0#0000000&|>+0#af5f00255&|=| +0#0000000&|8+0#e000002&|.|7| +0#0000000&@64 +> @74 +|#+0#0000e05&| |C|H|A|R| |#| +0#0000000&@66 +@75 +|"+0#e000002&|a|"| +0#0000000&|=+0#af5f00255&| +0#0000000&@1|"+0#e000002&|b|"| +0#0000000&@64 +|"+0#e000002&|a|"| +0#0000000&|/+0#af5f00255&|=| +0#0000000&|"+0#e000002&|b|"| +0#0000000&@64 +|"+0#e000002&|a|"| +0#0000000&|<+0#af5f00255&| +0#0000000&@1|"+0#e000002&|b|"| +0#0000000&@64 +|"+0#e000002&|a|"| +0#0000000&|<+0#af5f00255&|=| +0#0000000&|"+0#e000002&|b|"| +0#0000000&@64 +|"+0#e000002&|a|"| +0#0000000&|>+0#af5f00255&| +0#0000000&@1|"+0#e000002&|b|"| +0#0000000&@64 +|"+0#e000002&|a|"| +0#0000000&|>+0#af5f00255&|=| +0#0000000&|"+0#e000002&|b|"| +0#0000000&@64 +@75 +|#+0#0000e05&| |S|T|R|I|N|G| |#| +0#0000000&@64 +@75 +|"+0#e000002&|a@2|"| +0#0000000&|=+0#af5f00255&| +0#0000000&@1|"+0#e000002&|b@2|"| +0#0000000&@60 +|"+0#e000002&|a@2|"| +0#0000000&|/+0#af5f00255&|=| +0#0000000&|"+0#e000002&|b@2|"| +0#0000000&@60 +@57|7|3|,|0|-|1| @7|8|2|%| diff --git a/runtime/syntax/testdir/dumps/algol68_operators_05.dump b/runtime/syntax/testdir/dumps/algol68_operators_05.dump new file mode 100644 index 0000000000..f49d078aa2 --- /dev/null +++ b/runtime/syntax/testdir/dumps/algol68_operators_05.dump @@ -0,0 +1,20 @@ +|"+0#e000002#ffffff0|a@2|"| +0#0000000&|/+0#af5f00255&|=| +0#0000000&|"+0#e000002&|b@2|"| +0#0000000&@60 +|"+0#e000002&|a@2|"| +0#0000000&|<+0#af5f00255&| +0#0000000&@1|"+0#e000002&|b@2|"| +0#0000000&@60 +|"+0#e000002&|a@2|"| +0#0000000&|<+0#af5f00255&|=| +0#0000000&|"+0#e000002&|b@2|"| +0#0000000&@60 +|"+0#e000002&|a@2|"| +0#0000000&|>+0#af5f00255&| +0#0000000&@1|"+0#e000002&|b@2|"| +0#0000000&@60 +|"+0#e000002&|a@2|"| +0#0000000&|>+0#af5f00255&|=| +0#0000000&|"+0#e000002&|b@2|"| +0#0000000&@60 +> @74 +|"+0#e000002&|a@2|"| +0#0000000&|++0#af5f00255&| +0#0000000&|"+0#e000002&|b@2|"| +0#0000000&@61 +|"+0#e000002&|a@2|"| +0#0000000&|++0#af5f00255&| +0#0000000&|"+0#e000002&|b|"| +0#0000000&@63 +@3|4+0#e000002&|2| +0#0000000&|*+0#af5f00255&| +0#0000000&|"+0#e000002&|a@2|"| +0#0000000&@61 +|"+0#e000002&|a@2|"| +0#0000000&|*+0#af5f00255&| +0#0000000&|4+0#e000002&|2| +0#0000000&@64 +@75 +@4|s| |++0#af5f00255&|:|=| +0#0000000&|"+0#e000002&|a@2|"| +0#0000000&@59 +|"+0#e000002&|a@2|"| +0#0000000&|++0#af5f00255&|=|:| +0#0000000&|s| @63 +@4|s| |*+0#af5f00255&|:|=| +0#0000000&|5+0#e000002&| +0#0000000&@63 +@75 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|9|1|,|0|-|1| @7|B|o|t| diff --git a/runtime/syntax/testdir/input/algol68_operators.a68 b/runtime/syntax/testdir/input/algol68_operators.a68 new file mode 100644 index 0000000000..c1939e5964 --- /dev/null +++ b/runtime/syntax/testdir/input/algol68_operators.a68 @@ -0,0 +1,100 @@ +# + VIM_TEST_SETUP let g:algol68_symbolic_operators = 1 +# + + +# symbolic operators # + +# IDENTITY # + +x :=: y +x :/=: y + +# BOOL # + +~ TRUE +TRUE & FALSE +TRUE = FALSE +TRUE /= FALSE + +# INT # + ++42 +-42 + +42 + 87 +42 - 87 +42 * 87 +42 % 87 +42 %* 87 +42 / 87 +42 ** 87 +42 ^ 87 + +i +:= 42 +i -:= 42 +i *:= 42 +i %:= 42 +i %*:= 42 + +42 = 87 +42 /= 87 +42 < 87 +42 <= 87 +42 > 87 +42 >= 87 + +# REAL # + ++4.2 +-4.2 + +4.2 + 8.7 +4.2 - 8.7 +4.2 * 8.7 +4.2 / 8.7 + +4.2 ** 8.7 +4.2 ^ 8.7 +4.2 ** 87 +4.2 ^ 87 + +r +:= 42 +r -:= 42 +r *:= 42 +r /:= 42 + +4.2 = 8.7 +4.2 /= 8.7 +4.2 < 8.7 +4.2 <= 8.7 +4.2 > 8.7 +4.2 >= 8.7 + +# CHAR # + +"a" = "b" +"a" /= "b" +"a" < "b" +"a" <= "b" +"a" > "b" +"a" >= "b" + +# STRING # + +"aaa" = "bbb" +"aaa" /= "bbb" +"aaa" < "bbb" +"aaa" <= "bbb" +"aaa" > "bbb" +"aaa" >= "bbb" + +"aaa" + "bbb" +"aaa" + "b" + 42 * "aaa" +"aaa" * 42 + + s +:= "aaa" +"aaa" +=: s + s *:= 5 + From e25933014c1c7cc4fb40a6ed429b80fdb37ba28e Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sat, 2 May 2026 13:29:01 +0000 Subject: [PATCH 160/545] patch 9.2.0429: tests: flaky screendump Test_smoothscroll_incsearch() Problem: tests: flaky screendump Test_smoothscroll_incsearch() Solution: Replace screendump test by WaitForAssert() (Yasuhiro Matsumoto) VerifyScreenDump fails consistently on the macos-15-intel CI runner. Replace the dump comparisons with assertions that verify the actual invariant under test: that the visible buffer view stays unchanged across the four incremental-search keystrokes (i.e. skipcol is not reset). Drop the now-unused dump files. closes: #20118 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- .../dumps/Test_smooth_incsearch_1.dump | 8 -------- .../dumps/Test_smooth_incsearch_2.dump | 8 -------- .../dumps/Test_smooth_incsearch_3.dump | 8 -------- .../dumps/Test_smooth_incsearch_4.dump | 8 -------- src/testdir/test_scroll_opt.vim | 19 ++++++++++++++----- src/version.c | 2 ++ 6 files changed, 16 insertions(+), 37 deletions(-) delete mode 100644 src/testdir/dumps/Test_smooth_incsearch_1.dump delete mode 100644 src/testdir/dumps/Test_smooth_incsearch_2.dump delete mode 100644 src/testdir/dumps/Test_smooth_incsearch_3.dump delete mode 100644 src/testdir/dumps/Test_smooth_incsearch_4.dump diff --git a/src/testdir/dumps/Test_smooth_incsearch_1.dump b/src/testdir/dumps/Test_smooth_incsearch_1.dump deleted file mode 100644 index 830b855d1d..0000000000 --- a/src/testdir/dumps/Test_smooth_incsearch_1.dump +++ /dev/null @@ -1,8 +0,0 @@ -|<+0#4040ff13#ffffff0@2| +0#af5f00255&|a+0#0000000&@27| @7 -| +0#af5f00255&|1|2| | +0#0000000&@35 -| +0#af5f00255&|1|3| | +0#0000000&@35 -| +0#af5f00255&|1|4| |b+1#0000000&|b+0&&@2| @31 -| +0#af5f00255&|1|5| | +0#0000000&@35 -| +0#af5f00255&|1|6| | +0#0000000&@35 -| +0#af5f00255&|1|7| | +0#0000000&@35 -|/|b> @37 diff --git a/src/testdir/dumps/Test_smooth_incsearch_2.dump b/src/testdir/dumps/Test_smooth_incsearch_2.dump deleted file mode 100644 index 349e12861b..0000000000 --- a/src/testdir/dumps/Test_smooth_incsearch_2.dump +++ /dev/null @@ -1,8 +0,0 @@ -|<+0#4040ff13#ffffff0@2| +0#af5f00255&|a+0#0000000&@27| @7 -| +0#af5f00255&|1|2| | +0#0000000&@35 -| +0#af5f00255&|1|3| | +0#0000000&@35 -| +0#af5f00255&|1|4| |b+1#0000000&@1|b+0&&@1| @31 -| +0#af5f00255&|1|5| | +0#0000000&@35 -| +0#af5f00255&|1|6| | +0#0000000&@35 -| +0#af5f00255&|1|7| | +0#0000000&@35 -|/|b@1> @36 diff --git a/src/testdir/dumps/Test_smooth_incsearch_3.dump b/src/testdir/dumps/Test_smooth_incsearch_3.dump deleted file mode 100644 index 0be3d21932..0000000000 --- a/src/testdir/dumps/Test_smooth_incsearch_3.dump +++ /dev/null @@ -1,8 +0,0 @@ -|<+0#4040ff13#ffffff0@2| +0#af5f00255&|a+0#0000000&@27| @7 -| +0#af5f00255&|1|2| | +0#0000000&@35 -| +0#af5f00255&|1|3| | +0#0000000&@35 -| +0#af5f00255&|1|4| |b+1#0000000&@2|b+0&&| @31 -| +0#af5f00255&|1|5| | +0#0000000&@35 -| +0#af5f00255&|1|6| | +0#0000000&@35 -| +0#af5f00255&|1|7| | +0#0000000&@35 -|/|b@2> @35 diff --git a/src/testdir/dumps/Test_smooth_incsearch_4.dump b/src/testdir/dumps/Test_smooth_incsearch_4.dump deleted file mode 100644 index c81c24b407..0000000000 --- a/src/testdir/dumps/Test_smooth_incsearch_4.dump +++ /dev/null @@ -1,8 +0,0 @@ -|<+0#4040ff13#ffffff0@2| +0#af5f00255&|a+0#0000000&@27| @7 -| +0#af5f00255&|1|2| | +0#0000000&@35 -| +0#af5f00255&|1|3| | +0#0000000&@35 -| +0#af5f00255&|1|4| |b+1#0000000&@3| +0&&@31 -| +0#af5f00255&|1|5| | +0#0000000&@35 -| +0#af5f00255&|1|6| | +0#0000000&@35 -| +0#af5f00255&|1|7| | +0#0000000&@35 -|/|b@3> @34 diff --git a/src/testdir/test_scroll_opt.vim b/src/testdir/test_scroll_opt.vim index 7ee9fa2d27..6d4fc81b81 100644 --- a/src/testdir/test_scroll_opt.vim +++ b/src/testdir/test_scroll_opt.vim @@ -903,7 +903,7 @@ endfunc " skipcol should not reset when doing incremental search on the same word func Test_smoothscroll_incsearch() - CheckScreendump + CheckRunVimInTerminal let lines =<< trim END set smoothscroll number scrolloff=0 incsearch @@ -913,15 +913,24 @@ func Test_smoothscroll_incsearch() END call writefile(lines, 'XSmoothIncsearch', 'D') let buf = RunVimInTerminal('-S XSmoothIncsearch', #{rows: 8, cols: 40}) + let g:test_is_flaky = 1 call term_sendkeys(buf, "/b") - call VerifyScreenDump(buf, 'Test_smooth_incsearch_1', {}) + call WaitForAssert({-> assert_match('^/b\s*$', term_getline(buf, 8))}, 1000) + let view1 = map(range(1, 7), {_, i -> term_getline(buf, i)}) + call term_sendkeys(buf, "b") - call VerifyScreenDump(buf, 'Test_smooth_incsearch_2', {}) + call WaitForAssert({-> assert_match('^/bb\s*$', term_getline(buf, 8))}, 1000) + call assert_equal(view1, map(range(1, 7), {_, i -> term_getline(buf, i)})) + call term_sendkeys(buf, "b") - call VerifyScreenDump(buf, 'Test_smooth_incsearch_3', {}) + call WaitForAssert({-> assert_match('^/bbb\s*$', term_getline(buf, 8))}, 1000) + call assert_equal(view1, map(range(1, 7), {_, i -> term_getline(buf, i)})) + call term_sendkeys(buf, "b") - call VerifyScreenDump(buf, 'Test_smooth_incsearch_4', {}) + call WaitForAssert({-> assert_match('^/bbbb\s*$', term_getline(buf, 8))}, 1000) + call assert_equal(view1, map(range(1, 7), {_, i -> term_getline(buf, i)})) + call term_sendkeys(buf, "\") call StopVimInTerminal(buf) diff --git a/src/version.c b/src/version.c index 376f278c64..95d93bb21c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 429, /**/ 428, /**/ From 2219c890136f4c809ca4fa64d357ae46442bfa92 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sat, 2 May 2026 15:20:18 +0000 Subject: [PATCH 161/545] patch 9.2.0430: tests: Test_shortmess_F3() is flaky on MS-Windows Problem: tests: Test_shortmess_F3() is flaky on MS-Windows Solution: Increase the sleep to 3s (Yasuhiro Matsumoto) On MS-Windows time_differs() treats mtime as unchanged unless st_mtime differs by more than 1 second, so a 2-second sleep can fall short when the two writes straddle a second boundary. Bump the non-nanotime sleep to 3 seconds. closes: #20117 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/testdir/test_options.vim | 4 ++-- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index bbca7fe46f..fa2667525f 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -1450,7 +1450,7 @@ func Test_shortmess_F3() if has('nanotime') sleep 10m else - sleep 2 + sleep 3 endif call writefile(['bar'], 'X_dummy') bprev @@ -1460,7 +1460,7 @@ func Test_shortmess_F3() if has('nanotime') sleep 10m else - sleep 2 + sleep 3 endif call writefile(['baz'], 'X_dummy') checktime diff --git a/src/version.c b/src/version.c index 95d93bb21c..0ce32fb2e4 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 430, /**/ 429, /**/ From e1e92fea92ed80dd10ff0cb325433a97c1826b6a Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sat, 2 May 2026 15:39:55 +0000 Subject: [PATCH 162/545] patch 9.2.0431: blob encoding can be improved MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Problem: blob encoding can be improved Solution: Speed up blob encoding by avoiding per-byte ga_append() (Yasuhiro Matsumoto) Replace the per-byte ga_append loop in the VAR_BLOB branch of json_encode_item() with a single ga_grow for the worst case (2 + 4 * blen) and direct writes through a local pointer. Also read blob bytes through a local char_u* instead of going through blob_get() for each byte. Benchmark (1 MiB blob, 5 iterations, total seconds, median of 3 runs): | byte distribution | Before | After | Speedup | |---|---:|---:|---:| | 1-digit (0–9) | 0.0254 | 0.0174 | 1.46x | | 2-digit (10–99) | 0.0344 | 0.0064 | 5.38x | | 3-digit (100–255) | 0.0539 | 0.0102 | 5.28x | | mixed (0–255) | 0.0335 | 0.0093 | 3.60x | closes: #20113 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/json.c | 33 +++++++++++++++++++++------------ src/version.c | 2 ++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/json.c b/src/json.c index b0d98a290e..a3e58043d8 100644 --- a/src/json.c +++ b/src/json.c @@ -369,29 +369,38 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options) GA_CONCAT_LITERAL(gap, "[]"); else { - ga_append(gap, '['); - for (i = 0; i < b->bv_ga.ga_len; i++) + int blen = b->bv_ga.ga_len; + char_u *src; + char_u *dst; + + // Worst case: '[' + ']' + per-byte 3 digits + comma = 2 + 4*blen + if (ga_grow(gap, 2 + 4 * blen) == FAIL) + goto theend; + src = (char_u *)b->bv_ga.ga_data; + dst = (char_u *)gap->ga_data + gap->ga_len; + *dst++ = '['; + for (i = 0; i < blen; i++) { - int byte = blob_get(b, i); + int byte = src[i]; if (i > 0) - ga_append(gap, ','); - // blob bytes are 0-255, use simple conversion + *dst++ = ','; if (byte >= 100) { - ga_append(gap, '0' + byte / 100); - ga_append(gap, '0' + (byte / 10) % 10); - ga_append(gap, '0' + byte % 10); + *dst++ = '0' + byte / 100; + *dst++ = '0' + (byte / 10) % 10; + *dst++ = '0' + byte % 10; } else if (byte >= 10) { - ga_append(gap, '0' + byte / 10); - ga_append(gap, '0' + byte % 10); + *dst++ = '0' + byte / 10; + *dst++ = '0' + byte % 10; } else - ga_append(gap, '0' + byte); + *dst++ = '0' + byte; } - ga_append(gap, ']'); + *dst++ = ']'; + gap->ga_len = (int)(dst - (char_u *)gap->ga_data); } break; diff --git a/src/version.c b/src/version.c index 0ce32fb2e4..9208a50785 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 431, /**/ 430, /**/ From 3bd25c63b4eda363683c06b0dcc58e74f563d3f2 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sat, 2 May 2026 15:49:17 +0000 Subject: [PATCH 163/545] patch 9.2.0432: blob to string conversion can be improved Problem: blob to string conversion can be improved Solution: Compute the output size up front and use a single alloc plus mch_memmove() (Yasuhiro Matsumoto). Replace per-byte ga_append/snprintf loops with bulk allocation and mch_memmove in three hot paths: blob2string() (used by string()), string_from_blob(), and the UTF-16/UCS path of f_blob2str(). For a 16 MiB blob, string(blob) is ~28x faster and blob2str() is ~2x faster. Benchmark (16 MiB blob, 5 iterations, total seconds): | | Before | After | Speedup | |---|---:|---:|---:| | `string(blob)` | 6.422 | 0.225 | 28.5x | | `blob2str(b)` | 0.504 | 0.265 | 1.90x | | `blob2str(b, {encoding: 'utf-8'})` | 0.507 | 0.282 | 1.80x | | `blob2str(b, {encoding: 'utf-16le'})` | 0.407 | 0.202 | 2.01x | closes: #20112 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/blob.c | 48 ++++++++++++++++++++++------------- src/strings.c | 70 ++++++++++++++++++++++++++------------------------- src/version.c | 2 ++ 3 files changed, 69 insertions(+), 51 deletions(-) diff --git a/src/blob.c b/src/blob.c index bbdf2873d0..ad2b02bbbc 100644 --- a/src/blob.c +++ b/src/blob.c @@ -263,33 +263,47 @@ write_blob(FILE *fd, blob_T *blob) * Convert a blob to a readable form: "0z00112233.44556677.8899" */ char_u * -blob2string(blob_T *blob, char_u **tofree, char_u *numbuf) +blob2string(blob_T *blob, char_u **tofree, char_u *numbuf UNUSED) { - int i; - garray_T ga; + static const char hex_chars[] = "0123456789ABCDEF"; + int blen; + size_t total; + char_u *buf; + char_u *p; + char_u *src; - if (blob == NULL) + if (blob == NULL || (blen = blob_len(blob)) == 0) + { + *tofree = NULL; + return (char_u *)"0z"; + } + + // 2 ("0z") + 2 hex per byte + one '.' every 4 bytes + NUL terminator. + total = 2 + (size_t)blen * 2 + (size_t)((blen - 1) / 4) + 1; + buf = alloc(total); + if (buf == NULL) { *tofree = NULL; return (char_u *)"0z"; } - // Store bytes in the growarray. - ga_init2(&ga, 1, 4000); - GA_CONCAT_LITERAL(&ga, "0z"); - for (i = 0; i < blob_len(blob); i++) + p = buf; + *p++ = '0'; + *p++ = 'z'; + src = (char_u *)blob->bv_ga.ga_data; + for (int i = 0; i < blen; i++) { - size_t numbuflen; + unsigned b; if (i > 0 && (i & 3) == 0) - GA_CONCAT_LITERAL(&ga, "."); - numbuflen = vim_snprintf_safelen((char *)numbuf, NUMBUFLEN, - "%02X", blob_get(blob, i)); - ga_concat_len(&ga, numbuf, numbuflen); - } - ga_append(&ga, NUL); // append a NUL at the end - *tofree = ga.ga_data; - return *tofree; + *p++ = '.'; + b = src[i]; + *p++ = hex_chars[b >> 4]; + *p++ = hex_chars[b & 0xf]; + } + *p = NUL; + *tofree = buf; + return buf; } /* diff --git a/src/strings.c b/src/strings.c index ec81ee469a..ff63a3ef40 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1262,43 +1262,42 @@ blob_from_string(char_u *str, blob_T *blob) static int string_from_blob(blob_T *blob, long *start_idx, string_T *ret) { - garray_T str_ga; - long blen; - int idx; - - ga_init2(&str_ga, sizeof(char), 80); - - blen = blob_len(blob); + long blen = blob_len(blob); + long start = *start_idx; + char_u *src; + char_u *nl; + long line_len; - for (idx = *start_idx; idx < blen; idx++) + if (start >= blen) { - char_u byte = (char_u)blob_get(blob, idx); - if (byte == NL) - { - idx++; - break; - } - - if (byte == NUL) - byte = NL; - - ga_append(&str_ga, byte); + ret->string = vim_strsave((char_u *)""); + ret->length = 0; + *start_idx = blen; + return (ret->string == NULL) ? FAIL : OK; } - if (str_ga.ga_data != NULL) - { - ret->string = vim_strnsave(str_ga.ga_data, str_ga.ga_len); - ret->length = str_ga.ga_len; - } - else + src = (char_u *)blob->bv_ga.ga_data + start; + nl = (char_u *)memchr(src, NL, (size_t)(blen - start)); + line_len = (nl == NULL) ? (blen - start) : (long)(nl - src); + + ret->string = alloc(line_len + 1); + if (ret->string == NULL) { - ret->string = vim_strsave((char_u *)""); ret->length = 0; + return FAIL; } - *start_idx = idx; + if (line_len > 0) + mch_memmove(ret->string, src, (size_t)line_len); + ret->string[line_len] = NUL; + ret->length = (size_t)line_len; - ga_clear(&str_ga); - return (ret->string == NULL) ? FAIL : OK; + // A NUL byte in the blob represents a NL in the resulting string. + for (long i = 0; i < line_len; i++) + if (ret->string[i] == NUL) + ret->string[i] = NL; + + *start_idx = start + line_len + (nl != NULL ? 1 : 0); + return OK; } /* @@ -1486,11 +1485,14 @@ f_blob2str(typval_T *argvars, typval_T *rettv) garray_T blob_ga; int nul_size = (from_prop & ENC_4BYTE) ? 4 : 2; ga_init2(&blob_ga, 1, blen + nul_size); - for (long i = 0; i < blen; i++) - ga_append(&blob_ga, (int)(unsigned char)blob_get(blob, i)); - // Add NUL terminator (2 bytes for UTF-16/UCS-2, 4 bytes for UTF-32/UCS-4) - for (int i = 0; i < nul_size; i++) - ga_append(&blob_ga, NUL); + if (ga_grow(&blob_ga, blen + nul_size) == OK) + { + if (blen > 0) + mch_memmove(blob_ga.ga_data, blob->bv_ga.ga_data, (size_t)blen); + // NUL terminator (2 bytes for UTF-16/UCS-2, 4 bytes for UTF-32/UCS-4) + vim_memset((char_u *)blob_ga.ga_data + blen, 0, (size_t)nul_size); + blob_ga.ga_len = blen + nul_size; + } // Convert the entire blob at once vimconv_T vimconv; diff --git a/src/version.c b/src/version.c index 9208a50785..c75dd6a65f 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 432, /**/ 431, /**/ From 5c700152ae23c91b6edef3fa3e7ba06d40be0f9e Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sat, 2 May 2026 16:04:38 +0000 Subject: [PATCH 164/545] patch 9.2.0433: customlist completion cannot supply pum metadata Problem: customlist completion cannot supply pum metadata Solution: Allow each item returned by a customlist function to be either a string or a Dict with keys "word", "abbr", "kind", "menu" and "info" (Yasuhiro Matsumoto). closes: #20100 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/map.txt | 18 ++++- runtime/doc/version9.txt | 4 +- src/cmdexpand.c | 133 +++++++++++++++++++++++++++++++---- src/structs.h | 11 +++ src/testdir/test_cmdline.vim | 47 +++++++++++++ src/version.c | 2 + 6 files changed, 197 insertions(+), 18 deletions(-) diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 118e2d44e9..a79c0388cb 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1,4 +1,4 @@ -*map.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*map.txt* For Vim version 9.2. Last change: 2026 May 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1693,7 +1693,21 @@ For the "custom" argument, the function should return the completion candidates one per line in a newline separated string. *E1303* For the "customlist" argument, the function should return the completion -candidates as a Vim List. Non-string items in the list are ignored. +candidates as a Vim List. Each item may be either a string or a |Dictionary|. +A Dictionary item may have the following keys: + word (required) the text inserted into the command line when the + item is selected + abbr alternative text shown in the popup menu in place of "word", + when 'wildoptions' contains "pum"; useful when the inserted + text and the displayed text should differ + kind short kind text (one or two characters), shown in the popup + menu when 'wildoptions' contains "pum" + menu extra text shown after the match in the popup menu + info long description shown in the info popup; the |+popupwin| + feature is required to display it +Items that are neither a string nor a Dictionary, and Dictionary items without +a "word" key, are ignored. When 'wildoptions' does not contain "pum", only +"word" is shown. The function arguments are: ArgLead the leading portion of the argument currently being diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index c9ede397b8..114bdf7dac 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 May 01 +*version9.txt* For Vim version 9.2. Last change: 2026 May 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52623,6 +52623,8 @@ Other ~ 'completeopt' option - Channel can handle |Blob| messages |channel-open-options|. - Added the "u" flag to 'shortmess' to silence undo/redo messages: |shm-u| +- |:command-completion-customlist| can return a list of dictionaries with + kind/menu/info/abbr for the popup menu. Platform specific ~ ----------------- diff --git a/src/cmdexpand.c b/src/cmdexpand.c index a4891871f6..c265ecbe7f 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -412,10 +412,16 @@ cmdline_pum_create( compl_match_arraysize = numMatches; for (int i = 0; i < numMatches; i++) { - compl_match_array[i].pum_text = SHOW_MATCH(i); - compl_match_array[i].pum_info = NULL; - compl_match_array[i].pum_extra = NULL; - compl_match_array[i].pum_kind = NULL; + compl_match_array[i].pum_text = (xp->xp_files_abbr != NULL + && xp->xp_files_abbr[i] != NULL) + ? xp->xp_files_abbr[i] + : SHOW_MATCH(i); + compl_match_array[i].pum_info = xp->xp_files_info != NULL + ? xp->xp_files_info[i] : NULL; + compl_match_array[i].pum_extra = xp->xp_files_menu != NULL + ? xp->xp_files_menu[i] : NULL; + compl_match_array[i].pum_kind = xp->xp_files_kind != NULL + ? xp->xp_files_kind[i] : NULL; compl_match_array[i].pum_user_abbr_hlattr = -1; compl_match_array[i].pum_user_kind_hlattr = -1; } @@ -1021,6 +1027,31 @@ find_longest_match(expand_T *xp, int options) return ss; } + static void +free_xp_files_extra(expand_T *xp, int numfiles) +{ + if (xp->xp_files_abbr != NULL) + { + FreeWild(numfiles, xp->xp_files_abbr); + xp->xp_files_abbr = NULL; + } + if (xp->xp_files_kind != NULL) + { + FreeWild(numfiles, xp->xp_files_kind); + xp->xp_files_kind = NULL; + } + if (xp->xp_files_menu != NULL) + { + FreeWild(numfiles, xp->xp_files_menu); + xp->xp_files_menu = NULL; + } + if (xp->xp_files_info != NULL) + { + FreeWild(numfiles, xp->xp_files_info); + xp->xp_files_info = NULL; + } +} + /* * Do wildcard expansion on the string "str". * Chars that should not be expanded must be preceded with a backslash. @@ -1087,6 +1118,7 @@ ExpandOne( if (xp->xp_numfiles != -1 && mode != WILD_ALL && mode != WILD_LONGEST) { FreeWild(xp->xp_numfiles, xp->xp_files); + free_xp_files_extra(xp, xp->xp_numfiles); xp->xp_numfiles = -1; VIM_CLEAR(xp->xp_orig); @@ -1188,6 +1220,7 @@ ExpandCleanup(expand_T *xp) { if (xp->xp_numfiles >= 0) { + free_xp_files_extra(xp, xp->xp_numfiles); FreeWild(xp->xp_numfiles, xp->xp_files); xp->xp_numfiles = -1; } @@ -1424,7 +1457,10 @@ showmatches( } if (xp->xp_numfiles == -1) + { FreeWild(numMatches, matches); + free_xp_files_extra(xp, numMatches); + } return EXPAND_OK; } @@ -4124,6 +4160,12 @@ ExpandUserList( list_T *retlist; listitem_T *li; garray_T ga; + garray_T ga_abbr; + garray_T ga_kind; + garray_T ga_menu; + garray_T ga_info; + int have_extra = FALSE; + int i; *matches = NULL; *numMatches = 0; @@ -4132,31 +4174,92 @@ ExpandUserList( return FAIL; ga_init2(&ga, sizeof(char *), 3); + ga_init2(&ga_abbr, sizeof(char *), 3); + ga_init2(&ga_kind, sizeof(char *), 3); + ga_init2(&ga_menu, sizeof(char *), 3); + ga_init2(&ga_info, sizeof(char *), 3); // Loop over the items in the list. FOR_ALL_LIST_ITEMS(retlist, li) { - char_u *p; + char_u *p = NULL; + char_u *abbr = NULL; + char_u *kind = NULL; + char_u *menu = NULL; + char_u *info = NULL; - if (li->li_tv.v_type != VAR_STRING || li->li_tv.vval.v_string == NULL) - continue; // Skip non-string items and empty strings - - p = vim_strsave(li->li_tv.vval.v_string); - if (p == NULL) - break; - - if (ga_grow(&ga, 1) == FAIL) + if (li->li_tv.v_type == VAR_STRING) + { + if (li->li_tv.vval.v_string == NULL) + continue; // Skip empty strings + p = vim_strsave(li->li_tv.vval.v_string); + } + else if (li->li_tv.v_type == VAR_DICT + && li->li_tv.vval.v_dict != NULL) + { + dict_T *d = li->li_tv.vval.v_dict; + char_u *word = dict_get_string(d, "word", FALSE); + + if (word == NULL) + continue; // "word" is required + p = vim_strsave(word); + abbr = dict_get_string(d, "abbr", TRUE); + kind = dict_get_string(d, "kind", TRUE); + menu = dict_get_string(d, "menu", TRUE); + info = dict_get_string(d, "info", TRUE); + if (abbr != NULL || kind != NULL || menu != NULL || info != NULL) + have_extra = TRUE; + } + else + continue; // Skip other types + + if (p == NULL + || ga_grow(&ga, 1) == FAIL + || ga_grow(&ga_abbr, 1) == FAIL + || ga_grow(&ga_kind, 1) == FAIL + || ga_grow(&ga_menu, 1) == FAIL + || ga_grow(&ga_info, 1) == FAIL) { vim_free(p); + vim_free(abbr); + vim_free(kind); + vim_free(menu); + vim_free(info); break; } - ((char_u **)ga.ga_data)[ga.ga_len] = p; - ++ga.ga_len; + ((char_u **)ga.ga_data)[ga.ga_len++] = p; + ((char_u **)ga_abbr.ga_data)[ga_abbr.ga_len++] = abbr; + ((char_u **)ga_kind.ga_data)[ga_kind.ga_len++] = kind; + ((char_u **)ga_menu.ga_data)[ga_menu.ga_len++] = menu; + ((char_u **)ga_info.ga_data)[ga_info.ga_len++] = info; } list_unref(retlist); *matches = ga.ga_data; *numMatches = ga.ga_len; + if (have_extra && ga.ga_len > 0) + { + xp->xp_files_abbr = (char_u **)ga_abbr.ga_data; + xp->xp_files_kind = (char_u **)ga_kind.ga_data; + xp->xp_files_menu = (char_u **)ga_menu.ga_data; + xp->xp_files_info = (char_u **)ga_info.ga_data; + } + else + { + // No extra info collected; free the placeholder NULL entries. + for (i = 0; i < ga_abbr.ga_len; i++) + vim_free(((char_u **)ga_abbr.ga_data)[i]); + vim_free(ga_abbr.ga_data); + for (i = 0; i < ga_kind.ga_len; i++) + vim_free(((char_u **)ga_kind.ga_data)[i]); + vim_free(ga_kind.ga_data); + for (i = 0; i < ga_menu.ga_len; i++) + vim_free(((char_u **)ga_menu.ga_data)[i]); + vim_free(ga_menu.ga_data); + for (i = 0; i < ga_info.ga_len; i++) + vim_free(((char_u **)ga_info.ga_data)[i]); + vim_free(ga_info.ga_data); + } return OK; } #endif diff --git a/src/structs.h b/src/structs.h index 8429ebe29e..4d92ca75a1 100644 --- a/src/structs.h +++ b/src/structs.h @@ -678,6 +678,17 @@ typedef struct expand int xp_selected; // selected index in completion char_u *xp_orig; // originally expanded string char_u **xp_files; // list of files + char_u **xp_files_abbr; // optional parallel array of display + // strings (override xp_files for the + // pum text); NULL if unused + char_u **xp_files_kind; // optional parallel array of "kind" + // strings; NULL if unused + char_u **xp_files_menu; // optional parallel array of "menu" + // strings (shown after the match); + // NULL if unused + char_u **xp_files_info; // optional parallel array of "info" + // strings (shown in info popup); + // NULL if unused char_u *xp_line; // text being completed #define EXPAND_BUF_LEN 256 char_u xp_buf[EXPAND_BUF_LEN]; // buffer for returned match diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index e793292ff7..efe73a1e6f 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -4594,6 +4594,53 @@ func Test_custom_completion() delfunc Check_customlist_completion endfunc +" Test that 'customlist' completion accepts dict items with extra info +" (kind/menu/info) for display in the popup menu, and that string items still +" work in the same list. +func Test_customlist_dict_completion() + func DictComp(A, L, P) + return [ + \ {'word': 'apple', 'kind': 'f', 'menu': 'fruit', 'info': 'A red fruit'}, + \ {'word': 'banana', 'kind': 'f', 'menu': 'fruit', 'info': 'A yellow fruit'}, + \ {'word': 'carrot', 'kind': 'v', 'menu': 'vegetable', 'info': 'An orange vegetable'}, + \ 'plain', + \ ] + endfunc + command -nargs=1 -complete=customlist,DictComp DictCmd echo + + " getcompletion() returns only the "word" of each item; string items pass + " through unchanged. + call assert_equal(['apple', 'banana', 'carrot', 'plain'], + \ getcompletion('', 'customlist,DictComp')) + + " Items missing a "word" key are silently skipped. + func DictCompMissingWord(A, L, P) + return [{'kind': 'x'}, {'word': 'ok'}] + endfunc + call assert_equal(['ok'], + \ getcompletion('', 'customlist,DictCompMissingWord')) + + " Tab completion still selects the word. + call feedkeys(":DictCmd a\\\"\", 'xt') + call assert_equal('"DictCmd apple', @:) + + " "abbr" overrides display only; "word" is what gets inserted. + func DictCompAbbr(A, L, P) + return [{'word': 'apple', 'abbr': 'APPLE🍎'}] + endfunc + call assert_equal(['apple'], + \ getcompletion('', 'customlist,DictCompAbbr')) + command -nargs=1 -complete=customlist,DictCompAbbr DictAbbrCmd echo + call feedkeys(":DictAbbrCmd \\\"\", 'xt') + call assert_equal('"DictAbbrCmd apple', @:) + + delcommand DictAbbrCmd + delcommand DictCmd + delfunc DictComp + delfunc DictCompMissingWord + delfunc DictCompAbbr +endfunc + func Test_custom_completion_with_glob() func TestGlobComplete(A, L, P) return split(glob('Xglob*'), "\n") diff --git a/src/version.c b/src/version.c index c75dd6a65f..3a27d0c52b 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 433, /**/ 432, /**/ From fde5a56ecbf9101314ddcc572533e147a9fb11ff Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 3 May 2026 17:47:50 +0000 Subject: [PATCH 165/545] patch 9.2.0434: cscope: filename interpreted by /bin/sh Problem: cs_create_connection() builds the cscope command by interpolating csinfo[i].fname (and ppath, flags) into a string and lets the shell parse it. Shell metacharacters in a database filename are therefore evaluated by /bin/sh before cscope is exec'd, rather than being passed through as a literal path (q1uf3ng) Solution: Build argv directly and execvp() the cscope binary without an intervening shell. closes: #20119 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Christian Brabandt --- src/if_cscope.c | 125 ++++++++++++++++++++++++++---------- src/testdir/test_cscope.vim | 7 ++ src/version.c | 2 + 3 files changed, 100 insertions(+), 34 deletions(-) diff --git a/src/if_cscope.c b/src/if_cscope.c index 7962000777..54cae51f75 100644 --- a/src/if_cscope.c +++ b/src/if_cscope.c @@ -829,11 +829,11 @@ cs_create_connection(int i) int to_cs[2], from_cs[2]; # endif int cmdlen; - int len; char *prog, *cmd, *ppath = NULL; size_t proglen; # ifdef MSWIN int fd; + int len; SECURITY_ATTRIBUTES sa; PROCESS_INFORMATION pi; STARTUPINFO si; @@ -872,7 +872,6 @@ cs_create_connection(int i) else if (csinfo[i].pid == 0) // child: run cscope. { char **argv = NULL; - int argc = 0; if (dup2(to_cs[0], STDIN_FILENO) == -1) PERROR("cs_create_connection 1"); @@ -956,48 +955,93 @@ cs_create_connection(int i) // run the cscope command # ifdef UNIX - vim_snprintf(cmd, cmdlen, "/bin/sh -c \"exec %s -dl -f %s", - prog, csinfo[i].fname); -# else - vim_snprintf(cmd, cmdlen, "%s -dl -f %s", prog, csinfo[i].fname); -# endif - if (csinfo[i].ppath != NULL) - { - len = (int)STRLEN(cmd); - vim_snprintf(cmd + len, cmdlen - len, " -P%s", csinfo[i].ppath); - } - if (csinfo[i].flags != NULL) { - len = (int)STRLEN(cmd); - vim_snprintf(cmd + len, cmdlen - len, " %s", csinfo[i].flags); - } -# ifdef UNIX - // terminate the -c command argument - STRCAT(cmd, "\""); + garray_T ga_argv; + char **tok = NULL; + int tokc = 0; - // on Win32 we still need prog - vim_free(prog); -# endif - vim_free(ppath); + ga_init2(&ga_argv, sizeof(char *), 8); + + // 'cscopeprg' may be multi-word (e.g. "cscope --foo"); + // split it into separate argv entries. + if (build_argv_from_string((char_u *)prog, &tok, &tokc) == FAIL) + exit(EXIT_FAILURE); + for (int k = 0; k < tokc; ++k) + { + if (ga_grow(&ga_argv, 1) == FAIL) + exit(EXIT_FAILURE); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = tok[k]; + } + VIM_CLEAR(tok); + + // Literal arguments: "-dl", "-f", fname. + if (ga_grow(&ga_argv, 3) == FAIL) + exit(EXIT_FAILURE); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = + (char *)vim_strsave((char_u *)"-dl"); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = + (char *)vim_strsave((char_u *)"-f"); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = + (char *)vim_strsave((char_u *)csinfo[i].fname); + + // "-P" as a single argv entry, if set. + if (ppath != NULL) + { + size_t plen = STRLEN(ppath) + 3; + char *parg = alloc(plen); + + if (parg == NULL || ga_grow(&ga_argv, 1) == FAIL) + exit(EXIT_FAILURE); + vim_snprintf(parg, plen, "-P%s", ppath); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = parg; + } + + // 'flags' is intended to be a sequence of cscope tokens + // like "-q -i somefile"; split it the same way as prog. + if (csinfo[i].flags != NULL) + { + if (build_argv_from_string((char_u *)csinfo[i].flags, + &tok, &tokc) == FAIL) + exit(EXIT_FAILURE); + for (int k = 0; k < tokc; ++k) + { + if (ga_grow(&ga_argv, 1) == FAIL) + exit(EXIT_FAILURE); + ((char **)ga_argv.ga_data)[ga_argv.ga_len++] = tok[k]; + } + vim_free(tok); + } + + // NULL-terminate argv. + if (ga_grow(&ga_argv, 1) == FAIL) + exit(EXIT_FAILURE); + ((char **)ga_argv.ga_data)[ga_argv.ga_len] = NULL; + + vim_free(prog); + vim_free(ppath); + vim_free(cmd); -# if defined(UNIX) # if defined(HAVE_SETSID) || defined(HAVE_SETPGID) - // Change our process group to avoid cscope receiving SIGWINCH. + // Change our process group to avoid cscope receiving SIGWINCH. # if defined(HAVE_SETSID) - (void)setsid(); + (void)setsid(); # else - if (setpgid(0, 0) == -1) - PERROR(_("cs_create_connection setpgid failed")); + if (setpgid(0, 0) == -1) + PERROR(_("cs_create_connection setpgid failed")); # endif # endif - if (build_argv_from_string((char_u *)cmd, &argv, &argc) == FAIL) - exit(EXIT_FAILURE); - if (execvp(argv[0], argv) == -1) - PERROR(_("cs_create_connection exec failed")); + argv = (char **)ga_argv.ga_data; + if (argv[0] == NULL || execvp(argv[0], argv) == -1) + { + fprintf(stderr, "%s\n", + _("cs_create_connection exec failed")); + fflush(stderr); + } - exit(127); - // NOTREACHED + exit(127); + // NOTREACHED + } } else // parent. { @@ -1016,6 +1060,19 @@ cs_create_connection(int i) } # else // MSWIN + vim_snprintf(cmd, cmdlen, "%s -dl -f %s", prog, csinfo[i].fname); + if (csinfo[i].ppath != NULL) + { + len = (int)STRLEN(cmd); + vim_snprintf(cmd + len, cmdlen - len, " -P%s", csinfo[i].ppath); + } + if (csinfo[i].flags != NULL) + { + len = (int)STRLEN(cmd); + vim_snprintf(cmd + len, cmdlen - len, " %s", csinfo[i].flags); + } + vim_free(ppath); + // Create a new process to run cscope and use pipes to talk with it GetStartupInfo(&si); si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; diff --git a/src/testdir/test_cscope.vim b/src/testdir/test_cscope.vim index ddd4de0b89..f44a480ec0 100644 --- a/src/testdir/test_cscope.vim +++ b/src/testdir/test_cscope.vim @@ -288,6 +288,13 @@ func Test_cscopeWithCscopeConnections() call assert_equal(cscope_connection(5, 'out'), 0) call assert_equal(cscope_connection(-1, 'out'), 0) + cscope kill -1 + + " Test: cscope file with shell meta chars + call writefile([], 'Xcscope2.out`id>Xid`', 'D') + call assert_fails('cscope add Xcscope2.out`id>Xid`', 'E609:') + call assert_false(filereadable('Xid')) + cscope kill -1 call CscopeSetupOrClean(0) endfunc diff --git a/src/version.c b/src/version.c index 3a27d0c52b..6d8157e4aa 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 434, /**/ 433, /**/ From 190cb3c2b9c769a3972bcfd991a7b5b6cb771ef0 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 3 May 2026 16:10:03 +0000 Subject: [PATCH 166/545] patch 9.2.0435: [security]: backticks in 'path' may cause shell execution on completion Problem: [security]: Backticks enclosed shell commands in the 'path' option value are executed during completion (q1uf3ng). Solution: Skip path entries containing backticks, add P_SECURE to 'path' option, so that it cannot be set from a modeline (for symmetry with the 'cdpath' option) Github Advisory: https://github.com/vim/vim/security/advisories/GHSA-hwg5-3cxw-wvvg Supported by AI. Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 5 ++++- src/findfile.c | 4 ++++ src/optiondefs.h | 2 +- src/testdir/test_find_complete.vim | 17 +++++++++++++++++ src/testdir/test_modeline.vim | 14 ++++++++++++++ src/version.c | 2 ++ 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 7e1d4cb67e..4628a1624c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 May 01 +*options.txt* For Vim version 9.2. Last change: 2026 May 03 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6799,6 +6799,9 @@ A jump table for the options with a short description can be found at |Q_op|. < Replace the ';' with a ':' or whatever separator is used. Note that this doesn't work when $INCL contains a comma or white space. + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. + *'perldll'* 'perldll' string (default depends on the build) global diff --git a/src/findfile.c b/src/findfile.c index ed0ee76c49..af9523dcda 100644 --- a/src/findfile.c +++ b/src/findfile.c @@ -2412,6 +2412,10 @@ expand_path_option( { buflen = copy_option_part(&path_option, buf, MAXPATHL, " ,"); + // do not expand backticks, could have been set via a modeline + if (vim_strchr(buf, '`') != NULL) + continue; + if (buf[0] == '.' && (buf[1] == NUL || vim_ispathsep(buf[1]))) { size_t plen; diff --git a/src/optiondefs.h b/src/optiondefs.h index 0a93b70f8e..f712ab7a14 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -1958,7 +1958,7 @@ static struct vimoption options[] = (char_u *)&p_pm, PV_NONE, did_set_backupext_or_patchmode, NULL, {(char_u *)"", (char_u *)0L} SCTX_INIT}, - {"path", "pa", P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP, + {"path", "pa", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE|P_COMMA|P_NODUP, (char_u *)&p_path, PV_PATH, NULL, NULL, { #if defined(AMIGA) || defined(MSWIN) diff --git a/src/testdir/test_find_complete.vim b/src/testdir/test_find_complete.vim index 079fb78043..8b8b71c303 100644 --- a/src/testdir/test_find_complete.vim +++ b/src/testdir/test_find_complete.vim @@ -161,4 +161,21 @@ func Test_find_complete() set path& endfunc +" Verify that backticks in 'path' are not executed +func Test_find_completion_backtick_in_path() + CheckUnix + CheckExecutable id + + new Xpoc.c + setl path+=`id>Xrce_marker` + " Triggering completion must not execute the backtick command. + call getcompletion('', 'file_in_path') + call assert_false(filereadable('Xrce_marker')) + call feedkeys(":find \t\n", "xt") + call assert_false(filereadable('Xrce_marker')) + + bwipe! + call delete('Xrce_marker') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_modeline.vim b/src/testdir/test_modeline.vim index 6884ab473d..6cbf87638a 100644 --- a/src/testdir/test_modeline.vim +++ b/src/testdir/test_modeline.vim @@ -665,4 +665,18 @@ func Test_modeline_strict_cannot_be_set_from_modeline() let &modeline = modeline endfunc +" Verify that backticks in 'path' set from a modeline are not executed +func Test_path_modeline() + let lines =<< trim END + // vim: set path+=foobar : + END + call writefile(lines, 'Xpoc.c', 'D') + + set nomodelinestrict modeline + call assert_fails('split Xpoc.c', 'E520:') + + bwipe! + set modelinestrict& modeline& +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 6d8157e4aa..2691f438b0 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 435, /**/ 434, /**/ From 77677c33dec485aadd371da75cce55d449b51798 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 3 May 2026 18:32:11 +0000 Subject: [PATCH 167/545] patch 9.2.0436: Buffer overflow when parsing overlong errorformat lines Problem: When an error line in a file passed to :cfile / :cgetfile is longer than IOSIZE, qf_parse_file_pfx() copies the tail into the fixed-size IObuff with STRMOVE(), overflowing the heap buffer. The same code path can also loop indefinitely because qf_parse_file_pfx() always returns QF_MULTISCAN when a tail is present, and qf_init_ext() unconditionally goes to "restofline" without bounding the tail length (Nabih). Solution: Remove the STRMOVE() into IObuff. In the QF_MULTISCAN branch, alias linebuf into the tail directly and update linelen, requiring strict progress (new length less than the previous length) before retrying; otherwise ignore the line. closes: #20126 Supported by AI Signed-off-by: Christian Brabandt --- src/quickfix.c | 9 ++++++++- src/testdir/test_quickfix.vim | 22 ++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/quickfix.c b/src/quickfix.c index 902c4e6f66..feeec18125 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -1412,7 +1412,6 @@ qf_parse_file_pfx( *fields->namebuf = NUL; if (tail && *tail) { - STRMOVE(IObuff, skipwhite(tail)); qfl->qf_multiscan = TRUE; return QF_MULTISCAN; } @@ -1579,7 +1578,15 @@ qf_parse_line( { // global file names status = qf_parse_file_pfx(idx, fields, qfl, tail); if (status == QF_MULTISCAN) + { + char_u *s = skipwhite(tail); + int new_linelen = (int)STRLEN(s); + if (new_linelen >= linelen) + return QF_IGNORE_LINE; + linebuf = s; + linelen = new_linelen; goto restofline; + } } if (fmt_ptr->flags == '-') // generally exclude this line { diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 69a4f6dafa..64ec97f504 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -7006,4 +7006,26 @@ func Test_quickfix_longline_noeol() call assert_equal(['okay'], readfile("XDONE")) endfunc +func Test_efm_overlongline() + let save_efm = &efm + " %r captures the tail. + set efm=%+O(%.%#)%r,%f:%l:%m + + " First line is longer than IOSIZE (1025) so the parser puts it in + " growbuf; the %r tail then far exceeds IObuff. + let lines = ['(short)' .. repeat('x', 4000), 'Xfile:10:msg'] + call writefile(lines, 'Xqferrlong', 'D') + + " Must complete without hanging or crashing. + cgetfile Xqferrlong + + " The well-formed line that follows is still parsed. + let well_formed = filter(getqflist(), 'v:val.lnum == 10') + call assert_equal(1, len(well_formed)) + call assert_equal('msg', well_formed[0].text) + + let &efm = save_efm + call setqflist([], 'f') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 2691f438b0..f377ae58ee 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 436, /**/ 435, /**/ From 9d3019104c37bad56ff5bdc7614b26cb3fea7ce4 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 3 May 2026 18:37:05 +0000 Subject: [PATCH 168/545] patch 9.2.0437: MS-Windows: cursor flicker in vtp mode Problem: MS-Windows: cursor flicker in vtp mode Solution: Skip mch_update_cursor() in cursor_visible() when vtp is active (Yasuhiro Matsumoto). In vtp (ConPTY) mode the cursor visibility is controlled by DECTCEM (\033[?25h / \033[?25l). The follow-up call to mch_update_cursor() then re-emits DECSCUSR (\033[0 q etc.) on every visibility toggle even though the cursor shape did not change. Some terminals briefly redisplay the cursor when DECSCUSR arrives, so this can cause a visible flash at the position the cursor will be moved to next (e.g. column 0 ahead of a line redraw). In non-vtp mode the call is still required because SetConsoleCursorInfo() inside mch_set_cursor_shape() reads s_cursor_visible to apply the visibility change, so keep that path unchanged. closes: #20122 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/os_win32.c | 10 ++++++++++ src/version.c | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/os_win32.c b/src/os_win32.c index e8f8162f1b..ab12d5a051 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -7104,9 +7104,19 @@ cursor_visible(BOOL fVisible) s_cursor_visible = fVisible; if (vtp_working) + { + // In vtp mode, visibility is controlled solely by DECTCEM. Skip + // mch_update_cursor() since shape is independent of visibility and + // re-emitting DECSCUSR can cause the terminal to briefly redisplay + // the cursor while a redraw is in progress. vtp_printf("\033[?25%c", fVisible ? 'h' : 'l'); + return; + } # ifdef MCH_CURSOR_SHAPE + // Non-vtp Windows console: SetConsoleCursorInfo() consults + // s_cursor_visible inside mch_set_cursor_shape(), so the call is needed + // to apply the new visibility. mch_update_cursor(); # endif } diff --git a/src/version.c b/src/version.c index f377ae58ee..597842bf9c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 437, /**/ 436, /**/ From cb0b4cf45c627263c844348a0ec6388dcfef9fcb Mon Sep 17 00:00:00 2001 From: Felipe Matarazzo Date: Mon, 4 May 2026 19:10:37 +0000 Subject: [PATCH 169/545] Fix a few more typos closes: #20135 Signed-off-by: Felipe Matarazzo Signed-off-by: Christian Brabandt --- runtime/doc/ft_mp.txt | 4 ++-- runtime/ftplugin/ptx.vim | 3 ++- runtime/ftplugin/readline.vim | 3 ++- runtime/indent/cdl.vim | 3 ++- runtime/indent/javascriptreact.vim | 2 +- runtime/indent/rust.vim | 3 ++- runtime/indent/sh.vim | 5 +++-- runtime/indent/stylus.vim | 3 ++- runtime/indent/typescriptreact.vim | 2 +- src/normal.c | 2 +- src/vim9cmds.c | 2 +- 11 files changed, 19 insertions(+), 13 deletions(-) diff --git a/runtime/doc/ft_mp.txt b/runtime/doc/ft_mp.txt index 7106edf98a..f4ea042576 100644 --- a/runtime/doc/ft_mp.txt +++ b/runtime/doc/ft_mp.txt @@ -1,4 +1,4 @@ -*ft_mp.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*ft_mp.txt* For Vim version 9.2. Last change: 2026 May 04 This is the documentation for the METAFONT and MetaPost filetype plugins. Unless otherwise specified, the commands, settings and mappings defined below @@ -25,7 +25,7 @@ MetaPost documents, including syntax coloring, indentation, and completion. Defining indentation rules for METAFONT and MetaPost code is tricky and somewhat subjective, because the syntax is quite liberal. The plugin uses some heuristics that work well most of the time, but in particular cases you may -want to to override the automatic rules, so that the manually defined +want to override the automatic rules, so that the manually defined indentation is preserved by commands like `gg=G`. This can be achieved by appending `%>`, `%<`, `%=` or `%!` to a line to diff --git a/runtime/ftplugin/ptx.vim b/runtime/ftplugin/ptx.vim index 12b127c8fc..6e4c0f87a6 100644 --- a/runtime/ftplugin/ptx.vim +++ b/runtime/ftplugin/ptx.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file -" Language: Nvidia PTX (Parellel Thread Execution) +" Language: Nvidia PTX (Parallel Thread Execution) " Maintainer: Yinzuo Jiang " Last Change: 2024-12-05 +" 2026 May 04 by Vim Project: fix typo if exists("b:did_ftplugin") finish diff --git a/runtime/ftplugin/readline.vim b/runtime/ftplugin/readline.vim index d0b6c2d654..996b533b83 100644 --- a/runtime/ftplugin/readline.vim +++ b/runtime/ftplugin/readline.vim @@ -4,6 +4,7 @@ " Previous Maintainer: Nikolai Weibull " Last Change: 2024 Sep 19 (simplify keywordprg #15696) " 2024 Jul 22 by Vim project (use :hor term #17822) +" 2026 May 04 by Vim Project: fix typo if exists("b:did_ftplugin") finish @@ -26,7 +27,7 @@ if exists("loaded_matchit") && !exists("b:match_words") endif if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") - let b:browsefilter = "Readline Intialization Files (inputrc, .inputrc)\tinputrc;*.inputrc\n" + let b:browsefilter = "Readline Initialization Files (inputrc, .inputrc)\tinputrc;*.inputrc\n" if has("win32") let b:browsefilter ..= "All Files (*.*)\t*\n" else diff --git a/runtime/indent/cdl.vim b/runtime/indent/cdl.vim index da675698cf..63e4171ea6 100644 --- a/runtime/indent/cdl.vim +++ b/runtime/indent/cdl.vim @@ -2,6 +2,7 @@ " Maintainer: Raul Segura Acevedo (Invalid email address) " Doug Kearns " Last Change: 2022 Apr 06 +" 2026 May 04 by Vim Project: fix typo if exists("b:did_indent") "finish @@ -71,7 +72,7 @@ fun! CdlGetIndent(lnum) " One 'closing' element at the beginning of the line has already reduced the " indent, but 'else', 'elseif' & 'then' increment it for the next line. " '=' at the beginning already has the right indent (increased for - " asignments). + " assignments). let f = -1 let inicio = matchend(line, '^\c\s*\(else\a*\|then\|endif\|/[*/]\|[);={]\)') if inicio > 0 diff --git a/runtime/indent/javascriptreact.vim b/runtime/indent/javascriptreact.vim index a3482097ee..5edb3f1d8e 100644 --- a/runtime/indent/javascriptreact.vim +++ b/runtime/indent/javascriptreact.vim @@ -1,2 +1,2 @@ -" Placeholder for backwards compatilibity: .jsx used to stand for JavaScript. +" Placeholder for backwards compatibility: .jsx used to stand for JavaScript. runtime! indent/javascript.vim diff --git a/runtime/indent/rust.vim b/runtime/indent/rust.vim index f449a19de2..0fd05eb7cb 100644 --- a/runtime/indent/rust.vim +++ b/runtime/indent/rust.vim @@ -4,8 +4,9 @@ " Last Change: 2023-09-11 " 2024 Jul 04 by Vim Project: use shiftwidth() instead of hard-coding shifted values #15138 " 2025 Dec 29 by Vim Project: clean up -" 2025 Dec 31 by Vim Project: correcly indent after nested array literal #19042 +" 2025 Dec 31 by Vim Project: correctly indent after nested array literal #19042 " 2026 Jan 28 by Vim Project: fix indentation when a string literal contains 'if' #19265 +" 2026 May 04 by Vim Project: fix typo " For bugs, patches and license go to https://github.com/rust-lang/rust.vim " Note: upstream seems umaintained: https://github.com/rust-lang/rust.vim/issues/502 diff --git a/runtime/indent/sh.vim b/runtime/indent/sh.vim index daf9170bf3..ceeda9ed32 100644 --- a/runtime/indent/sh.vim +++ b/runtime/indent/sh.vim @@ -3,10 +3,11 @@ " Maintainer: Christian Brabandt " Original Author: Nikolai Weibull " Previous Maintainer: Peter Aronoff -" Latest Revision: 2019-10-24 +" Latest Revision: 20260504 " License: Vim (see :h license) " Repository: https://github.com/chrisbra/vim-sh-indent " Changelog: +" 20260504 - fix typo " 20250906 - indent function closing properly on multiline commands " 20250318 - Detect local arrays in functions " 20241411 - Detect dash character in function keyword for @@ -233,7 +234,7 @@ function! s:is_array(line) endfunction function! s:is_in_block(line) - " checks whether a:line is whithin a + " checks whether a:line is within a " block e.g. a shell function " foo() { " .. diff --git a/runtime/indent/stylus.vim b/runtime/indent/stylus.vim index 89634f0914..b222ab3210 100644 --- a/runtime/indent/stylus.vim +++ b/runtime/indent/stylus.vim @@ -2,6 +2,7 @@ " Language: Stylus " Maintainer: Marc Harter " Last Change: 2010 May 21 +" 2026 May 04 by Vim Project: fix typo " Based On: sass.vim from Tim Pope " if exists("b:did_indent") @@ -97,7 +98,7 @@ function! GetStylusIndent() let line = substitute(getline(lnum),'[\s()]\+$','','') " get last line strip ending whitespace let cline = substitute(substitute(getline(v:lnum),'\s\+$','',''),'^\s\+','','') " get current line, trimmed let lastcol = strlen(line) " get last col in prev line - let line = substitute(line,'^\s\+','','') " then remove preceeding whitespace + let line = substitute(line,'^\s\+','','') " then remove preceding whitespace let indent = indent(lnum) " get indent on prev line let cindent = indent(v:lnum) " get indent on current line let increase = indent + &sw " increase indent by the shift width diff --git a/runtime/indent/typescriptreact.vim b/runtime/indent/typescriptreact.vim index 052bddddbd..5d9339f21a 100644 --- a/runtime/indent/typescriptreact.vim +++ b/runtime/indent/typescriptreact.vim @@ -1,2 +1,2 @@ -" Placeholder for backwards compatilibity: .tsx used to stand for TypeScript. +" Placeholder for backwards compatibility: .tsx used to stand for TypeScript. runtime! indent/typescript.vim diff --git a/src/normal.c b/src/normal.c index b402aa81da..b74c937c77 100644 --- a/src/normal.c +++ b/src/normal.c @@ -6513,7 +6513,7 @@ set_op_var(int optype) /* * Handle linewise operator "dd", "yy", etc. * - * "_" is is a strange motion command that helps make operators more logical. + * "_" is a strange motion command that helps make operators more logical. * It is actually implemented, but not documented in the real Vi. This motion * command actually refers to "the current line". Commands like "dd" and "yy" * are really an alternate form of "d_" and "y_". It does accept a count, so diff --git a/src/vim9cmds.c b/src/vim9cmds.c index 5005cf898a..1a8ff15f48 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -215,7 +215,7 @@ compile_lock_unlock( // These checks are reminiscent of the variable_exists function. // But most of the matches require special handling. - // If bare name is is locally accessible, except for local var, + // If bare name is locally accessible, except for local var, // then put it on the stack to use with ISN_LOCKUNLOCK. // This could be v.memb, v[idx_key]; bare class variable, // function arg. The item on the stack, will be passed From bb807ebc8a7a6ad3f3d330cf19ce775cb40a2811 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Mon, 4 May 2026 19:17:52 +0000 Subject: [PATCH 170/545] runtime(doc): Tweak documentation style closes: #20134 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 16 ++++++++-------- runtime/doc/options.txt | 8 ++++---- runtime/doc/syntax.txt | 31 +++++++++++++++---------------- runtime/doc/tabpage.txt | 4 ++-- runtime/doc/vim9.txt | 7 +------ 5 files changed, 30 insertions(+), 36 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index b294b63884..e9036c18f1 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 Apr 28 +*builtin.txt* For Vim version 9.2. Last change: 2026 May 04 VIM REFERENCE MANUAL by Bram Moolenaar @@ -11904,16 +11904,16 @@ tabpanel_getinfo() *tabpanel_getinfo()* tabpanel_scroll({n} [, {opts}]) *tabpanel_scroll()* Scroll the tabpanel by {n} rows. Positive values scroll down - (later tabs become visible), negative values scroll up. The - new offset is clamped to the valid range. + (later tab pages become visible), negative values scroll up. + The new offset is clamped to the valid range. When {opts} is a |Dictionary| and its "absolute" entry is - |TRUE|, {n} is used as the new absolute scroll offset - instead of a delta. + |TRUE|, {n} is used as the new absolute scroll offset instead + of a delta. - Returns |TRUE| if the scroll offset changed, |FALSE| - otherwise (for example when the tabpanel is not shown, or - the offset is already at the requested value). + Returns |TRUE| if the scroll offset changed, |FALSE| otherwise + (for example when the tabpanel is not shown, or the offset is + already at the requested value). Return type: |vim9-boolean| diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 4628a1624c..c4b2df144b 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 May 03 +*options.txt* For Vim version 9.2. Last change: 2026 May 04 VIM REFERENCE MANUAL by Bram Moolenaar @@ -10418,9 +10418,9 @@ A jump table for the options with a short description can be found at |Q_op|. "noselect" If 'wildmenu' is enabled, show the menu but do not preselect the first item. "noinsert" If 'wildmenu' is enabled, show the menu and preselect - the first match, but do not insert it in the - command line. If both "noinsert" and "noselect" are - present, "noselect" takes precedence. + the first match, but do not insert it in the command + line. If both "noinsert" and "noselect" are present, + "noselect" takes precedence. If only one match exists, it is completed fully, unless "noselect" or "noinsert" is specified. diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index efebe56674..677e7cc3eb 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 Apr 30 +*syntax.txt* For Vim version 9.2. Last change: 2026 May 04 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6135,15 +6135,16 @@ StatusLine Status line of current window. current window's status line. see |hl-VertSplit|. *hl-StatusLineNC* StatusLineNC status lines of not-current windows. - Also used for the vertical separator cell adjacent to a + Also used for the vertical separator cell adjacent to the non-current window's status line. see |hl-VertSplitNC|. Note: If this is equal to "StatusLine", Vim will use "^^^" in the status line of the current window. *hl-StatusLineTerm* StatusLineTerm Status line of current window, if it is a |terminal| window. *hl-StatusLineTermNC* -StatusLineTermNC Status lines of not-current windows that is a - |terminal| window. +StatusLineTermNC + Status lines of not-current windows that is a |terminal| + window. *hl-TabLine* TabLine Tab pages line, not active tab page label. *hl-TabLineFill* @@ -6163,19 +6164,19 @@ Title Titles for output from ":set all", ":autocmd" etc. *hl-TitleBar* TitleBar Title bar for the active Gui's window. This feature is supported only in the MS-Windows GUI. - See |gui-w32-title-bar| for details + See |gui-w32-title-bar| for details. Only the `guibg` and `guifg` highlight arguments are effective. *hl-TitleBarNC* TitleBarNC Title bar for inactive Gui's window. This feature is supported only in the MS-Windows GUI. - See |gui-w32-title-bar| for details + See |gui-w32-title-bar| for details. Only the `guibg` and `guifg` highlight arguments are effective. *hl-Visual* Visual Visual mode selection. *hl-VisualNOS* VisualNOS Visual mode selection when vim is "Not Owning the Selection". - Only X11 Gui's |gui-x11|, |xterm-clipboard| and |wayland-selections| - supports this. + Only X11 Gui's |gui-x11|, |xterm-clipboard| and + |wayland-selections| supports this. *hl-WarningMsg* WarningMsg Warning messages. *hl-WildMenu* @@ -6195,10 +6196,9 @@ Menu Current font, background and foreground colors of the menus. Also used for the toolbar. Applicable highlight arguments: font, guibg, guifg. - NOTE: For Motif the font argument actually - specifies a fontset at all times, no matter if 'guifontset' is - empty, and as such it is tied to the current |:language| when - set. + NOTE: For Motif the font argument actually specifies a fontset + at all times, no matter if 'guifontset' is empty, and as such + it is tied to the current |:language| when set. *hl-Scrollbar* Scrollbar Current background and foreground of the main window's @@ -6209,10 +6209,9 @@ Scrollbar Current background and foreground of the main window's Tooltip Current font, background and foreground of the tooltips. Applicable highlight arguments: font, guibg, guifg. - NOTE: For Motif the font argument actually - specifies a fontset at all times, no matter if 'guifontset' is - empty, and as such it is tied to the current |:language| when - set. + NOTE: For Motif the font argument actually specifies a fontset + at all times, no matter if 'guifontset' is empty, and as such + it is tied to the current |:language| when set. ============================================================================== 15. Linking groups *:hi-link* *:highlight-link* *E412* *E413* diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt index d6db4e8d04..1b6f781272 100644 --- a/runtime/doc/tabpage.txt +++ b/runtime/doc/tabpage.txt @@ -1,4 +1,4 @@ -*tabpage.txt* For Vim version 9.2. Last change: 2026 Apr 28 +*tabpage.txt* For Vim version 9.2. Last change: 2026 May 04 VIM REFERENCE MANUAL by Bram Moolenaar @@ -516,7 +516,7 @@ a new, empty tab page; it is treated the same as a single click. For finer-grained control, the 'tabpanel' value may contain |stl-%[FuncName]| click regions. Clicks on those regions are dispatched to the callback -function instead of falling through to tab selection. +function instead of falling through to tab page selection. ============================================================================== 6. Setting 'guitablabel' *setting-guitablabel* diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index b107d62c7a..5d6b71b5c2 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*vim9.txt* For Vim version 9.2. Last change: 2026 May 04 VIM REFERENCE MANUAL by Bram Moolenaar @@ -2355,11 +2355,6 @@ in Vim9 script. let &laststatus=v:true vim9cmd &laststatus = true -< - - Not using a string where an argument requires a string (|E1174|) >vim - - echo substitute('Hallo', 'a', 'e', v:true) - vim9cmd echo substitute('Hallo', 'a', 'e', true) # E1174: String... < One consequence is that the item type of a list or dict given to |map()| must not change when its type is either declared or inferred. For example, this From 20a124a6e0e2445c500ccc7c496a8fe5d334aed8 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Mon, 4 May 2026 19:22:25 +0000 Subject: [PATCH 171/545] patch 9.2.0438: tests: test_plugin_termdebug is flaky Problem: Test_termdebug_tbreak(), Test_termdebug_basic(), and Test_termdebug_toggle_break() use synchronous assert_equal() to check breakpoint signs immediately after sending commands to gdb. On slow CI (ASAN, ARM64, macOS) gdb may not have processed the response yet, causing the sign to be missing. Solution: Wrap the three assertions in WaitForAssert() to poll until the signs are placed, matching the pattern already used by the other assertions in the same tests (Jesse Rosenstock). closes: #20133 Co-authored-by: Gemini Signed-off-by: Jesse Rosenstock Signed-off-by: Christian Brabandt --- src/testdir/test_plugin_termdebug.vim | 12 ++++++------ src/version.c | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/testdir/test_plugin_termdebug.vim b/src/testdir/test_plugin_termdebug.vim index f651806483..4e89252228 100644 --- a/src/testdir/test_plugin_termdebug.vim +++ b/src/testdir/test_plugin_termdebug.vim @@ -162,10 +162,10 @@ func Test_termdebug_basic() Break 9 call term_wait(gdb_buf) redraw! - call assert_equal([ + call WaitForAssert({-> assert_equal([ \ {'lnum': 9, 'id': 1014, 'name': 'debugBreakpoint1.0', \ 'priority': 110, 'group': 'TermDebug'}], - \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) Run call term_wait(gdb_buf, 400) redraw! @@ -324,12 +324,12 @@ func Test_termdebug_tbreak() call term_wait(gdb_buf) redraw! " both temporary and normal breakpoint signs were displayed... - call assert_equal([ + call WaitForAssert({-> assert_equal([ \ {'lnum': temp_bp_line, 'id': 1014, 'name': 'debugBreakpoint1.0', \ 'priority': 110, 'group': 'TermDebug'}, \ {'lnum': bp_line, 'id': 2014, 'name': 'debugBreakpoint2.0', \ 'priority': 110, 'group': 'TermDebug'}], - \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) Run call term_wait(gdb_buf, 400) @@ -717,10 +717,10 @@ func Test_termdebug_toggle_break() call term_wait(gdb_buf) redraw! - call assert_equal([ + call WaitForAssert({-> assert_equal([ \ {'lnum': bp_line, 'id': 1014, 'name': 'debugBreakpoint1.0', \ 'priority': 110, 'group': 'TermDebug'}], - \ sign_getplaced('', #{group: 'TermDebug'})[0].signs) + \ sign_getplaced('', #{group: 'TermDebug'})[0].signs)}) RunOrContinue call term_wait(gdb_buf, 400) diff --git a/src/version.c b/src/version.c index 597842bf9c..59084a3625 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 438, /**/ 437, /**/ From 3bfffcc29054faff2dbec2d765317ee09e9ef827 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 4 May 2026 19:53:10 +0000 Subject: [PATCH 172/545] patch 9.2.0439: completion: info popup not removed in cmdline mode Problem: Info popup isn't removed when selecting an item that doesn't have "info" in cmdline completion, which is inconsistent with Insert mode behavior. Solution: Set pum_call_update_screen in cmdline mode (zeertzjq). closes: #20128 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/popupwin.c | 4 ++ .../dumps/Test_customlist_info_popup_1.dump | 12 ++++++ .../dumps/Test_customlist_info_popup_2.dump | 12 ++++++ .../dumps/Test_customlist_info_popup_3.dump | 12 ++++++ .../dumps/Test_customlist_info_popup_4.dump | 12 ++++++ .../dumps/Test_customlist_info_popup_5.dump | 12 ++++++ src/testdir/test_cmdline.vim | 43 +++++++++++++++++++ src/version.c | 2 + 8 files changed, 109 insertions(+) create mode 100644 src/testdir/dumps/Test_customlist_info_popup_1.dump create mode 100644 src/testdir/dumps/Test_customlist_info_popup_2.dump create mode 100644 src/testdir/dumps/Test_customlist_info_popup_3.dump create mode 100644 src/testdir/dumps/Test_customlist_info_popup_4.dump create mode 100644 src/testdir/dumps/Test_customlist_info_popup_5.dump diff --git a/src/popupwin.c b/src/popupwin.c index fb6e17d058..f019943805 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -5894,6 +5894,10 @@ popup_hide_info(void) { popup_on_cmdline = wp->w_popup_flags & POPF_ON_CMDLINE; popup_hide(wp); + if (State & MODE_CMDLINE) + // Cmdline mode doesn't normally call update_screen(), so it's + // necessary to use pum_call_update_screen() here. + pum_call_update_screen(); } } diff --git a/src/testdir/dumps/Test_customlist_info_popup_1.dump b/src/testdir/dumps/Test_customlist_info_popup_1.dump new file mode 100644 index 0000000000..0bb1ddb7b1 --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_1.dump @@ -0,0 +1,12 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @26|╔+0#0000001#e0e0e08|═@12|X| +0#4040ff13#ffffff0@31 +|~| @6| +0#0000001#e0e0e08|a|p@1|l|e| @1|f| |f|r|u|i|t| @4|║| |A| |r|e|d| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@31 +|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|╚+0&#e0e0e08|═@12|⇲| +0#4040ff13#ffffff0@31 +|~| @6| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@46 +|:+0#0000000&|D|i|c|t|C|m|d| |a|p@1|l|e> @60 diff --git a/src/testdir/dumps/Test_customlist_info_popup_2.dump b/src/testdir/dumps/Test_customlist_info_popup_2.dump new file mode 100644 index 0000000000..3959110152 --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_2.dump @@ -0,0 +1,12 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @6| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@15|X| +0#4040ff13#ffffff0@28 +|~| @6| +0#0000001#e0e0e08|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|║| |A| |y|e|l@1|o|w| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@28 +|~| @6| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| |╚+0&#e0e0e08|═@15|⇲| +0#4040ff13#ffffff0@28 +|~| @6| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@46 +|:+0#0000000&|D|i|c|t|C|m|d| |b|a|n|a|n|a> @59 diff --git a/src/testdir/dumps/Test_customlist_info_popup_3.dump b/src/testdir/dumps/Test_customlist_info_popup_3.dump new file mode 100644 index 0000000000..f67d860c5d --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_3.dump @@ -0,0 +1,12 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @6| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@20|X| +0#4040ff13#ffffff0@23 +|~| @6| +0#0000001#e0e0e08|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| |║| |A|n| |o|r|a|n|g|e| |v|e|g|e|t|a|b|l|e| |║| +0#4040ff13#ffffff0@23 +|~| @6| +0#0000001#ffd7ff255|p|l|a|i|n| @13|╚+0&#e0e0e08|═@20|⇲| +0#4040ff13#ffffff0@23 +|:+0#0000000&|D|i|c|t|C|m|d| |c|a|r@1|o|t> @59 diff --git a/src/testdir/dumps/Test_customlist_info_popup_4.dump b/src/testdir/dumps/Test_customlist_info_popup_4.dump new file mode 100644 index 0000000000..0092776a94 --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_4.dump @@ -0,0 +1,12 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @6| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#e0e0e08|p|l|a|i|n| @13| +0#4040ff13#ffffff0@46 +|:+0#0000000&|D|i|c|t|C|m|d| |p|l|a|i|n> @60 diff --git a/src/testdir/dumps/Test_customlist_info_popup_5.dump b/src/testdir/dumps/Test_customlist_info_popup_5.dump new file mode 100644 index 0000000000..37242aec78 --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_5.dump @@ -0,0 +1,12 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @6| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@46 +|:+0#0000000&|D|i|c|t|C|m|d| > @65 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index efe73a1e6f..d58ff2b6d0 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -4641,6 +4641,49 @@ func Test_customlist_dict_completion() delfunc DictCompAbbr endfunc +func Test_customlist_dict_completion_info_popup() + CheckScreendump + + let lines =<< trim END + func DictComp(A, L, P) + return [ + \ {'word': 'apple', 'kind': 'f', 'menu': 'fruit', 'info': 'A red fruit'}, + \ {'word': 'banana', 'kind': 'f', 'menu': 'fruit', 'info': 'A yellow fruit'}, + \ {'word': 'carrot', 'kind': 'v', 'menu': 'vegetable', 'info': 'An orange vegetable'}, + \ 'plain', + \ ] + endfunc + command -nargs=1 -complete=customlist,DictComp DictCmd echo + set wildmenu wildoptions=pum completeopt=menu,popup + END + call writefile(lines, 'XTest_customlist_info_popup', 'D') + let rows = 12 + let buf = RunVimInTerminal('-S XTest_customlist_info_popup', {'rows': rows}) + + call term_sendkeys(buf, ":DictCmd \") + call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd apple') + 1)], g:test_timeout, ((rows - 4), 'A red fruit')) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_1', {}) + + call term_sendkeys(buf, "\") + call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd banana') + 1)], g:test_timeout, ((rows - 3), 'A yellow fruit')) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_2', {}) + + call term_sendkeys(buf, "\") + call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd carrot') + 1)], g:test_timeout, ((rows - 2), 'An orange vegetable')) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_3', {}) + + call term_sendkeys(buf, "\") + call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd plain') + 1)], g:test_timeout, ((rows - 1), '^\~\s\+plain\s\+$')) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_4', {}) + + call term_sendkeys(buf, "\") + call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd ') + 1)], g:test_timeout) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_5', {}) + + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) +endfunc + func Test_custom_completion_with_glob() func TestGlobComplete(A, L, P) return split(glob('Xglob*'), "\n") diff --git a/src/version.c b/src/version.c index 59084a3625..81809c9ea4 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 439, /**/ 438, /**/ From 0c998003bc6ae20e10a6a7362edf52da2eb12019 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 4 May 2026 19:58:27 +0000 Subject: [PATCH 173/545] patch 9.2.0440: MS-Windows: cursor flicker during update_screen() Problem: MS-Windows: cursor flicker during update_screen() Solution: Hide the cursor during update_screen() to avoid Windows ConPTY flicker (Yasuhiro Matsumoto). On terminals that do not honor synchronized output mode (e.g. Windows ConPTY), update_screen() emits cell positioning and content as multiple Win32 console writes through mch_write(), which the terminal renders as separate frames. This shows up as the cursor briefly jumping to column 1 of rows being redrawn, especially during async redraws around the popup completion menu. Disable the cursor with cursor_off() at the start of update_screen() and restore it with cursor_on() at the end, but only when synchronized output mode is not active. When it is, the redraw is already atomic from the terminal's view and hiding the cursor would only add visible blink with no benefit. closes: #20121 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/drawscreen.c | 7 +++++++ src/proto/term.pro | 1 + src/term.c | 11 +++++++++++ src/version.c | 2 ++ 4 files changed, 21 insertions(+) diff --git a/src/drawscreen.c b/src/drawscreen.c index 702d686aba..cecfcccb62 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -160,6 +160,11 @@ update_screen(int type_arg) } updating_screen = TRUE; + // Hide the cursor while redrawing when sync output is not active, to + // avoid visible cursor flicker on terminals like Windows ConPTY. + int hid_cursor = !sync_output_active(); + if (hid_cursor) + cursor_off(); term_set_sync_output(TERM_SYNC_OUTPUT_ENABLE); #ifdef FEAT_PROP_POPUP @@ -447,6 +452,8 @@ update_screen(int type_arg) #endif term_set_sync_output(TERM_SYNC_OUTPUT_DISABLE); + if (hid_cursor) + cursor_on(); return OK; } diff --git a/src/proto/term.pro b/src/proto/term.pro index 9653a35af3..3f099338be 100644 --- a/src/proto/term.pro +++ b/src/proto/term.pro @@ -98,5 +98,6 @@ void cterm_color2rgb(int nr, char_u *r, char_u *g, char_u *b, char_u *ansi_idx); int term_replace_keycodes(char_u *ta_buf, int ta_len, int len_arg); void term_disable_dec(void); void term_set_win_resize(bool state); +int sync_output_active(void); void term_set_sync_output(int flags); /* vim: set ft=c : */ diff --git a/src/term.c b/src/term.c index 07eb5974b7..37f05ed016 100644 --- a/src/term.c +++ b/src/term.c @@ -8094,6 +8094,17 @@ term_set_win_resize(bool state) } #endif + int +sync_output_active(void) +{ +#ifdef FEAT_GUI + if (gui.in_use) + return TRUE; +#endif + return p_tsy && (sync_output_setting == 1 || sync_output_setting == 2) + && *T_BSU != NUL && *T_ESU != NUL; +} + /* * Enable or disable synchronized output if possible. Specification can be found * here: diff --git a/src/version.c b/src/version.c index 81809c9ea4..34f3dd8508 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 440, /**/ 439, /**/ From 8c7d824b73ff939890f13c3792e45833a651a840 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Mon, 4 May 2026 20:03:46 +0000 Subject: [PATCH 174/545] patch 9.2.0441: statusline: click handler not called on multi-line statusline Problem: With a multi-line statusline clicking on a "%[FuncName]...%[]" or "%@FuncName@..." region defined on a row other than the last drawn row does not invoke the handler (Christian Robinson, after v9.2.0338) Solution: In win_redr_custom() the click region table reflects only the last iteration of the per-row draw loop, so click regions are recorded only for the last row. Move the click-region resolution inside the loop and append regions for each row using vim_realloc(). This also fixes a leak of clicktab[].funcname for non-last rows (Hirohito Higashi). fixes: #20116 closes: #20120 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/screen.c | 204 ++++++++++++++++---------------- src/testdir/test_statusline.vim | 38 ++++++ src/version.c | 2 + 3 files changed, 140 insertions(+), 104 deletions(-) diff --git a/src/screen.c b/src/screen.c index 5dfaed93c1..da4d6e2783 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1414,6 +1414,36 @@ win_redr_custom( char_u *stl_tmp = (stl == NULL) ? (char_u *)"" : stl; int col_save = col; + // Determine where click regions for this draw will be stored, and reset + // the destination so each row in the loop below can append. This must + // be done before the loop because for a multi-line statusline each row + // contributes its own click regions. + stl_click_region_T **out_regions = NULL; + int *out_count = NULL; + int region_base_col = 0; + if (!draw_ruler) + { + if (wp != NULL) + { + out_regions = &wp->w_stl_click; + out_count = &wp->w_stl_click_count; + region_base_col = wp->w_wincol; + } + else + { + // 'tabline': store regions in global state since there is no + // associated window. + out_regions = &tabline_stl_click; + out_count = &tabline_stl_click_count; + region_base_col = firstwin->w_wincol; + } + + for (n = 0; n < *out_count; n++) + vim_free((*out_regions)[n].funcname); + VIM_CLEAR(*out_regions); + *out_count = 0; + } + for (int i = 0; i < stlh_cnt; i++) { col = col_save; @@ -1472,6 +1502,76 @@ win_redr_custom( curattr = highlight_user[hltab[n].userhl - 1]; } screen_puts(p, row + i, col, curattr); + + // Append click regions for this row. clicktab reflects the line + // just rendered, so each row of a multi-line statusline contributes + // its own regions. + if (out_regions != NULL) + { + int click_count = 0; + for (n = 0; clicktab[n].start != NULL; n++) + click_count++; + + if (click_count > 0) + { + stl_click_region_T *new_arr = vim_realloc(*out_regions, + sizeof(stl_click_region_T) + * (*out_count + click_count)); + if (new_arr != NULL) + { + char_u *cur_funcname = NULL; + int cur_minwid = 0; + int region_start = region_base_col; + + *out_regions = new_arr; + + len = 0; + p = buf; + for (n = 0; clicktab[n].start != NULL; n++) + { + len += vim_strnsize(p, + (int)(clicktab[n].start - p)); + p = clicktab[n].start; + + if (cur_funcname != NULL) + { + stl_click_region_T *r = + &(*out_regions)[*out_count]; + r->row = row + i; + r->col_start = region_start; + r->col_end = region_base_col + len; + r->funcname = vim_strsave(cur_funcname); + r->minwid = cur_minwid; + r->tabnr = 0; + (*out_count)++; + } + + cur_funcname = clicktab[n].funcname; + cur_minwid = clicktab[n].minwid; + region_start = region_base_col + len; + } + + // Close final region if it extends to the end. + if (cur_funcname != NULL) + { + stl_click_region_T *r = + &(*out_regions)[*out_count]; + r->row = row + i; + r->col_start = region_start; + r->col_end = region_base_col + maxwidth; + r->funcname = vim_strsave(cur_funcname); + r->minwid = cur_minwid; + r->tabnr = 0; + (*out_count)++; + } + } + } + } + + // Free the funcname strings allocated by build_stl_str_hl_local() + // for this line. They have been copied into the region array above. + for (n = 0; clicktab[n].start != NULL; n++) + vim_free(clicktab[n].funcname); } ewp->w_p_crb = p_crb_save; @@ -1501,110 +1601,6 @@ win_redr_custom( TabPageIdxs[col++] = fillchar; } - // Resolve click function regions for statusline or tabline. - if (!draw_ruler) - { - stl_click_region_T **out_regions; - int *out_count; - int base_col; - int base_row; - int click_count = 0; - - // clicktab reflects the last iteration of the draw loop above, so - // the regions belong to the last drawn row. - base_row = row + stlh_cnt - 1; - - if (wp != NULL) - { - out_regions = &wp->w_stl_click; - out_count = &wp->w_stl_click_count; - base_col = wp->w_wincol; - } - else - { - // 'tabline': store regions in global state since there is no - // associated window. - out_regions = &tabline_stl_click; - out_count = &tabline_stl_click_count; - base_col = firstwin->w_wincol; - } - - // Count the click regions. - for (n = 0; clicktab[n].start != NULL; n++) - click_count++; - - // Free old click regions. - if (*out_regions != NULL) - { - for (n = 0; n < *out_count; n++) - vim_free((*out_regions)[n].funcname); - VIM_CLEAR(*out_regions); - } - *out_count = 0; - - if (click_count > 0) - { - stl_click_region_T *regions; - int rcount = 0; - - regions = ALLOC_MULT(stl_click_region_T, click_count); - if (regions != NULL) - { - char_u *cur_funcname = NULL; - int cur_minwid = 0; - int region_start = base_col; - - // Walk through click records converting buffer positions - // to screen columns. - len = 0; - p = buf; - for (n = 0; clicktab[n].start != NULL; n++) - { - len += vim_strnsize(p, - (int)(clicktab[n].start - p)); - p = clicktab[n].start; - - // Close previous region if there was one. - if (cur_funcname != NULL) - { - regions[rcount].row = base_row; - regions[rcount].col_start = region_start; - regions[rcount].col_end = base_col + len; - regions[rcount].funcname = - vim_strsave(cur_funcname); - regions[rcount].minwid = cur_minwid; - regions[rcount].tabnr = 0; - rcount++; - } - - cur_funcname = clicktab[n].funcname; - cur_minwid = clicktab[n].minwid; - region_start = base_col + len; - } - - // Close final region if it extends to the end. - if (cur_funcname != NULL) - { - regions[rcount].row = base_row; - regions[rcount].col_start = region_start; - regions[rcount].col_end = base_col + maxwidth; - regions[rcount].funcname = - vim_strsave(cur_funcname); - regions[rcount].minwid = cur_minwid; - regions[rcount].tabnr = 0; - rcount++; - } - - *out_regions = regions; - *out_count = rcount; - } - } - - // Free the funcname strings allocated by build_stl_str_hl_local(). - for (n = 0; clicktab[n].start != NULL; n++) - vim_free(clicktab[n].funcname); - } - theend: if (override_success) pop_highlight_overrides(); diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 79e857cf52..6128e08b66 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -817,6 +817,44 @@ func Test_statusline_click_multiple_regions() let &laststatus = save_ls endfunc +" Click on a region in any row of a multi-line statusline (issue #20116). +func Test_statusline_click_multiline() + let save_mouse = &mouse + let save_stl = &statusline + let save_ls = &laststatus + let save_stlo = &statuslineopt + set mouse=a + set laststatus=2 + + " First row contains the click region, second row is filled with fillchar. + set statusline=%[StlClickTestFunc][Click]%[]%@\ \ \ \ + set statuslineopt=maxheight:2,fixedheight + redraw! + + let stl_row = win_screenpos(0)[0] + winheight(0) + + " Click on [Click] in the first row of the multi-line statusline. + call test_setmouse(stl_row, 2) + call feedkeys("\\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal(0, g:stl_click_info.minwid) + unlet! g:stl_click_info + + " A click region on the second row should also be recognized. + set statusline=row1%@%2[StlClickTestFunc][Click2]%[] + redraw! + call test_setmouse(stl_row + 1, 2) + call feedkeys("\\", 'xt') + call assert_true(exists('g:stl_click_info')) + call assert_equal(2, g:stl_click_info.minwid) + unlet! g:stl_click_info + + let &mouse = save_mouse + let &statusline = save_stl + let &laststatus = save_ls + let &statuslineopt = save_stlo +endfunc + func Test_statusline_click_region_extends_to_end() let save_mouse = &mouse let save_stl = &statusline diff --git a/src/version.c b/src/version.c index 34f3dd8508..b64fd60b82 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 441, /**/ 440, /**/ From b10159bcc22aa1c976234a6b2677f11438bef953 Mon Sep 17 00:00:00 2001 From: mityu Date: Mon, 4 May 2026 20:24:07 +0000 Subject: [PATCH 175/545] Fix wrong comment in getchar.c The comment for `do_key_input_pre()` function says that it handles the InsertCharPre autocommand, but what the function actually handles is the KeyInputPre autocommand. closes: #20142 Signed-off-by: mityu Signed-off-by: Christian Brabandt --- src/getchar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/getchar.c b/src/getchar.c index 57e407b899..1fe155dcd9 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -2205,7 +2205,7 @@ vgetc(void) #ifdef FEAT_EVAL /* - * Handle the InsertCharPre autocommand. + * Handle the KeyInputPre autocommand. * "c" is the character that was typed. * Return new input character. */ From 1903020b82eb49c26c8f83ad29f7f2d1d317a81e Mon Sep 17 00:00:00 2001 From: Arnaud Rebillout Date: Mon, 4 May 2026 20:31:51 +0000 Subject: [PATCH 176/545] runtime(autopkgtest): update syntax script Fix some typos, and move a deprecated keyword where it belongs closes: #20141 Signed-off-by: Arnaud Rebillout Signed-off-by: James McCoy Signed-off-by: Christian Brabandt --- runtime/syntax/autopkgtest.vim | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/runtime/syntax/autopkgtest.vim b/runtime/syntax/autopkgtest.vim index 5dc1b5f6f5..56698129ee 100644 --- a/runtime/syntax/autopkgtest.vim +++ b/runtime/syntax/autopkgtest.vim @@ -2,6 +2,7 @@ " Language: Debian autopkgtest control files " Maintainer: Debian Vim Maintainers " Last Change: 2025 Jul 05 +" 2026 May 05 by Vim project: fix typos " URL: https://salsa.debian.org/vim-team/vim-debian/blob/main/syntax/autopkgtest.vim " " Specification of the autopkgtest format is available at: @@ -31,10 +32,10 @@ syn match autopkgtestTests contained "[a-z0-9][a-z0-9+.-]\+\%(,\=\s*[a-z0-9][a-z syn match autopkgtestArbitrary contained "[^#]*" syn keyword autopkgtestRestrictions contained \ allow-stderr - \ breaks-testbe - \ build-neede + \ breaks-testbed + \ build-needed \ flaky - \ hint-testsuite-trigger + \ hint-testsuite-triggers \ isolation-container \ isolation-machine \ needs-internet @@ -43,10 +44,11 @@ syn keyword autopkgtestRestrictions contained \ needs-sudo \ rw-build-tree \ skip-foreign-architecture - \ skip-not-installable \ skippable \ superficial -syn keyword autopkgtestDeprecatedRestrictions contained needs-recommends +syn keyword autopkgtestDeprecatedRestrictions contained + \ needs-recommends + \ skip-not-installable syn match autopkgtestFeatures contained 'test-name=[^, ]*\%([, ]*[^, #]\)*,\=' syn match autopkgtestDepends contained '\%(@builddeps@\|@recommends@\|@\)' From 2bfddbea47e1afa79ecd094040418abdba88bc83 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Tue, 5 May 2026 18:56:56 +0000 Subject: [PATCH 177/545] patch 9.2.0442: completion: i_CTRL-X_CTRL-V doesn't use dict from customlist Problem: Completion with i_CTRL-X_CTRL-V doesn't use dict from cmdline "customlist" completion. Solution: Include abbr/kind/menu/info in the completion items (zeertzjq). closes: #20139 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/cmdexpand.c | 2 +- src/insexpand.c | 27 +++++++++++++++- src/proto/cmdexpand.pro | 1 + ...ump => Test_customlist_info_popup_01.dump} | 0 ...ump => Test_customlist_info_popup_02.dump} | 0 ...ump => Test_customlist_info_popup_03.dump} | 0 ...ump => Test_customlist_info_popup_04.dump} | 0 ...ump => Test_customlist_info_popup_05.dump} | 0 .../dumps/Test_customlist_info_popup_06.dump | 12 +++++++ .../dumps/Test_customlist_info_popup_07.dump | 12 +++++++ .../dumps/Test_customlist_info_popup_08.dump | 12 +++++++ .../dumps/Test_customlist_info_popup_09.dump | 12 +++++++ .../dumps/Test_customlist_info_popup_10.dump | 12 +++++++ .../dumps/Test_customlist_info_popup_11.dump | 12 +++++++ src/testdir/test_cmdline.vim | 31 ++++++++++++++++--- src/version.c | 2 ++ 16 files changed, 128 insertions(+), 7 deletions(-) rename src/testdir/dumps/{Test_customlist_info_popup_1.dump => Test_customlist_info_popup_01.dump} (100%) rename src/testdir/dumps/{Test_customlist_info_popup_2.dump => Test_customlist_info_popup_02.dump} (100%) rename src/testdir/dumps/{Test_customlist_info_popup_3.dump => Test_customlist_info_popup_03.dump} (100%) rename src/testdir/dumps/{Test_customlist_info_popup_4.dump => Test_customlist_info_popup_04.dump} (100%) rename src/testdir/dumps/{Test_customlist_info_popup_5.dump => Test_customlist_info_popup_05.dump} (100%) create mode 100644 src/testdir/dumps/Test_customlist_info_popup_06.dump create mode 100644 src/testdir/dumps/Test_customlist_info_popup_07.dump create mode 100644 src/testdir/dumps/Test_customlist_info_popup_08.dump create mode 100644 src/testdir/dumps/Test_customlist_info_popup_09.dump create mode 100644 src/testdir/dumps/Test_customlist_info_popup_10.dump create mode 100644 src/testdir/dumps/Test_customlist_info_popup_11.dump diff --git a/src/cmdexpand.c b/src/cmdexpand.c index c265ecbe7f..eb4f362b2b 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -1027,7 +1027,7 @@ find_longest_match(expand_T *xp, int options) return ss; } - static void + void free_xp_files_extra(expand_T *xp, int numfiles) { if (xp->xp_files_abbr != NULL) diff --git a/src/insexpand.c b/src/insexpand.c index a603ed7b08..bbb15b003e 100644 --- a/src/insexpand.c +++ b/src/insexpand.c @@ -4801,7 +4801,32 @@ get_next_cmdline_completion(void) if (expand_cmdline(&compl_xp, compl_pattern.string, (int)compl_pattern.length, &num_matches, &matches) == EXPAND_OK) - ins_compl_add_matches(num_matches, matches, FALSE); + { + int add_r = OK; + int dir = compl_direction; + + for (int i = 0; i < num_matches && add_r != FAIL; i++) + { + char_u *(cptext[CPT_COUNT]) = {NULL, NULL, NULL, NULL}; + + if (compl_xp.xp_files_abbr != NULL) + cptext[CPT_ABBR] = compl_xp.xp_files_abbr[i]; + if (compl_xp.xp_files_kind != NULL) + cptext[CPT_KIND] = compl_xp.xp_files_kind[i]; + if (compl_xp.xp_files_menu != NULL) + cptext[CPT_MENU] = compl_xp.xp_files_menu[i]; + if (compl_xp.xp_files_info != NULL) + cptext[CPT_INFO] = compl_xp.xp_files_info[i]; + + add_r = ins_compl_add(matches[i], -1, NULL, cptext, NULL, dir, + CP_FAST, FALSE, NULL, FUZZY_SCORE_NONE); + if (add_r == OK) + // if dir was BACKWARD then honor it just once + dir = FORWARD; + } + FreeWild(num_matches, matches); + free_xp_files_extra(&compl_xp, num_matches); + } } /* diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro index 50383b98cc..67dff09ca4 100644 --- a/src/proto/cmdexpand.pro +++ b/src/proto/cmdexpand.pro @@ -8,6 +8,7 @@ void cmdline_pum_cleanup(cmdline_info_T *cclp); int cmdline_compl_startcol(void); char_u *cmdline_compl_pattern(void); int cmdline_compl_is_fuzzy(void); +void free_xp_files_extra(expand_T *xp, int numfiles); char_u *ExpandOne(expand_T *xp, char_u *str, char_u *orig, int options, int mode); void ExpandInit(expand_T *xp); void ExpandCleanup(expand_T *xp); diff --git a/src/testdir/dumps/Test_customlist_info_popup_1.dump b/src/testdir/dumps/Test_customlist_info_popup_01.dump similarity index 100% rename from src/testdir/dumps/Test_customlist_info_popup_1.dump rename to src/testdir/dumps/Test_customlist_info_popup_01.dump diff --git a/src/testdir/dumps/Test_customlist_info_popup_2.dump b/src/testdir/dumps/Test_customlist_info_popup_02.dump similarity index 100% rename from src/testdir/dumps/Test_customlist_info_popup_2.dump rename to src/testdir/dumps/Test_customlist_info_popup_02.dump diff --git a/src/testdir/dumps/Test_customlist_info_popup_3.dump b/src/testdir/dumps/Test_customlist_info_popup_03.dump similarity index 100% rename from src/testdir/dumps/Test_customlist_info_popup_3.dump rename to src/testdir/dumps/Test_customlist_info_popup_03.dump diff --git a/src/testdir/dumps/Test_customlist_info_popup_4.dump b/src/testdir/dumps/Test_customlist_info_popup_04.dump similarity index 100% rename from src/testdir/dumps/Test_customlist_info_popup_4.dump rename to src/testdir/dumps/Test_customlist_info_popup_04.dump diff --git a/src/testdir/dumps/Test_customlist_info_popup_5.dump b/src/testdir/dumps/Test_customlist_info_popup_05.dump similarity index 100% rename from src/testdir/dumps/Test_customlist_info_popup_5.dump rename to src/testdir/dumps/Test_customlist_info_popup_05.dump diff --git a/src/testdir/dumps/Test_customlist_info_popup_06.dump b/src/testdir/dumps/Test_customlist_info_popup_06.dump new file mode 100644 index 0000000000..72324ff0a8 --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_06.dump @@ -0,0 +1,12 @@ +|D+0&#ffffff0|i|c|t|C|m|d| |a|p@1|l|e> @13|╔+0#0000001#e0e0e08|═@12|X| +0#0000000#ffffff0@32 +|~+0#4040ff13&| @5| +0#0000001#e0e0e08|a|p@1|l|e| @1|f| |f|r|u|i|t| @4|║| |A| |r|e|d| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@32 +|~| @5| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|╚+0&#e0e0e08|═@12|⇲| +0#4040ff13#ffffff0@32 +|~| @5| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@47 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|-+2#0000000&@1| |C|o|m@1|a|n|d|-|l|i|n|e| |c|o|m|p|l|e|t|i|o|n| |(|^|V|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |4| +0#0000000&@26 diff --git a/src/testdir/dumps/Test_customlist_info_popup_07.dump b/src/testdir/dumps/Test_customlist_info_popup_07.dump new file mode 100644 index 0000000000..f11f3ea9f1 --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_07.dump @@ -0,0 +1,12 @@ +|D+0&#ffffff0|i|c|t|C|m|d| |b|a|n|a|n|a> @60 +|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@15|X| +0#4040ff13#ffffff0@29 +|~| @5| +0#0000001#e0e0e08|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|║| |A| |y|e|l@1|o|w| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@29 +|~| @5| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| |╚+0&#e0e0e08|═@15|⇲| +0#4040ff13#ffffff0@29 +|~| @5| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@47 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|-+2#0000000&@1| |C|o|m@1|a|n|d|-|l|i|n|e| |c|o|m|p|l|e|t|i|o|n| |(|^|V|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |2| |o|f| |4| +0#0000000&@26 diff --git a/src/testdir/dumps/Test_customlist_info_popup_08.dump b/src/testdir/dumps/Test_customlist_info_popup_08.dump new file mode 100644 index 0000000000..adbe3b71af --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_08.dump @@ -0,0 +1,12 @@ +|D+0&#ffffff0|i|c|t|C|m|d| |c|a|r@1|o|t> @60 +|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@20|X| +0#4040ff13#ffffff0@24 +|~| @5| +0#0000001#e0e0e08|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| |║| |A|n| |o|r|a|n|g|e| |v|e|g|e|t|a|b|l|e| |║| +0#4040ff13#ffffff0@24 +|~| @5| +0#0000001#ffd7ff255|p|l|a|i|n| @13|╚+0&#e0e0e08|═@20|⇲| +0#4040ff13#ffffff0@24 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|-+2#0000000&@1| |C|o|m@1|a|n|d|-|l|i|n|e| |c|o|m|p|l|e|t|i|o|n| |(|^|V|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |3| |o|f| |4| +0#0000000&@26 diff --git a/src/testdir/dumps/Test_customlist_info_popup_09.dump b/src/testdir/dumps/Test_customlist_info_popup_09.dump new file mode 100644 index 0000000000..236c7dcd1e --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_09.dump @@ -0,0 +1,12 @@ +|D+0&#ffffff0|i|c|t|C|m|d| |p|l|a|i|n> @61 +|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#e0e0e08|p|l|a|i|n| @13| +0#4040ff13#ffffff0@47 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|-+2#0000000&@1| |C|o|m@1|a|n|d|-|l|i|n|e| |c|o|m|p|l|e|t|i|o|n| |(|^|V|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |4| |o|f| |4| +0#0000000&@26 diff --git a/src/testdir/dumps/Test_customlist_info_popup_10.dump b/src/testdir/dumps/Test_customlist_info_popup_10.dump new file mode 100644 index 0000000000..5c07e54db1 --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_10.dump @@ -0,0 +1,12 @@ +|D+0&#ffffff0|i|c|t|C|m|d| > @66 +|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@47 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|-+2#0000000&@1| |C|o|m@1|a|n|d|-|l|i|n|e| |c|o|m|p|l|e|t|i|o|n| |(|^|V|^|N|^|P|)| |B+0#e000002&|a|c|k| |a|t| |o|r|i|g|i|n|a|l| +0#0000000&@22 diff --git a/src/testdir/dumps/Test_customlist_info_popup_11.dump b/src/testdir/dumps/Test_customlist_info_popup_11.dump new file mode 100644 index 0000000000..830dfcff04 --- /dev/null +++ b/src/testdir/dumps/Test_customlist_info_popup_11.dump @@ -0,0 +1,12 @@ +|s+0&#ffffff0|i|g|n| |u|n|d|e|f|i|n|e> @61 +|~+0#4040ff13&| @2| +0#0000001#e0e0e08|u|n|d|e|f|i|n|e| @6| +0#4040ff13#ffffff0@54 +|~| @2| +0#0000001#ffd7ff255|u|n|p|l|a|c|e| @7| +0#4040ff13#ffffff0@54 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|-+2#0000000&@1| |C|o|m@1|a|n|d|-|l|i|n|e| |c|o|m|p|l|e|t|i|o|n| |(|^|V|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |2| +0#0000000&@26 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index d58ff2b6d0..7b3449e2bd 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -4662,25 +4662,46 @@ func Test_customlist_dict_completion_info_popup() call term_sendkeys(buf, ":DictCmd \") call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd apple') + 1)], g:test_timeout, ((rows - 4), 'A red fruit')) - call VerifyScreenDump(buf, 'Test_customlist_info_popup_1', {}) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_01', {}) call term_sendkeys(buf, "\") call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd banana') + 1)], g:test_timeout, ((rows - 3), 'A yellow fruit')) - call VerifyScreenDump(buf, 'Test_customlist_info_popup_2', {}) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_02', {}) call term_sendkeys(buf, "\") call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd carrot') + 1)], g:test_timeout, ((rows - 2), 'An orange vegetable')) - call VerifyScreenDump(buf, 'Test_customlist_info_popup_3', {}) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_03', {}) call term_sendkeys(buf, "\") call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd plain') + 1)], g:test_timeout, ((rows - 1), '^\~\s\+plain\s\+$')) - call VerifyScreenDump(buf, 'Test_customlist_info_popup_4', {}) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_04', {}) call term_sendkeys(buf, "\") call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':DictCmd ') + 1)], g:test_timeout) - call VerifyScreenDump(buf, 'Test_customlist_info_popup_5', {}) + call VerifyScreenDump(buf, 'Test_customlist_info_popup_05', {}) call term_sendkeys(buf, "\") + + " Tests for Insert mode i_CTRL-X_CTRL-V + call term_sendkeys(buf, "iDictCmd \\") + call VerifyScreenDump(buf, 'Test_customlist_info_popup_06', {}) + + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_customlist_info_popup_07', {}) + + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_customlist_info_popup_08', {}) + + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_customlist_info_popup_09', {}) + + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_customlist_info_popup_10', {}) + + " Starting another i_CTRL-X_CTRL-V completion should not leak memory + call term_sendkeys(buf, "\sign un\\") + call VerifyScreenDump(buf, 'Test_customlist_info_popup_11', {}) + call StopVimInTerminal(buf) endfunc diff --git a/src/version.c b/src/version.c index b64fd60b82..5dde38b7b4 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 442, /**/ 441, /**/ From cf947e7ef09fc8a63f447b54b107585b8f3b7abe Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Tue, 5 May 2026 19:02:59 +0000 Subject: [PATCH 178/545] patch 9.2.0443: GUI: cancelling save dialog overwrites or discards unnamed buffer Problem: When closing gvim with an unsaved unnamed buffer, choosing "Yes" in the "Save changes?" dialog and then "Cancel" in the file selection dialog either silently writes the buffer to a file named "Untitled" (overwriting any existing file with that name) or discards the buffer altogether (vibs29, after v9.1.0265). Solution: In dialog_changed(), if browse_save_fname() leaves the buffer without a file name, treat it as a cancel and return without saving. Also stop clearing the modified flag in the restore path on write failure, so the unsaved changes are kept and the caller (e.g. gui_shell_closed()) can also cancel the close. Pre-fill the file dialog with "Untitled" to match the preceding "Save changes to ..." prompt. Add a test for the write-failure path (Hirohito Higashi). fixes: #20132 closes: #20143 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/ex_cmds2.c | 9 ++++++--- src/testdir/test_buffer.vim | 22 ++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index 7012c98962..afe9c4b58a 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -141,7 +141,7 @@ browse_save_fname(buf_T *buf) char_u *fname; fname = do_browse(BROWSE_SAVE, (char_u *)_("Save As"), - NULL, NULL, NULL, NULL, buf); + (char_u *)"Untitled", NULL, NULL, NULL, buf); if (fname == NULL) return; @@ -182,6 +182,10 @@ dialog_changed( # ifdef FEAT_BROWSE // May get file name, when there is none browse_save_fname(buf); + + // User cancelled the file dialog; keep the buffer modified. + if (buf->b_fname == NULL) + return; # endif empty_bufname = buf->b_fname == NULL ? TRUE : FALSE; if (empty_bufname) @@ -194,13 +198,12 @@ dialog_changed( return; } - // restore to empty when write failed + // restore to empty when write failed or was cancelled if (empty_bufname) { buf->b_fname = NULL; VIM_CLEAR(buf->b_ffname); VIM_CLEAR(buf->b_sfname); - unchanged(buf, TRUE, FALSE); } } else if (ret == VIM_NO) diff --git a/src/testdir/test_buffer.vim b/src/testdir/test_buffer.vim index 934bc6fe1e..4f0f1acb24 100644 --- a/src/testdir/test_buffer.vim +++ b/src/testdir/test_buffer.vim @@ -342,6 +342,28 @@ func Test_goto_buf_with_confirm() close! endfunc +" Test for issue #20132: when saving an unnamed buffer fails the modified +" flag must be kept, otherwise the buffer is silently discarded. +func Test_dialog_changed_keep_modified_on_write_fail() + CheckUnix + CheckNotGui + CheckFeature dialog_con + CheckNotFeature dialog_con_gui + CheckNotRoot + + call writefile(['existing'], 'Untitled', 'D') + call setfperm('Untitled', 'r--r--r--') + + new + call setline(1, 'test') + call feedkeys('y', 'L') + silent! confirm bdel + call assert_true(&modified) + call assert_equal(['existing'], readfile('Untitled')) + + bw! +endfunc + " Test for splitting buffer with 'switchbuf' func Test_buffer_switchbuf() new Xswitchbuf diff --git a/src/version.c b/src/version.c index 5dde38b7b4..61342e15f0 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 443, /**/ 442, /**/ From 88fb739918a0d2ca4277e1e79b4fd5799e9b0128 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Tue, 5 May 2026 19:47:19 +0000 Subject: [PATCH 179/545] patch 9.2.0444: Cannot set 'path' option via modeline Problem: Cannot set 'path' option via modeline (zeertzjq, after v9.2.0435) Solution: Revert the part that disallows setting 'path' via modeline. closes: #20137 Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 3 --- src/optiondefs.h | 2 +- src/testdir/test_find_complete.vim | 9 ++++++++- src/testdir/test_modeline.vim | 14 -------------- src/version.c | 2 ++ 5 files changed, 11 insertions(+), 19 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index c4b2df144b..2baf8a01a7 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6799,9 +6799,6 @@ A jump table for the options with a short description can be found at |Q_op|. < Replace the ';' with a ':' or whatever separator is used. Note that this doesn't work when $INCL contains a comma or white space. - This option cannot be set from a |modeline| or in the |sandbox|, for - security reasons. - *'perldll'* 'perldll' string (default depends on the build) global diff --git a/src/optiondefs.h b/src/optiondefs.h index f712ab7a14..0a93b70f8e 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -1958,7 +1958,7 @@ static struct vimoption options[] = (char_u *)&p_pm, PV_NONE, did_set_backupext_or_patchmode, NULL, {(char_u *)"", (char_u *)0L} SCTX_INIT}, - {"path", "pa", P_STRING|P_EXPAND|P_VI_DEF|P_SECURE|P_COMMA|P_NODUP, + {"path", "pa", P_STRING|P_EXPAND|P_VI_DEF|P_COMMA|P_NODUP, (char_u *)&p_path, PV_PATH, NULL, NULL, { #if defined(AMIGA) || defined(MSWIN) diff --git a/src/testdir/test_find_complete.vim b/src/testdir/test_find_complete.vim index 8b8b71c303..db1860dc40 100644 --- a/src/testdir/test_find_complete.vim +++ b/src/testdir/test_find_complete.vim @@ -165,9 +165,15 @@ endfunc func Test_find_completion_backtick_in_path() CheckUnix CheckExecutable id + set nomodelinestrict modeline + let lines =<< trim END + // vim: set path+=`id>Xrce_marker` : + END + + call writefile(lines, 'Xpoc.c', 'D') new Xpoc.c - setl path+=`id>Xrce_marker` + call assert_match('`id>Xrce_marker`', &path) " Triggering completion must not execute the backtick command. call getcompletion('', 'file_in_path') call assert_false(filereadable('Xrce_marker')) @@ -176,6 +182,7 @@ func Test_find_completion_backtick_in_path() bwipe! call delete('Xrce_marker') + set modelinestrict& modeline& endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_modeline.vim b/src/testdir/test_modeline.vim index 6cbf87638a..6884ab473d 100644 --- a/src/testdir/test_modeline.vim +++ b/src/testdir/test_modeline.vim @@ -665,18 +665,4 @@ func Test_modeline_strict_cannot_be_set_from_modeline() let &modeline = modeline endfunc -" Verify that backticks in 'path' set from a modeline are not executed -func Test_path_modeline() - let lines =<< trim END - // vim: set path+=foobar : - END - call writefile(lines, 'Xpoc.c', 'D') - - set nomodelinestrict modeline - call assert_fails('split Xpoc.c', 'E520:') - - bwipe! - set modelinestrict& modeline& -endfunc - " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 61342e15f0..a859c2ceda 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 444, /**/ 443, /**/ From 40fc78f0a185f003844334e7c9dd217d6d993143 Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Tue, 5 May 2026 19:50:46 +0000 Subject: [PATCH 180/545] patch 9.2.0445: win_fix_scroll() called before win_comp_pos() in command_height() Problem: win_fix_scroll(true) is called before win_comp_pos() in command_height(). Solution: Move win_fix_scroll(true) after win_comp_pos(), matching the ordering used in win_drag_status_line() (Jesse Rosenstock). Patch 9.2.0413 added win_fix_scroll(true) to command_height() to handle splitkeep when cmdheight changes, but placed the call before win_comp_pos(). win_fix_scroll() reads w_winrow to detect window movement (https://github.com/vim/vim/blob/620557bd48865fa3d927901764d2747bf68597b5/src/window.c#L7266), but w_winrow is not recomputed until win_comp_pos() runs (https://github.com/vim/vim/blob/620557bd48865fa3d927901764d2747bf68597b5/src/window.c#L6516). This causes incorrect scroll adjustments and was breaking Test_smoothscroll_incsearch on macOS CI. closes: #20138 Co-authored-by: Gemini Signed-off-by: Jesse Rosenstock Signed-off-by: Christian Brabandt --- src/version.c | 2 ++ src/window.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/version.c b/src/version.c index a859c2ceda..4d3f68d05c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 445, /**/ 444, /**/ diff --git a/src/window.c b/src/window.c index 8813ffa808..eb4e494d19 100644 --- a/src/window.c +++ b/src/window.c @@ -7591,10 +7591,10 @@ command_height(void) } if (p_ch < old_p_ch && command_frame_height && frp != NULL) frame_add_height(frp, (int)(old_p_ch - p_ch)); - win_fix_scroll(true); // Recompute window positions. win_comp_pos(); + win_fix_scroll(true); cmdline_row = Rows - p_ch; redraw_cmdline = TRUE; From 7ccc273a4cb6012e5afbdb94d0f2597bc01fe2fd Mon Sep 17 00:00:00 2001 From: "J. Paulo Seibt" Date: Tue, 5 May 2026 20:06:15 +0000 Subject: [PATCH 181/545] patch 9.2.0446: runtime(netrw): off-by-one bug in s:NetrwUnMarkFile() Problem: off-by-one bug in s:NetrwUnMarkFile() Solution: Correctly loop through all buffers to unlet all variables (J. Paulo Seibt) When the function loops through buffers to clear s:netrwmarkfilelist_# and s:netrwmarkfilemtch_#, it skips the last one at bufnr('$'), messing up mark highlights and causing other functions that operate on those arrays (like delete or rename) to target stale marked files. The bufnr() help page says that bufnr("$") returns the highest buffer number of existing buffers, so while ibuf < bufnr("$") does not clear the last buffer-local arrays. To reproduce: Just opening a fresh Vim and running :Ex opens a netrw buffer at the highest number. Then, typing mu after marking some files triggers the mark highlight bug, and finally typing D would act like calling the delete function against the previous marked files, as the buffer-local arrays where not touched by s:NetrwUnMarkFile. closes: #20129 Signed-off-by: J. Paulo Seibt Signed-off-by: Christian Brabandt --- .../pack/dist/opt/netrw/autoload/netrw.vim | 30 ++---------------- src/testdir/test_plugin_netrw.vim | 31 +++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index 538ab0377b..90fd70472a 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -1,33 +1,7 @@ " Creator: Charles E Campbell " Previous Maintainer: Luca Saccarola " Maintainer: This runtime file is looking for a new maintainer. -" Last Change: -" 2025 Aug 07 by Vim Project (use correct "=~#" for netrw_stylesize option #17901) -" 2025 Aug 07 by Vim Project (netrw#BrowseX() distinguishes remote files #17794) -" 2025 Aug 22 by Vim Project netrw#Explore handle terminal correctly #18069 -" 2025 Sep 05 by Vim Project ensure netrw#fs#Dirname() returns trailing slash #18199 -" 2025 Sep 11 by Vim Project only keep cursor position in tree mode #18275 -" 2025 Sep 17 by Vim Project tighten the regex to handle remote compressed archives #18318 -" 2025 Sep 18 by Vim Project 'equalalways' not always respected #18358 -" 2025 Oct 01 by Vim Project fix navigate to parent folder #18464 -" 2025 Oct 26 by Vim Project fix parsing of remote user names #18611 -" 2025 Oct 27 by Vim Project align comment after #18611 -" 2025 Nov 01 by Vim Project fix NetrwChgPerm #18674 -" 2025 Nov 13 by Vim Project don't wipe unnamed buffers #18740 -" 2025 Nov 18 by Vim Project use UNC paths when using scp and Windows paths #18764 -" 2025 Nov 28 by Vim Project fix undefined variable in *NetrwMenu #18829 -" 2025 Dec 26 by Vim Project fix use of g:netrw_cygwin #19015 -" 2026 Jan 19 by Vim Project do not create swapfiles #18854 -" 2026 Feb 15 by Vim Project fix global variable initialization for MS-Windows #19287 -" 2026 Feb 21 by Vim Project better absolute path detection on MS-Windows #19477 -" 2026 Feb 27 by Vim Project Make the hostname validation more strict -" 2026 Mar 01 by Vim Project include portnumber in hostname checking #19533 -" 2026 Apr 01 by Vim Project use fnameescape() with netrw#FileUrlEdit() -" 2026 Apr 05 by Vim Project Fix netrw#RFC2396() #19913 -" 2026 Apr 15 by Vim Project Add missing escape() -" 2026 Apr 19 by Vim Project expand ~ on Windows #20003 -" 2026 Apr 21 by Vim Project fix shell-injection via tempfile suffix (sftp://, file://) -" 2026 Apr 21 by Vim Project drop unused g:netrw_tmpfile_escape +" Last Change: 2026 May 05 " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -6348,7 +6322,7 @@ function s:NetrwUnMarkFile(islocal) endif let ibuf= 1 - while ibuf < bufnr("$") + while ibuf <= bufnr("$") if exists("s:netrwmarkfilelist_".ibuf) unlet s:netrwmarkfilelist_{ibuf} unlet s:netrwmarkfilemtch_{ibuf} diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim index 893e424362..b66bb4a72e 100644 --- a/src/testdir/test_plugin_netrw.vim +++ b/src/testdir/test_plugin_netrw.vim @@ -138,6 +138,33 @@ function Test_NetrwValidateHostname(hostname) abort return s:NetrwValidateHostname(a:hostname) endfunction +" Test unmarking all files via s:NetrwUnMarkFile() +function Test_NetrwUnMarkFile() + " set up: init global and buffer-local mark lists + new + + " initializing the global mark list, the loop handles the buffer-local + let s:netrwmarkfilelist = ['test_file'] + + " making sure every buffer has a mark list and match pattern + let ibuf = 1 + while ibuf <= bufnr('$') + let s:netrwmarkfilelist_{ibuf} = ['test_mark'] + let s:netrwmarkfilemtch_{ibuf} = 'test_mark' + let ibuf = ibuf + 1 + endwhile + + call s:NetrwUnMarkFile(1) + + call assert_false(exists('s:netrwmarkfilelist'), 'Global list should be wiped') + call assert_false(exists('s:netrwmarkfilelist_' . bufnr('$')), 'Last buffer-local list should be wiped') + call assert_false(exists('s:netrwmarkfilemtch_' . bufnr('$')), 'Last buffer-local match str should be wiped') + call assert_false(exists('s:netrwmarkfilelist_1'), 'First buffer-local list should be wiped') + call assert_false(exists('s:netrwmarkfilemtch_1'), 'First buffer-local match str should be wiped') + + " wipe out the test buffer + bw +endfunction " }}} END @@ -645,4 +672,8 @@ func Test_netrw_Home_tilde() bw! endfunc +func Test_netrw_unmark_all() + call Test_NetrwUnMarkFile() +endfunc + " vim:ts=8 sts=2 sw=2 et diff --git a/src/version.c b/src/version.c index 4d3f68d05c..2f0af9ba11 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 446, /**/ 445, /**/ From c06002f3cb07c374bfc1a1310cf13ee1914e86da Mon Sep 17 00:00:00 2001 From: magnus-rattlehead Date: Tue, 5 May 2026 20:35:32 +0000 Subject: [PATCH 182/545] patch 9.2.0447: cindent does not ignore comments Problem: When find_start_brace() scans backwards for the enclosing block, '{' and '}' inside // and /* */ comments are counted, producing wrong indent for code following such comments (rendcrx). Solution: Implement FM_SKIPCOMM in findmatchlimit() to track block- comment state and skip matches inside comments. Pass FM_SKIPCOMM from cindent's call sites (find_start_brace, find_match_char, cin_iswhileofdo, get_c_indent). fixes: #4 fixes: #648 fixes: #19578 closes: #19581 closes: #20111 Signed-off-by: magnus-rattlehead Signed-off-by: Christian Brabandt --- runtime/doc/version9.txt | 3 +- src/cindent.c | 8 ++-- src/search.c | 54 ++++++++++++++++++++++-- src/testdir/test_cindent.vim | 80 ++++++++++++++++++++++++++++++++++++ src/version.c | 2 + src/vim.h | 2 +- 6 files changed, 139 insertions(+), 10 deletions(-) diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 114bdf7dac..a1b9afce71 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 May 02 +*version9.txt* For Vim version 9.2. Last change: 2026 May 05 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52625,6 +52625,7 @@ Other ~ - Added the "u" flag to 'shortmess' to silence undo/redo messages: |shm-u| - |:command-completion-customlist| can return a list of dictionaries with kind/menu/info/abbr for the popup menu. +- |C-indenting| detects comments better. Platform specific ~ ----------------- diff --git a/src/cindent.c b/src/cindent.c index ddd268176b..94c01db7e8 100644 --- a/src/cindent.c +++ b/src/cindent.c @@ -1153,7 +1153,7 @@ find_match_char(int c, int ind_maxparen) // XXX cursor_save = curwin->w_cursor; ind_maxp_wk = ind_maxparen; retry: - if ((trypos = findmatchlimit(NULL, c, 0, ind_maxp_wk)) != NULL) + if ((trypos = findmatchlimit(NULL, c, FM_SKIPCOMM, ind_maxp_wk)) != NULL) { // check if the ( is in a // comment if ((colnr_T)cin_skip2pos(trypos) > trypos->col) @@ -1396,7 +1396,7 @@ cin_iswhileofdo (char_u *p, linenr_T lnum) // XXX ++p; ++curwin->w_cursor.col; } - if ((trypos = findmatchlimit(NULL, 0, 0, + if ((trypos = findmatchlimit(NULL, 0, FM_SKIPCOMM, curbuf->b_ind_maxparen)) != NULL && *cin_skipcomment(ml_get_pos(trypos) + 1) == ';') retval = TRUE; @@ -1732,7 +1732,7 @@ find_start_brace(void) // XXX static pos_T pos_copy; cursor_save = curwin->w_cursor; - while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP, 0)) != NULL) + while ((trypos = findmatchlimit(NULL, '{', FM_BLOCKSTOP | FM_SKIPCOMM, 0)) != NULL) { pos_copy = *trypos; // copy pos_T, next findmatch will change it trypos = &pos_copy; @@ -2547,7 +2547,7 @@ get_c_indent(void) line = ml_get_curline(); look_col = (int)(look - line); curwin->w_cursor.col = look_col + 1; - if ((trypos = findmatchlimit(NULL, ')', 0, + if ((trypos = findmatchlimit(NULL, ')', FM_SKIPCOMM, curbuf->b_ind_maxparen)) != NULL && trypos->lnum == our_paren_pos.lnum diff --git a/src/search.c b/src/search.c index bb85873dc0..12977dc8fa 100644 --- a/src/search.c +++ b/src/search.c @@ -2165,7 +2165,7 @@ find_mps_values( * flags: FM_BACKWARD search backwards (when initc is '/', '*' or '#') * FM_FORWARD search forwards (when initc is '/', '*' or '#') * FM_BLOCKSTOP stop at start/end of block ({ or } in column 0) - * FM_SKIPCOMM skip comments (not implemented yet!) + * FM_SKIPCOMM skip over comments (cursor must start outside a block comment) * * "oap" is only used to set oap->motion_type for a linewise motion, it can be * NULL @@ -2201,6 +2201,8 @@ findmatchlimit( int comment_col = MAXCOL; // start of / / comment int lispcomm = FALSE; // inside of Lisp-style comment int lisp = curbuf->b_p_lisp; // engage Lisp-specific hacks ;) + int skip_comments = (flags & FM_SKIPCOMM) != 0; + int in_block_comment = FALSE; // inside /* */ block comment pos = curwin->w_cursor; pos.coladd = 0; @@ -2429,10 +2431,14 @@ findmatchlimit( CLEAR_POS(&match_pos); // backward search: Check if this line contains a single-line comment - if ((backwards && comment_dir) || lisp) + if ((backwards && comment_dir) || lisp || skip_comments) comment_col = check_linecomment(linep); if (lisp && comment_col != MAXCOL && pos.col > (colnr_T)comment_col) lispcomm = TRUE; // find match inside this comment + // skip // comment portion at starting position + if (skip_comments && !in_block_comment && comment_col != MAXCOL + && backwards && pos.col > (colnr_T)comment_col) + pos.col = comment_col; while (!got_int) { @@ -2460,11 +2466,15 @@ findmatchlimit( line_breakcheck(); // Check if this line contains a single-line comment - if (comment_dir || lisp) + if (comment_dir || lisp || skip_comments) comment_col = check_linecomment(linep); // skip comment if (lisp && comment_col != MAXCOL) pos.col = comment_col; + else if (skip_comments && !in_block_comment + && comment_col != MAXCOL + && pos.col > (colnr_T)comment_col) + pos.col = comment_col; } else { @@ -2495,7 +2505,7 @@ findmatchlimit( pos.col = 0; do_quotes = -1; line_breakcheck(); - if (lisp) // find comment pos in new line + if (lisp || skip_comments) // find comment pos in new line comment_col = check_linecomment(linep); } else @@ -2507,6 +2517,37 @@ findmatchlimit( } } + // Track block comment state when FM_SKIPCOMM is set. + // Backward: '/' of end-marker enters comment; '*' of start-marker exits. + // Forward: '/' of start-marker enters comment; '/' of end-marker exits. + if (skip_comments && !comment_dir) + { + if (backwards) + { + // Guard pos.col < comment_col: don't misread '*/' at the '//' + // position as a block-comment end-marker. + if (!in_block_comment && pos.col > 0 + && linep[pos.col - 1] == '*' && linep[pos.col] == '/' + && (comment_col == MAXCOL || (int)pos.col < comment_col)) + in_block_comment = TRUE; + else if (in_block_comment && pos.col > 0 + && linep[pos.col - 1] == '/' && linep[pos.col] == '*') + in_block_comment = FALSE; + } + else + { + // Guard pos.col < comment_col: don't treat '/*' inside a '//' + // comment as a block-comment start-marker. + if (!in_block_comment && linep[pos.col] == '/' + && linep[pos.col + 1] == '*' + && (comment_col == MAXCOL || (int)pos.col < comment_col)) + in_block_comment = TRUE; + else if (in_block_comment && pos.col > 0 + && linep[pos.col - 1] == '*' && linep[pos.col] == '/') + in_block_comment = FALSE; + } + } + /* * If FM_BLOCKSTOP given, stop at a '{' or '}' in column 0. */ @@ -2750,6 +2791,11 @@ findmatchlimit( && check_prevcol(linep, pos.col - 1, '#', NULL)) break; + // Skip matches inside comments when FM_SKIPCOMM is set. + if (skip_comments && (in_block_comment + || (comment_col != MAXCOL && (int)pos.col >= comment_col))) + break; + // Check for match outside of quotes, and inside of // quotes when the start is also inside of quotes. if ((!inquote || start_in_quotes == TRUE) diff --git a/src/testdir/test_cindent.vim b/src/testdir/test_cindent.vim index 4884dbb304..f050d82f8c 100644 --- a/src/testdir/test_cindent.vim +++ b/src/testdir/test_cindent.vim @@ -5526,5 +5526,85 @@ def Test_find_brace_backwards() bwipe! enddef +" Brackets inside comments must not affect C indent calculation (FM_SKIPCOMM) +def Test_cindent_comment_brackets() + # stray } in inline block comment must not confuse enclosing-brace search + new + setl cindent sw=4 + var code =<< trim [CODE] + int foo() { + /* } */ + int bar; + } + [CODE] + setline(1, code) + cursor(3, 1) + normal == + assert_equal(' int bar;', getline(3)) + bwipe! + + # stray } in // line comment: same + new + setl cindent sw=4 + var code2 =<< trim [CODE] + int foo() { + // } + int bar; + } + [CODE] + setline(1, code2) + cursor(3, 1) + normal == + assert_equal(' int bar;', getline(3)) + bwipe! + + # stray } on continuation line inside multi-line block comment + new + setl cindent sw=4 + var code3 =<< trim [CODE] + int foo() { + /* + } + */ + int bar; + } + [CODE] + setline(1, code3) + cursor(5, 1) + normal == + assert_equal(' int bar;', getline(5)) + bwipe! + + # { in inline block comment must not be treated as enclosing brace + new + setl cindent sw=4 + var code4 =<< trim [CODE] + int foo() { + /* { */ + int bar; + } + [CODE] + setline(1, code4) + cursor(3, 1) + normal == + assert_equal(' int bar;', getline(3)) + bwipe! + + # ) in inline block comment must not be treated as enclosing brace + new + setl cindent sw=4 + var code5 =<< trim [CODE] + some_func(arg1, + /* ) */ arg2, + arg3); + [CODE] + setline(1, code5) + cursor(3, 1) + normal == + assert_equal(' arg3);', getline(3)) + bwipe! + +enddef + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 2f0af9ba11..78efc6c777 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 447, /**/ 446, /**/ diff --git a/src/vim.h b/src/vim.h index 37183f130a..8949e867de 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1095,7 +1095,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring); #define FM_BACKWARD 0x01 // search backwards #define FM_FORWARD 0x02 // search forwards #define FM_BLOCKSTOP 0x04 // stop at start/end of block -#define FM_SKIPCOMM 0x08 // skip comments +#define FM_SKIPCOMM 0x08 // skip comments (cursor must start outside) // Values for action argument for do_buffer() and close_buffer() #define DOBUF_GOTO 0 // go to specified buffer From 4bcc8ba93d4d7b6b216dc6a0365e21676b70c715 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 6 May 2026 18:02:09 +0000 Subject: [PATCH 183/545] patch 9.2.0448: Vim9: dangling cmdline pointer after skip_expr_cctx() Problem: Vim9: dangling cmdline pointer after skip_expr_cctx() (Foxe Chen) Solution: Extract the cmdline restoration logic from compile_lambda into a helper restore_cmdline_arg() and call it from skip_expr_cctx() too, so a skipped lambda inside an "else" branch does not leave "*arg" pointing into freed evalarg memory (Yasuhiro Matsumoto). fixes: #20147 closes: #20148 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/testdir/test_vim9_cmd.vim | 18 +++++++++++++++++ src/version.c | 2 ++ src/vim9expr.c | 37 +++++++++++++++++++++++------------ 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index b54a58b509..ce05898061 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -2094,6 +2094,24 @@ def Test_lambda_crash() v9.CheckScriptFailureList(lines, ["E1356:", "E1405:"]) enddef +def Test_skipped_lambda_after_else() + var lines =<< trim END + vim9script + def g:Warn(msg: string) + if has('patch-9.0.0321') + echo msg + else + timer_start(100, (_) => { + echohl WarningMsg | echom msg | echohl None + }, {repeat: 0}) + endif + enddef + defcompile + END + v9.CheckScriptSuccess(lines) + delfunc! g:Warn +enddef + def s:check_previewpopup(expected_title: string) var id = popup_findpreview() assert_notequal(id, 0) diff --git a/src/version.c b/src/version.c index 78efc6c777..0c23e6a518 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 448, /**/ 447, /**/ diff --git a/src/vim9expr.c b/src/vim9expr.c index a6c4f1ea10..9d6033464c 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -1741,6 +1741,29 @@ compile_tuple( return generate_NEWTUPLE(cctx, count, FALSE); } +/* + * Restore "*arg" from a temporary cmdline copy. + */ + static void +restore_cmdline_arg(evalarg_T *evalarg, char_u **arg, cctx_T *cctx) +{ + garray_T *gap; + char_u *line; + size_t off; + + if (!evalarg->eval_using_cmdline || cctx == NULL) + return; + + gap = &evalarg->eval_tofree_ga; + if (gap->ga_len == 0) + return; + + off = *arg - ((char_u **)gap->ga_data)[gap->ga_len - 1]; + line = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum]; + *arg = line + off; + evalarg->eval_using_cmdline = FALSE; +} + /* * Parse a lambda: "(arg, arg) => expr" * "*arg" points to the '('. @@ -1803,18 +1826,7 @@ compile_lambda(char_u **arg, cctx_T *cctx) compile_def_function(ufunc, FALSE, compile_type, cctx); } - // The last entry in evalarg.eval_tofree_ga is a copy of the last line and - // "*arg" may point into it. Point into the original line to avoid a - // dangling pointer. - if (evalarg.eval_using_cmdline) - { - garray_T *gap = &evalarg.eval_tofree_ga; - size_t off = *arg - ((char_u **)gap->ga_data)[gap->ga_len - 1]; - - *arg = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum] - + off; - evalarg.eval_using_cmdline = FALSE; - } + restore_cmdline_arg(&evalarg, arg, cctx); clear_evalarg(&evalarg, NULL); @@ -2348,6 +2360,7 @@ skip_expr_cctx(char_u **arg, cctx_T *cctx) init_evalarg(&evalarg); evalarg.eval_cctx = cctx; skip_expr(arg, &evalarg); + restore_cmdline_arg(&evalarg, arg, cctx); clear_evalarg(&evalarg, NULL); } From 0ab4316fced588dd8d5aba1284c0ada14aa8eb7a Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 6 May 2026 18:17:00 +0000 Subject: [PATCH 184/545] patch 9.2.0449: Make proto fails in non GTK builds Problem: Make proto fails when not building the GTK gui Solution: Test for $GLIB_COMPILE_RESOURCES as done elsewhere closes: #20145 Signed-off-by: Christian Brabandt --- src/Makefile | 8 ++++++-- src/version.c | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index 2a8ff097bd..97200a6b07 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2152,7 +2152,9 @@ proto/%.pro: %.c @$(PYTHON) $(GEN_PROTO_CMD) $< $(GEN_PROTO_ARG) proto/gui_gtk_gresources.pro: auto/gui_gtk_gresources.c - @$(PYTHON) $(GEN_PROTO_CMD) $< $(GEN_PROTO_ARG) + @if test -n "$(GLIB_COMPILE_RESOURCES)"; then \ + $(PYTHON) $(GEN_PROTO_CMD) $< $(GEN_PROTO_ARG) ; \ + fi notags: -rm -f tags @@ -3175,7 +3177,9 @@ GUI_GTK_RES_INPUTS = \ ../pixmaps/stock_vim_window_split_vertical.png auto/gui_gtk_gresources.c: gui_gtk_res.xml $(GUI_GTK_RES_INPUTS) - $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=../pixmaps --generate --c-name=gui_gtk --manual-register gui_gtk_res.xml + if test -z "$(GLIB_COMPILE_RESOURCES)"; then touch $@; else \ + $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=../pixmaps --generate --c-name=gui_gtk --manual-register gui_gtk_res.xml; \ + fi auto/gui_gtk_gresources.h: gui_gtk_res.xml $(GUI_GTK_RES_INPUTS) if test -z "$(GLIB_COMPILE_RESOURCES)"; then touch $@; else \ $(GLIB_COMPILE_RESOURCES) --target=$@ --sourcedir=../pixmaps --generate --c-name=gui_gtk --manual-register gui_gtk_res.xml; \ diff --git a/src/version.c b/src/version.c index 0c23e6a518..3cbce87d87 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 449, /**/ 448, /**/ From 4cbdef8e30cd5da5d3a0941ab88bf082b0b1e164 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 6 May 2026 18:24:51 +0000 Subject: [PATCH 185/545] runtime(vim9): Check cmd.exe on WSL is executable closes: #20150 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/autoload/dist/vim9.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/dist/vim9.vim b/runtime/autoload/dist/vim9.vim index 4d8785d4ae..cd4022aba7 100644 --- a/runtime/autoload/dist/vim9.vim +++ b/runtime/autoload/dist/vim9.vim @@ -3,7 +3,7 @@ vim9script # Vim runtime support library # # Maintainer: The Vim Project -# Last Change: 2026 Apr 17 +# Last Change: 2026 May 06 export def IsSafeExecutable(filetype: string, executable: string): bool if empty(exepath(executable)) @@ -51,7 +51,7 @@ if has('unix') execute $'silent !cmd /c start "" /b {args} {Redir()}' | redraw! enddef endif - elseif exists('$WSL_DISTRO_NAME') # use cmd.exe to start GUI apps in WSL + elseif exists('$WSL_DISTRO_NAME') && executable('cmd.exe') # use cmd.exe to start GUI apps in WSL export def Launch(args: string) const command = (args =~? '\v<\f+\.(exe|com|bat|cmd)>') ? $'cmd.exe /c start /b {args} {Redir()}' From 92993329178cb1f72d700fff45ca86e1c2d369f8 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 6 May 2026 20:50:00 +0200 Subject: [PATCH 186/545] patch 9.2.0450: [security]: heap buffer overflow in spellfile.c read_compound() Problem: read_compound() in spellfile.c computes the size of the regex pattern buffer using signed-int arithmetic on the attacker controlled SN_COMPOUND sectionlen. With sectionlen=0x40000008 and UTF-8 encoding active the multiplication wraps to 27 while the per-byte loop writes up to ~1B bytes, overflowing the heap. Reachable when loading a crafted .spl file (e.g. via 'set spell' after a modeline sets 'spelllang'). The cp/ap/crp allocations have the same int + 1 overflow class (Daniel Cervera) Solution: Use type size_t as buffer size and reject values larger than COMPOUND_MAX_LEN (100000). Apply the same size_t treatment to the cp/ap/crp allocations. Github Advisory: https://github.com/vim/vim/security/advisories/GHSA-q4jv-r9gj-6cwv Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Christian Brabandt --- src/spellfile.c | 20 ++++++++++++++------ src/testdir/test_spellfile.vim | 4 ++++ src/version.c | 2 ++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/spellfile.c b/src/spellfile.c index a9a347a89a..5102dad5b6 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -290,6 +290,9 @@ #define CF_WORD 0x01 #define CF_UPPER 0x02 +// Max allowed length for COMPOUND section +#define COMPOUND_MAX_LEN 100000 + /* * Loop through all the siblings of a node (including the node) */ @@ -1219,6 +1222,8 @@ read_compound(FILE *fd, slang_T *slang, int len) char_u *crp; int cnt; garray_T *gap; + size_t patsize; + size_t flagsize; if (todo < 2) return SP_FORMERROR; // need at least two bytes @@ -1275,16 +1280,19 @@ read_compound(FILE *fd, slang_T *slang, int len) // "a[bc]/a*b+" -> "^\(a[bc]\|a*b\+\)$". // Inserting backslashes may double the length, "^\(\)$" is 7 bytes. // Conversion to utf-8 may double the size. - c = todo * 2 + 7; + if ((size_t)todo > COMPOUND_MAX_LEN) + return SP_FORMERROR; + patsize = (size_t)todo * 2 + 7; if (enc_utf8) - c += todo * 2; - pat = alloc(c); + patsize += (size_t)todo * 2; + flagsize = (size_t)todo + 1; + pat = alloc(patsize); if (pat == NULL) return SP_OTHERERROR; // We also need a list of all flags that can appear at the start and one // for all flags. - cp = alloc(todo + 1); + cp = alloc(flagsize); if (cp == NULL) { vim_free(pat); @@ -1293,7 +1301,7 @@ read_compound(FILE *fd, slang_T *slang, int len) slang->sl_compstartflags = cp; *cp = NUL; - ap = alloc(todo + 1); + ap = alloc(flagsize); if (ap == NULL) { vim_free(pat); @@ -1305,7 +1313,7 @@ read_compound(FILE *fd, slang_T *slang, int len) // And a list of all patterns in their original form, for checking whether // compounding may work in match_compoundrule(). This is freed when we // encounter a wildcard, the check doesn't work then. - crp = alloc(todo + 1); + crp = alloc(flagsize); slang->sl_comprules = crp; pp = pat; diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim index f46a25d99e..8f3ef4907d 100644 --- a/src/testdir/test_spellfile.vim +++ b/src/testdir/test_spellfile.vim @@ -334,6 +334,10 @@ func Test_spellfile_format_error() " SN_COMPOUND: incorrect comppatlen call Spellfile_Test(0z080000000007040101000000020165, 'E758:') + " SN_COMPOUND: oversized sectionlen + let v = eval('0z08004000000803010161' .. repeat('61', 50) .. 'FF') + call Spellfile_Test(v, 'E759:') + " SN_INFO: missing info call Spellfile_Test(0z0F0000000005040101, '') diff --git a/src/version.c b/src/version.c index 3cbce87d87..ff0960c227 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 450, /**/ 449, /**/ From 58124789aaf98fd96cd94a738633fd652a5e079a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 7 May 2026 19:29:06 +0000 Subject: [PATCH 187/545] patch 9.2.0451: 'findfunc' can't return extra info for cmdline completion Problem: 'findfunc' can't return extra info for cmdline completion (Maxim Kim). Solution: Handle 'findfunc' return value in cmdline completion like that of "customlist" functions (zeertzjq). fixes: #20155 closes: #20158 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 6 ++- src/cmdexpand.c | 45 ++++++++++++------- src/ex_docmd.c | 23 +--------- src/proto/cmdexpand.pro | 1 + src/proto/ex_docmd.pro | 2 +- .../dumps/Test_compl_findfunc_dict_01.dump | 12 +++++ .../dumps/Test_compl_findfunc_dict_02.dump | 12 +++++ src/testdir/test_cmdline.vim | 32 +++++++++++++ src/version.c | 2 + 9 files changed, 94 insertions(+), 41 deletions(-) create mode 100644 src/testdir/dumps/Test_compl_findfunc_dict_01.dump create mode 100644 src/testdir/dumps/Test_compl_findfunc_dict_02.dump diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 2baf8a01a7..e82cd30555 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 May 04 +*options.txt* For Vim version 9.2. Last change: 2026 May 07 VIM REFERENCE MANUAL by Bram Moolenaar @@ -3983,7 +3983,9 @@ A jump table for the options with a short description can be found at |Q_op|. |String| and is the |:find| command argument. The second argument is a |Boolean| and is set to |v:true| when the function is called to get a List of command-line completion matches for the |:find| command. - The function should return a List of strings. + The function should return a List of strings, or, in the command-line + completion case, whatever a |:command-completion-customlist| function + may return. The function is called only once per |:find| command invocation. The function can process all the directories specified in 'path'. diff --git a/src/cmdexpand.c b/src/cmdexpand.c index eb4f362b2b..a763456a1f 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -3156,7 +3156,7 @@ expand_files_and_dirs( if (xp->xp_context == EXPAND_FINDFUNC) { #ifdef FEAT_EVAL - ret = expand_findfunc(pat, matches, numMatches); + ret = expand_findfunc(xp, pat, matches, numMatches); #endif } else @@ -4148,16 +4148,13 @@ ExpandUserDefined( return OK; } -/* - * Expand names with a list returned by a function defined by the user. - */ - static int -ExpandUserList( - expand_T *xp, + void +expand_process_user_list( + list_T *retlist, char_u ***matches, - int *numMatches) + int *numMatches, + expand_T *xp) { - list_T *retlist; listitem_T *li; garray_T ga; garray_T ga_abbr; @@ -4167,12 +4164,6 @@ ExpandUserList( int have_extra = FALSE; int i; - *matches = NULL; - *numMatches = 0; - retlist = call_user_expand_func(call_func_retlist, xp); - if (retlist == NULL) - return FAIL; - ga_init2(&ga, sizeof(char *), 3); ga_init2(&ga_abbr, sizeof(char *), 3); ga_init2(&ga_kind, sizeof(char *), 3); @@ -4190,7 +4181,7 @@ ExpandUserList( if (li->li_tv.v_type == VAR_STRING) { if (li->li_tv.vval.v_string == NULL) - continue; // Skip empty strings + continue; // Skip NULL strings p = vim_strsave(li->li_tv.vval.v_string); } else if (li->li_tv.v_type == VAR_DICT @@ -4233,7 +4224,6 @@ ExpandUserList( ((char_u **)ga_menu.ga_data)[ga_menu.ga_len++] = menu; ((char_u **)ga_info.ga_data)[ga_info.ga_len++] = info; } - list_unref(retlist); *matches = ga.ga_data; *numMatches = ga.ga_len; @@ -4260,6 +4250,27 @@ ExpandUserList( vim_free(((char_u **)ga_info.ga_data)[i]); vim_free(ga_info.ga_data); } +} + +/* + * Expand names with a list returned by a function defined by the user. + */ + static int +ExpandUserList( + expand_T *xp, + char_u ***matches, + int *numMatches) +{ + list_T *retlist; + + *matches = NULL; + *numMatches = 0; + retlist = call_user_expand_func(call_func_retlist, xp); + if (retlist == NULL) + return FAIL; + + expand_process_user_list(retlist, matches, numMatches, xp); + list_unref(retlist); return OK; } #endif diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 7ea20b2628..7b31bed99a 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -7066,7 +7066,7 @@ call_findfunc(char_u *pat, int cmdcomplete) * Returns OK on success and FAIL otherwise. */ int -expand_findfunc(char_u *pat, char_u ***files, int *numMatches) +expand_findfunc(expand_T *xp, char_u *pat, char_u ***files, int *numMatches) { list_T *l; int len; @@ -7086,26 +7086,7 @@ expand_findfunc(char_u *pat, char_u ***files, int *numMatches) return FAIL; } - *files = ALLOC_MULT(char_u *, len); - if (*files == NULL) - { - list_free(l); - return FAIL; - } - - // Copy all the List items - listitem_T *li; - int idx = 0; - FOR_ALL_LIST_ITEMS(l, li) - { - if (li->li_tv.v_type == VAR_STRING) - { - (*files)[idx] = vim_strsave(li->li_tv.vval.v_string); - idx++; - } - } - - *numMatches = idx; + expand_process_user_list(l, files, numMatches, xp); list_free(l); return OK; diff --git a/src/proto/cmdexpand.pro b/src/proto/cmdexpand.pro index 67dff09ca4..390868a51f 100644 --- a/src/proto/cmdexpand.pro +++ b/src/proto/cmdexpand.pro @@ -20,6 +20,7 @@ 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 expand_process_user_list(list_T *retlist, char_u ***matches, int *numMatches, expand_T *xp); 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/ex_docmd.pro b/src/proto/ex_docmd.pro index afaec22827..df44d02004 100644 --- a/src/proto/ex_docmd.pro +++ b/src/proto/ex_docmd.pro @@ -47,7 +47,7 @@ void tabpage_close_other(tabpage_T *tp, int forceit); void ex_stop(exarg_T *eap); void handle_drop(int filec, char_u **filev, int split, void (*callback)(void *), void *cookie); void handle_any_postponed_drop(void); -int expand_findfunc(char_u *pat, char_u ***files, int *numMatches); +int expand_findfunc(expand_T *xp, char_u *pat, char_u ***files, int *numMatches); char *did_set_findfunc(optset_T *args); void free_findfunc_option(void); int set_ref_in_findfunc(int copyID); diff --git a/src/testdir/dumps/Test_compl_findfunc_dict_01.dump b/src/testdir/dumps/Test_compl_findfunc_dict_01.dump new file mode 100644 index 0000000000..3c93811384 --- /dev/null +++ b/src/testdir/dumps/Test_compl_findfunc_dict_01.dump @@ -0,0 +1,12 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @3| +0#0000001#e0e0e08|X|p|l|a|i|n| @8| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|X|f|i|l|e|1| |F| |f|i|l|e| @1| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|X|f|i|l|e|2| |F| |f|i|l|e| @1| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|X|d|i|r|1| @1|D| |d|i|r| @2| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|X|d|i|r|2| @1|D| |d|i|r| @2| +0#4040ff13#ffffff0@53 +|:+0#0000000&|f|i|n|d| |X|p|l|a|i|n> @62 diff --git a/src/testdir/dumps/Test_compl_findfunc_dict_02.dump b/src/testdir/dumps/Test_compl_findfunc_dict_02.dump new file mode 100644 index 0000000000..0e39d0486b --- /dev/null +++ b/src/testdir/dumps/Test_compl_findfunc_dict_02.dump @@ -0,0 +1,12 @@ +| +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @3| +0#0000001#ffd7ff255|X|p|l|a|i|n| @8| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|X|f|i|l|e|1| |F| |f|i|l|e| @1| +0#4040ff13#ffffff0@53 +|~| @3| +0#0000001#ffd7ff255|X|f|i|l|e|2| |F| |f|i|l|e| @1|╔+0&#e0e0e08|═@8|X| +0#4040ff13#ffffff0@42 +|~| @3| +0#0000001#e0e0e08|X|d|i|r|1| @1|D| |d|i|r| @2|║| |1|s|t| |d|i|r| |║| +0#4040ff13#ffffff0@42 +|~| @3| +0#0000001#ffd7ff255|X|d|i|r|2| @1|D| |d|i|r| @2|╚+0&#e0e0e08|═@8|⇲| +0#4040ff13#ffffff0@42 +|:+0#0000000&|f|i|n|d| |X|d|i|r|1> @63 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 7b3449e2bd..6809ef5f78 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -4705,6 +4705,38 @@ func Test_customlist_dict_completion_info_popup() call StopVimInTerminal(buf) endfunc +func Test_cmdline_complete_findfunc_dict() + CheckScreendump + + let lines =<< trim END + set wildmenu wildoptions=pum completeopt=menu,popup + func FindComplete(cmdarg, cmdcomplete) + return [ + \ 'Xplain', + \ {'word': 'Xfile1', 'kind': 'F', 'menu': 'file', 'info': '1st file'}, + \ {'word': 'Xfile2', 'kind': 'F', 'menu': 'file', 'info': '2nd file'}, + \ {'word': 'Xdir1', 'kind': 'D', 'menu': 'dir', 'info': '1st dir'}, + \ {'word': 'Xdir2', 'kind': 'D', 'menu': 'dir', 'info': '2nd dir'}, + \ ] + endfunc + set findfunc=FindComplete + END + call writefile(lines, 'XTest_compl_findfunc_dict', 'D') + let rows = 12 + let buf = RunVimInTerminal('-S XTest_compl_findfunc_dict', {'rows': rows}) + + call term_sendkeys(buf, ":find \") + call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':find Xplain') + 1)], g:test_timeout, ((rows - 5), '^\~\s\+Xplain\s\+$')) + call VerifyScreenDump(buf, 'Test_compl_findfunc_dict_01', {}) + + call term_sendkeys(buf, "\") + call WaitForTermCurPosAndLinesToMatch(buf, [rows, (strlen(':find Xdir1') + 1)], g:test_timeout, ((rows - 2), '1st dir')) + call VerifyScreenDump(buf, 'Test_compl_findfunc_dict_02', {}) + + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) +endfunc + func Test_custom_completion_with_glob() func TestGlobComplete(A, L, P) return split(glob('Xglob*'), "\n") diff --git a/src/version.c b/src/version.c index ff0960c227..8ab3ed1d49 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 451, /**/ 450, /**/ From b9871cef1073dfbcb74b73d9b939b374d6bd50e5 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 7 May 2026 19:40:16 +0000 Subject: [PATCH 188/545] patch 9.2.0452: screen.c popup opacity blend logic is duplicated Problem: screen_line() has four near-identical blocks computing the popup_attr, the combined attr, the blend value and the underlying base attr in sequence when handling popups with opacity. The duplication makes the function long and hard to follow, and changes have to be applied to all four sites. Solution: Extract the shared computation into popup_blend_with_base() and popup_base_attr_or() helpers, and cache per-popup attrs once via popup_opacity_T. No behavior change (Yasuhiro Matsumoto). closes: #20154 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/screen.c | 302 ++++++++++++++++++++++++++------------------------ src/version.c | 2 + 2 files changed, 160 insertions(+), 144 deletions(-) diff --git a/src/screen.c b/src/screen.c index da4d6e2783..ec2babfef6 100644 --- a/src/screen.c +++ b/src/screen.c @@ -468,6 +468,52 @@ skip_for_popup(int row, int col) } #ifdef FEAT_PROP_POPUP +/* + * Cached attributes of the current opacity popup, computed once per + * screen_line() to avoid recomputing them inside the per-cell loop. + */ +typedef struct { + int popup_attr; // get_win_attr(screen_opacity_popup) + int blend; // screen_opacity_popup->w_popup_blend +} popup_opacity_T; + + static inline void +popup_opacity_init(popup_opacity_T *po) +{ + po->popup_attr = get_win_attr(screen_opacity_popup); + po->blend = screen_opacity_popup->w_popup_blend; +} + +/* + * Read the underlying base screen attribute at (row, col), falling back + * to "fallback" when the position is outside base_screenattrs. + */ + static inline int +popup_base_attr_or(int row, int col, int fallback) +{ + int base = fallback; + + popup_get_base_screen_cell(row, col, NULL, &base, NULL); + return base; +} + +/* + * Blend "char_attr" through the cached opacity popup over "base_attr". + * "blend_fg" is TRUE when both fg and bg should blend (space cells), FALSE + * when only bg should blend so the popup fg is preserved (text cells). + */ + static inline int +popup_blend_with_base( + const popup_opacity_T *po, + int char_attr, + int base_attr, + int blend_fg) +{ + int combined = hl_combine_attr(po->popup_attr, char_attr); + + return hl_blend_attr(base_attr, combined, po->blend, blend_fg); +} + /* * For a double-wide character at a popup boundary with opacity:0 * (blend==100), the two cells may have different underlying attrs. @@ -558,6 +604,14 @@ screen_line( // 2: occupies two display cells bool override_success = push_highlight_overrides(wp->w_hl, wp->w_hl_len); +#ifdef FEAT_PROP_POPUP + popup_opacity_T po; // cached when drawing an opacity popup + int drawing_opacity_popup = + screen_opacity_popup != NULL + && (flags & SLF_POPUP); + if (drawing_opacity_popup) + popup_opacity_init(&po); +#endif // Check for illegal row and col, just in case. if (row >= Rows) @@ -674,128 +728,100 @@ screen_line( // For popup with opacity windows: if drawing a space, show the // underlying character with the popup's attributes blended in. int opacity_blank = FALSE; - // Check if the popup is drawing a space and the background is the - // second cell of a wide character. Skip drawing to preserve the - // wide character that was drawn in the previous cell. - if (screen_opacity_popup != NULL - && (flags & SLF_POPUP) - && ScreenLines[off_from] == ' ' - && (!enc_utf8 || ScreenLinesUC[off_from] == 0) - && ScreenLines[off_to] == 0 - && (!enc_utf8 || ScreenLinesUC[off_to] == 0) - && off_to > 0 - && enc_utf8 && ScreenLinesUC[off_to - 1] != 0 - && utf_char2cells(ScreenLinesUC[off_to - 1]) == 2) - { - // This is the second cell of a wide character. Don't overwrite it. - opacity_blank = TRUE; - redraw_this = FALSE; - } - else if (screen_opacity_popup != NULL - && (flags & SLF_POPUP) + // Popup with opacity: when drawing a space, blend the popup attrs + // onto whatever is underneath instead of overwriting it. + if (drawing_opacity_popup && ScreenLines[off_from] == ' ' - && (!enc_utf8 || ScreenLinesUC[off_from] == 0) - && (ScreenLines[off_to] != 0 - || (enc_utf8 && ScreenLinesUC[off_to] != 0))) + && (!enc_utf8 || ScreenLinesUC[off_from] == 0)) { - int bg_char_cells = 1; - if (enc_utf8 && ScreenLinesUC[off_to] != 0) - bg_char_cells = utf_char2cells(ScreenLinesUC[off_to]); - - // For wide background character, check if the next popup cell - // is also a space. If not, the wide char would be partially - // covered by a popup character, so don't show it. - if (bg_char_cells == 2) + int popup_src_attr = ScreenAttrs[off_from]; + int bg_is_empty = ScreenLines[off_to] == 0 + && (!enc_utf8 || ScreenLinesUC[off_to] == 0); + + // Case A: target cell is the second half of a wide char from a + // previous draw. Don't overwrite it. + if (bg_is_empty + && off_to > 0 + && enc_utf8 && ScreenLinesUC[off_to - 1] != 0 + && utf_char2cells(ScreenLinesUC[off_to - 1]) == 2) { - if (col + 1 >= endcol || off_from + 1 >= max_off_from - || off_to + 1 >= max_off_to) + opacity_blank = TRUE; + redraw_this = FALSE; + } + // Case B: target cell holds a real character — blend popup attrs + // over it, preserving the underlying glyph. + else if (ScreenLines[off_to] != 0 + || (enc_utf8 && ScreenLinesUC[off_to] != 0)) + { + int bg_char_cells = (enc_utf8 && ScreenLinesUC[off_to] != 0) + ? utf_char2cells(ScreenLinesUC[off_to]) : 1; + + if (bg_char_cells == 2) { - // Wide char doesn't fit at the edge. Replace with a - // blended space so opacity is still applied. - int char_attr = ScreenAttrs[off_from]; - int popup_attr = get_win_attr(screen_opacity_popup); - int combined = hl_combine_attr(popup_attr, char_attr); - int blend = screen_opacity_popup->w_popup_blend; - ScreenLines[off_to] = ' '; - if (enc_utf8) - ScreenLinesUC[off_to] = 0; - int base_attr = ScreenAttrs[off_to]; - popup_get_base_screen_cell(row, col + coloff, - NULL, &base_attr, NULL); - ScreenAttrs[off_to] = hl_blend_attr(base_attr, - combined, blend, TRUE); - screen_char(off_to, row, col + coloff); - opacity_blank = TRUE; - redraw_this = FALSE; - goto skip_opacity; + if (col + 1 >= endcol || off_from + 1 >= max_off_from + || off_to + 1 >= max_off_to) + { + // Wide char doesn't fit at the edge. Replace with a + // blended space so opacity is still applied. + int base_attr; + ScreenLines[off_to] = ' '; + if (enc_utf8) + ScreenLinesUC[off_to] = 0; + base_attr = popup_base_attr_or(row, col + coloff, + ScreenAttrs[off_to]); + ScreenAttrs[off_to] = popup_blend_with_base(&po, + popup_src_attr, base_attr, TRUE); + screen_char(off_to, row, col + coloff); + opacity_blank = TRUE; + redraw_this = FALSE; + goto skip_opacity; + } + // Wide bg char must be followed by a popup space, otherwise + // a popup glyph would corrupt its right half. + if (!(ScreenLines[off_from + 1] == ' ' + && (!enc_utf8 || ScreenLinesUC[off_from + 1] == 0))) + goto skip_opacity; } - int next_off_from = off_from + 1; - if (!(ScreenLines[next_off_from] == ' ' - && (!enc_utf8 || ScreenLinesUC[next_off_from] == 0))) + + // Keep the underlying character and blend the popup attrs over + // it. blend_fg=TRUE because we're drawing a space, so the + // foreground (the underlying glyph) should also blend. + opacity_blank = TRUE; + int base_attr = popup_base_attr_or(row, col + coloff, + ScreenAttrs[off_to]); + ScreenAttrs[off_to] = popup_blend_with_base(&po, + popup_src_attr, base_attr, TRUE); + screen_char(off_to, row, col + coloff); + // For wide bg char also blend the second cell; its base may be + // outside the popup area. + if (bg_char_cells == 2) { - // Next cell is not a space, don't show the wide char. - goto skip_opacity; + int base_attr2 = popup_base_attr_or(row, col + coloff + 1, + ScreenAttrs[off_to + 1]); + ScreenAttrs[off_to + 1] = popup_blend_with_base(&po, + popup_src_attr, base_attr2, TRUE); + if (po.blend == 100) + resolve_wide_char_opacity_attrs(row, + col + coloff, col + coloff + 1, + &ScreenAttrs[off_to], &ScreenAttrs[off_to + 1]); } + redraw_this = FALSE; } - - opacity_blank = TRUE; - // Keep the underlying character and blend its foreground color - // from popup background color to original color. - // Combine the popup window color with the character's own - // attribute (e.g. match highlight) so that its background - // color is preserved on blank cells. - int char_attr = ScreenAttrs[off_from]; - int popup_attr = get_win_attr(screen_opacity_popup); - int combined = hl_combine_attr(popup_attr, char_attr); - int blend = screen_opacity_popup->w_popup_blend; - int base_attr = ScreenAttrs[off_to]; - popup_get_base_screen_cell(row, col + coloff, - NULL, &base_attr, NULL); - ScreenAttrs[off_to] = hl_blend_attr(base_attr, - combined, blend, TRUE); - screen_char(off_to, row, col + coloff); - // For wide background character, also update the second cell - // with its own base attr (it may be outside the popup area). - if (bg_char_cells == 2) + // Case C: popup space overlaps the second half of a destroyed wide + // character. Blend so the cell matches its neighbors instead of + // appearing as a solid-colored gap. + else if (bg_is_empty) { - int base_attr2 = ScreenAttrs[off_to + 1]; - popup_get_base_screen_cell(row, col + coloff + 1, - NULL, &base_attr2, NULL); - ScreenAttrs[off_to + 1] = hl_blend_attr(base_attr2, - combined, blend, TRUE); - if (blend == 100) - resolve_wide_char_opacity_attrs(row, - col + coloff, col + coloff + 1, - &ScreenAttrs[off_to], &ScreenAttrs[off_to + 1]); + int base_attr; + ScreenLines[off_to] = ' '; + base_attr = popup_base_attr_or(row, col + coloff, + ScreenAttrs[off_to]); + ScreenAttrs[off_to] = popup_blend_with_base(&po, + popup_src_attr, base_attr, TRUE); + screen_char(off_to, row, col + coloff); + opacity_blank = TRUE; + redraw_this = FALSE; } - redraw_this = FALSE; - } - // When a popup space overlaps the second half of a destroyed wide - // character (e.g., the first half was overwritten by popup content), - // the underlying cell has ScreenLines == 0 and no valid wide char - // at the previous cell. Apply opacity blending so that the cell - // matches surrounding opacity-blended cells instead of appearing - // as a solid-colored gap. - else if (screen_opacity_popup != NULL - && (flags & SLF_POPUP) - && ScreenLines[off_from] == ' ' - && (!enc_utf8 || ScreenLinesUC[off_from] == 0) - && ScreenLines[off_to] == 0 - && (!enc_utf8 || ScreenLinesUC[off_to] == 0)) - { - int char_attr = ScreenAttrs[off_from]; - int popup_attr = get_win_attr(screen_opacity_popup); - int combined = hl_combine_attr(popup_attr, char_attr); - int blend = screen_opacity_popup->w_popup_blend; - ScreenLines[off_to] = ' '; - int base_attr = ScreenAttrs[off_to]; - popup_get_base_screen_cell(row, col + coloff, - NULL, &base_attr, NULL); - ScreenAttrs[off_to] = hl_blend_attr(base_attr, - combined, blend, TRUE); - screen_char(off_to, row, col + coloff); - opacity_blank = TRUE; - redraw_this = FALSE; } skip_opacity: #endif @@ -947,49 +973,37 @@ screen_line( ScreenAttrs[off_to] = ScreenAttrs[off_from]; #ifdef FEAT_PROP_POPUP - // For popup with opacity text: blend background with underlying. - if (screen_opacity_popup != NULL - && (flags & SLF_POPUP) - && screen_opacity_popup->w_popup_blend > 0) + // For popup with opacity text: blend popup attrs over the underlying + // base. blend_fg=FALSE so the popup foreground (the real glyph) is + // preserved while only the background blends. + if (drawing_opacity_popup && po.blend > 0) { - int char_attr = ScreenAttrs[off_from]; - int popup_attr = get_win_attr(screen_opacity_popup); - int blend = screen_opacity_popup->w_popup_blend; - int combined = hl_combine_attr(popup_attr, char_attr); - int underlying_attr = 0; - - popup_get_base_screen_cell(row, col + coloff, - NULL, &underlying_attr, NULL); - - // For double-wide characters, a terminal cannot render - // different background colors for the left and right - // halves. When one half is over a lower opacity popup - // and the other is not, use the non-popup side's - // underlying attr for both to avoid color leaking. + int popup_src_attr = ScreenAttrs[off_from]; + int scol1 = col + coloff; + int base1 = popup_base_attr_or(row, scol1, 0); + if (char_cells == 2) { - int underlying_attr2 = 0; - int scol1 = col + coloff; + int base2 = popup_base_attr_or(row, scol1 + 1, 0); + + // Terminals can't render different bg colors for the two + // halves of a wide char. If one half is over a lower + // opacity popup and the other isn't, use the non-popup + // side for both halves to avoid color leaking. int over1 = popup_is_over_opacity(row, scol1); int over2 = popup_is_over_opacity(row, scol1 + 1); - - popup_get_base_screen_cell(row, scol1 + 1, - NULL, &underlying_attr2, NULL); if (over1 != over2) { - // One half is over a lower popup, the other is - // not. Use the non-popup side for both. if (over1) - underlying_attr = underlying_attr2; + base1 = base2; else - underlying_attr2 = underlying_attr; + base2 = base1; } - ScreenAttrs[off_to + 1] = hl_blend_attr( - underlying_attr2, combined, blend, - FALSE); + ScreenAttrs[off_to + 1] = popup_blend_with_base(&po, + popup_src_attr, base2, FALSE); } - ScreenAttrs[off_to] = hl_blend_attr(underlying_attr, - combined, blend, FALSE); + ScreenAttrs[off_to] = popup_blend_with_base(&po, + popup_src_attr, base1, FALSE); } else #endif diff --git a/src/version.c b/src/version.c index 8ab3ed1d49..9d5136df60 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 452, /**/ 451, /**/ From c895390e58fb080f94dcc1c60757a3d697698515 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Fri, 8 May 2026 20:57:34 +0000 Subject: [PATCH 189/545] patch 9.2.0453: vertical separator of statusline blend into active statusline Problem: Since v9.2.0349, the vertical separator cell at status line rows is drawn as a space with StatusLine highlight, hiding the user's 'fillchars' "vert" or "stl"/"stlnc" character at that cell (after v9.2.0349) Solution: Drop the status line blend. At status line rows the separator cell goes back to using the status fillchar when adjacent status lines are connected, or the vsep character otherwise. (Same as before v9.2.0348) Keep the VertSplitNC highlight group introduced in v9.2.0349. The highlight (VertSplit vs VertSplitNC) is selected based on whether the current window is adjacent to the separator at the row. Vertical separators are redrawn on current-window changes and on :redrawstatus[!] so the VertSplit/VertSplitNC highlight is updated immediately. fixes: #20089 related: #19951 closes: #20167 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/syntax.txt | 31 +---- runtime/syntax/vim.vim | 2 +- src/drawscreen.c | 12 +- src/proto/screen.pro | 1 - src/screen.c | 117 +++++------------- src/testdir/dumps/Test_VertSplitNC_2.dump | 2 +- src/testdir/dumps/Test_VertSplitNC_3.dump | 2 +- src/testdir/dumps/Test_VertSplitNC_fc_1.dump | 12 -- src/testdir/dumps/Test_VertSplitNC_fc_2.dump | 12 -- src/testdir/dumps/Test_VertSplitNC_stl_1.dump | 20 --- src/testdir/dumps/Test_VertSplitNC_stl_2.dump | 20 --- src/testdir/dumps/Test_VertSplitNC_stl_3.dump | 20 --- src/testdir/dumps/Test_VertSplitNC_whl2.dump | 2 +- .../dumps/Test_VertSplitNC_winbar_1.dump | 2 +- src/testdir/dumps/Test_diff_anchors_07.dump | 2 +- ...st_diff_anchors_scrollbind_topline_01.dump | 2 +- ...st_diff_anchors_scrollbind_topline_02.dump | 2 +- ...st_diff_anchors_scrollbind_topline_03.dump | 2 +- ...st_diff_anchors_scrollbind_topline_04.dump | 2 +- ...st_diff_anchors_scrollbind_topline_05.dump | 2 +- ...st_diff_anchors_scrollbind_topline_06.dump | 2 +- ...st_diff_anchors_scrollbind_topline_07.dump | 2 +- ...st_diff_anchors_scrollbind_topline_08.dump | 2 +- ...st_diff_anchors_scrollbind_topline_13.dump | 2 +- ...st_diff_anchors_scrollbind_topline_14.dump | 2 +- ...st_diff_anchors_scrollbind_topline_15.dump | 2 +- ...st_diff_anchors_scrollbind_topline_16.dump | 2 +- ...st_diff_anchors_scrollbind_topline_17.dump | 2 +- .../dumps/Test_diff_get_put_linematch_16.dump | 2 +- .../dumps/Test_diff_get_put_linematch_17.dump | 2 +- .../dumps/Test_diff_get_put_linematch_18.dump | 2 +- .../dumps/Test_diff_get_put_linematch_19.dump | 2 +- .../dumps/Test_diff_get_put_linematch_3.dump | 2 +- .../dumps/Test_diff_get_put_linematch_4.dump | 2 +- .../dumps/Test_diff_inline_multiline_07.dump | 2 +- .../dumps/Test_diff_inline_multiline_08.dump | 2 +- .../dumps/Test_diff_inline_multiline_09.dump | 2 +- .../dumps/Test_diff_inline_multiline_10.dump | 2 +- src/testdir/dumps/Test_diff_rnu_01.dump | 2 +- src/testdir/dumps/Test_diff_rnu_02.dump | 2 +- src/testdir/dumps/Test_diff_rnu_03.dump | 2 +- src/testdir/dumps/Test_diff_scroll_1.dump | 2 +- src/testdir/dumps/Test_diff_scroll_2.dump | 2 +- .../dumps/Test_diff_scroll_change_03.dump | 2 +- src/testdir/dumps/Test_diff_topline_1.dump | 2 +- src/testdir/dumps/Test_diff_topline_2.dump | 2 +- src/testdir/dumps/Test_diff_topline_4.dump | 2 +- .../dumps/Test_diff_with_cul_bri_01.dump | 2 +- .../dumps/Test_diff_with_cul_bri_02.dump | 2 +- .../dumps/Test_diff_with_cul_bri_03.dump | 2 +- .../dumps/Test_diff_with_cul_bri_04.dump | 2 +- .../dumps/Test_diff_with_cursorline_01.dump | 2 +- .../dumps/Test_diff_with_cursorline_02.dump | 2 +- .../dumps/Test_diff_with_cursorline_03.dump | 2 +- .../dumps/Test_display_fillchars_1.dump | 4 +- .../dumps/Test_display_fillchars_2.dump | 4 +- src/testdir/dumps/Test_hor_scroll_1.dump | 2 +- src/testdir/dumps/Test_hor_scroll_2.dump | 2 +- src/testdir/dumps/Test_hor_scroll_3.dump | 2 +- src/testdir/dumps/Test_hor_scroll_4.dump | 2 +- src/testdir/dumps/Test_hor_scroll_5.dump | 2 +- .../Test_laststatus_vsplit_row_height3_1.dump | 2 +- src/testdir/dumps/Test_linematch_3diffs1.dump | 2 +- src/testdir/dumps/Test_listchars_01.dump | 2 +- src/testdir/dumps/Test_listchars_02.dump | 2 +- src/testdir/dumps/Test_listchars_03.dump | 2 +- src/testdir/dumps/Test_listchars_04.dump | 2 +- src/testdir/dumps/Test_listchars_05.dump | 2 +- src/testdir/dumps/Test_listchars_06.dump | 2 +- src/testdir/dumps/Test_listchars_07.dump | 2 +- .../dumps/Test_popup_prop_not_visible_01.dump | 2 +- .../Test_popup_prop_not_visible_01a.dump | 2 +- .../Test_popup_prop_not_visible_01b.dump | 2 +- .../dumps/Test_popup_prop_not_visible_02.dump | 2 +- .../dumps/Test_popup_prop_not_visible_03.dump | 2 +- src/testdir/dumps/Test_prop_diff_mode_2.dump | 2 +- .../dumps/Test_smooth_diff_change_line_1.dump | 2 +- .../dumps/Test_smooth_diff_change_line_2.dump | 2 +- .../dumps/Test_smooth_diff_change_line_3.dump | 2 +- .../Test_smooth_diff_change_line_3a.dump | 2 +- .../dumps/Test_smooth_diff_change_line_4.dump | 2 +- .../dumps/Test_switchwin_clear_pum_02.dump | 2 +- .../Test_tabpanel_drawing_scrolling_3.dump | 2 +- ...Test_terminal_wincolor_split_MyWinCol.dump | 4 +- ...est_terminal_wincolor_split_MyWinCol2.dump | 4 +- src/testdir/dumps/Test_winhighlight_14.dump | 2 +- src/testdir/dumps/Test_winhighlight_15.dump | 2 +- src/testdir/dumps/Test_winhighlight_2.dump | 2 +- src/testdir/dumps/Test_winhighlight_6.dump | 2 +- .../dumps/Test_winhighlight_hlsearch_2.dump | 2 +- .../dumps/Test_winscrolled_once_only_1.dump | 2 +- src/testdir/test_highlight.vim | 75 ----------- src/testdir/test_statusline.vim | 6 +- src/testdir/test_window_cmd.vim | 4 +- src/version.c | 2 + src/vim.h | 2 +- src/window.c | 13 +- 97 files changed, 140 insertions(+), 379 deletions(-) delete mode 100644 src/testdir/dumps/Test_VertSplitNC_fc_1.dump delete mode 100644 src/testdir/dumps/Test_VertSplitNC_fc_2.dump delete mode 100644 src/testdir/dumps/Test_VertSplitNC_stl_1.dump delete mode 100644 src/testdir/dumps/Test_VertSplitNC_stl_2.dump delete mode 100644 src/testdir/dumps/Test_VertSplitNC_stl_3.dump diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 677e7cc3eb..6c3888508a 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 May 04 +*syntax.txt* For Vim version 9.2. Last change: 2026 May 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -5992,28 +5992,13 @@ EndOfBuffer Filler lines (~) after the last line in the buffer. *hl-ErrorMsg* ErrorMsg Error messages on the command line. *hl-VertSplit* -VertSplit Column separating vertically split windows that are adjacent - to the current window. The cell character and highlight at - each screen row depend on what is at that row on both sides - of the separator: - - On a status line row of the current window: the cell is - drawn as a space with the |hl-StatusLine| highlight, so it - visually merges into the current window's status line. - - On any other row (including a row that is a status line - row of the adjacent non-current window but not of the - current window): the cell is drawn with the "vert" item of - 'fillchars' and the VertSplit highlight. +VertSplit Column separating vertically split windows that is adjacent + to the current window. Drawn with the "vert" item of + 'fillchars'. *hl-VertSplitNC* VertSplitNC Column separating vertically split windows where neither - adjacent window is the current window. The cell character - and highlight at each screen row depend on what is at that - row on both sides of the separator: - - On a status line row of either adjacent (non-current) - window: the cell is drawn as a space with the - |hl-StatusLineNC| highlight of that window, so it visually - merges into that window's status line. - - On any other row: the cell is drawn with the "vert" item - of 'fillchars' and the VertSplitNC highlight. + adjacent window is the current window. Drawn with the + "vert" item of 'fillchars'. By default, highlighted like |hl-VertSplit|. *hl-Folded* Folded Line used for closed folds. @@ -6131,12 +6116,8 @@ SpellRare Word that is recognized by the spellchecker as one that is This will be combined with the highlighting used otherwise. *hl-StatusLine* StatusLine Status line of current window. - Also used for the vertical separator cell adjacent to the - current window's status line. see |hl-VertSplit|. *hl-StatusLineNC* StatusLineNC status lines of not-current windows. - Also used for the vertical separator cell adjacent to the - non-current window's status line. see |hl-VertSplitNC|. Note: If this is equal to "StatusLine", Vim will use "^^^" in the status line of the current window. *hl-StatusLineTerm* diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index baa5741381..698e551a7d 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -2,7 +2,7 @@ " Language: Vim script " Maintainer: Hirohito Higashi " Doug Kearns -" Last Change: 2026 Apr 28 +" Last Change: 2026 May 08 " Former Maintainer: Charles E. Campbell " DO NOT CHANGE DIRECTLY. diff --git a/src/drawscreen.c b/src/drawscreen.c index cecfcccb62..751e09aa3f 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -620,7 +620,10 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED) for (i = 0; i < wp->w_status_height; i++) { int r = row + i; - fillchar = sep_cell_at_row(&attr, wp, r); + if (stl_connected(wp)) + fillchar = fillchar_status(&attr, wp); + else + fillchar = fillchar_vsep(&attr, wp, r); screen_putchar(fillchar, r, W_ENDCOL(wp), attr); } } @@ -3431,13 +3434,6 @@ redraw_statuslines(void) if (ret) pop_highlight_overrides(); } - if (redraw_vseps) - { - redraw_vseps = FALSE; - FOR_ALL_WINDOWS(wp) - if (wp->w_vsep_width > 0) - draw_vsep_win(wp, 0); - } if (redraw_tabline) draw_tabline(); diff --git a/src/proto/screen.pro b/src/proto/screen.pro index afaa86287a..a1d5714d8f 100644 --- a/src/proto/screen.pro +++ b/src/proto/screen.pro @@ -49,7 +49,6 @@ void draw_tabline(void); void get_trans_bufname(buf_T *buf); int fillchar_status(int *attr, win_T *wp); int fillchar_vsep(int *attr, win_T *wp, int row); -int sep_cell_at_row(int *attr, win_T *wp, int row); int redrawing(void); int messaging(void); void comp_col(void); diff --git a/src/screen.c b/src/screen.c index ec2babfef6..a482c400af 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1155,7 +1155,9 @@ screen_line( { if (!skip_for_popup(row, col + coloff)) { - int c = sep_cell_at_row(&hl, wp, row); + int c; + + c = fillchar_vsep(&hl, wp, row); if (ScreenLines[off_to] != (schar_T)c || (enc_utf8 && (int)ScreenLinesUC[off_to] != (c >= 0x80 ? c : 0)) @@ -1207,22 +1209,40 @@ rl_mirror(char_u *str) /* * Draw the vertical separator right of window "wp" starting with line "row". + * Includes status line rows so the VertSplit/VertSplitNC highlight is + * refreshed after a current window change even for non-current windows + * whose status lines are not redrawn. */ void draw_vsep_win(win_T *wp, int row) { - int hl; - if (!wp->w_vsep_width) return; - // Draw the vertical separator right of this window, row by row, so - // that the cell can differ per row depending on adjacent windows. - for (int r = W_WINROW(wp) + row; r < W_WINROW(wp) + wp->w_height; ++r) + int content_end = W_WINROW(wp) + wp->w_height; + + // Content rows: VertSplit/VertSplitNC based on adjacency. + for (int r = W_WINROW(wp) + row; r < content_end; ++r) { - int c = sep_cell_at_row(&hl, wp, r); + int hl; + int c = fillchar_vsep(&hl, wp, r); screen_fill(r, r + 1, W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); } + + // Status line rows: when the status line is connected with the right + // neighbour the separator cell is the status line fillchar; otherwise + // it is the vsep char. + if (wp->w_status_height != 0) + { + int hl; + int c; + if (stl_connected(wp)) + c = fillchar_status(&hl, wp); + else + c = fillchar_vsep(&hl, wp, content_end); + for (int r = content_end; r < content_end + wp->w_status_height; ++r) + screen_fill(r, r + 1, W_ENDCOL(wp), W_ENDCOL(wp) + 1, c, ' ', hl); + } } /* @@ -5231,10 +5251,11 @@ vsep_row_is_curwin(win_T *wp, int row) return true; // Check if curwin is immediately to the right of wp's separator and - // "row" is within curwin's row range (including the winbar). + // "row" is within curwin's row range (winbar + content + status line). if (curwin->w_wincol == W_ENDCOL(wp) + wp->w_vsep_width && row >= curwin->w_winrow - && row < W_WINROW(curwin) + curwin->w_height) + && row < W_WINROW(curwin) + curwin->w_height + + curwin->w_status_height) return true; return false; @@ -5243,7 +5264,8 @@ vsep_row_is_curwin(win_T *wp, int row) /* * Get the character to use in a separator between vertically split windows. * Get its attributes in "*attr". - * "row" is the screen row number used to determine VertSplit or VertSplitNC. + * Use VertSplit when the separator is adjacent to the current window, + * VertSplitNC otherwise. */ int fillchar_vsep(int *attr, win_T *wp, int row) @@ -5263,81 +5285,6 @@ fillchar_vsep(int *attr, win_T *wp, int row) return wp->w_fill_chars.vert; } -/* - * Find the window immediately to the right of "wp"'s right separator at - * screen row "row". Returns NULL if there is none. - */ - static win_T * -right_neighbor_at_row(win_T *wp, int row) -{ - win_T *rn; - int rcol = W_ENDCOL(wp) + wp->w_vsep_width; - - FOR_ALL_WINDOWS(rn) - if (rn->w_wincol == rcol - && row >= rn->w_winrow - && row < W_WINROW(rn) + rn->w_height + rn->w_status_height) - return rn; - return NULL; -} - -/* - * Return true if window "wn" has its status line at screen row "row". - */ - static bool -win_status_at(win_T *wn, int row) -{ - return wn != NULL && wn->w_status_height > 0 - && row >= W_WINROW(wn) + wn->w_height - && row < W_WINROW(wn) + wn->w_height + wn->w_status_height; -} - -/* - * Decide the character and highlight to draw at the separator cell on the - * right edge of window "wp" at screen row "row". Returns the character via - * the return value and sets "*attr" to the highlight attribute. - * - * Rule: - * 1. If curwin is on either side of the separator at this row: - * - At curwin's status line row: draw a space with StatusLine highlight - * so curwin's status line extends across the separator. - * - Otherwise (curwin's content row): draw the vsep char with VertSplit. - * 2. If curwin is not adjacent here: - * - If the right neighbor has its status line at this row: draw a space - * with the right neighbor's StatusLineNC. - * - Else if "wp" has its status line at this row: draw a space with - * wp's StatusLineNC. - * - Else (both sides have content): draw the vsep char with VertSplitNC. - */ - int -sep_cell_at_row(int *attr, win_T *wp, int row) -{ - win_T *rn = right_neighbor_at_row(wp, row); - bool curwin_adjacent = (wp == curwin) || (rn == curwin); - - if (curwin_adjacent) - { - if (win_status_at(curwin, row)) - { - (void)fillchar_status(attr, curwin); - return ' '; - } - return fillchar_vsep(attr, wp, row); - } - - if (win_status_at(rn, row)) - { - (void)fillchar_status(attr, rn); - return ' '; - } - if (win_status_at(wp, row)) - { - (void)fillchar_status(attr, wp); - return ' '; - } - return fillchar_vsep(attr, wp, row); -} - /* * Return TRUE if redrawing should currently be done. */ diff --git a/src/testdir/dumps/Test_VertSplitNC_2.dump b/src/testdir/dumps/Test_VertSplitNC_2.dump index 8afa526ac0..c0da105f94 100644 --- a/src/testdir/dumps/Test_VertSplitNC_2.dump +++ b/src/testdir/dumps/Test_VertSplitNC_2.dump @@ -8,5 +8,5 @@ |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 -|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| +0#ffffff16#0000e05|<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p +|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#ffffff16#0000e05| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p | +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_3.dump b/src/testdir/dumps/Test_VertSplitNC_3.dump index 614de055ea..0922ede594 100644 --- a/src/testdir/dumps/Test_VertSplitNC_3.dump +++ b/src/testdir/dumps/Test_VertSplitNC_3.dump @@ -8,5 +8,5 @@ |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 -|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| +0#ffffff16#0000e05|<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p +|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#ffffff16#0000e05| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p | +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_fc_1.dump b/src/testdir/dumps/Test_VertSplitNC_fc_1.dump deleted file mode 100644 index 6aa0dbf449..0000000000 --- a/src/testdir/dumps/Test_VertSplitNC_fc_1.dump +++ /dev/null @@ -1,12 +0,0 @@ ->f+0&#ffffff0|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#6c6c6c255&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|<+0#ffffff16#0000e05|o| |N|a|m|e|]| |[|+|]|=|1|,|1|=@5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]|-|1|,|1|-@5|T|o|p| |<| |N|a|m|e|]| |[|+|]|-|1|,|1|-@5|T|o|p -| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_fc_2.dump b/src/testdir/dumps/Test_VertSplitNC_fc_2.dump deleted file mode 100644 index 58652b933b..0000000000 --- a/src/testdir/dumps/Test_VertSplitNC_fc_2.dump +++ /dev/null @@ -1,12 +0,0 @@ -|f+0&#ffffff0|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&>f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|f|i|l@1|c|h|a|r|s| |t|e|s|t| @10||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9||+0#40ff4011&|f+0#0000000&|i|l@1|c|h|a|r|s| |t|e|s|t| @9 -|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]|-|1|,|1|-@5|T|o|p| +0#ffffff16#0000e05|<| |N|a|m|e|]| |[|+|]|=|1|,|1|=@5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]|-|1|,|1|-@5|T|o|p -| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_stl_1.dump b/src/testdir/dumps/Test_VertSplitNC_stl_1.dump deleted file mode 100644 index 5e4d8bb009..0000000000 --- a/src/testdir/dumps/Test_VertSplitNC_stl_1.dump +++ /dev/null @@ -1,20 +0,0 @@ -|m+0&#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&>m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p| |[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p -| @74 -@75 -@75 -| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_stl_2.dump b/src/testdir/dumps/Test_VertSplitNC_stl_2.dump deleted file mode 100644 index c7ceac388b..0000000000 --- a/src/testdir/dumps/Test_VertSplitNC_stl_2.dump +++ /dev/null @@ -1,20 +0,0 @@ -|m+0&#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&>m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#ffffff16#0000e05@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p| |[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p -| @74 -@75 -@75 -| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_stl_3.dump b/src/testdir/dumps/Test_VertSplitNC_stl_3.dump deleted file mode 100644 index 2d4189ed6b..0000000000 --- a/src/testdir/dumps/Test_VertSplitNC_stl_3.dump +++ /dev/null @@ -1,20 +0,0 @@ -|m+0&#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#6c6c6c255&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22| +0#0000001#a8a8a8255@37 -|m+0#0000000#ffffff0|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&>m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|m|u|l|t|i| |s|t|l| |t|e|s|t| @22||+0#40ff4011&|m+0#0000000&|u|l|t|i| |s|t|l| |t|e|s|t| @22 -|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p| +0#ffffff16#0000e05|[|N|o| |N|a|m|e|]| @20|1|,|1| |T|o|p -| +0#0000001#a8a8a8255@36| +0#ffffff16#0000e05@37 -| +0#0000001#a8a8a8255@36| +0#ffffff16#0000e05@37 -| +0#0000001#a8a8a8255@36| +0#ffffff16#0000e05@37 -| +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_whl2.dump b/src/testdir/dumps/Test_VertSplitNC_whl2.dump index 825775e591..9701df25b4 100644 --- a/src/testdir/dumps/Test_VertSplitNC_whl2.dump +++ b/src/testdir/dumps/Test_VertSplitNC_whl2.dump @@ -8,5 +8,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump b/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump index 4fbee462da..ce880fa309 100644 --- a/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump +++ b/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump @@ -8,5 +8,5 @@ |w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 |w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 |w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 -|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p| +0#ffffff16#0000e05|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p +|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p| |[+0#ffffff16#0000e05|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p | +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_diff_anchors_07.dump b/src/testdir/dumps/Test_diff_anchors_07.dump index eda58e9fd7..310fa6544b 100644 --- a/src/testdir/dumps/Test_diff_anchors_07.dump +++ b/src/testdir/dumps/Test_diff_anchors_07.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@22||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255| @20||+1&#ffffff0| +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@21 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@22||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|3+0#0000000#5fd7ff255| @20||+1&#ffffff0| +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@21 |~+0&#ffffff0| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump index 5616d5f371..a0ffa9f301 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump index 3d66db8d6e..9e5c9dc94e 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump index fdab541440..76f8617683 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump index 705446a3dd..1c069d8ded 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump index 5daf6573d3..87bbc572e5 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump index 6cae25e1d4..1f5917cf47 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump index 705446a3dd..1c069d8ded 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump index a2ade506e4..aadabe077d 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump index af99924276..99cb0ded0f 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump index c6a418a4b4..3c35c7741a 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump index cf6bce8d0d..432cadb94d 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump index 4bff316f42..b95c2661a6 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump index 9496933153..7daaf7a9ac 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_16.dump b/src/testdir/dumps/Test_diff_get_put_linematch_16.dump index eeb984d682..d4649d2052 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_16.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_16.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|D+0#0000000#5fd7ff255|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|6|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|6|,|1| @11|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_17.dump b/src/testdir/dumps/Test_diff_get_put_linematch_17.dump index b8426a39a4..69737fa58c 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_17.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_17.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|D+0#0000000#5fd7ff255|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|8|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|8|,|1| @11|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_18.dump b/src/testdir/dumps/Test_diff_get_put_linematch_18.dump index 2b94da9551..49ab243871 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_18.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_18.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|D+0#0000000#5fd7ff255|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|6|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|9|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|6|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|9|,|1| @11|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_19.dump b/src/testdir/dumps/Test_diff_get_put_linematch_19.dump index 8fdd9f608a..cbea3e5602 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_19.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_19.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|D+0#0000000#ffffff0|E|F| @31||+1&&| +0#0000e05#a8a8a8255@1|D+0#0000000#ffffff0|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1>s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|1|3|,|1| @10|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|7|,|1| @10|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|1|3|,|1| @10|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|7|,|1| @10|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_3.dump b/src/testdir/dumps/Test_diff_get_put_linematch_3.dump index ce905130e9..cb62a9ef71 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_3.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_3.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 |:+0&&|5|,|1|0|d|i|f@1|g|e|t| @62 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_4.dump b/src/testdir/dumps/Test_diff_get_put_linematch_4.dump index c6ea6ddc85..16c2cd874d 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_4.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_4.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 |:+0&&|4|,|1|7|d|i|f@1|g|e|t| @62 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_07.dump b/src/testdir/dumps/Test_diff_inline_multiline_07.dump index b76d549b27..de58a6ac30 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_07.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_07.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_08.dump b/src/testdir/dumps/Test_diff_inline_multiline_08.dump index 51c35f5afd..901ea4699d 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_08.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_08.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_09.dump b/src/testdir/dumps/Test_diff_inline_multiline_09.dump index dc36ed8ffc..91f59cda25 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_09.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_09.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_10.dump b/src/testdir/dumps/Test_diff_inline_multiline_10.dump index ad86c4684d..212c247975 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_10.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_10.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_rnu_01.dump b/src/testdir/dumps/Test_diff_rnu_01.dump index d51d66674c..dc7c0f9101 100644 --- a/src/testdir/dumps/Test_diff_rnu_01.dump +++ b/src/testdir/dumps/Test_diff_rnu_01.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_rnu_02.dump b/src/testdir/dumps/Test_diff_rnu_02.dump index d33a2ee0ab..32ffc4003d 100644 --- a/src/testdir/dumps/Test_diff_rnu_02.dump +++ b/src/testdir/dumps/Test_diff_rnu_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_rnu_03.dump b/src/testdir/dumps/Test_diff_rnu_03.dump index 0d29b84081..8c8b938449 100644 --- a/src/testdir/dumps/Test_diff_rnu_03.dump +++ b/src/testdir/dumps/Test_diff_rnu_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_scroll_1.dump b/src/testdir/dumps/Test_diff_scroll_1.dump index 9713461012..ee53c35bbb 100644 --- a/src/testdir/dumps/Test_diff_scroll_1.dump +++ b/src/testdir/dumps/Test_diff_scroll_1.dump @@ -8,5 +8,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|d+0#0000000#5fd7ff255|o|l|o|r| @29 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|s+0#0000000#5fd7ff255|i|t| @31 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|a+0#0000000#5fd7ff255|m|e|t|,| @29 -|X+1&#ffffff0|l|e|f|t| @13|5|,|0|-|1| @9|T|o|p| +3&&|X|r|i|g|h|t| @12|5|,|0|-|1| @9|T|o|p +|X+1&#ffffff0|l|e|f|t| @13|5|,|0|-|1| @9|T|o|p| |X+3&&|r|i|g|h|t| @12|5|,|0|-|1| @9|T|o|p |"+0&&|X|r|i|g|h|t|"| |3@1|L|,| |2|6|3|B| @56 diff --git a/src/testdir/dumps/Test_diff_scroll_2.dump b/src/testdir/dumps/Test_diff_scroll_2.dump index d6cd1b9c99..4cac386a6c 100644 --- a/src/testdir/dumps/Test_diff_scroll_2.dump +++ b/src/testdir/dumps/Test_diff_scroll_2.dump @@ -8,5 +8,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|d+0#0000000#5fd7ff255|o|l|o|r| @29 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|s+0#0000000#5fd7ff255|i|t| @31 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|a+0#0000000#5fd7ff255|m|e|t|,| @29 -|X+1&#ffffff0|l|e|f|t| @13|6|,|1| @11|T|o|p| +3&&|X|r|i|g|h|t| @12|6|,|1| @11|T|o|p +|X+1&#ffffff0|l|e|f|t| @13|6|,|1| @11|T|o|p| |X+3&&|r|i|g|h|t| @12|6|,|1| @11|T|o|p |"+0&&|X|r|i|g|h|t|"| |3@1|L|,| |2|6|3|B| @56 diff --git a/src/testdir/dumps/Test_diff_scroll_change_03.dump b/src/testdir/dumps/Test_diff_scroll_change_03.dump index 5a4088975b..6d0c8600f6 100644 --- a/src/testdir/dumps/Test_diff_scroll_change_03.dump +++ b/src/testdir/dumps/Test_diff_scroll_change_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|7|,|3| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|5|,|3| @10|B|o|t +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|7|,|3| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|5|,|3| @10|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_topline_1.dump b/src/testdir/dumps/Test_diff_topline_1.dump index 5b5122621c..1d4c9145f0 100644 --- a/src/testdir/dumps/Test_diff_topline_1.dump +++ b/src/testdir/dumps/Test_diff_topline_1.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|4| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|5| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|6| @32 -|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% +|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_topline_2.dump b/src/testdir/dumps/Test_diff_topline_2.dump index a0f3db79c0..2d28bd373a 100644 --- a/src/testdir/dumps/Test_diff_topline_2.dump +++ b/src/testdir/dumps/Test_diff_topline_2.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|4| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|5| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|6| @32 -|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% +|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% |9+0&&| @73 diff --git a/src/testdir/dumps/Test_diff_topline_4.dump b/src/testdir/dumps/Test_diff_topline_4.dump index 94414b1e79..6043d7ad73 100644 --- a/src/testdir/dumps/Test_diff_topline_4.dump +++ b/src/testdir/dumps/Test_diff_topline_4.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|1+0#0000000#5fd7ff255|9| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|0| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|1| @32 -|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p +|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_01.dump b/src/testdir/dumps/Test_diff_with_cul_bri_01.dump index 3855fa17c5..8151088e04 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_01.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_01.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_02.dump b/src/testdir/dumps/Test_diff_with_cul_bri_02.dump index c1ae50d6d4..62126f6b39 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_02.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_02.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_03.dump b/src/testdir/dumps/Test_diff_with_cul_bri_03.dump index bdf8be065e..f32be7385b 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_03.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_03.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_04.dump b/src/testdir/dumps/Test_diff_with_cul_bri_04.dump index aa3ce8e5d4..9489f9ab04 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_04.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_04.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cursorline_01.dump b/src/testdir/dumps/Test_diff_with_cursorline_01.dump index 0b9bcb9bda..31d8b2a189 100644 --- a/src/testdir/dumps/Test_diff_with_cursorline_01.dump +++ b/src/testdir/dumps/Test_diff_with_cursorline_01.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cursorline_02.dump b/src/testdir/dumps/Test_diff_with_cursorline_02.dump index 68014acb2c..9aef7d1fa3 100644 --- a/src/testdir/dumps/Test_diff_with_cursorline_02.dump +++ b/src/testdir/dumps/Test_diff_with_cursorline_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cursorline_03.dump b/src/testdir/dumps/Test_diff_with_cursorline_03.dump index 595de46ce1..5bc9e13167 100644 --- a/src/testdir/dumps/Test_diff_with_cursorline_03.dump +++ b/src/testdir/dumps/Test_diff_with_cursorline_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|4|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|4|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_display_fillchars_1.dump b/src/testdir/dumps/Test_display_fillchars_1.dump index 9568e09c5f..b0b1642bff 100644 --- a/src/testdir/dumps/Test_display_fillchars_1.dump +++ b/src/testdir/dumps/Test_display_fillchars_1.dump @@ -2,11 +2,11 @@ |w|i|n|d|o|w| |2| @28|++1&&|w+0&&|i|n|d|o|w| |1| @28 |w|i|n|d|o|w| |2| @28|++1&&|w+0&&|i|n|d|o|w| |1| @28 |y+0#4040ff13&| @35|++1#0000000&|x+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]|b@5|1|,|1|b@11|A|l@1| |[|N|o| |N|a|m|e|]| |[|+|]|a@5|1|,|1|a@11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]|b@5|1|,|1|b@11|A|l@1|b|[|N|o| |N|a|m|e|]| |[|+|]|a@5|1|,|1|a@11|A|l@1 >w+0&&|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |w|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |w|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |o+0#4040ff13&| @35|>+1#0000000&|z+0#4040ff13&| @35 |o| @35|>+1#0000000&|z+0#4040ff13&| @35 -|[+3#0000000&|N|o| |N|a|m|e|]| |[|+|]|4@5|1|,|1|4@11|A|l@1| |[+1&&|N|o| |N|a|m|e|]| |[|+|]|c@5|1|,|1|c@11|A|l@1 +|[+3#0000000&|N|o| |N|a|m|e|]| |[|+|]|4@5|1|,|1|4@11|A|l@1|4|[+1&&|N|o| |N|a|m|e|]| |[|+|]|c@5|1|,|1|c@11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_display_fillchars_2.dump b/src/testdir/dumps/Test_display_fillchars_2.dump index f62488861f..b983face03 100644 --- a/src/testdir/dumps/Test_display_fillchars_2.dump +++ b/src/testdir/dumps/Test_display_fillchars_2.dump @@ -2,11 +2,11 @@ |w|i|n|d|o|w| |2| @28|++1&&|w+0&&|i|n|d|o|w| |1| @28 |w|i|n|d|o|w| |2| @28|++1&&|w+0&&|i|n|d|o|w| |1| @28 |y+0#4040ff13&| @35|++1#0000000&|x+0#4040ff13&| @35 -|[+3#0000000&|N|o| |N|a|m|e|]| |[|+|]|2@5|1|,|1|2@11|A|l@1| |[+1&&|N|o| |N|a|m|e|]| |[|+|]|a@5|1|,|1|a@11|A|l@1 +|[+3#0000000&|N|o| |N|a|m|e|]| |[|+|]|2@5|1|,|1|2@11|A|l@1|2|[+1&&|N|o| |N|a|m|e|]| |[|+|]|a@5|1|,|1|a@11|A|l@1 |w+0&&|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |w|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |w|i|n|d|o|w| |4| @28|>+1&&|w+0&&|i|n|d|o|w| |3| @28 |o+0#4040ff13&| @35|>+1#0000000&|z+0#4040ff13&| @35 |o| @35|>+1#0000000&|z+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]|d@5|1|,|1|d@11|A|l@1| |[|N|o| |N|a|m|e|]| |[|+|]|c@5|1|,|1|c@11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]|d@5|1|,|1|d@11|A|l@1|d|[|N|o| |N|a|m|e|]| |[|+|]|c@5|1|,|1|c@11|A|l@1 |:+0&&|w|i|n|c|m|d| |k| @65 diff --git a/src/testdir/dumps/Test_hor_scroll_1.dump b/src/testdir/dumps/Test_hor_scroll_1.dump index 65b16f3ccc..615afb8e12 100644 --- a/src/testdir/dumps/Test_hor_scroll_1.dump +++ b/src/testdir/dumps/Test_hor_scroll_1.dump @@ -4,5 +4,5 @@ @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_hor_scroll_2.dump b/src/testdir/dumps/Test_hor_scroll_2.dump index 2b6afa0ecd..dfe2e98ae0 100644 --- a/src/testdir/dumps/Test_hor_scroll_2.dump +++ b/src/testdir/dumps/Test_hor_scroll_2.dump @@ -4,5 +4,5 @@ @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_hor_scroll_3.dump b/src/testdir/dumps/Test_hor_scroll_3.dump index 93f94a0ecc..db9fd6ff1b 100644 --- a/src/testdir/dumps/Test_hor_scroll_3.dump +++ b/src/testdir/dumps/Test_hor_scroll_3.dump @@ -4,5 +4,5 @@ @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 |:+0&&|w|i|n|d|o| |:|s|e|t| |c|u|r|s|o|r|l|i|n|e| @52 diff --git a/src/testdir/dumps/Test_hor_scroll_4.dump b/src/testdir/dumps/Test_hor_scroll_4.dump index 830423ad35..81821604d0 100644 --- a/src/testdir/dumps/Test_hor_scroll_4.dump +++ b/src/testdir/dumps/Test_hor_scroll_4.dump @@ -4,5 +4,5 @@ @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 |:+0&&|w|i|n|d|o| |:|s|e|t| |c|u|r|s|o|r|l|i|n|e| @52 diff --git a/src/testdir/dumps/Test_hor_scroll_5.dump b/src/testdir/dumps/Test_hor_scroll_5.dump index d706910fc0..f2986f3796 100644 --- a/src/testdir/dumps/Test_hor_scroll_5.dump +++ b/src/testdir/dumps/Test_hor_scroll_5.dump @@ -4,5 +4,5 @@ @20||+1&&| +0&&@53 @20||+1&&| +0&&@53 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|4|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|4|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|4|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|4|1| @10|A|l@1 |:+0&&|w|i|n|d|o| |:|s|e|t| |n|o|c|u|r|s|o|r|l|i|n|e| |n|o|c|u|r|s|o|r|c|o|l|u|m|n| @35 diff --git a/src/testdir/dumps/Test_laststatus_vsplit_row_height3_1.dump b/src/testdir/dumps/Test_laststatus_vsplit_row_height3_1.dump index 82ea8b8f8b..6d24637e32 100644 --- a/src/testdir/dumps/Test_laststatus_vsplit_row_height3_1.dump +++ b/src/testdir/dumps/Test_laststatus_vsplit_row_height3_1.dump @@ -3,6 +3,6 @@ |~| @73 |[+3#0000000&|N|o| |N|a|m|e|]| @47|0|,|0|-|1| @9|A|l@1 | +0&&@36||+1&&| +0&&@36 -|~+0#4040ff13&| @35| +1#0000000&|[|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 +|~+0#4040ff13&| @35||+1#0000000&|[|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 |[|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1| @37 | +0&&@74 diff --git a/src/testdir/dumps/Test_linematch_3diffs1.dump b/src/testdir/dumps/Test_linematch_3diffs1.dump index a010d27617..d8f23e84bd 100644 --- a/src/testdir/dumps/Test_linematch_3diffs1.dump +++ b/src/testdir/dumps/Test_linematch_3diffs1.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|0|-|1| @3|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|0|-|1| @3|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|0|-|1| @3|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|0|-|1| @3|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|0|-|1| @3|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|0|-|1| @3|A|l@1 |"+0&&|X|d|i|f|i|l|e|3|"| |5|L|,| |4|5|B| @56 diff --git a/src/testdir/dumps/Test_listchars_01.dump b/src/testdir/dumps/Test_listchars_01.dump index a7de310690..72fff585c2 100644 --- a/src/testdir/dumps/Test_listchars_01.dump +++ b/src/testdir/dumps/Test_listchars_01.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|]| |1|,| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|]| |1|,| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_02.dump b/src/testdir/dumps/Test_listchars_02.dump index d7e05df9ae..b7be52e890 100644 --- a/src/testdir/dumps/Test_listchars_02.dump +++ b/src/testdir/dumps/Test_listchars_02.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @2||+1#0000000&|~+0#4040ff13&| @32 |~| @18||+1#0000000&|~+0#4040ff13&| @2||+1#0000000&|~+0#4040ff13&| @32 |~| @18||+1#0000000&|~+0#4040ff13&| @2||+1#0000000&|~+0#4040ff13&| @32 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1|,| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1|,| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @10|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_03.dump b/src/testdir/dumps/Test_listchars_03.dump index d54b3e50fa..13aca6d2f7 100644 --- a/src/testdir/dumps/Test_listchars_03.dump +++ b/src/testdir/dumps/Test_listchars_03.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @1||+1#0000000&|~+0#4040ff13&| @33 |~| @18||+1#0000000&|~+0#4040ff13&| @1||+1#0000000&|~+0#4040ff13&| @33 |~| @18||+1#0000000&|~+0#4040ff13&| @1||+1#0000000&|~+0#4040ff13&| @33 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @10|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_04.dump b/src/testdir/dumps/Test_listchars_04.dump index 694e6eadca..e62c5aff20 100644 --- a/src/testdir/dumps/Test_listchars_04.dump +++ b/src/testdir/dumps/Test_listchars_04.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| ||+1#0000000&|~+0#4040ff13&| @34 |~| @18||+1#0000000&|~+0#4040ff13&| ||+1#0000000&|~+0#4040ff13&| @34 |~| @18||+1#0000000&|~+0#4040ff13&| ||+1#0000000&|~+0#4040ff13&| @34 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @11|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @11|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_05.dump b/src/testdir/dumps/Test_listchars_05.dump index 7ecb79738a..89b759fb42 100644 --- a/src/testdir/dumps/Test_listchars_05.dump +++ b/src/testdir/dumps/Test_listchars_05.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_06.dump b/src/testdir/dumps/Test_listchars_06.dump index 9176c5fded..9f4b5d30e2 100644 --- a/src/testdir/dumps/Test_listchars_06.dump +++ b/src/testdir/dumps/Test_listchars_06.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| +3&&|<|]| |1|,| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<+3&&|]| |1|,| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 |:+0&&|s|e|t| |n|o|w|r|a|p| |f|o|l|d|c|o|l|u|m|n|=|4| @35 diff --git a/src/testdir/dumps/Test_listchars_07.dump b/src/testdir/dumps/Test_listchars_07.dump index 572bf248ff..ad81656a50 100644 --- a/src/testdir/dumps/Test_listchars_07.dump +++ b/src/testdir/dumps/Test_listchars_07.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| +3&&|<| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<+3&&| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 |:+0&&|s|e|t| |n|o|w|r|a|p| |f|o|l|d|c|o|l|u|m|n|=|4| @35 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_01.dump b/src/testdir/dumps/Test_popup_prop_not_visible_01.dump index 20f4d46586..dbcfda1161 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_01.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_01.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 -|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump b/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump index e048949de9..0b97cb832d 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 -|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 |:+0&&|c|a|l@1| |p|o|p|u|p|_|h|i|d|e|(|g|:|s|o|m|e|_|i|d|)| @47 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump b/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump index c91b201003..e8a5c2e353 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 -|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 |:+0&&|c|a|l@1| |p|o|p|u|p|_|s|h|o|w|(|g|:|s|o|m|e|_|i|d|)| @47 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_02.dump b/src/testdir/dumps/Test_popup_prop_not_visible_02.dump index e4b2aec90f..1d1a5531c9 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_02.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_02.dump @@ -6,5 +6,5 @@ |~+0#4040ff13#ffffff0| @55||+1#0000000&|~+0#4040ff13&| @15 |~| @55||+1#0000000&|~+0#4040ff13&| @15 |~| @55||+1#0000000&|~+0#4040ff13&| @15 -|[+1#0000000&|N|o| |N|a|m|e|]| @29|0|,|0|-|1| @9|A|l@1| +3&&|<|m|e|]| |[|+|]| |1|,|0|-|1| @2 +|[+1#0000000&|N|o| |N|a|m|e|]| @29|0|,|0|-|1| @9|A|l@1| |<+3&&|m|e|]| |[|+|]| |1|,|0|-|1| @2 |:+0&&|v|e|r|t| |r|e|s|i|z|e| |-|1|4| @58 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_03.dump b/src/testdir/dumps/Test_popup_prop_not_visible_03.dump index a056cd867a..caaa881fe3 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_03.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_03.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @63||+1#0000000&|~+0#4040ff13&| @7 |~| @63||+1#0000000&|~+0#4040ff13&| @7 |~| @63||+1#0000000&|~+0#4040ff13&| @7 -|[+1#0000000&|N|o| |N|a|m|e|]| @37|0|,|0|-|1| @9|A|l@1| +3&&|<|[|+|]| |1|,|0|- +|[+1#0000000&|N|o| |N|a|m|e|]| @37|0|,|0|-|1| @9|A|l@1| |<+3&&|[|+|]| |1|,|0|- |:+0&&|v|e|r|t| |r|e|s|i|z|e| |-|8| @59 diff --git a/src/testdir/dumps/Test_prop_diff_mode_2.dump b/src/testdir/dumps/Test_prop_diff_mode_2.dump index 453665d998..243ec0dc18 100644 --- a/src/testdir/dumps/Test_prop_diff_mode_2.dump +++ b/src/testdir/dumps/Test_prop_diff_mode_2.dump @@ -6,5 +6,5 @@ |~+0#4040ff13#ffffff0| @28||+1#0000000&| +0#af5f00255&@1|5| |0+0#0000000#ffd7ff255@2|9+2&#ff404010| +0&#ffd7ff255@20 |~+0#4040ff13#ffffff0| @28||+1#0000000&| +0#af5f00255&@3|<+0#0000000#ffd7ff255|t|e|x|t|>| @18 |~+0#4040ff13#ffffff0| @28||+1#0000000&|~+0#4040ff13&| @27 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1|-|7| @5|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1|-|7| @5|A|l@1 |:+0&&|w|i|n|d|o| |s|e|t| |n|u|m|b|e|r| @42 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_1.dump b/src/testdir/dumps/Test_smooth_diff_change_line_1.dump index 28f2e1d995..c634f1dd31 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_1.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_1.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_2.dump b/src/testdir/dumps/Test_smooth_diff_change_line_2.dump index 4c2d28d0e6..691fe289fd 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_2.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_2.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|7| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|7| @11|A|l@1 |-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@62 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_3.dump b/src/testdir/dumps/Test_smooth_diff_change_line_3.dump index e47fb5d34b..c4178404d8 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_3.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_3.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump b/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump index 0c7e70c273..d13141eb28 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_4.dump b/src/testdir/dumps/Test_smooth_diff_change_line_4.dump index 37ce70a758..b789df3fb7 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_4.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_4.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_switchwin_clear_pum_02.dump b/src/testdir/dumps/Test_switchwin_clear_pum_02.dump index cf5f9dbc9c..39013c09fd 100644 --- a/src/testdir/dumps/Test_switchwin_clear_pum_02.dump +++ b/src/testdir/dumps/Test_switchwin_clear_pum_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|w+1#0000000&|i|n|_|b| |[|+|]| @9|1|,|1|0| @10|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|9| @11|A|l@1 +|w+1#0000000&|i|n|_|b| |[|+|]| @9|1|,|1|0| @10|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|9| @11|A|l@1 |-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@62 diff --git a/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump b/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump index 0ee2efe9db..b041b672f6 100644 --- a/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump +++ b/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump @@ -6,5 +6,5 @@ | +1&&@19| +0#af5f00255&@1|6| ||+1#0000000&| +0#af5f00255&|3|0| |t+0#0000000&|e|x|t| |t|e|x|t| |t|e|x|t| |t | +1&&@19| +0#af5f00255&@1|7| ||+1#0000000&| +0#af5f00255&|3|1| |t+0#0000000&|e|x|t| |t|e|x|t| |t|e|x|t| |t | +1&&@19| +0#af5f00255&@1|8| ||+1#0000000&| +0#af5f00255&|3|2| |t+0#0000000&|e|x|t| |t|e|x|t| |t|e|x|t| |t -| +1&&@19|<| |1|,| +3&&|<|.|t|x|t| |[|+|]| |2|8|,|1| @2|2|6|% +| +1&&@19|<| |1|,| |<+3&&|.|t|x|t| |[|+|]| |2|8|,|1| @2|2|6|% | +1&&@19| +0&&@24 diff --git a/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol.dump b/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol.dump index d2e69dc822..3633ffe540 100644 --- a/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol.dump +++ b/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol.dump @@ -4,12 +4,12 @@ | +0#ff404010#e0e0004@36||+1#0000000#ffffff0|3+0&&| @35 | +0#ff404010#e0e0004@36||+1#0000000#ffffff0|4+0&&| @35 | +0#ff404010#e0e0004@36||+1#0000000#ffffff0|5+0&&| @35 -|!+2#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @23|6+0#0000000#ffffff0| @35 +|!+2#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22||+1#0000000#ffffff0|6+0&&| @35 |h+0#00e0003#5fd7ff255|e|l@1|o| @31||+1#0000000#ffffff0|7+0&&| @35 |h+0#00e0003#5fd7ff255|e|l@1|o| @31||+1#0000000#ffffff0|8+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|9+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|1+0&&|0| @34 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|1+0&&@1| @34 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|1+0&&|2| @34 -|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22| +1#0000000#ffffff0|[|N|o| |N|a|m|e|]| |[|+|]| @23 +|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @23|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @23 |:+0&&|s|e|t| |w|i|n|c|o|l|o|r|=|M|y|W|i|n|C|o|l| @52 diff --git a/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol2.dump b/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol2.dump index 8e5796f622..ce69e3b4df 100644 --- a/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol2.dump +++ b/src/testdir/dumps/Test_terminal_wincolor_split_MyWinCol2.dump @@ -4,12 +4,12 @@ | +0#ff404010#e0e0004@36||+1#0000000#ffffff0| +0#0000001#4040ff13@36 | +0#ff404010#e0e0004@36||+1#0000000#ffffff0| +0#0000001#4040ff13@36 | +0#ff404010#e0e0004@36||+1#0000000#ffffff0| +0#0000001#4040ff13@36 -|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22| +2&&|!|c|a|t| |[|r|u|n@1|i|n|g|]| @22 +|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22||+1#0000000#ffffff0|!+2#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22 |h+0#00e0003#5fd7ff255|e|l@1|o| @31||+1#0000000#ffffff0|0+0&&| @35 |h+0#00e0003#5fd7ff255|e|l@1|o| @31||+1#0000000#ffffff0|1+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|2+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|3+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|4+0&&| @35 | +0#00e0003#5fd7ff255@36||+1#0000000#ffffff0|5+0&&| @35 -|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @22| +1#0000000#ffffff0|[|N|o| |N|a|m|e|]| |[|+|]| @23 +|!+0#ffffff16#00e0003|c|a|t| |[|r|u|n@1|i|n|g|]| @23|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @23 |:+0&&|s|e|t| |w|i|n|c|o|l|o|r|=|M|y|W|i|n|C|o|l|2| @51 diff --git a/src/testdir/dumps/Test_winhighlight_14.dump b/src/testdir/dumps/Test_winhighlight_14.dump index 176bf6ea99..1ea869d656 100644 --- a/src/testdir/dumps/Test_winhighlight_14.dump +++ b/src/testdir/dumps/Test_winhighlight_14.dump @@ -4,5 +4,5 @@ |S+8&&|i|x| @33||+1&&|S+0&&|i|x| @33 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 |:+0&&|w|i|n|c|m|d| |l| @65 diff --git a/src/testdir/dumps/Test_winhighlight_15.dump b/src/testdir/dumps/Test_winhighlight_15.dump index 7d69f21b56..a90302a86f 100644 --- a/src/testdir/dumps/Test_winhighlight_15.dump +++ b/src/testdir/dumps/Test_winhighlight_15.dump @@ -4,5 +4,5 @@ |S+8#0000000#ffffff0|i|x| @33||+1&&|S+0#ffffff16#e000002|i|x| @33 |~+0#4040ff13#ffffff0| @35||+1#0000000&|~+0#4040ff13#e000002| @35 |~+0&#ffffff0| @35||+1#0000000&|~+0#4040ff13#e000002| @35 -|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 +|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 |:+0&&|s|e|t|l|o|c|a|l| |w|h|l|=|N|o|r|m|a|l|:|E|r@1|o|r|M|s|g| @45 diff --git a/src/testdir/dumps/Test_winhighlight_2.dump b/src/testdir/dumps/Test_winhighlight_2.dump index 72f754114e..b84ef70531 100644 --- a/src/testdir/dumps/Test_winhighlight_2.dump +++ b/src/testdir/dumps/Test_winhighlight_2.dump @@ -4,5 +4,5 @@ |S|i|x| @33||+1&&|S+0&&|i|x| @33 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 |:+0&&|w|i|n|c|m|d| |l| @65 diff --git a/src/testdir/dumps/Test_winhighlight_6.dump b/src/testdir/dumps/Test_winhighlight_6.dump index e482465ca4..f5753e1187 100644 --- a/src/testdir/dumps/Test_winhighlight_6.dump +++ b/src/testdir/dumps/Test_winhighlight_6.dump @@ -4,5 +4,5 @@ |S|i|x| @33||+1&&|S+0&&|i|x| @33 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 |:+0&&|s|e|t|l|o|c|a|l| |w|h|l|=|V|e|r|t|S|p|l|i|t|:|E|r@1|o|r|M|s|g| @42 diff --git a/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump b/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump index 37a9087bba..30d643854e 100644 --- a/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump +++ b/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 |/+0&&|F> @72 diff --git a/src/testdir/dumps/Test_winscrolled_once_only_1.dump b/src/testdir/dumps/Test_winscrolled_once_only_1.dump index 7407f46863..56d640107e 100644 --- a/src/testdir/dumps/Test_winscrolled_once_only_1.dump +++ b/src/testdir/dumps/Test_winscrolled_once_only_1.dump @@ -5,6 +5,6 @@ |a+0#0000000&@2| @26||+1&&|~+0#4040ff13&| @27 |b+0#0000000&@2| @26||+1&&|~+0#4040ff13&| @27 |~| @28||+1#0000000&|~+0#4040ff13&| @27 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @1|2|,|1| @7|B|o|t +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @1|2|,|1| @7|B|o|t |1+0&&| |1|0@2| |[|'|r|o|w|'|,| |[@1|'|c|o|l|'|,| |[@1|'|l|e|a|f|'|,| |1|0@1|2|]|,| |[|'|l|e|a|f|'|,| |1|0@1|1|]@2|,| |[ |'|l|e|a|f|'|,| |1|0@2|]@2| @44 diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim index 1a017083bf..7ce2e9a2e8 100644 --- a/src/testdir/test_highlight.vim +++ b/src/testdir/test_highlight.vim @@ -1714,81 +1714,6 @@ func Test_VertSplitNC() call StopVimInTerminal(buf) endfunc -func Test_VertSplitNC_multiline_stl() - CheckScreendump - - let lines =<< trim END - hi StatusLine ctermfg=White ctermbg=DarkBlue cterm=NONE - hi StatusLineNC ctermfg=Black ctermbg=Gray cterm=NONE - hi VertSplit ctermfg=Green ctermbg=NONE cterm=NONE - hi VertSplitNC ctermfg=DarkGray ctermbg=NONE cterm=NONE - set statuslineopt=maxheight:4,fixedheight - set statusline=%f%=%l,%c\ %P - call setline(1, repeat(['multi stl test'], 20)) - vsplit - wincmd l - sp - sp - wincmd k - wincmd = - END - call writefile(lines, 'Xtest_vertsplitNC_stl', 'D') - - let buf = RunVimInTerminal('-S Xtest_vertsplitNC_stl', {'rows': 20}) - call TermWait(buf) - - " Right-top window is current: the separator cell on its status line - " row should be a space with StatusLine highlight. Other status - " line rows should use VertSplitNC. - call VerifyScreenDump(buf, 'Test_VertSplitNC_stl_1', {}) - - " Move to right-middle window - call term_sendkeys(buf, "\j") - call TermWait(buf) - call VerifyScreenDump(buf, 'Test_VertSplitNC_stl_2', {}) - - " Move to right-bottom window - call term_sendkeys(buf, "\j") - call TermWait(buf) - call VerifyScreenDump(buf, 'Test_VertSplitNC_stl_3', {}) - - call StopVimInTerminal(buf) -endfunc - -func Test_VertSplitNC_fillchars() - CheckScreendump - - let lines =<< trim END - hi StatusLine ctermfg=White ctermbg=DarkBlue cterm=NONE - hi StatusLineNC ctermfg=Black ctermbg=Gray cterm=NONE - hi VertSplit ctermfg=Green ctermbg=NONE cterm=NONE - hi VertSplitNC ctermfg=DarkGray ctermbg=NONE cterm=NONE - set fillchars=vert:\|,stl:=,stlnc:- - call setline(1, repeat(['fillchars test'], 20)) - vsplit - vsplit - END - call writefile(lines, 'Xtest_vertsplitNC_fc', 'D') - - let buf = RunVimInTerminal('-S Xtest_vertsplitNC_fc', {'rows': 12}) - call TermWait(buf) - - " Left window is current. Non-status-line rows show '|' with - " VertSplit (left sep) and VertSplitNC (right sep). On the status - " line row, the separator cell is a space (not '=' from stl, and - " not '|' from vert) with StatusLine highlight. - call VerifyScreenDump(buf, 'Test_VertSplitNC_fc_1', {}) - - " Move to middle window: both separators on non-status rows use - " VertSplit. On the status line row both separator cells are - " spaces with StatusLine highlight. - call term_sendkeys(buf, "\l") - call TermWait(buf) - call VerifyScreenDump(buf, 'Test_VertSplitNC_fc_2', {}) - - call StopVimInTerminal(buf) -endfunc - " Test that 'winhighlight' of window left of separator does not apply when " drawing the window to the right of the separator. func Test_VertSplitNC_winhighlight() diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 6128e08b66..0b1359cd53 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -447,7 +447,7 @@ func Test_statusline() set fillchars=stl:^,stlnc:=,vert:\|,fold:-,diff:- vsplit set statusline=x%=y - call assert_match('^x^\+y x=\+y$', s:get_statusline()) + call assert_match('^x^\+y^x=\+y$', s:get_statusline()) set fillchars& close @@ -548,9 +548,9 @@ func Test_statusline_mbyte_fillchar() set statusline=a%=b call assert_match('^a\+━\+b$', s:get_statusline()) vnew - call assert_match('^a\+━\+b a\+═\+b$', s:get_statusline()) + call assert_match('^a\+━\+b━a\+═\+b$', s:get_statusline()) wincmd w - call assert_match('^a\+═\+b a\+━\+b$', s:get_statusline()) + call assert_match('^a\+═\+b═a\+━\+b$', s:get_statusline()) set statusline& fillchars& %bw! endfunc diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index b1305c77e1..b5ec9e5e75 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -284,7 +284,7 @@ func Test_window_split_no_room() call setwinvar(winnr('k'), '&statusline', '@#') let last_stl_row = win_screenpos(0)[0] - 1 redraw - call assert_equal('@# ', GetScreenStr(last_stl_row)) + call assert_equal('@#|', GetScreenStr(last_stl_row)) call assert_equal('~ |', GetScreenStr(&lines - &cmdheight)) call assert_fails('wincmd H', 'E36:') @@ -292,7 +292,7 @@ func Test_window_split_no_room() call assert_equal(info, s:win_layout_info()) call setwinvar(winnr('k'), '&statusline', '=-') redraw - call assert_equal('=- ', GetScreenStr(last_stl_row)) + call assert_equal('=-|', GetScreenStr(last_stl_row)) call assert_equal('~ |', GetScreenStr(&lines - &cmdheight)) %bw! diff --git a/src/version.c b/src/version.c index 9d5136df60..713701f967 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 453, /**/ 452, /**/ diff --git a/src/vim.h b/src/vim.h index 8949e867de..ee67e4078a 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1534,7 +1534,7 @@ typedef enum , HLF_S // status lines , HLF_SNC // status lines of not-current windows , HLF_C // column to separate vertically split windows - , HLF_CNC // column separator for not-current windows + , HLF_CNC // column to separate vertically split non-current windows , HLF_T // Titles for output from ":set all", ":autocmd" etc. , HLF_V // Visual mode , HLF_VNC // Visual mode, autoselecting and not clipboard owner diff --git a/src/window.c b/src/window.c index eb4e494d19..46302b848d 100644 --- a/src/window.c +++ b/src/window.c @@ -5829,16 +5829,13 @@ win_enter_ext(win_T *wp, int flags) #endif redraw_tabline = TRUE; redraw_vseps = TRUE; - // Need to redraw all status lines so that the vsep character at - // status line rows is updated for the new current window. - { - win_T *ww; - FOR_ALL_WINDOWS(ww) - ww->w_redr_status = true; - } #if defined(FEAT_TABPANEL) redraw_tabpanel = TRUE; #endif + // Need to schedule a redraw so that the vertical separator highlight is + // updated for the new current window. The status line redraw of curwin + // is already requested via "curwin->w_redr_status". + redraw_later(UPD_VALID); if (restart_edit) redraw_later(UPD_VALID); // causes status line redraw @@ -7892,8 +7889,6 @@ frame_change_statusline_height(void) tabpage_T *tp; int global_stlh; - redraw_vseps = TRUE; - // First pass: find space-constrained global height. global_stlh = stlo_mh; FOR_ALL_TABPAGES(tp) From b207b5a2a38dd8f45417e69c2e659c4c05ad3c1e Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 8 May 2026 21:06:08 +0000 Subject: [PATCH 190/545] patch 9.2.0454: tests: no test that "abbr" in customlist completion is shown Problem: No test that "abbr" in customlist completion is shown in pum. Solution: Add some "abbr" fields to the existing test (zeertzjq). closes: #20165 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/testdir/dumps/Test_customlist_info_popup_01.dump | 4 ++-- src/testdir/dumps/Test_customlist_info_popup_02.dump | 4 ++-- src/testdir/dumps/Test_customlist_info_popup_03.dump | 4 ++-- src/testdir/dumps/Test_customlist_info_popup_04.dump | 4 ++-- src/testdir/dumps/Test_customlist_info_popup_05.dump | 4 ++-- src/testdir/dumps/Test_customlist_info_popup_06.dump | 4 ++-- src/testdir/dumps/Test_customlist_info_popup_07.dump | 4 ++-- src/testdir/dumps/Test_customlist_info_popup_08.dump | 4 ++-- src/testdir/dumps/Test_customlist_info_popup_09.dump | 4 ++-- src/testdir/dumps/Test_customlist_info_popup_10.dump | 4 ++-- src/testdir/test_cmdline.vim | 4 ++-- src/version.c | 2 ++ 12 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/testdir/dumps/Test_customlist_info_popup_01.dump b/src/testdir/dumps/Test_customlist_info_popup_01.dump index 0bb1ddb7b1..a17ff601eb 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_01.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_01.dump @@ -5,8 +5,8 @@ |~| @73 |~| @73 |~| @26|╔+0#0000001#e0e0e08|═@12|X| +0#4040ff13#ffffff0@31 -|~| @6| +0#0000001#e0e0e08|a|p@1|l|e| @1|f| |f|r|u|i|t| @4|║| |A| |r|e|d| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@31 -|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|╚+0&#e0e0e08|═@12|⇲| +0#4040ff13#ffffff0@31 +|~| @6| +0#0000001#e0e0e08|🍎*&| +&@4|f| |f|r|u|i|t| @4|║| |A| |r|e|d| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@31 +|~| @6| +0#0000001#ffd7ff255|🍌*&| +&@4|f| |f|r|u|i|t| @4|╚+0&#e0e0e08|═@12|⇲| +0#4040ff13#ffffff0@31 |~| @6| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@46 |~| @6| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@46 |:+0#0000000&|D|i|c|t|C|m|d| |a|p@1|l|e> @60 diff --git a/src/testdir/dumps/Test_customlist_info_popup_02.dump b/src/testdir/dumps/Test_customlist_info_popup_02.dump index 3959110152..c8c9c54ae3 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_02.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_02.dump @@ -5,8 +5,8 @@ |~| @73 |~| @73 |~| @73 -|~| @6| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@15|X| +0#4040ff13#ffffff0@28 -|~| @6| +0#0000001#e0e0e08|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|║| |A| |y|e|l@1|o|w| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@28 +|~| @6| +0#0000001#ffd7ff255|🍎*&| +&@4|f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@15|X| +0#4040ff13#ffffff0@28 +|~| @6| +0#0000001#e0e0e08|🍌*&| +&@4|f| |f|r|u|i|t| @4|║| |A| |y|e|l@1|o|w| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@28 |~| @6| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| |╚+0&#e0e0e08|═@15|⇲| +0#4040ff13#ffffff0@28 |~| @6| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@46 |:+0#0000000&|D|i|c|t|C|m|d| |b|a|n|a|n|a> @59 diff --git a/src/testdir/dumps/Test_customlist_info_popup_03.dump b/src/testdir/dumps/Test_customlist_info_popup_03.dump index f67d860c5d..4aa47adfcb 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_03.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_03.dump @@ -5,8 +5,8 @@ |~| @73 |~| @73 |~| @73 -|~| @6| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 -|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@20|X| +0#4040ff13#ffffff0@23 +|~| @6| +0#0000001#ffd7ff255|🍎*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|🍌*&| +&@4|f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@20|X| +0#4040ff13#ffffff0@23 |~| @6| +0#0000001#e0e0e08|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| |║| |A|n| |o|r|a|n|g|e| |v|e|g|e|t|a|b|l|e| |║| +0#4040ff13#ffffff0@23 |~| @6| +0#0000001#ffd7ff255|p|l|a|i|n| @13|╚+0&#e0e0e08|═@20|⇲| +0#4040ff13#ffffff0@23 |:+0#0000000&|D|i|c|t|C|m|d| |c|a|r@1|o|t> @59 diff --git a/src/testdir/dumps/Test_customlist_info_popup_04.dump b/src/testdir/dumps/Test_customlist_info_popup_04.dump index 0092776a94..18cb1ba1f9 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_04.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_04.dump @@ -5,8 +5,8 @@ |~| @73 |~| @73 |~| @73 -|~| @6| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 -|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|🍎*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|🍌*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 |~| @6| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@46 |~| @6| +0#0000001#e0e0e08|p|l|a|i|n| @13| +0#4040ff13#ffffff0@46 |:+0#0000000&|D|i|c|t|C|m|d| |p|l|a|i|n> @60 diff --git a/src/testdir/dumps/Test_customlist_info_popup_05.dump b/src/testdir/dumps/Test_customlist_info_popup_05.dump index 37242aec78..a4b6d1517c 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_05.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_05.dump @@ -5,8 +5,8 @@ |~| @73 |~| @73 |~| @73 -|~| @6| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 -|~| @6| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|🍎*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 +|~| @6| +0#0000001#ffd7ff255|🍌*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@46 |~| @6| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@46 |~| @6| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@46 |:+0#0000000&|D|i|c|t|C|m|d| > @65 diff --git a/src/testdir/dumps/Test_customlist_info_popup_06.dump b/src/testdir/dumps/Test_customlist_info_popup_06.dump index 72324ff0a8..7b8eba89db 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_06.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_06.dump @@ -1,6 +1,6 @@ |D+0&#ffffff0|i|c|t|C|m|d| |a|p@1|l|e> @13|╔+0#0000001#e0e0e08|═@12|X| +0#0000000#ffffff0@32 -|~+0#4040ff13&| @5| +0#0000001#e0e0e08|a|p@1|l|e| @1|f| |f|r|u|i|t| @4|║| |A| |r|e|d| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@32 -|~| @5| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|╚+0&#e0e0e08|═@12|⇲| +0#4040ff13#ffffff0@32 +|~+0#4040ff13&| @5| +0#0000001#e0e0e08|🍎*&| +&@4|f| |f|r|u|i|t| @4|║| |A| |r|e|d| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@32 +|~| @5| +0#0000001#ffd7ff255|🍌*&| +&@4|f| |f|r|u|i|t| @4|╚+0&#e0e0e08|═@12|⇲| +0#4040ff13#ffffff0@32 |~| @5| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@47 |~| @5| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@47 |~| @73 diff --git a/src/testdir/dumps/Test_customlist_info_popup_07.dump b/src/testdir/dumps/Test_customlist_info_popup_07.dump index f11f3ea9f1..a0202af08b 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_07.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_07.dump @@ -1,6 +1,6 @@ |D+0&#ffffff0|i|c|t|C|m|d| |b|a|n|a|n|a> @60 -|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@15|X| +0#4040ff13#ffffff0@29 -|~| @5| +0#0000001#e0e0e08|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|║| |A| |y|e|l@1|o|w| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@29 +|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|🍎*&| +&@4|f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@15|X| +0#4040ff13#ffffff0@29 +|~| @5| +0#0000001#e0e0e08|🍌*&| +&@4|f| |f|r|u|i|t| @4|║| |A| |y|e|l@1|o|w| |f|r|u|i|t| |║| +0#4040ff13#ffffff0@29 |~| @5| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| |╚+0&#e0e0e08|═@15|⇲| +0#4040ff13#ffffff0@29 |~| @5| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@47 |~| @73 diff --git a/src/testdir/dumps/Test_customlist_info_popup_08.dump b/src/testdir/dumps/Test_customlist_info_popup_08.dump index adbe3b71af..90f9de7f70 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_08.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_08.dump @@ -1,6 +1,6 @@ |D+0&#ffffff0|i|c|t|C|m|d| |c|a|r@1|o|t> @60 -|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 -|~| @5| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@20|X| +0#4040ff13#ffffff0@24 +|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|🍎*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|🍌*&| +&@4|f| |f|r|u|i|t| @4|╔+0&#e0e0e08|═@20|X| +0#4040ff13#ffffff0@24 |~| @5| +0#0000001#e0e0e08|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| |║| |A|n| |o|r|a|n|g|e| |v|e|g|e|t|a|b|l|e| |║| +0#4040ff13#ffffff0@24 |~| @5| +0#0000001#ffd7ff255|p|l|a|i|n| @13|╚+0&#e0e0e08|═@20|⇲| +0#4040ff13#ffffff0@24 |~| @73 diff --git a/src/testdir/dumps/Test_customlist_info_popup_09.dump b/src/testdir/dumps/Test_customlist_info_popup_09.dump index 236c7dcd1e..5b048f8499 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_09.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_09.dump @@ -1,6 +1,6 @@ |D+0&#ffffff0|i|c|t|C|m|d| |p|l|a|i|n> @61 -|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 -|~| @5| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|🍎*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|🍌*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 |~| @5| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@47 |~| @5| +0#0000001#e0e0e08|p|l|a|i|n| @13| +0#4040ff13#ffffff0@47 |~| @73 diff --git a/src/testdir/dumps/Test_customlist_info_popup_10.dump b/src/testdir/dumps/Test_customlist_info_popup_10.dump index 5c07e54db1..67775f75a7 100644 --- a/src/testdir/dumps/Test_customlist_info_popup_10.dump +++ b/src/testdir/dumps/Test_customlist_info_popup_10.dump @@ -1,6 +1,6 @@ |D+0&#ffffff0|i|c|t|C|m|d| > @66 -|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|a|p@1|l|e| @1|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 -|~| @5| +0#0000001#ffd7ff255|b|a|n|a|n|a| |f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~+0#4040ff13&| @5| +0#0000001#ffd7ff255|🍎*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 +|~| @5| +0#0000001#ffd7ff255|🍌*&| +&@4|f| |f|r|u|i|t| @4| +0#4040ff13#ffffff0@47 |~| @5| +0#0000001#ffd7ff255|c|a|r@1|o|t| |v| |v|e|g|e|t|a|b|l|e| | +0#4040ff13#ffffff0@47 |~| @5| +0#0000001#ffd7ff255|p|l|a|i|n| @13| +0#4040ff13#ffffff0@47 |~| @73 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 6809ef5f78..c10cff90bd 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -4647,8 +4647,8 @@ func Test_customlist_dict_completion_info_popup() let lines =<< trim END func DictComp(A, L, P) return [ - \ {'word': 'apple', 'kind': 'f', 'menu': 'fruit', 'info': 'A red fruit'}, - \ {'word': 'banana', 'kind': 'f', 'menu': 'fruit', 'info': 'A yellow fruit'}, + \ {'word': 'apple', 'kind': 'f', 'menu': 'fruit', 'info': 'A red fruit', 'abbr': '🍎'}, + \ {'word': 'banana', 'kind': 'f', 'menu': 'fruit', 'info': 'A yellow fruit', 'abbr': '🍌'}, \ {'word': 'carrot', 'kind': 'v', 'menu': 'vegetable', 'info': 'An orange vegetable'}, \ 'plain', \ ] diff --git a/src/version.c b/src/version.c index 713701f967..0103f023e2 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 454, /**/ 453, /**/ From 9694ff58fe510bf3906a7b6817429e29d5975987 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 8 May 2026 21:09:48 +0000 Subject: [PATCH 191/545] patch 9.2.0455: 'findfunc' only allows extra info for cmdline completion Problem: 'findfunc' only allows extra info for cmdline completion, not for actually finding files (Maxim Kim, after 9.2.0451). Solution: Handle returning a list of dicts when actually finding files. Also fix crash on NULL string (zeertzjq). fixes: #20163 closes: #20164 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 7 +++--- src/cmdexpand.c | 10 ++++----- src/ex_docmd.c | 12 ++++++++-- src/testdir/test_findfile.vim | 42 ++++++++++++++++++++++++++++++----- src/version.c | 2 ++ 5 files changed, 57 insertions(+), 16 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e82cd30555..969a17c164 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 May 07 +*options.txt* For Vim version 9.2. Last change: 2026 May 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -3983,9 +3983,8 @@ A jump table for the options with a short description can be found at |Q_op|. |String| and is the |:find| command argument. The second argument is a |Boolean| and is set to |v:true| when the function is called to get a List of command-line completion matches for the |:find| command. - The function should return a List of strings, or, in the command-line - completion case, whatever a |:command-completion-customlist| function - may return. + The function should return a List, which is handled similarly to the + return value of a |:command-completion-customlist| function. The function is called only once per |:find| command invocation. The function can process all the directories specified in 'path'. diff --git a/src/cmdexpand.c b/src/cmdexpand.c index a763456a1f..3c660b5ea2 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -4172,22 +4172,22 @@ expand_process_user_list( // Loop over the items in the list. FOR_ALL_LIST_ITEMS(retlist, li) { + typval_T *tv = &li->li_tv; char_u *p = NULL; char_u *abbr = NULL; char_u *kind = NULL; char_u *menu = NULL; char_u *info = NULL; - if (li->li_tv.v_type == VAR_STRING) + if (tv->v_type == VAR_STRING) { - if (li->li_tv.vval.v_string == NULL) + if (tv->vval.v_string == NULL) continue; // Skip NULL strings p = vim_strsave(li->li_tv.vval.v_string); } - else if (li->li_tv.v_type == VAR_DICT - && li->li_tv.vval.v_dict != NULL) + else if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { - dict_T *d = li->li_tv.vval.v_dict; + dict_T *d = tv->vval.v_dict; char_u *word = dict_get_string(d, "word", FALSE); if (word == NULL) diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 7b31bed99a..11ea59e3b0 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -7119,8 +7119,16 @@ findfunc_find_file(char_u *findarg, int findarg_len, int count) else { listitem_T *li = list_find(fname_list, count - 1); - if (li != NULL && li->li_tv.v_type == VAR_STRING) - ret_fname = vim_strsave(li->li_tv.vval.v_string); + + if (li != NULL) + { + typval_T *tv = &li->li_tv; + + if (tv->v_type == VAR_STRING && tv->vval.v_string != NULL) + ret_fname = vim_strsave(tv->vval.v_string); + else if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) + ret_fname = dict_get_string(tv->vval.v_dict, "word", TRUE); + } } } diff --git a/src/testdir/test_findfile.vim b/src/testdir/test_findfile.vim index 42c3fb6431..a5f1f6fdd4 100644 --- a/src/testdir/test_findfile.vim +++ b/src/testdir/test_findfile.vim @@ -329,22 +329,22 @@ func Test_findfunc() set findfunc=FindFuncBasic find Xfindfunc3 - call assert_match('Xfindfunc3.c', @%) + call assert_match('Xfindfunc3\.c', @%) bw! 2find Xfind - call assert_match('Xfindfunc2.c', @%) + call assert_match('Xfindfunc2\.c', @%) bw! call assert_fails('4find Xfind', 'E347: No more file "Xfind" found in path') call assert_fails('find foobar', 'E345: Can''t find file "foobar" in path') sfind Xfindfunc2.c - call assert_match('Xfindfunc2.c', @%) + call assert_match('Xfindfunc2\.c', @%) call assert_equal(2, winnr('$')) %bw! call assert_fails('sfind foobar', 'E345: Can''t find file "foobar" in path') tabfind Xfindfunc3.c - call assert_match('Xfindfunc3.c', @%) + call assert_match('Xfindfunc3\.c', @%) call assert_equal(2, tabpagenr()) %bw! call assert_fails('tabfind foobar', 'E345: Can''t find file "foobar" in path') @@ -352,12 +352,44 @@ func Test_findfunc() " Test garbage collection call test_garbagecollect_now() find Xfindfunc2 - call assert_match('Xfindfunc2.c', @%) + call assert_match('Xfindfunc2\.c', @%) bw! delfunc FindFuncBasic call test_garbagecollect_now() call assert_fails('find Xfindfunc2', 'E117: Unknown function: FindFuncBasic') + " 'findfunc' with dicts in the returned list + func FindFuncDict(pat, cmdcomplete) + return [ + \ #{word: 'Xfindfunc1.c', abbr: 'Xff1.c'}, + \ #{word: 'Xfindfunc2.c'}, + \ 'Xfindfunc3.c', + "\ invalid values + \ #{abbr: 'XXX'}, + \ test_null_dict(), + \ test_null_string(), + \ ] + endfunc + + set findfunc=FindFuncDict + find Xfind + call assert_match('Xfindfunc1\.c', @%) + bw! + 2find Xfind + call assert_match('Xfindfunc2\.c', @%) + bw! + 3find Xfind + call assert_match('Xfindfunc3\.c', @%) + bw! + " These invalid values should not crash + 4find Xfind + 5find Xfind + 6find Xfind + call assert_fails('7find Xfind', 'E347: No more file "Xfind" found in path') + call assert_equal('', @%) + %bw! + delfunc FindFuncDict + " Buffer-local option func GlobalFindFunc(pat, cmdcomplete) return ['global'] diff --git a/src/version.c b/src/version.c index 0103f023e2..36f96c0550 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 455, /**/ 454, /**/ From 7644d9d6114d0b17bfaebb069dd68ae0d7d907fb Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Fri, 8 May 2026 21:14:52 +0000 Subject: [PATCH 192/545] patch 9.2.0456: stray p character displayed on some terms Problem: stray p character displayed on some terms Solution: Make sending DECRQM more strict and disable it for a few more terminals (Foxe Chen) fixes: #20156 fixes: #20140 closes: #20161 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/term.c | 4 ---- src/version.c | 2 ++ 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/term.c b/src/term.c index 37f05ed016..08a070cef6 100644 --- a/src/term.c +++ b/src/term.c @@ -5182,7 +5182,6 @@ handle_version_response(int first, int *arg, int argc, char_u *tp) may_adjust_color_count(256); // Libvterm can handle SGR mouse reporting. term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR; - term_props[TPR_DECRQM].tpr_status = TPR_YES; } if (version == 95) @@ -5228,7 +5227,6 @@ handle_version_response(int first, int *arg, int argc, char_u *tp) term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_SGR; else if (version >= 95) term_props[TPR_MOUSE].tpr_status = TPR_MOUSE_XTERM2; - term_props[TPR_DECRQM].tpr_status = TPR_YES; } // Detect terminals that set $TERM to something like @@ -5243,13 +5241,11 @@ handle_version_response(int first, int *arg, int argc, char_u *tp) if (arg[1] >= 2500) { term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES; - term_props[TPR_DECRQM].tpr_status = TPR_YES; } else if (version == 136 && arg[2] == 0) { term_props[TPR_UNDERLINE_RGB].tpr_status = TPR_YES; - term_props[TPR_DECRQM].tpr_status = TPR_YES; // PuTTY sends 0;136;0 if (arg[0] == 0) diff --git a/src/version.c b/src/version.c index 36f96c0550..81c72052ee 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 456, /**/ 455, /**/ From 2f00656b3480ed5384ce0513402025de9643462c Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Fri, 8 May 2026 21:22:28 +0000 Subject: [PATCH 193/545] patch 9.2.0457: Compile warning about unused variable Problem: Compile warning about unused variable (Tony Mechelynck, after v9.2.0452) Solution: Initialize the variable Signed-off-by: Christian Brabandt --- src/screen.c | 1 + src/version.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/src/screen.c b/src/screen.c index a482c400af..57b041d40d 100644 --- a/src/screen.c +++ b/src/screen.c @@ -609,6 +609,7 @@ screen_line( int drawing_opacity_popup = screen_opacity_popup != NULL && (flags & SLF_POPUP); + CLEAR_FIELD(po); if (drawing_opacity_popup) popup_opacity_init(&po); #endif diff --git a/src/version.c b/src/version.c index 81c72052ee..709f46b8df 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 457, /**/ 456, /**/ From 84ae09dd79b9888ba71dc2a28f9afcac3e7b8901 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Fri, 8 May 2026 21:29:21 +0000 Subject: [PATCH 194/545] patch 9.2.0458: Crash with invalid shellredir/shellpipe value Problem: Crash with invalid shellredir/shellpipe value (bfredl) Solution: Validate the option and allow only a single "%s". fixes: #20157 closes: #20159 Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 4 ++++ runtime/doc/tags | 1 + src/errors.h | 2 ++ src/option.c | 37 +++++++++++++++++++++++++++++++ src/optiondefs.h | 4 ++-- src/po/vim.pot | 6 ++++- src/proto/option.pro | 1 + src/testdir/test_options.vim | 2 ++ src/testdir/util/gen_opt_test.vim | 4 ++++ src/version.c | 2 ++ 10 files changed, 60 insertions(+), 3 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 969a17c164..c3753f197f 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -7846,6 +7846,7 @@ A jump table for the options with a short description can be found at |Q_op|. Note: When using a pipe like "| tee", you'll lose the exit code of the shell command. This might be configurable by your shell, look for the pipefail option (for bash and zsh, use ":set -o pipefail"). + Only a single "%s" value is allowed. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. @@ -7889,6 +7890,9 @@ A jump table for the options with a short description can be found at |Q_op|. become obsolete (at least for Unix). This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. + *E1577* + Only a single "%s" item is allowed in the option value. + *'shellslash'* *'ssl'* *'noshellslash'* *'nossl'* 'shellslash' 'ssl' boolean (default off) diff --git a/runtime/doc/tags b/runtime/doc/tags index 4ad699ed55..3168235dad 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -4779,6 +4779,7 @@ E1573 channel.txt /*E1573* E1574 channel.txt /*E1574* E1575 builtin.txt /*E1575* E1576 tagsrch.txt /*E1576* +E1577 options.txt /*E1577* E158 sign.txt /*E158* E159 sign.txt /*E159* E16 cmdline.txt /*E16* diff --git a/src/errors.h b/src/errors.h index 384dfaab9c..3ba7eca8f5 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3811,3 +3811,5 @@ EXTERN char e_cannot_create_pipes[] #endif EXTERN char e_tag_file_entry_must_not_be_url[] INIT(= N_("E1576: Tag file entry must not be a URL")); +EXTERN char e_invalid_format_string_single_percent_s[] + INIT(= N_("E1577: Invalid format string, only one \"%s\" is allowed")); diff --git a/src/option.c b/src/option.c index b44a3990ad..426e58e29b 100644 --- a/src/option.c +++ b/src/option.c @@ -4527,6 +4527,43 @@ did_set_maxsearchcount(optset_T *args UNUSED) #undef MAX_SEARCH_COUNT } +/* + * Validate 'shellpipe'/'shellredir' option. + */ + char * +did_set_shellpipe_redir(optset_T *args) +{ + char_u *p; + bool seen = false; + + for (p = args->os_newval.string; *p != NUL; ++p) + { + if (*p != '%') + continue; + + if (p[1] == NUL) + return e_invalid_format_string_single_percent_s; + + if (p[1] == '%') + { + ++p; // skip second % + continue; + } + + if (p[1] == 's') + { + if (seen) + return e_invalid_format_string_single_percent_s; + + seen = true; + ++p; // consume 's' + continue; + } + return e_invalid_format_string_single_percent_s; + } + return NULL; +} + #if defined(BACKSLASH_IN_FILENAME) /* diff --git a/src/optiondefs.h b/src/optiondefs.h index 0a93b70f8e..afcaf96319 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -2323,7 +2323,7 @@ static struct vimoption options[] = (char_u *)0L} SCTX_INIT}, {"shellpipe", "sp", P_STRING|P_VI_DEF|P_SECURE, #ifdef FEAT_QUICKFIX - (char_u *)&p_sp, PV_NONE, NULL, NULL, + (char_u *)&p_sp, PV_NONE, did_set_shellpipe_redir, NULL, { # if defined(UNIX) (char_u *)"| tee", @@ -2340,7 +2340,7 @@ static struct vimoption options[] = (char_u *)&p_shq, PV_NONE, NULL, NULL, {(char_u *)"", (char_u *)0L} SCTX_INIT}, {"shellredir", "srr", P_STRING|P_VI_DEF|P_SECURE, - (char_u *)&p_srr, PV_NONE, NULL, NULL, + (char_u *)&p_srr, PV_NONE, did_set_shellpipe_redir, NULL, {(char_u *)">", (char_u *)0L} SCTX_INIT}, {"shellslash", "ssl", P_BOOL|P_VI_DEF, #ifdef BACKSLASH_IN_FILENAME diff --git a/src/po/vim.pot b/src/po/vim.pot index 4dd6ba9093..5db2850389 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-04-29 19:49+0000\n" +"POT-Creation-Date: 2026-05-07 19:25+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -8860,6 +8860,10 @@ msgstr "" msgid "E1576: Tag file entry must not be a URL" msgstr "" +#, c-format +msgid "E1577: Invalid format string, only one \"%s\" is allowed" +msgstr "" + #. type of cmdline window or 0 #. result of cmdline window or 0 #. buffer of cmdline window or NULL diff --git a/src/proto/option.pro b/src/proto/option.pro index ae586ea978..ad8c9043ab 100644 --- a/src/proto/option.pro +++ b/src/proto/option.pro @@ -65,6 +65,7 @@ char *did_set_pyxversion(optset_T *args); char *did_set_readonly(optset_T *args); char *did_set_scrollbind(optset_T *args); char *did_set_maxsearchcount(optset_T *args); +char *did_set_shellpipe_redir(optset_T *args); char *did_set_shellslash(optset_T *args); char *did_set_shiftwidth_tabstop(optset_T *args); char *did_set_showtabline(optset_T *args); diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index fa2667525f..340ca36419 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -2658,6 +2658,8 @@ func Test_string_option_revert_on_failure() \ ['selection', 'exclusive', 'a123'], \ ['selectmode', 'cmd', 'a123'], \ ['sessionoptions', 'options', 'a123'], + \ ['shellpipe', '>%s', "%s%s%s"], + \ ['shellredir', '>%s', "%s%s%s"], \ ['shortmess', 'w', '2'], \ ['showbreak', '>>', "\x01"], \ ['showcmdloc', 'statusline', 'a123'], diff --git a/src/testdir/util/gen_opt_test.vim b/src/testdir/util/gen_opt_test.vim index 91ed2c80cd..f5d71ad185 100644 --- a/src/testdir/util/gen_opt_test.vim +++ b/src/testdir/util/gen_opt_test.vim @@ -307,6 +307,10 @@ let test_values = { \ 'sessionoptions': [['', 'blank', 'curdir', 'sesdir', \ 'help,options,slash'], \ ['xxx', 'curdir,sesdir']], + \ 'shellpipe': [[ '', '>', '>%s2>&1', '\|tee', '\|&tee', '2>&1\|tee', '%%'], + \ ['%s%s%s', '%s%p%d']], + \ 'shellredir': [[ '', '>', '>%s2>&1', '\|tee', '\|&tee', '2>&1\|tee', '%%'], + \ ['%s%s%s', '%s%p%d']], \ 'showcmdloc': [['', 'last', 'statusline', 'tabline'], ['xxx']], \ 'signcolumn': [['', 'auto', 'no', 'yes', 'number'], ['xxx', 'no,yes']], \ 'spellfile': [['', 'file.en.add', 'xxx.en.add,yyy.gb.add,zzz.ja.add', diff --git a/src/version.c b/src/version.c index 709f46b8df..2aefdf7e5b 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 458, /**/ 457, /**/ From fbec828c7e3144aad2c9b9a207773ad0a4a46b9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 9 May 2026 13:49:43 +0000 Subject: [PATCH 195/545] CI: Bump the github-actions group across 1 directory with 2 updates Bumps the github-actions group with 2 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action) and [actions/labeler](https://github.com/actions/labeler). Updates `github/codeql-action` from 4.35.2 to 4.35.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v4.35.2...v4.35.3) Updates `actions/labeler` from 6 to 6.0.1 - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/v6...v6.0.1) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.35.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: actions/labeler dependency-version: 6.0.1 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... closes: #20171 Signed-off-by: dependabot[bot] Signed-off-by: Christian Brabandt --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/label.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4c546fc570..17a12b97c9 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -48,7 +48,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4.35.2 + uses: github/codeql-action/init@v4.35.3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -59,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v4.35.2 + uses: github/codeql-action/autobuild@v4.35.3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -73,4 +73,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4.35.2 + uses: github/codeql-action/analyze@v4.35.3 diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index e413603bd4..6be0b2f8dd 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -17,6 +17,6 @@ jobs: pull-requests: write steps: - - uses: actions/labeler@v6 + - uses: actions/labeler@v6.0.1 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" From 124f8bececb1a50e85965d12ccd52c6c25a73122 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 9 May 2026 14:13:52 +0000 Subject: [PATCH 196/545] patch 9.2.0459: tests: test_termcodes fails (after v9.2.0456) Problem: tests: test_termcodes fails, because it disabled DECRQM, but did not adjust the expected values in the test (after v9.2.0456) Solution: Update the test related: #20161 closes: #20173 Signed-off-by: Christian Brabandt --- src/testdir/test_termcodes.vim | 14 +++++++------- src/version.c | 2 ++ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/testdir/test_termcodes.vim b/src/testdir/test_termcodes.vim index eaf60f53be..adabeeedbf 100644 --- a/src/testdir/test_termcodes.vim +++ b/src/testdir/test_termcodes.vim @@ -1720,7 +1720,7 @@ func Test_xx01_term_style_response() \ underline_rgb: 'u', \ mouse: 's', \ kitty: 'u', - \ decrqm: 'y' + \ decrqm: 'u' \ }, terminalprops()) set t_RV= @@ -1777,7 +1777,7 @@ func Run_libvterm_konsole_response(code) \ underline_rgb: 'u', \ mouse: 's', \ kitty: 'u', - \ decrqm: 'y' + \ decrqm: 'u' \ }, terminalprops()) endfunc @@ -1853,7 +1853,7 @@ func Test_xx05_mintty_response() \ underline_rgb: 'y', \ mouse: 's', \ kitty: 'u', - \ decrqm: 'y' + \ decrqm: 'u' \ }, terminalprops()) set t_RV= @@ -1916,7 +1916,7 @@ func Do_check_t_8u_set_reset(set_by_user) \ underline_rgb: 'u', \ mouse: 's', \ kitty: 'u', - \ decrqm: 'y' + \ decrqm: 'u' \ }, terminalprops()) call assert_equal(a:set_by_user ? default_value : '', &t_8u) endfunc @@ -1956,7 +1956,7 @@ func Test_xx07_xterm_response() \ underline_rgb: 'y', \ mouse: 'u', \ kitty: 'u', - \ decrqm: 'y' + \ decrqm: 'u' \ }, terminalprops()) " xterm >= 95 < 277 "xterm2" @@ -1973,7 +1973,7 @@ func Test_xx07_xterm_response() \ underline_rgb: 'u', \ mouse: '2', \ kitty: 'u', - \ decrqm: 'y' + \ decrqm: 'u' \ }, terminalprops()) " xterm >= 277: "sgr" @@ -1990,7 +1990,7 @@ func Test_xx07_xterm_response() \ underline_rgb: 'u', \ mouse: 's', \ kitty: 'u', - \ decrqm: 'y' + \ decrqm: 'u' \ }, terminalprops()) " xterm >= 279: "sgr" and cursor_style not reset; also check t_8u reset, diff --git a/src/version.c b/src/version.c index 2aefdf7e5b..5ffa63f7fe 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 459, /**/ 458, /**/ From abd74fa122451882b3b4e7b83813cf3b17e8109a Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 9 May 2026 14:18:53 +0000 Subject: [PATCH 197/545] patch 9.2.0460: did_set_shellpipe_redir() in wrong file Problem: did_set_shellpipe_redir() is a callback for a string option, but is not in optionstr.c (after 9.2.0458). Solution: Move it to optionstr.c. Also add missing change from patch 9.2.0455 (zeertzjq). related: #20159 related: #20164 closes: #20170 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/cmdexpand.c | 2 +- src/option.c | 38 -------------------------------------- src/optionstr.c | 37 +++++++++++++++++++++++++++++++++++++ src/proto/option.pro | 1 - src/proto/optionstr.pro | 1 + src/version.c | 2 ++ 6 files changed, 41 insertions(+), 40 deletions(-) diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 3c660b5ea2..f313981984 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -4183,7 +4183,7 @@ expand_process_user_list( { if (tv->vval.v_string == NULL) continue; // Skip NULL strings - p = vim_strsave(li->li_tv.vval.v_string); + p = vim_strsave(tv->vval.v_string); } else if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) { diff --git a/src/option.c b/src/option.c index 426e58e29b..4197d4d8b7 100644 --- a/src/option.c +++ b/src/option.c @@ -4527,44 +4527,6 @@ did_set_maxsearchcount(optset_T *args UNUSED) #undef MAX_SEARCH_COUNT } -/* - * Validate 'shellpipe'/'shellredir' option. - */ - char * -did_set_shellpipe_redir(optset_T *args) -{ - char_u *p; - bool seen = false; - - for (p = args->os_newval.string; *p != NUL; ++p) - { - if (*p != '%') - continue; - - if (p[1] == NUL) - return e_invalid_format_string_single_percent_s; - - if (p[1] == '%') - { - ++p; // skip second % - continue; - } - - if (p[1] == 's') - { - if (seen) - return e_invalid_format_string_single_percent_s; - - seen = true; - ++p; // consume 's' - continue; - } - return e_invalid_format_string_single_percent_s; - } - return NULL; -} - - #if defined(BACKSLASH_IN_FILENAME) /* * Process the updated 'shellslash' option value. diff --git a/src/optionstr.c b/src/optionstr.c index 59d4909130..b60e595868 100644 --- a/src/optionstr.c +++ b/src/optionstr.c @@ -4179,6 +4179,43 @@ expand_set_sessionoptions( } #endif +/* + * Validate 'shellpipe'/'shellredir' option. + */ + char * +did_set_shellpipe_redir(optset_T *args) +{ + char_u *p; + bool seen = false; + + for (p = args->os_newval.string; *p != NUL; ++p) + { + if (*p != '%') + continue; + + if (p[1] == NUL) + return e_invalid_format_string_single_percent_s; + + if (p[1] == '%') + { + ++p; // skip second % + continue; + } + + if (p[1] == 's') + { + if (seen) + return e_invalid_format_string_single_percent_s; + + seen = true; + ++p; // consume 's' + continue; + } + return e_invalid_format_string_single_percent_s; + } + return NULL; +} + /* * The 'shortmess' option is changed. */ diff --git a/src/proto/option.pro b/src/proto/option.pro index ad8c9043ab..ae586ea978 100644 --- a/src/proto/option.pro +++ b/src/proto/option.pro @@ -65,7 +65,6 @@ char *did_set_pyxversion(optset_T *args); char *did_set_readonly(optset_T *args); char *did_set_scrollbind(optset_T *args); char *did_set_maxsearchcount(optset_T *args); -char *did_set_shellpipe_redir(optset_T *args); char *did_set_shellslash(optset_T *args); char *did_set_shiftwidth_tabstop(optset_T *args); char *did_set_showtabline(optset_T *args); diff --git a/src/proto/optionstr.pro b/src/proto/optionstr.pro index cb174e73c0..c8a8b12ff5 100644 --- a/src/proto/optionstr.pro +++ b/src/proto/optionstr.pro @@ -152,6 +152,7 @@ char *did_set_selectmode(optset_T *args); int expand_set_selectmode(optexpand_T *args, int *numMatches, char_u ***matches); char *did_set_sessionoptions(optset_T *args); int expand_set_sessionoptions(optexpand_T *args, int *numMatches, char_u ***matches); +char *did_set_shellpipe_redir(optset_T *args); char *did_set_shortmess(optset_T *args); int expand_set_shortmess(optexpand_T *args, int *numMatches, char_u ***matches); char *did_set_showbreak(optset_T *args); diff --git a/src/version.c b/src/version.c index 5ffa63f7fe..6ac372ac5d 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 460, /**/ 459, /**/ From 4f610f07b76b4f34d011179c2531ab4517ac11e8 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 9 May 2026 14:41:37 +0000 Subject: [PATCH 198/545] patch 9.2.0461: Corrupted undofile causes use-after-free Problem: The four pointer-resolution loops in u_read_undo() lack an i != j guard, so a header whose uh_next.seq equals its own uh_seq resolves uh_next.ptr to itself. On buffer close, u_freeheader() sees uhp->uh_next.ptr != NULL and skips updating b_u_oldhead, so u_blockfree() dereferences the freed header on the next iteration. The same pattern applies to uh_prev, uh_alt_next and uh_alt_prev. A crafted .un~ file in the same directory as a text file can trigger the use-after-free and subsequent double-free when the buffer is closed. (Daniel Cervera) Solution: Add an i != j guard to each of the four resolution loops, matching the guard already present in the duplicate-detection loop above. closes: #20168 Supported by AI Signed-off-by: Christian Brabandt --- src/testdir/test_undo.vim | 37 ++++++++++++++++++ src/undo.c | 80 +++++++++++++++++++++++---------------- src/version.c | 2 + 3 files changed, 87 insertions(+), 32 deletions(-) diff --git a/src/testdir/test_undo.vim b/src/testdir/test_undo.vim index f03d3ef091..21b9dcfdab 100644 --- a/src/testdir/test_undo.vim +++ b/src/testdir/test_undo.vim @@ -967,4 +967,41 @@ func Test_undo_line_backspace_after_insert_cmd_edit() bwipe! endfunc +" Corrupted undo file via cyclic cross-references caused +" double free +func Test_corrupted_undofile() + CheckFeature persistent_undo + let _uf = &undofile + set undofile + new + call setline(1, 'hello') + let b=eval('0z56696D9F556E446FE50002F3AEFE62965A91903610' .. + \ 'F0E23CC8A69D5B87CEA6D28E75489B0D2CA02ED7993C' .. + \ '00000001000000000000000000000000000000010000' .. + \ '00010000000100000001000000010000000100000000' .. + \ '3B9ACA00005FD0000000010000000000000000000000' .. + \ '00000000010000000100000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '00000000000000000000000000000000000000000000' .. + \ '000000000000000000000000000000000000003B9ACA' .. + \ '00003581E7AA') + call writefile(b, 'Xundo', 'bD') + rundo Xundo + bw! + let &undofile = _uf +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/undo.c b/src/undo.c index ac38a8bf21..06efd92301 100644 --- a/src/undo.c +++ b/src/undo.c @@ -2081,38 +2081,54 @@ u_read_undo(char_u *name, char_u *hash, char_u *orig_name UNUSED) corruption_error("duplicate uh_seq", file_name); goto error; } - for (j = 0; j < num_head; j++) - if (uhp_table[j] != NULL - && uhp_table[j]->uh_seq == uhp->uh_next.seq) - { - uhp->uh_next.ptr = uhp_table[j]; - SET_FLAG(j); - break; - } - for (j = 0; j < num_head; j++) - if (uhp_table[j] != NULL - && uhp_table[j]->uh_seq == uhp->uh_prev.seq) - { - uhp->uh_prev.ptr = uhp_table[j]; - SET_FLAG(j); - break; - } - for (j = 0; j < num_head; j++) - if (uhp_table[j] != NULL - && uhp_table[j]->uh_seq == uhp->uh_alt_next.seq) - { - uhp->uh_alt_next.ptr = uhp_table[j]; - SET_FLAG(j); - break; - } - for (j = 0; j < num_head; j++) - if (uhp_table[j] != NULL - && uhp_table[j]->uh_seq == uhp->uh_alt_prev.seq) - { - uhp->uh_alt_prev.ptr = uhp_table[j]; - SET_FLAG(j); - break; - } + { + int seq = uhp->uh_next.seq; + uhp->uh_next.ptr = NULL; + for (j = 0; j < num_head; j++) + if (uhp_table[j] != NULL && i != j + && uhp_table[j]->uh_seq == seq) + { + uhp->uh_next.ptr = uhp_table[j]; + SET_FLAG(j); + break; + } + } + { + int seq = uhp->uh_prev.seq; + uhp->uh_prev.ptr = NULL; + for (j = 0; j < num_head; j++) + if (uhp_table[j] != NULL && i != j + && uhp_table[j]->uh_seq == seq) + { + uhp->uh_prev.ptr = uhp_table[j]; + SET_FLAG(j); + break; + } + } + { + int seq = uhp->uh_alt_next.seq; + uhp->uh_alt_next.ptr = NULL; + for (j = 0; j < num_head; j++) + if (uhp_table[j] != NULL && i != j + && uhp_table[j]->uh_seq == seq) + { + uhp->uh_alt_next.ptr = uhp_table[j]; + SET_FLAG(j); + break; + } + } + { + int seq = uhp->uh_alt_prev.seq; + uhp->uh_alt_prev.ptr = NULL; + for (j = 0; j < num_head; j++) + if (uhp_table[j] != NULL && i != j + && uhp_table[j]->uh_seq == seq) + { + uhp->uh_alt_prev.ptr = uhp_table[j]; + SET_FLAG(j); + break; + } + } if (old_header_seq > 0 && old_idx < 0 && uhp->uh_seq == old_header_seq) { old_idx = i; diff --git a/src/version.c b/src/version.c index 6ac372ac5d..6b5b0e7e11 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 461, /**/ 460, /**/ From 3a1ac7ced28179a5b39a7135b5c242ec8bc06797 Mon Sep 17 00:00:00 2001 From: "K.Takata" Date: Sun, 10 May 2026 16:34:01 +0000 Subject: [PATCH 199/545] patch 9.2.0462: MS-Windows: workaround for assert error on GUI Problem: When Vim is built with debug mode, gvim causes an assertion error and stops working when running on Visual Studio Debugger. Solution: Stop calling _set_fmode() if not needed (Ken Takata). closes: #20181 Signed-off-by: K.Takata Signed-off-by: Christian Brabandt --- src/os_win32.c | 3 ++- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/os_win32.c b/src/os_win32.c index ab12d5a051..31fa169a5a 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -8100,7 +8100,8 @@ mch_fopen(const char *name, const char *mode) vim_free(wm); #if defined(DEBUG) && _MSC_VER >= 1400 - _set_fmode(oldMode); + if (oldMode != 0) + _set_fmode(oldMode); #endif return f; } diff --git a/src/version.c b/src/version.c index 6b5b0e7e11..fd79c4354c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 462, /**/ 461, /**/ From 6b8d2262d3e94d5f4fdbe29ea621946f2718fe53 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sun, 10 May 2026 16:41:35 +0000 Subject: [PATCH 200/545] patch 9.2.0463: Not able to use legacy expression evaluation in a vim9script maps Problem: Not able to use legacy expression evaluation in a vim9script maps Solution: Explicitly set script version to 1 when the :legacy modifier has been used (Yegappan Lakshmanan). fixe: #20176 closes: #20177 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- src/map.c | 9 ++++++--- src/testdir/test_vim9_cmd.vim | 11 +++++++++++ src/version.c | 2 ++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/map.c b/src/map.c index 5f07ef4a81..1c0d0ac7fd 100644 --- a/src/map.c +++ b/src/map.c @@ -277,6 +277,8 @@ map_add( else { mp->m_script_ctx = current_sctx; + if (cmdmod.cmod_flags & CMOD_LEGACY) + mp->m_script_ctx.sc_version = 1; mp->m_script_ctx.sc_lnum += SOURCING_LNUM; } #endif @@ -871,6 +873,8 @@ do_map( #ifdef FEAT_EVAL mp->m_expr = expr; mp->m_script_ctx = current_sctx; + if (cmdmod.cmod_flags & CMOD_LEGACY) + mp->m_script_ctx.sc_version = 1; mp->m_script_ctx.sc_lnum += SOURCING_LNUM; #endif mp_result[keyround - 1] = mp; @@ -1822,11 +1826,10 @@ eval_map_expr( save_cursor = curwin->w_cursor; save_msg_col = msg_col; save_msg_row = msg_row; + + current_sctx.sc_version = mp->m_script_ctx.sc_version; if (mp->m_script_ctx.sc_version == SCRIPT_VERSION_VIM9) - { current_sctx.sc_sid = mp->m_script_ctx.sc_sid; - current_sctx.sc_version = SCRIPT_VERSION_VIM9; - } // Note: the evaluation may make "mp" invalid. p = eval_to_string(expr, FALSE, FALSE); diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index ce05898061..f90d719715 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -2145,5 +2145,16 @@ def Test_syntax_enable_clear() syntax clear enddef +" Test for using legacy expression evaluation in a vim9script map +def Test_map_legacy_expr() + var lines =<< trim END + legacy inoremap 'hello' . 'world' + new + feedkeys("a\", 'xt') + assert_equal(['helloworld'], getline(1, '$')) + bw! + END + v9.CheckDefAndScriptSuccess(lines) +enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index fd79c4354c..1e9a692ea1 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 463, /**/ 462, /**/ From ec76ac620b870abb4ccc9298250691e88a2adb29 Mon Sep 17 00:00:00 2001 From: "J. Paulo Seibt" Date: Sun, 10 May 2026 16:50:02 +0000 Subject: [PATCH 201/545] patch 9.2.0464: runtime(netrw): bookmarking directory uses current dir Problem: runtime(netrw): bookmarking directory uses current dir Solution: Correctly handle netrw actual directory (J. Paulo Seibt) fixes: #10481 closes: #20169 Signed-off-by: J. Paulo Seibt Signed-off-by: Christian Brabandt --- .../pack/dist/opt/netrw/autoload/netrw.vim | 9 ++-- src/testdir/test_plugin_netrw.vim | 41 +++++++++++++++++++ src/version.c | 2 + 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index 90fd70472a..7d73af8fae 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -1,7 +1,7 @@ " Creator: Charles E Campbell " Previous Maintainer: Luca Saccarola " Maintainer: This runtime file is looking for a new maintainer. -" Last Change: 2026 May 05 +" Last Change: 2026 May 10 " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -8989,11 +8989,10 @@ function s:MakeBookmark(fname) if index(g:netrw_bookmarklist,a:fname) == -1 " curdir not currently in g:netrw_bookmarklist, so include it if isdirectory(s:NetrwFile(a:fname)) && a:fname !~ '/$' - call add(g:netrw_bookmarklist,a:fname.'/') - elseif a:fname !~ '/' - call add(g:netrw_bookmarklist,getcwd()."/".a:fname) + " Directory without a trailing slash + call add(g:netrw_bookmarklist, s:NetrwFile(a:fname) . '/') else - call add(g:netrw_bookmarklist,a:fname) + call add(g:netrw_bookmarklist, s:NetrwFile(a:fname)) endif call sort(g:netrw_bookmarklist) endif diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim index b66bb4a72e..5a86c29b7b 100644 --- a/src/testdir/test_plugin_netrw.vim +++ b/src/testdir/test_plugin_netrw.vim @@ -165,6 +165,13 @@ function Test_NetrwUnMarkFile() " wipe out the test buffer bw endfunction + +function Test_MakeBookmark(netrw_curdir, fname) + new + let b:netrw_curdir = a:netrw_curdir + call s:MakeBookmark(a:fname) + bw +endfunction " }}} END @@ -676,4 +683,38 @@ func Test_netrw_unmark_all() call Test_NetrwUnMarkFile() endfunc +" Creating a bookmark from a marked file should use b:netrw_curdir as head directory +func Test_netrw_bookmark_marked_file() + let save_keepdir = g:netrw_keepdir + let save_workdir = getcwd() + let save_bookmarklist = exists('g:netrw_bookmarklist') ? g:netrw_bookmarklist : v:null + + " Make sure Vim's working directory diverge from Netrw's + let g:netrw_keepdir = 1 + let g:netrw_bookmarklist = [] + let test_workdir = 'Xtest_workdir' + let test_netrw_curdir = test_workdir . '/Xtest_netrw_curdir' + call mkdir(test_netrw_curdir, 'p') + call writefile([], test_netrw_curdir . '/test_file') + + execute 'cd ' . test_workdir + call Test_MakeBookmark(test_netrw_curdir, 'test_file') + + call assert_equal(1, len(g:netrw_bookmarklist)) + call assert_match(test_netrw_curdir . '/test_file', g:netrw_bookmarklist[0]) + + " Tear down + execute 'cd ' . save_workdir + call delete(test_workdir, 'rf') + + let g:netrw_keepdir = save_keepdir + if save_bookmarklist is v:null + unlet g:netrw_bookmarklist + else + let g:netrw_bookmarklist = save_bookmarklist + endif + + bw! +endfunc + " vim:ts=8 sts=2 sw=2 et diff --git a/src/version.c b/src/version.c index 1e9a692ea1..339bc44593 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 464, /**/ 463, /**/ From 2020e0bade4b1123460d1e1255ac41aa06577943 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 10 May 2026 17:00:59 +0000 Subject: [PATCH 202/545] patch 9.2.0465: modeline: foldmarker cannot be set with modelinestrict Problem: modeline: foldmarker cannot be set with modelinestrict (Lyderic Landry, after v9.2.0350) Solution: Add foldmarker option to the whitelist fixes: #20028 closes: #20174 Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 3 ++- src/option.c | 1 + src/testdir/test_modeline.vim | 3 ++- src/version.c | 2 ++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index c3753f197f..99e4e70a3a 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 May 08 +*options.txt* For Vim version 9.2. Last change: 2026 May 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6216,6 +6216,7 @@ A jump table for the options with a short description can be found at |Q_op|. 'filetype' 'foldcolumn' 'foldenable' + 'foldmarker' 'foldmethod' 'modifiable' 'readonly' diff --git a/src/option.c b/src/option.c index 4197d4d8b7..31f84c98d1 100644 --- a/src/option.c +++ b/src/option.c @@ -1554,6 +1554,7 @@ static char *modeline_whitelist[] = "filetype", "foldcolumn", "foldenable", + "foldmarker", "foldmethod", "modifiable", "readonly", diff --git a/src/testdir/test_modeline.vim b/src/testdir/test_modeline.vim index 6884ab473d..cb489ee15f 100644 --- a/src/testdir/test_modeline.vim +++ b/src/testdir/test_modeline.vim @@ -563,11 +563,12 @@ func Test_modeline_strict_allowed() set modeline modelinestrict " Whitelisted options should work - call writefile(['vim: set ts=2 sw=4 et :'], 'Xmodeline_strict', 'D') + call writefile(['vim: set ts=2 sw=4 et foldmarker=[,]:'], 'Xmodeline_strict', 'D') split Xmodeline_strict call assert_equal(2, &ts) call assert_equal(4, &sw) call assert_equal(1, &et) + call assert_equal('[,]', &foldmarker) bwipe! " 'filetype' should work diff --git a/src/version.c b/src/version.c index 339bc44593..102a0d4eb4 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 465, /**/ 464, /**/ From 3a8b9e09b2925c8788814627c57c9aa16c7a7f2e Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 10 May 2026 17:16:08 +0000 Subject: [PATCH 203/545] patch 9.2.0466: popup: redraw can use stale blended cells Problem: popup: redraw can use stale blended cells Solution: Save the old popup area and redraw the newly exposed region so opacity popups don't show stale blended cells when another popup moves or closes. Consolidate redraw helpers so the saved-area and exposed-area logic is shared across move/hide/close/settext/setoptions. Refactor popup redrawing code, add a regressions tests (Yasuhiro Matsumoto). closes: #20172 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/popupwin.c | 828 ++++++++++++------ .../Test_popup_opacity_move_after_close.dump | 8 + src/testdir/test_popup.vim | 28 + src/version.c | 2 + 4 files changed, 601 insertions(+), 265 deletions(-) create mode 100644 src/testdir/dumps/Test_popup_opacity_move_after_close.dump diff --git a/src/popupwin.c b/src/popupwin.c index f019943805..dcbe0e854c 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -20,6 +20,58 @@ typedef struct { poppos_T pp_val; } poppos_entry_T; +// Snapshot of the popup's drawn rectangle. Used to redraw what becomes +// exposed when the popup moves, resizes, hides or closes. "active" is TRUE +// when the popup is an opacity popup that contributes to the blended +// background; non-opacity popups rely on popup_mask instead. +typedef struct { + int active; + int winrow; + int wincol; + int height; + int width; + int leftoff; + int zindex; +} popup_area_T; + +// Snapshot of the screen cells under an opacity popup's padding, captured +// before win_update() overwrites them so the padding can be re-blended +// against the original background. "lines" is NULL when no snapshot was +// taken; popup_free_saved_screen() releases all owned buffers. +typedef struct { + schar_T *lines; + int *attrs; + u8char_T *linesuc; + int start_row; + int start_col; + int rows; + int cols; +} popup_saved_screen_T; + +// Snapshot of popup style/option fields used by popup_setoptions() to detect +// which option changes require a redraw or reposition. +typedef struct { + linenr_T firstline; + int blend; + int flags; + int zindex; + char_u *scrollbar_highlight; + char_u *thumb_highlight; + char_u *border_highlight[4]; +} popup_style_snapshot_T; + +// Snapshot of popup layout fields used by popup_adjust_position() to detect +// whether the position or size changed and the popup mask must be refreshed. +typedef struct { + int winrow; + int wincol; + int width; + int height; + int leftcol; + int leftoff; + int has_scrollbar; +} popup_layout_T; + static poppos_entry_T poppos_entries[] = { {STR_LITERAL_INIT("botleft"), POPPOS_BOTLEFT}, {STR_LITERAL_INIT("topleft"), POPPOS_TOPLEFT}, @@ -46,8 +98,20 @@ static void may_start_message_win_timer(win_T *wp); static int popup_on_cmdline = FALSE; static void popup_adjust_position(win_T *wp); +static bool popup_area_changed(win_T *wp, popup_area_T *area); +static void popup_redraw_exposed_area(popup_area_T *area); +static void popup_save_area(win_T *wp, popup_area_T *area); +static void popup_free_saved_screen(popup_saved_screen_T *saved_screen); +static void popup_save_padding_screen(win_T *wp, + popup_saved_screen_T *saved_screen); +static bool popup_layout_changed(win_T *wp, popup_layout_T *layout); +static bool popup_style_changed(win_T *wp, popup_style_snapshot_T *style); +static void popup_save_style(win_T *wp, popup_style_snapshot_T *style); +static void popup_save_layout(win_T *wp, popup_layout_T *layout); static void redraw_under_popup_area(int winrow, int wincol, int height, int width, int leftoff); +static void redraw_overlapped_opacity_popups(int winrow, int wincol, + int height, int width, int leftoff, int zindex); /* * Get option value for "key", which is "line" or "col". @@ -1282,13 +1346,7 @@ popup_adjust_position(win_T *wp) int extra_height = top_extra + bot_extra; int extra_width = left_extra + right_extra; int w_height_before_limit; - int org_winrow = wp->w_winrow; - int org_wincol = wp->w_wincol; - int org_width = wp->w_width; - int org_height = wp->w_height; - int org_leftcol = wp->w_leftcol; - int org_leftoff = wp->w_popup_leftoff; - int org_has_scrollbar = wp->w_has_scrollbar; + popup_layout_T org_layout; int minwidth, minheight; int maxheight = Rows; int wantline = wp->w_wantline; // adjusted for textprop @@ -1296,6 +1354,8 @@ popup_adjust_position(win_T *wp) int use_wantcol = wantcol != 0; int adjust_height_for_top_aligned = FALSE; + popup_save_layout(wp, &org_layout); + wp->w_winrow = 0; wp->w_wincol = 0; wp->w_leftcol = 0; @@ -1813,7 +1873,7 @@ popup_adjust_position(win_T *wp) wp->w_height = avail > 0 ? avail : 0; } - if (wp->w_height != org_height) + if (wp->w_height != org_layout.height) win_comp_scroll(wp); wp->w_popup_last_changedtick = CHANGEDTICK(wp->w_buffer); @@ -1826,13 +1886,7 @@ popup_adjust_position(win_T *wp) // Need to update popup_mask if the position or size changed. // And redraw windows and statuslines that were behind the popup. - if (org_winrow != wp->w_winrow - || org_wincol != wp->w_wincol - || org_leftcol != wp->w_leftcol - || org_leftoff != wp->w_popup_leftoff - || org_width != wp->w_width - || org_height != wp->w_height - || org_has_scrollbar != wp->w_has_scrollbar) + if (popup_layout_changed(wp, &org_layout)) { redraw_win_later(wp, UPD_NOT_VALID); if (wp->w_popup_flags & POPF_ON_CMDLINE) @@ -3096,6 +3150,8 @@ f_popup_close(typval_T *argvars, typval_T *rettv UNUSED) void popup_hide(win_T *wp) { + popup_area_T old_area; + #ifdef FEAT_TERMINAL if (error_if_term_popup_window()) return; @@ -3103,13 +3159,21 @@ popup_hide(win_T *wp) if ((wp->w_popup_flags & POPF_HIDDEN) != 0) return; + popup_save_area(wp, &old_area); + wp->w_popup_flags |= POPF_HIDDEN; // Do not decrement b_nwindows, we still reference the buffer. if (wp->w_winrow + popup_height(wp) >= cmdline_row) clear_cmdline = TRUE; - redraw_all_later(UPD_NOT_VALID); + + if (old_area.active) + popup_redraw_exposed_area(&old_area); + else + redraw_all_later(UPD_NOT_VALID); + status_redraw_all(); - popup_mask_refresh = TRUE; + if (!old_area.active) + popup_mask_refresh = TRUE; } /* @@ -3136,12 +3200,25 @@ f_popup_hide(typval_T *argvars, typval_T *rettv UNUSED) void popup_show(win_T *wp) { + bool popup_active; + if ((wp->w_popup_flags & POPF_HIDDEN) == 0) return; + popup_active = (wp->w_popup_flags & POPF_OPACITY) && wp->w_popup_blend > 0; wp->w_popup_flags &= ~POPF_HIDDEN; - redraw_all_later(UPD_NOT_VALID); - popup_mask_refresh = TRUE; + if (popup_active) + { + wp->w_redr_type = UPD_NOT_VALID; + wp->w_lines_valid = 0; + if (must_redraw < UPD_VALID) + must_redraw = UPD_VALID; + } + else + { + redraw_all_later(UPD_NOT_VALID); + popup_mask_refresh = TRUE; + } } /* @@ -3182,14 +3259,7 @@ f_popup_settext(typval_T *argvars, typval_T *rettv UNUSED) { int id; win_T *wp; -#ifdef FEAT_PROP_POPUP - int old_popup_active; -#endif - int old_winrow; - int old_wincol; - int old_popup_height; - int old_popup_width; - int old_popup_leftoff; + popup_area_T old_area; if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL @@ -3201,15 +3271,7 @@ f_popup_settext(typval_T *argvars, typval_T *rettv UNUSED) if (wp == NULL) return; -#ifdef FEAT_PROP_POPUP - old_popup_active = (wp->w_popup_flags & POPF_OPACITY) - && wp->w_popup_blend > 0; -#endif - old_winrow = wp->w_winrow; - old_wincol = wp->w_wincol; - old_popup_height = popup_height(wp); - old_popup_width = popup_width(wp); - old_popup_leftoff = wp->w_popup_leftoff; + popup_save_area(wp, &old_area); if (check_for_string_or_list_arg(argvars, 1) == FAIL) return; @@ -3226,16 +3288,8 @@ f_popup_settext(typval_T *argvars, typval_T *rettv UNUSED) must_redraw = UPD_VALID; popup_adjust_position(wp); -#ifdef FEAT_PROP_POPUP - if (old_popup_active - && (old_winrow != wp->w_winrow - || old_wincol != wp->w_wincol - || old_popup_height != popup_height(wp) - || old_popup_width != popup_width(wp) - || old_popup_leftoff != wp->w_popup_leftoff)) - redraw_under_popup_area(old_winrow, old_wincol, - old_popup_height, old_popup_width, old_popup_leftoff); -#endif + if (popup_area_changed(wp, &old_area)) + popup_redraw_exposed_area(&old_area); } /* @@ -3244,9 +3298,10 @@ f_popup_settext(typval_T *argvars, typval_T *rettv UNUSED) void f_popup_setbuf(typval_T *argvars, typval_T *rettv UNUSED) { - int id; - win_T *wp; - buf_T *buf; + int id; + win_T *wp; + buf_T *buf; + popup_area_T old_area; rettv->v_type = VAR_BOOL; rettv->vval.v_number = VVAL_FALSE; @@ -3274,6 +3329,8 @@ f_popup_setbuf(typval_T *argvars, typval_T *rettv UNUSED) if (wp->w_buffer != buf) { + popup_save_area(wp, &old_area); + wp->w_buffer->b_nwindows--; win_init_popup_win(wp, buf); set_local_options_default(wp, FALSE); @@ -3282,6 +3339,9 @@ f_popup_setbuf(typval_T *argvars, typval_T *rettv UNUSED) swap_exists_action = SEA_NONE; redraw_win_later(wp, UPD_NOT_VALID); popup_adjust_position(wp); + + if (popup_area_changed(wp, &old_area)) + popup_redraw_exposed_area(&old_area); } rettv->vval.v_number = VVAL_TRUE; } @@ -3289,10 +3349,17 @@ f_popup_setbuf(typval_T *argvars, typval_T *rettv UNUSED) static void popup_free(win_T *wp) { + popup_area_T old_area; + + popup_save_area(wp, &old_area); + sign_undefine_by_name(popup_get_sign_name(wp), FALSE); wp->w_buffer->b_locked = FALSE; if (wp->w_winrow + popup_height(wp) >= cmdline_row) clear_cmdline = TRUE; + + popup_redraw_exposed_area(&old_area); + win_free_popup(wp); #ifdef HAS_MESSAGE_WINDOW @@ -3300,9 +3367,11 @@ popup_free(win_T *wp) message_win = NULL; #endif - redraw_all_later(UPD_NOT_VALID); + if (!old_area.active) + redraw_all_later(UPD_NOT_VALID); status_redraw_all(); - popup_mask_refresh = TRUE; + if (!old_area.active) + popup_mask_refresh = TRUE; } static void @@ -3420,6 +3489,389 @@ close_all_popups(int force) return; } +/* + * Save the current popup area that may need to be restored later. + */ + static void +popup_save_area(win_T *wp, popup_area_T *area) +{ + area->active = (wp->w_popup_flags & POPF_OPACITY) && wp->w_popup_blend > 0; + area->winrow = wp->w_winrow; + area->wincol = wp->w_wincol; + area->height = popup_height(wp); + area->width = popup_width(wp); + area->leftoff = wp->w_popup_leftoff; + area->zindex = wp->w_zindex; +} + +/* + * Save popup style-related fields that affect redraw/reposition decisions. + */ + static void +popup_save_style(win_T *wp, popup_style_snapshot_T *style) +{ + int i; + + style->firstline = wp->w_firstline; + style->blend = wp->w_popup_blend; + style->flags = wp->w_popup_flags; + style->zindex = wp->w_zindex; + style->scrollbar_highlight = wp->w_scrollbar_highlight; + style->thumb_highlight = wp->w_thumb_highlight; + for (i = 0; i < 4; i++) + style->border_highlight[i] = wp->w_border_highlight[i]; +} + +/* + * Return true if style changes require at least a popup redraw. + */ + static bool +popup_style_changed(win_T *wp, popup_style_snapshot_T *style) +{ + int i; + + if (style->firstline != wp->w_firstline + || style->flags != wp->w_popup_flags + || style->scrollbar_highlight != wp->w_scrollbar_highlight + || style->thumb_highlight != wp->w_thumb_highlight) + return true; + for (i = 0; i < 4; i++) + if (style->border_highlight[i] != wp->w_border_highlight[i]) + return true; + return false; +} + +/* + * Save popup layout fields that affect mask refresh and local redraw. + */ + static void +popup_save_layout(win_T *wp, popup_layout_T *layout) +{ + layout->winrow = wp->w_winrow; + layout->wincol = wp->w_wincol; + layout->width = wp->w_width; + layout->height = wp->w_height; + layout->leftcol = wp->w_leftcol; + layout->leftoff = wp->w_popup_leftoff; + layout->has_scrollbar = wp->w_has_scrollbar; +} + +/* + * Return true when the popup layout changed. + */ + static bool +popup_layout_changed(win_T *wp, popup_layout_T *layout) +{ + return layout->winrow != wp->w_winrow + || layout->wincol != wp->w_wincol + || layout->leftcol != wp->w_leftcol + || layout->leftoff != wp->w_popup_leftoff + || layout->width != wp->w_width + || layout->height != wp->w_height + || layout->has_scrollbar != wp->w_has_scrollbar; +} + +/* + * Return true when the popup no longer covers the saved area. + */ + static bool +popup_area_changed(win_T *wp, popup_area_T *area) +{ + return area->winrow != wp->w_winrow + || area->wincol != wp->w_wincol + || area->height != popup_height(wp) + || area->width != popup_width(wp) + || area->leftoff != wp->w_popup_leftoff; +} + +/* + * If "wp" is a visible opacity popup at or below "zindex" whose drawn area + * overlaps the rectangle, mark it for full redraw so its blended background + * is recomputed. + */ + static void +mark_overlapped_opacity_popup(win_T *wp, int area_top, int area_bot, + int area_left, int area_right, int zindex) +{ + if ((wp->w_popup_flags & POPF_HIDDEN) + || (wp->w_popup_flags & POPF_OPACITY) == 0 + || wp->w_popup_blend == 0 + || wp->w_zindex > zindex + || wp->w_winrow >= area_bot + || wp->w_winrow + popup_height(wp) <= area_top + || wp->w_wincol >= area_right + || wp->w_wincol + popup_width(wp) - wp->w_popup_leftoff + <= area_left) + return; + + wp->w_redr_type = UPD_NOT_VALID; + wp->w_lines_valid = 0; +} + +/* + * Mark lower or equal zindex opacity popups that overlap with a popup area + * for redraw. Their blended background may have included the old popup. + */ + static void +redraw_overlapped_opacity_popups(int winrow, int wincol, int height, int width, + int leftoff, int zindex) +{ + win_T *wp; + int area_top = winrow; + int area_bot = winrow + height; + int area_left = wincol; + int area_right = wincol + width - leftoff; + + FOR_ALL_POPUPWINS(wp) + mark_overlapped_opacity_popup(wp, area_top, area_bot, area_left, + area_right, zindex); + FOR_ALL_POPUPWINS_IN_TAB(curtab, wp) + mark_overlapped_opacity_popup(wp, area_top, area_bot, area_left, + area_right, zindex); + + if (must_redraw < UPD_VALID) + must_redraw = UPD_VALID; +} + +/* + * Replace pum_bg_* cells [left, right) of screen row "r" with the underlying + * buffer text, so the pum's opacity padding shows the buffer through. Cells + * outside any window (or past the end of the buffer line) become spaces. + * + * Used after an opacity popup that overlapped pum_bg_* is dismissed: the + * stale popup content held in pum_bg_* would otherwise leak through the + * pum's opacity blend as a ghost. This walks the buffer line and writes + * the displayed character at each visual column directly into pum_bg_*. + * + * Limitations: handles plain text and tabs; folds, conceal, virtual text and + * other rendering features fall back to a space, which still beats showing a + * stale popup char. + */ + static void +refill_pum_bg_row_from_buffer(int r, int left, int right) +{ + int line_cp = r; + int col_cp = left; + win_T *wp; + linenr_T lnum; + int row_for_lnum; + int col_for_lnum = 0; + char_u *line; + char_u *p; + int win_text_col; + int screen_col; + int soff_base = (r - pum_bg_top) * pum_bg_cols; + int c; + + // Default: fill the range with spaces so trailing/empty cells render as + // plain pum bg through opacity. + for (c = left; c < right; ++c) + { + pum_bg_lines[soff_base + c] = ' '; + if (pum_bg_attrs != NULL) + pum_bg_attrs[soff_base + c] = 0; + if (enc_utf8 && pum_bg_linesUC != NULL) + pum_bg_linesUC[soff_base + c] = 0; + if (enc_utf8) + { + int k; + for (k = 0; k < MAX_MCO; ++k) + if (pum_bg_linesC[k] != NULL) + pum_bg_linesC[k][soff_base + c] = 0; + } + } + + wp = mouse_find_win(&line_cp, &col_cp, IGNORE_POPUP); + if (wp == NULL || line_cp < 0 || line_cp >= wp->w_height) + return; + if (wp->w_buffer == NULL || wp->w_buffer->b_ml.ml_mfp == NULL) + return; + + // Compute the buffer line for this screen row. + row_for_lnum = line_cp; + if (mouse_comp_pos(wp, &row_for_lnum, &col_for_lnum, &lnum, NULL)) + return; // past end of buffer + if (lnum < 1 || lnum > wp->w_buffer->b_ml.ml_line_count) + return; + + line = ml_get_buf(wp->w_buffer, lnum, FALSE); + if (line == NULL) + return; + + // Walk the buffer line and write each displayed cell into pum_bg_*. + // win_text_col is the screen column where the buffer text starts inside + // the window (after sign/number/fold columns and horizontal scroll). + win_text_col = wp->w_wincol + win_col_off(wp); + if (!wp->w_p_wrap) + win_text_col -= wp->w_leftcol; + screen_col = win_text_col; + p = line; + while (*p != NUL && screen_col < right) + { + int char_cells; + int byte_count; + int soff = soff_base + screen_col; + int in_range = (screen_col >= left && screen_col < right); + + if (*p == '\t') + { + int ts = (int)wp->w_buffer->b_p_ts; + char_cells = ts > 0 ? ts - ((screen_col - win_text_col) % ts) : 1; + byte_count = 1; + if (in_range) + { + pum_bg_lines[soff] = ' '; + if (enc_utf8 && pum_bg_linesUC != NULL) + pum_bg_linesUC[soff] = 0; + } + } + else if (has_mbyte) + { + char_cells = mb_ptr2cells(p); + byte_count = mb_ptr2len(p); + if (in_range) + { + pum_bg_lines[soff] = *p; + if (enc_utf8 && pum_bg_linesUC != NULL) + pum_bg_linesUC[soff] = (*p < 0x80) ? 0 : mb_ptr2char(p); + } + } + else + { + char_cells = 1; + byte_count = 1; + if (in_range) + { + pum_bg_lines[soff] = (*p < 0x20) ? ' ' : *p; + if (enc_utf8 && pum_bg_linesUC != NULL) + pum_bg_linesUC[soff] = 0; + } + } + if (in_range && pum_bg_attrs != NULL) + pum_bg_attrs[soff] = 0; + + // For wide chars / tabs the trailing cells are zeroed already (by the + // initial space fill we did above). Just skip past them. + p += byte_count; + screen_col += char_cells; + } +} + +/* + * Redraw what becomes exposed when an opacity popup moves, resizes or closes. + */ + static void +popup_redraw_exposed_area(popup_area_T *area) +{ + if (!area->active) + return; + + redraw_under_popup_area(area->winrow, area->wincol, area->height, + area->width, area->leftoff); + redraw_overlapped_opacity_popups(area->winrow, area->wincol, + area->height, area->width, area->leftoff, area->zindex); + + // If the closing/moving popup overlapped the pum's saved background, + // pum_bg_* still holds the dismissed popup's content. When the pum + // next blends opacity it would restore those stale chars at padding + // cells, leaving a ghost. + // + // We can't re-snapshot via update_screen from here: the surrounding + // update_screen has updating_screen set, so a nested call would no-op. + // Instead, replace the overlapping pum_bg_* cells with the actual + // underlying buffer text so the pum's opacity padding shows the buffer + // through, just as it would if the popup had never been there. + if (pum_bg_lines != NULL + && area->winrow < pum_bg_bot + && area->winrow + area->height > pum_bg_top) + { + int top = MAX(area->winrow, pum_bg_top); + int bot = MIN(area->winrow + area->height, pum_bg_bot); + int left = MAX(area->wincol, 0); + int right = MIN(area->wincol + area->width, pum_bg_cols); + int r; + + for (r = top; r < bot; ++r) + refill_pum_bg_row_from_buffer(r, left, right); + } +} + +/* + * Release saved screen data used for opacity padding redraw. + */ + static void +popup_free_saved_screen(popup_saved_screen_T *saved_screen) +{ + vim_free(saved_screen->lines); + vim_free(saved_screen->attrs); + vim_free(saved_screen->linesuc); + CLEAR_POINTER(saved_screen); +} + +/* + * Save the screen area that opacity padding may need to blend against. + * On entry "saved_screen" must be zero-initialised. When the snapshot is + * unavailable (no opacity popup, no padding, allocation failure) "lines" is + * left NULL and callers should fall back to plain screen_fill(). The caller + * must always release the snapshot with popup_free_saved_screen(). + */ + static void +popup_save_padding_screen(win_T *wp, popup_saved_screen_T *saved_screen) +{ + if (screen_opacity_popup == NULL + || (wp->w_popup_padding[0] == 0 && wp->w_popup_padding[1] == 0 + && wp->w_popup_padding[2] == 0 && wp->w_popup_padding[3] == 0)) + return; + + saved_screen->start_row = wp->w_winrow + wp->w_popup_border[0]; + saved_screen->start_col = wp->w_wincol + wp->w_popup_border[3]; + saved_screen->rows = wp->w_popup_padding[0] + wp->w_height + + wp->w_popup_padding[2]; + saved_screen->cols = wp->w_popup_padding[3] + wp->w_width + + wp->w_popup_padding[1]; + + // Include one column to the left to handle wide chars that overlap the + // padding boundary. + if (saved_screen->start_col > 0) + { + --saved_screen->start_col; + ++saved_screen->cols; + } + + saved_screen->lines = ALLOC_MULT(schar_T, + saved_screen->rows * saved_screen->cols); + saved_screen->attrs = ALLOC_MULT(int, + saved_screen->rows * saved_screen->cols); + if (enc_utf8) + saved_screen->linesuc = ALLOC_MULT(u8char_T, + saved_screen->rows * saved_screen->cols); + + if (saved_screen->lines == NULL || saved_screen->attrs == NULL) + return; + + for (int r = 0; r < saved_screen->rows; r++) + { + int screen_row = saved_screen->start_row + r; + + if (screen_row >= 0 && screen_row < screen_Rows) + for (int c = 0; c < saved_screen->cols; c++) + { + int screen_col = saved_screen->start_col + c; + + if (screen_col >= 0 && screen_col < screen_Columns) + { + int off = LineOffset[screen_row] + screen_col; + int save_off = r * saved_screen->cols + c; + + saved_screen->lines[save_off] = ScreenLines[off]; + saved_screen->attrs[save_off] = ScreenAttrs[off]; + if (enc_utf8 && saved_screen->linesuc != NULL) + saved_screen->linesuc[save_off] = ScreenLinesUC[off]; + } + } + } +} + /* * Force windows under a popup area to redraw. */ @@ -3472,13 +3924,7 @@ f_popup_move(typval_T *argvars, typval_T *rettv UNUSED) dict_T *dict; int id; win_T *wp; - int old_winrow; - int old_wincol; - int old_height; - int old_width; - int old_popup_height; - int old_popup_width; - int old_popup_leftoff; + popup_area_T old_area; if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL @@ -3494,14 +3940,7 @@ f_popup_move(typval_T *argvars, typval_T *rettv UNUSED) return; dict = argvars[1].vval.v_dict; - // Save old position for redrawing - old_winrow = wp->w_winrow; - old_wincol = wp->w_wincol; - old_height = wp->w_height; - old_width = wp->w_width; - old_popup_height = popup_height(wp); - old_popup_width = popup_width(wp); - old_popup_leftoff = wp->w_popup_leftoff; + popup_save_area(wp, &old_area); apply_move_options(wp, dict); @@ -3514,14 +3953,11 @@ f_popup_move(typval_T *argvars, typval_T *rettv UNUSED) // redrawing the affected lines in regular windows to clear the old // position. Transparent popups don't participate in popup_mask, so // we need to manually mark the old area's lines for redraw. - if (old_winrow != wp->w_winrow || old_wincol != wp->w_wincol - || old_height != wp->w_height || old_width != wp->w_width) + if (popup_area_changed(wp, &old_area)) { redraw_win_later(wp, UPD_NOT_VALID); - if ((wp->w_popup_flags & POPF_OPACITY) && wp->w_popup_blend > 0) - redraw_under_popup_area(old_winrow, old_wincol, - old_popup_height, old_popup_width, old_popup_leftoff); + popup_redraw_exposed_area(&old_area); } } @@ -3534,24 +3970,10 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED) dict_T *dict; int id; win_T *wp; - linenr_T old_firstline; -#ifdef FEAT_PROP_POPUP - int old_blend; - int old_popup_active; -#endif - int old_winrow; - int old_wincol; - int old_popup_height; - int old_popup_width; - int old_popup_leftoff; - int old_zindex; - int old_popup_flags; - char_u *old_scrollbar_highlight; - char_u *old_thumb_highlight; - char_u *old_border_highlight[4]; + popup_area_T old_area; + popup_style_snapshot_T old_style; int need_redraw = FALSE; int need_reposition = FALSE; - int i; if (in_vim9script() && (check_for_number_arg(argvars, 0) == FAIL @@ -3566,23 +3988,8 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED) if (check_for_nonnull_dict_arg(argvars, 1) == FAIL) return; dict = argvars[1].vval.v_dict; - old_firstline = wp->w_firstline; -#ifdef FEAT_PROP_POPUP - old_blend = wp->w_popup_blend; - old_popup_active = (wp->w_popup_flags & POPF_OPACITY) - && wp->w_popup_blend > 0; -#endif - old_winrow = wp->w_winrow; - old_wincol = wp->w_wincol; - old_popup_height = popup_height(wp); - old_popup_width = popup_width(wp); - old_popup_leftoff = wp->w_popup_leftoff; - old_zindex = wp->w_zindex; - old_popup_flags = wp->w_popup_flags; - old_scrollbar_highlight = wp->w_scrollbar_highlight; - old_thumb_highlight = wp->w_thumb_highlight; - for (i = 0; i < 4; i++) - old_border_highlight[i] = wp->w_border_highlight[i]; + popup_save_area(wp, &old_area); + popup_save_style(wp, &old_style); (void)apply_options(wp, dict, FALSE); @@ -3593,28 +4000,16 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED) wp->w_topline = wp->w_firstline; // Check if visual options changed and redraw if needed - if (old_firstline != wp->w_firstline) - need_redraw = TRUE; - if (old_zindex != wp->w_zindex) + if (old_style.zindex != wp->w_zindex) { need_redraw = TRUE; need_reposition = TRUE; } - if (old_popup_flags != wp->w_popup_flags) - { + else if (popup_style_changed(wp, &old_style)) need_redraw = TRUE; + + if (old_style.flags != wp->w_popup_flags) need_reposition = TRUE; - } - if (old_scrollbar_highlight != wp->w_scrollbar_highlight) - need_redraw = TRUE; - if (old_thumb_highlight != wp->w_thumb_highlight) - need_redraw = TRUE; - for (i = 0; i < 4; i++) - if (old_border_highlight[i] != wp->w_border_highlight[i]) - { - need_redraw = TRUE; - break; - } if (need_reposition) { @@ -3633,16 +4028,14 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED) must_redraw = UPD_VALID; } -#ifdef FEAT_PROP_POPUP // Force redraw if opacity value changed - if (old_blend != wp->w_popup_blend) + if (old_style.blend != wp->w_popup_blend) { redraw_win_later(wp, UPD_NOT_VALID); // Also redraw windows below the popup redraw_all_later(UPD_NOT_VALID); popup_mask_refresh = TRUE; } -#endif // Always recalculate popup position/size: other options like border, // close, padding may have changed without affecting w_popup_flags. @@ -3650,16 +4043,8 @@ f_popup_setoptions(typval_T *argvars, typval_T *rettv UNUSED) // position or size actually changed. popup_adjust_position(wp); -#ifdef FEAT_PROP_POPUP - if (old_popup_active - && (old_winrow != wp->w_winrow - || old_wincol != wp->w_wincol - || old_popup_height != popup_height(wp) - || old_popup_width != popup_width(wp) - || old_popup_leftoff != wp->w_popup_leftoff)) - redraw_under_popup_area(old_winrow, old_wincol, - old_popup_height, old_popup_width, old_popup_leftoff); -#endif + if (popup_area_changed(wp, &old_area)) + popup_redraw_exposed_area(&old_area); } /* @@ -4881,26 +5266,20 @@ static void draw_opacity_padding_cell( int row, int col, - schar_T *saved_screenlines, - int *saved_screenattrs, - u8char_T *saved_screenlinesuc, - int save_start_row, - int save_start_col, - int save_rows, - int save_cols, + popup_saved_screen_T *saved_screen, int pad_start_col, int pad_end_col) { int off = LineOffset[row] + col; - int r = row - save_start_row; - int c = col - save_start_col; + int r = row - saved_screen->start_row; + int c = col - saved_screen->start_col; - if (r >= 0 && r < save_rows && c >= 0 && c < save_cols) + if (r >= 0 && r < saved_screen->rows && c >= 0 && c < saved_screen->cols) { - int save_off = r * save_cols + c; + int save_off = r * saved_screen->cols + c; // If this is the second cell of a wide background character, blend // the wide character instead of overwriting it. - if (enc_utf8 && saved_screenlinesuc != NULL) + if (enc_utf8 && saved_screen->linesuc != NULL) { int base_col = col - 1; int base_off = off - 1; @@ -4919,9 +5298,9 @@ draw_opacity_padding_cell( } if (!wide_prev && save_off > 0) { - if (saved_screenlinesuc[save_off - 1] != 0 - && utf_char2cells(saved_screenlinesuc[save_off - 1]) == 2 - && saved_screenlines[save_off] == 0) + if (saved_screen->linesuc[save_off - 1] != 0 + && utf_char2cells(saved_screen->linesuc[save_off - 1]) == 2 + && saved_screen->lines[save_off] == 0) wide_prev = TRUE; } @@ -4940,7 +5319,7 @@ draw_opacity_padding_cell( // covered by the popup background. ScreenLines[base_off] = ' '; ScreenLinesUC[base_off] = 0; - ScreenAttrs[base_off] = saved_screenattrs[base_save_off]; + ScreenAttrs[base_off] = saved_screen->attrs[base_save_off]; popup_set_base_screen_cell(row, base_col, ScreenLines[base_off], ScreenAttrs[base_off], @@ -4951,7 +5330,7 @@ draw_opacity_padding_cell( // Use left half's attr since the right half of a // wide char may have an unreliable attr value. ScreenLines[off] = ' '; - ScreenAttrs[off] = saved_screenattrs[base_save_off]; + ScreenAttrs[off] = saved_screen->attrs[base_save_off]; if (enc_utf8) ScreenLinesUC[off] = 0; int popup_attr_val = @@ -4970,9 +5349,9 @@ draw_opacity_padding_cell( // but the saved data has a wide char. Restore it // spanning both the content cell and padding cell. if (base_save_off >= 0 - && saved_screenlinesuc[base_save_off] != 0 + && saved_screen->linesuc[base_save_off] != 0 && utf_char2cells( - saved_screenlinesuc[base_save_off]) == 2 + saved_screen->linesuc[base_save_off]) == 2 && ScreenLines[base_off] == ' ' && ScreenLinesUC[base_off] == 0) { @@ -4982,11 +5361,11 @@ draw_opacity_padding_cell( screen_opacity_popup->w_popup_blend; ScreenLines[base_off] = - saved_screenlines[base_save_off]; + saved_screen->lines[base_save_off]; ScreenLinesUC[base_off] = - saved_screenlinesuc[base_save_off]; + saved_screen->linesuc[base_save_off]; ScreenAttrs[base_off] = - saved_screenattrs[base_save_off]; + saved_screen->attrs[base_save_off]; ScreenAttrs[base_off] = hl_blend_attr( ScreenAttrs[base_off], popup_attr_val, blend, TRUE); @@ -5011,7 +5390,7 @@ draw_opacity_padding_cell( // Use left half's attr since the right half of a // wide char may have an unreliable attr value. ScreenLines[off] = ' '; - ScreenAttrs[off] = saved_screenattrs[base_save_off]; + ScreenAttrs[off] = saved_screen->attrs[base_save_off]; if (enc_utf8 && ScreenLinesUC != NULL) ScreenLinesUC[off] = 0; int popup_attr_val = get_win_attr(screen_opacity_popup); @@ -5027,12 +5406,12 @@ draw_opacity_padding_cell( // Base cell is inside the saved area, redraw the wide char. if (save_off > 0) { - ScreenLines[base_off] = saved_screenlines[base_save_off]; - ScreenAttrs[base_off] = saved_screenattrs[base_save_off]; - ScreenLines[off] = saved_screenlines[save_off]; - ScreenAttrs[off] = saved_screenattrs[save_off]; - ScreenLinesUC[base_off] = saved_screenlinesuc[base_save_off]; - ScreenLinesUC[off] = saved_screenlinesuc[save_off]; + ScreenLines[base_off] = saved_screen->lines[base_save_off]; + ScreenAttrs[base_off] = saved_screen->attrs[base_save_off]; + ScreenLines[off] = saved_screen->lines[save_off]; + ScreenAttrs[off] = saved_screen->attrs[save_off]; + ScreenLinesUC[base_off] = saved_screen->linesuc[base_save_off]; + ScreenLinesUC[off] = saved_screen->linesuc[save_off]; int popup_attr_val = get_win_attr(screen_opacity_popup); int blend = screen_opacity_popup->w_popup_blend; @@ -5048,10 +5427,10 @@ draw_opacity_padding_cell( return; } } - ScreenLines[off] = saved_screenlines[save_off]; - ScreenAttrs[off] = saved_screenattrs[save_off]; - if (enc_utf8 && saved_screenlinesuc != NULL) - ScreenLinesUC[off] = saved_screenlinesuc[save_off]; + ScreenLines[off] = saved_screen->lines[save_off]; + ScreenAttrs[off] = saved_screen->attrs[save_off]; + if (enc_utf8 && saved_screen->linesuc != NULL) + ScreenLinesUC[off] = saved_screen->linesuc[save_off]; // If the saved character is wide and would extend past the padding // area into the content area, replace with a space to avoid @@ -5083,19 +5462,11 @@ fill_opacity_padding( int end_row, int start_col, int end_col, - schar_T *saved_screenlines, - int *saved_screenattrs, - u8char_T *saved_screenlinesuc, - int save_start_row, - int save_start_col, - int save_rows, - int save_cols) + popup_saved_screen_T *saved_screen) { for (int pad_row = start_row; pad_row < end_row; pad_row++) for (int pad_col = start_col; pad_col < end_col; pad_col++) - draw_opacity_padding_cell(pad_row, pad_col, - saved_screenlines, saved_screenattrs, saved_screenlinesuc, - save_start_row, save_start_col, save_rows, save_cols, + draw_opacity_padding_cell(pad_row, pad_col, saved_screen, start_col, end_col); } @@ -5220,65 +5591,11 @@ update_popups(void (*win_update)(win_T *wp)) } #endif - // Save background ScreenLines for padding opacity. - // We need to save it before win_update() overwrites it. - schar_T *saved_screenlines = NULL; - int *saved_screenattrs = NULL; - u8char_T *saved_screenlinesuc = NULL; - int save_start_row = 0; - int save_start_col = 0; - int save_rows = 0; - int save_cols = 0; - - if (screen_opacity_popup != NULL - && (wp->w_popup_padding[0] > 0 || wp->w_popup_padding[1] > 0 - || wp->w_popup_padding[2] > 0 || wp->w_popup_padding[3] > 0)) - { - // Calculate the area to save (all padding regions including top/bottom) - save_start_row = wp->w_winrow + wp->w_popup_border[0]; - save_start_col = wp->w_wincol + wp->w_popup_border[3]; - save_rows = wp->w_popup_padding[0] + wp->w_height + wp->w_popup_padding[2]; - save_cols = wp->w_popup_padding[3] + wp->w_width + wp->w_popup_padding[1]; - - // Include one column to the left to handle wide chars that overlap - // the padding boundary. - if (save_start_col > 0) - { - --save_start_col; - ++save_cols; - } - - // Allocate buffers - saved_screenlines = ALLOC_MULT(schar_T, save_rows * save_cols); - saved_screenattrs = ALLOC_MULT(int, save_rows * save_cols); - if (enc_utf8) - saved_screenlinesuc = ALLOC_MULT(u8char_T, save_rows * save_cols); + // Save background ScreenLines for padding opacity before win_update() + // overwrites them. + popup_saved_screen_T saved_screen = { 0 }; - // Save the background - if (saved_screenlines != NULL && saved_screenattrs != NULL) - { - for (int r = 0; r < save_rows; r++) - { - int screen_row = save_start_row + r; - if (screen_row >= 0 && screen_row < screen_Rows) - { - for (int c = 0; c < save_cols; c++) - { - int screen_col = save_start_col + c; - if (screen_col >= 0 && screen_col < screen_Columns) - { - int off = LineOffset[screen_row] + screen_col; - int save_off = r * save_cols + c; - saved_screenlines[save_off] = ScreenLines[off]; - saved_screenattrs[save_off] = ScreenAttrs[off]; - if (enc_utf8 && saved_screenlinesuc != NULL) - saved_screenlinesuc[save_off] = ScreenLinesUC[off]; - } - } - } - } - } - } + popup_save_padding_screen(wp, &saved_screen); // Set flags in popup_transparent[] for masked cells. update_popup_transparent(wp, 1); @@ -5457,19 +5774,14 @@ update_popups(void (*win_update)(win_T *wp)) if (title_len > 0 && row == wp->w_winrow) { // top padding and no border; do not draw over the title - if (screen_opacity_popup != NULL && saved_screenlines != NULL) + if (screen_opacity_popup != NULL && saved_screen.lines != NULL) { // Left of title fill_opacity_padding(row, row + 1, padcol, title_wincol, - saved_screenlines, saved_screenattrs, - saved_screenlinesuc, save_start_row, save_start_col, - save_rows, save_cols); + &saved_screen); // Right of title fill_opacity_padding(row, row + 1, - title_wincol + title_len, padendcol, - saved_screenlines, saved_screenattrs, - saved_screenlinesuc, save_start_row, save_start_col, - save_rows, save_cols); + title_wincol + title_len, padendcol, &saved_screen); } else { @@ -5482,12 +5794,10 @@ update_popups(void (*win_update)(win_T *wp)) top_padding -= 1; } // Draw remaining top padding rows - if (screen_opacity_popup != NULL && saved_screenlines != NULL) + if (screen_opacity_popup != NULL && saved_screen.lines != NULL) { fill_opacity_padding(row, row + top_padding, padcol, padendcol, - saved_screenlines, saved_screenattrs, - saved_screenlinesuc, save_start_row, save_start_col, - save_rows, save_cols); + &saved_screen); } else { @@ -5565,11 +5875,9 @@ update_popups(void (*win_update)(win_T *wp)) } if (pad_left > 0) { - if (screen_opacity_popup != NULL && saved_screenlines != NULL) + if (screen_opacity_popup != NULL && saved_screen.lines != NULL) fill_opacity_padding(row, row + 1, col, col + pad_left, - saved_screenlines, saved_screenattrs, - saved_screenlinesuc, save_start_row, - save_start_col, save_rows, save_cols); + &saved_screen); else screen_fill(row, row + 1, col, col + pad_left, ' ', ' ', popup_attr); @@ -5603,11 +5911,9 @@ update_popups(void (*win_update)(win_T *wp)) + wp->w_popup_padding[3] + wp->w_width + wp->w_leftcol; int pad_col_end = pad_col_start + wp->w_popup_padding[1]; - if (screen_opacity_popup != NULL && saved_screenlines != NULL) + if (screen_opacity_popup != NULL && saved_screen.lines != NULL) fill_opacity_padding(row, row + 1, pad_col_start, pad_col_end, - saved_screenlines, saved_screenattrs, - saved_screenlinesuc, save_start_row, save_start_col, - save_rows, save_cols); + &saved_screen); else screen_fill(row, row + 1, pad_col_start, pad_col_end, ' ', ' ', popup_attr); @@ -5631,11 +5937,9 @@ update_popups(void (*win_update)(win_T *wp)) // bottom padding row = wp->w_winrow + wp->w_popup_border[0] + wp->w_popup_padding[0] + wp->w_height; - if (screen_opacity_popup != NULL && saved_screenlines != NULL) + if (screen_opacity_popup != NULL && saved_screen.lines != NULL) fill_opacity_padding(row, row + wp->w_popup_padding[2], - padcol, padendcol, saved_screenlines, saved_screenattrs, - saved_screenlinesuc, save_start_row, save_start_col, - save_rows, save_cols); + padcol, padendcol, &saved_screen); else screen_fill(row, row + wp->w_popup_padding[2], padcol, padendcol, ' ', ' ', popup_attr); @@ -5677,13 +5981,7 @@ update_popups(void (*win_update)(win_T *wp)) update_popup_transparent(wp, 0); - // Free saved background data - if (saved_screenlines != NULL) - vim_free(saved_screenlines); - if (saved_screenattrs != NULL) - vim_free(saved_screenattrs); - if (saved_screenlinesuc != NULL) - vim_free(saved_screenlinesuc); + popup_free_saved_screen(&saved_screen); // Clear popup with opacity context. screen_opacity_popup = NULL; diff --git a/src/testdir/dumps/Test_popup_opacity_move_after_close.dump b/src/testdir/dumps/Test_popup_opacity_move_after_close.dump new file mode 100644 index 0000000000..8988d85a13 --- /dev/null +++ b/src/testdir/dumps/Test_popup_opacity_move_after_close.dump @@ -0,0 +1,8 @@ +>a+0&#ffffff0|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y +|z| @23 +|a|b|c|d|e|f|g|h|i|j|k|╔+0#0000001#875f87255|═@4|╗|s+0#0000000#ffffff0|t|u|v|w|x|y +|z| @9|║+0#0000001#875f87255|A| +0#ffd7ff255&@3|║+0#0000001&| +0#0000000#ffffff0@6 +|a|b|c|d|e|f|g|h|i|j|k|╚+0#0000001#875f87255|═@4|╝|s+0#0000000#ffffff0|t|u|v|w|x|y +|z| @23 +|@+0#4040ff13&@2| @21 +| +0#0000000&@12|1|,|1| @4|T|o|p| diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim index 2ceaec945a..adead6bac4 100644 --- a/src/testdir/test_popup.vim +++ b/src/testdir/test_popup.vim @@ -2600,4 +2600,32 @@ func Test_pumopt_opacity_100() call StopVimInTerminal(buf) endfunc +" When an opacity popup above another one is closed, moving the lower popup +" into the old area must blend against the restored background, not the stale +" contents of the closed popup. +func Test_popup_opacity_move_after_close() + CheckScreendump + let lines =<< trim END + call setline(1, repeat(['abcdefghijklmnopqrstuvwxyz'], 8)) + let g:popup_a = popup_create('A', #{ + \ line: 3, col: 5, padding: [0, 4, 0, 0], border: [], + \ opacity: 50, zindex: 10 + \ }) + let g:popup_b = popup_create('BBBB', #{ + \ line: 3, col: 12, border: [], opacity: 50, zindex: 20 + \ }) + func MovePopupA(timer) abort + call popup_close(g:popup_b) + call popup_move(g:popup_a, #{col: 12}) + endfunc + call timer_start(100, 'MovePopupA') + END + + call writefile(lines, 'Xpopupopacitymove', 'D') + let buf = RunVimInTerminal('-S Xpopupopacitymove', #{rows: 8, cols: 25}) + call TermWait(buf, 150) + call VerifyScreenDump(buf, 'Test_popup_opacity_move_after_close', {}) + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 102a0d4eb4..705339b344 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 466, /**/ 465, /**/ From 5ef1eec5c5a351d84ce1434c250aad255da7c4f4 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Sun, 10 May 2026 18:14:01 +0000 Subject: [PATCH 204/545] patch 9.2.0467: multi-line statusline loses highlighting attributes Problem: In a multi-line statusline (and 'tabpanel'), %#XX# / %N* set on one row do not persist on subsequent rows. build_stl_str_hl_local() rebuilds stl_items[] from scratch on every line break ("%@" or "\n"), so the highlight is reset at each row boundary even though within a row it stays until %* (or another %# / %*). Solution: Carry the last Highlight item's stl_minwid across line breaks via a new in/out int* parameter "carry_hl". At the start of each row, pre-insert a Highlight item from the carried value so the row begins under the same highlight; before returning, update the carried value with the row's final Highlight item. Apply the same carry to the tabpanel rendering loop (Hirohito Higashi). related: #19123 closes: #20180 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/buffer.c | 46 +++++++++++++++---- src/proto/buffer.pro | 4 +- src/screen.c | 4 +- src/tabpanel.c | 5 +- .../Test_multistatusline_carry_hl_01.dump | 9 ++++ .../dumps/Test_tabpanel_carry_hl_01.dump | 9 ++++ src/testdir/test_statuslineopt.vim | 29 ++++++++++++ src/testdir/test_tabpanel.vim | 30 ++++++++++++ src/version.c | 2 + 9 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 src/testdir/dumps/Test_multistatusline_carry_hl_01.dump create mode 100644 src/testdir/dumps/Test_tabpanel_carry_hl_01.dump diff --git a/src/buffer.c b/src/buffer.c index 44e504c537..43cba3ad8c 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -50,7 +50,7 @@ static int build_stl_str_hl_local(stl_mode_T mode, win_T *wp, char_u *out, size_t outlen, char_u **fmt_arg, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab, - stl_clickrec_T **clicktab, int *lbreaks); + stl_clickrec_T **clicktab, int *lbreaks, int *carry_hl); #endif static int append_arg_number(win_T *wp, char_u *buf, size_t buflen, int add_file); static void free_buffer(buf_T *); @@ -4393,7 +4393,7 @@ build_stl_str_hl( { return build_stl_str_hl_local(STL_MODE_SINGLE, wp, out, outlen, &fmt, opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, clicktab, - NULL); + NULL, NULL); } int @@ -4408,11 +4408,13 @@ build_stl_str_hl_mline( int maxwidth, stl_hlrec_T **hltab, // return: HL attributes (can be NULL) stl_hlrec_T **tabtab, // return: tab page nrs (can be NULL) - stl_clickrec_T **clicktab) // return: click func regions (can be NULL) + stl_clickrec_T **clicktab, // return: click func regions (can be NULL) + int *carry_hl) // (in/out) %# / %* highlight carried across + // line breaks (can be NULL) { return build_stl_str_hl_local(STL_MODE_MULTI, wp, out, outlen, fmt, opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, clicktab, - NULL); + NULL, carry_hl); } # ifdef ENABLE_STL_MODE_MULTI_NL @@ -4428,11 +4430,13 @@ build_stl_str_hl_mline_nl( int maxwidth, stl_hlrec_T **hltab, // return: HL attributes (can be NULL) stl_hlrec_T **tabtab, // return: tab page nrs (can be NULL) - stl_clickrec_T **clicktab) // return: click func regions (can be NULL) + stl_clickrec_T **clicktab, // return: click func regions (can be NULL) + int *carry_hl) // (in/out) %# / %* highlight carried across + // line breaks (can be NULL) { return build_stl_str_hl_local(STL_MODE_MULTI_NL, wp, out, outlen, fmt, opt_name, opt_scope, fillchar, maxwidth, hltab, tabtab, clicktab, - NULL); + NULL, carry_hl); } # endif @@ -4453,7 +4457,8 @@ get_stl_rendered_height( ++emsg_off; (void)build_stl_str_hl_local(STL_MODE_GET_RENDERED_HEIGHT, wp, buf, sizeof(buf), &fmt, - opt_name, opt_scope, 0, 0, NULL, NULL, NULL, &rendered_height); + opt_name, opt_scope, 0, 0, NULL, NULL, NULL, &rendered_height, + NULL); --emsg_off; return rendered_height; } @@ -4489,7 +4494,9 @@ build_stl_str_hl_local( stl_hlrec_T **hltab, // return: HL attributes (can be NULL) stl_hlrec_T **tabtab, // return: tab page nrs (can be NULL) stl_clickrec_T **clicktab, // return: click func regions (can be NULL) - int *rendered_height) // return: stl rendered height (can be NULL) + int *rendered_height, // return: stl rendered height (can be NULL) + int *carry_hl) // (in/out) %# / %* highlight carried across + // line breaks (can be NULL) { linenr_T lnum; colnr_T len; @@ -4614,6 +4621,18 @@ build_stl_str_hl_local( # endif p = out; curitem = 0; + + // Pre-insert a Highlight item from carry_hl so that %# / %* set on a + // previous multi-line statusline row continues to apply on this row. + if (carry_hl != NULL && *carry_hl != 0) + { + stl_items[curitem].stl_type = Highlight; + stl_items[curitem].stl_start = p; + stl_items[curitem].stl_minwid = *carry_hl; + stl_items[curitem].stl_clickfunc = NULL; + curitem++; + } + prevchar_isflag = TRUE; prevchar_isitem = FALSE; for (s = usefmt; *s != NUL; ) @@ -5446,6 +5465,17 @@ build_stl_str_hl_local( outputlen = (size_t)(p - out); itemcnt = curitem; + // Remember the most recent %# / %* highlight so the next row of a + // multi-line statusline can resume it. + if (carry_hl != NULL) + { + int last_hl = 0; + for (l = 0; l < itemcnt; l++) + if (stl_items[l].stl_type == Highlight) + last_hl = stl_items[l].stl_minwid; + *carry_hl = last_hl; + } + if (mode == STL_MODE_MULTI # ifdef ENABLE_STL_MODE_MULTI_NL || mode == STL_MODE_MULTI_NL diff --git a/src/proto/buffer.pro b/src/proto/buffer.pro index 13c273d6aa..7c29256426 100644 --- a/src/proto/buffer.pro +++ b/src/proto/buffer.pro @@ -50,8 +50,8 @@ void maketitle(void); void resettitle(void); void free_titles(void); int build_stl_str_hl(win_T *wp, char_u *out, size_t outlen, char_u *fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab, stl_clickrec_T **clicktab); -int build_stl_str_hl_mline(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab, stl_clickrec_T **clicktab); -int build_stl_str_hl_mline_nl(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab, stl_clickrec_T **clicktab); +int build_stl_str_hl_mline(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab, stl_clickrec_T **clicktab, int *carry_hl); +int build_stl_str_hl_mline_nl(win_T *wp, char_u *out, size_t outlen, char_u **fmt, char_u *opt_name, int opt_scope, int fillchar, int maxwidth, stl_hlrec_T **hltab, stl_hlrec_T **tabtab, stl_clickrec_T **clicktab, int *carry_hl); int get_stl_rendered_height(win_T *wp, char_u *fmt, char_u *opt_name, int opt_scope); int get_rel_pos(win_T *wp, char_u *buf, int buflen); char_u *fix_fname(char_u *fname); diff --git a/src/screen.c b/src/screen.c index 57b041d40d..9c76b8763b 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1479,6 +1479,7 @@ win_redr_custom( *out_count = 0; } + int carry_hl = 0; for (int i = 0; i < stlh_cnt; i++) { col = col_save; @@ -1487,7 +1488,8 @@ win_redr_custom( &stl_tmp, opt_name, opt_scope, fillchar, maxwidth, &hltab, &tabtab, - &clicktab); + &clicktab, + &carry_hl); // Make all characters printable. p = transstr(buf); diff --git a/src/tabpanel.c b/src/tabpanel.c index 1e833c7b65..f7889a5e59 100644 --- a/src/tabpanel.c +++ b/src/tabpanel.c @@ -688,6 +688,8 @@ do_by_tplmode( if (usefmt != NULL && *usefmt != NUL) { + int carry_hl = 0; + while (*usefmt != NUL) { char_u buf[IOSIZE]; @@ -708,7 +710,8 @@ do_by_tplmode( (args.cwp, buf, sizeof(buf), &usefmt, opt_name, opt_scope, TPL_FILLCHAR, args.col_end - args.col_start, &hltab, &tabtab, - tplmode == TPLMODE_REDRAW ? &clicktab : NULL); + tplmode == TPLMODE_REDRAW ? &clicktab : NULL, + &carry_hl); args.prow = &row; args.pcol = &col; diff --git a/src/testdir/dumps/Test_multistatusline_carry_hl_01.dump b/src/testdir/dumps/Test_multistatusline_carry_hl_01.dump new file mode 100644 index 0000000000..724d844bd6 --- /dev/null +++ b/src/testdir/dumps/Test_multistatusline_carry_hl_01.dump @@ -0,0 +1,9 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|L+3#0000000&|1|A| @68|L+0&#ffff4012|1|B +|L|2| |c|a|r@1|i|e|d| |S|e|a|r|c|h| @57 +|L+3&#ffffff0|3| |r|e|s|e|t| @66 +|L+0#ffff4012#4040ff13|4| |u|s|e|r|2| @66 +|L|5| |c|a|r@1|i|e|d| |u|s|e|r|2| @58 +|L+3#0000000#ffffff0|6| |r|e|s|e|t| @66 +| +0&&@74 diff --git a/src/testdir/dumps/Test_tabpanel_carry_hl_01.dump b/src/testdir/dumps/Test_tabpanel_carry_hl_01.dump new file mode 100644 index 0000000000..87a130f293 --- /dev/null +++ b/src/testdir/dumps/Test_tabpanel_carry_hl_01.dump @@ -0,0 +1,9 @@ +|L+2&#ffffff0|1|A| @16> +0&&@39 +|L+0&#ffff4012|1|B| @16|~+0#4040ff13#ffffff0| @38 +|L+0#0000000#ffff4012|2| |c|a|r@1|i|e|d| |S|e|a|r|c|h| @2|~+0#4040ff13#ffffff0| @38 +|L+2#0000000&|3| |r|e|s|e|t| @11|~+0#4040ff13&| @38 +|L+0#ffff4012#4040ff13|4| |u|s|e|r|2| @11|~+0#4040ff13#ffffff0| @38 +|L+0#ffff4012#4040ff13|5| |c|a|r@1|i|e|d| @9|~+0#4040ff13#ffffff0| @38 +|L+2#0000000&|6| |r|e|s|e|t| @11|~+0#4040ff13&| @38 +| +1#0000000&@19|~+0#4040ff13&| @38 +| +1#0000000&@19| +0&&@21|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/test_statuslineopt.vim b/src/testdir/test_statuslineopt.vim index 6454dbff87..653529e356 100644 --- a/src/testdir/test_statuslineopt.vim +++ b/src/testdir/test_statuslineopt.vim @@ -235,6 +235,35 @@ func Test_multistatusline_highlight() call StopVimInTerminal(buf) endfunc +func Test_multistatusline_carry_hl() + CheckScreendump + + " %#XX# / %N* set on one row should persist on subsequent rows until %* + " (or another %# / %*) changes it. + let lines =<< trim END + func MyStatusLine() + return 'L1A%=%#Search#L1B%@' + \ .. 'L2 carried Search%@' + \ .. '%*L3 reset%@' + \ .. '%2*L4 user2%@' + \ .. 'L5 carried user2%@' + \ .. '%*L6 reset' + endfunc + + hi User2 ctermfg=Yellow ctermbg=Blue + set laststatus=2 + set statuslineopt=maxheight:6 + set statusline=%!MyStatusLine() + END + call writefile(lines, 'XTest_multistatusline_carry_hl', 'D') + + let buf = g:RunVimInTerminal('-S XTest_multistatusline_carry_hl', {'rows': 9}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_multistatusline_carry_hl_01', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_statuslineopt_default_stl() CheckScreendump diff --git a/src/testdir/test_tabpanel.vim b/src/testdir/test_tabpanel.vim index b5bf678a10..3b5fe390f4 100644 --- a/src/testdir/test_tabpanel.vim +++ b/src/testdir/test_tabpanel.vim @@ -1129,6 +1129,36 @@ func Test_tabpanel_empty() set tabpanel& endfunc +func Test_tabpanel_carry_hl() + CheckScreendump + + " %#XX# / %N* set on one row of a tabpanel should persist on subsequent + " rows until %* (or another %# / %*) changes it. Both "%@" and "\n" are + " accepted as line breaks in 'tabpanel'. + let lines =<< trim END + func MyTabPanel() + return "L1A\n" + \ .. "%#Search#L1B\n" + \ .. "L2 carried Search\n" + \ .. "%*L3 reset\n" + \ .. "%2*L4 user2\n" + \ .. "L5 carried\n" + \ .. "%*L6 reset" + endfunc + + hi User2 ctermfg=Yellow ctermbg=Blue + set showtabpanel=2 + set tabpanelopt=columns:20 + set tabpanel=%!MyTabPanel() + END + call writefile(lines, 'XTest_tabpanel_carry_hl', 'D') + + let buf = RunVimInTerminal('-S XTest_tabpanel_carry_hl', {'rows': 9, 'cols': 60}) + call VerifyScreenDump(buf, 'Test_tabpanel_carry_hl_01', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_tabpanel_getinfo_and_scroll() CheckScreendump diff --git a/src/version.c b/src/version.c index 705339b344..a5ba4c7960 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 467, /**/ 466, /**/ From ef1ecc3b61fd9f462ae3571ed98dd09457659e63 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 10 May 2026 18:20:01 +0000 Subject: [PATCH 205/545] patch 9.2.0468: popups: not correctly updated from a CmdlineChanged autocommand Problem: popup_show() from a CmdlineChanged autocommand doesn't update the screen (Mao-Yining) Solution: Refresh the screen when popups need redraw (Yasuhiro Matsumoto). popup_settext()/popup_show() called from a CmdlineChanged autocommand did not refresh the screen because cmdline mode normally skips update_screen(), so async info-popup updates only became visible after a manual :redraw. Refresh the screen when popups need redrawing right after the autocommand. fixes: #20175 closes: #20179 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/ex_getln.c | 10 +++++ .../Test_wildmenu_pum_info_async_update.dump | 14 +++++++ src/testdir/test_cmdline.vim | 42 +++++++++++++++++++ src/version.c | 2 + 4 files changed, 68 insertions(+) create mode 100644 src/testdir/dumps/Test_wildmenu_pum_info_async_update.dump diff --git a/src/ex_getln.c b/src/ex_getln.c index e5a9ce776f..7e26d6cdb4 100644 --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -2696,7 +2696,17 @@ getcmdline_int( && (ccline.cmdpos != prev_cmdpos || (prev_cmdbuff != NULL && STRCMP(prev_cmdbuff, ccline.cmdbuff) != 0))) + { trigger_cmd_autocmd(cmdline_type, EVENT_CMDLINECHANGED); +#ifdef FEAT_PROP_POPUP + // Show popup updates from the autocmd without a manual :redraw. + if (popup_need_redraw()) + { + update_screen(0); + redrawcmd(); + } +#endif + } // Trigger CursorMovedC autocommands. if (ccline.cmdpos != prev_cmdpos) diff --git a/src/testdir/dumps/Test_wildmenu_pum_info_async_update.dump b/src/testdir/dumps/Test_wildmenu_pum_info_async_update.dump new file mode 100644 index 0000000000..249f509e23 --- /dev/null +++ b/src/testdir/dumps/Test_wildmenu_pum_info_async_update.dump @@ -0,0 +1,14 @@ +| +0&#ffffff0@49 +|~+0#4040ff13&| @48 +|~| @48 +|~| @48 +|~| @48 +|~| @48 +|~| @48 +|~| @48 +|~| @48 +|~| @48 +|~| @19|╔+0#0000001#e0e0e08|═@5|X| +0#4040ff13#ffffff0@20 +|~| @3| +0#0000001#e0e0e08|1| @13|║| |d|o|n|e| |║| +0#4040ff13#ffffff0@20 +|~| @3| +0#0000001#ffd7ff255|2| @13|╚+0&#e0e0e08|═@5|⇲| +0#4040ff13#ffffff0@20 +|:+0#0000000&|T|e|s|t| |1> @42 diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index c10cff90bd..7c1365b6a9 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -3190,6 +3190,48 @@ func Test_wildmenu_pum() call StopVimInTerminal(buf) endfunc +" Test that popup_settext() from a CmdlineChanged autocmd updates the +" cmdline-mode info popup without needing an explicit :redraw (#20175). +func Test_wildmenu_pum_info_async_update() + CheckScreendump + CheckRunVimInTerminal + + let lines =<< trim END + vim9script + set completeopt=menuone,popup + set wildmenu wildoptions=pum wildcharm= + augroup Test + au! + au CmdlineChanged : UpdateInfo() + augroup END + + def UpdateInfo() + if getcmdcompltype() != 'customlist,Complete' + return + endif + var id = popup_findinfo() + if id != 0 + id->popup_settext('done') + popup_show(id) + endif + enddef + + def Complete(_, _, _): list> + return [{word: '1', info: 'lazy'}, {word: '2', info: 'lazy'}] + enddef + + command! -nargs=1 -complete=customlist,Complete Test echo + END + call writefile(lines, 'XtestWildmenuInfoAsync', 'D') + let buf = RunVimInTerminal('-S XtestWildmenuInfoAsync', #{rows: 14, cols: 50}) + call term_sendkeys(buf, ":Test \") + call TermWait(buf, 200) + call VerifyScreenDump(buf, 'Test_wildmenu_pum_info_async_update', {}) + + call term_sendkeys(buf, "\") + call StopVimInTerminal(buf) +endfunc + " Test for wildmenumode() with the cmdline popup menu func Test_wildmenumode_with_pum() set wildmenu diff --git a/src/version.c b/src/version.c index a5ba4c7960..f1e40ddb20 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 468, /**/ 467, /**/ From e3d9929109dadef35624ea7a68a6dbf0ca013f46 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 10 May 2026 18:53:57 +0000 Subject: [PATCH 206/545] patch 9.2.0469: popup: textprop-anchored popups bleed past host window edges Problem: A popup anchored to a text property in a split window is positioned relative to the screen and may extend into adjacent splits or off-screen regions. There is no way to confine the popup to the window that contains the textprop. Solution: Add the "clipwindow" popup option to allow clipping the text property popup to the host window (Yasuhiro Matsumoto). Adds a "clipwindow" boolean option to popup_create()/popup_setoptions(). When set on a textprop-anchored popup, the popup's drawn extent is confined to its host (textprop) window's content rectangle so the popup no longer bleeds across a horizontal split's statusline (top/bottom) or a vsplit's separator (right) into another window. The popup keeps its full logical size and position; only the rows or columns that fall outside the host window's content area are skipped during drawing, so a popup that scrolls toward the host's edge looks visually "cut off" without its borders being relocated. popup_getoptions and popup_getpos continue to report the unclipped geometry. Implementation: - w_popup_topoff / w_popup_bottomoff record how many rows of the popup fall outside the host on each side. popup_adjust_position() computes them from the host rectangle after the logical layout is finalised, and update_popups() and the popup-mask builder subtract them when emitting cells/borders/scrollbar and when marking popup-owned cells. win_update() is bracketed by transient w_height/w_topline/w_winrow adjustments so the buffer's drawn content matches the visible row range. - w_popup_rightclip is the horizontal counterpart for the host's right edge: the right border, padding and content columns past the host are not drawn. win_update() is bracketed by a transient w_width reduction so the buffer text is not written past the host's right edge either. - When the textprop scrolls just above the host window's top, the popup is kept visible by extending the prop search above topline (new helper find_prop_in_lines) and synthesising a negative screen_row so the top-clip path can roll the popup off the top. When the textprop has scrolled far enough that even the bottom border would overlap the host edge -- or when the popup would overflow the host's left edge at all -- the popup is hidden, and unhidden again once it comes back within range. - The "reduce-height" / "clamp winrow to 0" fallbacks in popup_adjust_position are bypassed for host-clipped popups so the popup keeps its natural anchored position instead of being snapped to the screen edge. Left-edge partial clipping is intentionally not supported: it would require shrinking the buffer width during win_update, which reflows wrapped lines and corrupts the displayed content; the popup is hidden instead. closes: #20166 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/popup.txt | 36 +- runtime/doc/tags | 1 + runtime/doc/version9.txt | 3 +- src/popupwin.c | 579 ++++++++++++++++-- src/proto/textprop.pro | 1 + src/structs.h | 16 + .../Test_popup_clipwindow_bottom_clip.dump | 14 + .../dumps/Test_popup_clipwindow_hidden.dump | 14 + .../Test_popup_clipwindow_left_clip.dump | 14 + .../Test_popup_clipwindow_right_clip.dump | 14 + .../dumps/Test_popup_clipwindow_top_clip.dump | 14 + src/testdir/test_popupwin.vim | 216 +++++++ src/textprop.c | 40 +- src/version.c | 2 + src/vim.h | 1 + 15 files changed, 892 insertions(+), 73 deletions(-) create mode 100644 src/testdir/dumps/Test_popup_clipwindow_bottom_clip.dump create mode 100644 src/testdir/dumps/Test_popup_clipwindow_hidden.dump create mode 100644 src/testdir/dumps/Test_popup_clipwindow_left_clip.dump create mode 100644 src/testdir/dumps/Test_popup_clipwindow_right_clip.dump create mode 100644 src/testdir/dumps/Test_popup_clipwindow_top_clip.dump diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt index 250127b9b6..2afa93a756 100644 --- a/runtime/doc/popup.txt +++ b/runtime/doc/popup.txt @@ -1,4 +1,4 @@ -*popup.txt* For Vim version 9.2. Last change: 2026 May 01 +*popup.txt* For Vim version 9.2. Last change: 2026 May 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -712,6 +712,15 @@ The second argument of |popup_create()| is a dictionary with options: when "textprop" is present. textpropid Used to identify the text property when "textprop" is present. Use zero to reset. + clipwindow Only used when "textprop" is set. When TRUE the popup + is kept within the window containing the text + property: if the text property scrolls past that + window's top, bottom, left or right edge, the popup + is clipped at that edge instead of being drawn + outside it. Once the text property has scrolled out + of the window the popup is hidden. + Default FALSE. + See |popup-clipwindow|. fixed When FALSE (the default), and: - "pos" is "botleft" or "topleft", and - the popup would be truncated at the right edge of @@ -949,6 +958,31 @@ If the window for which the popup was defined is closed, the popup is closed. If the popup cannot fit in the desired position, it may show at a nearby position. + +CLIP TEXTPROP POPUP TO HOST WINDOW *popup-clipwindow* + +When the popup is anchored to a text property in a split window, the popup is +by default drawn relative to the whole screen and may extend past the edges of +the window that contains the text property (the "host window"). Setting +"clipwindow" to TRUE keeps the popup within window's content area: +parts of the popup that fall outside the window are clipped, and the popup is +hidden once the text property has scrolled entirely past one of the edges. + +Example: a tall popup anchored above the cursor that should never spill into +the window below the split: > + call popup_create(body, #{ + \ textprop: 'marker', + \ textpropid: id, + \ pos: 'topleft', + \ line: -1, col: 0, + \ posinvert: v:false, + \ clipwindow: v:true, + \ }) +< +With "posinvert" left at its default (TRUE) the popup may be flipped to the +opposite side of the text property when there is no room; set it to FALSE to +keep the requested side and rely on "clipwindow" to clip the overflow. + Some hints: - To avoid collision with other plugins the text property type name has to be unique. You can also use the "bufnr" item to make it local to a buffer. diff --git a/runtime/doc/tags b/runtime/doc/tags index 3168235dad..8eef8cf28b 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -9791,6 +9791,7 @@ popt-option print.txt /*popt-option* popup popup.txt /*popup* popup-buffer popup.txt /*popup-buffer* popup-callback popup.txt /*popup-callback* +popup-clipwindow popup.txt /*popup-clipwindow* popup-close popup.txt /*popup-close* popup-examples popup.txt /*popup-examples* popup-filter popup.txt /*popup-filter* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index a1b9afce71..f4cdc702b7 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 May 05 +*version9.txt* For Vim version 9.2. Last change: 2026 May 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52588,6 +52588,7 @@ Popups ~ - 'previewpopup' supports the same values as 'completepopup' (except for "align"). - Support "opacity" setting for 'completepopup' option. +- Support for clipping textproperty popups |popup-clipwindow|. Diff mode ~ --------- diff --git a/src/popupwin.c b/src/popupwin.c index dcbe0e854c..5bd156367e 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -70,6 +70,10 @@ typedef struct { int leftcol; int leftoff; int has_scrollbar; + int topoff; + int bottomoff; + int leftclip; + int rightclip; } popup_layout_T; static poppos_entry_T poppos_entries[] = { @@ -838,6 +842,15 @@ apply_general_options(win_T *wp, dict_T *dict) wp->w_popup_flags &= ~POPF_POSINVERT; } + nr = dict_get_bool(dict, "clipwindow", -1); + if (nr != -1) + { + if (nr) + wp->w_popup_flags |= POPF_CLIPWINDOW; + else + wp->w_popup_flags &= ~POPF_CLIPWINDOW; + } + nr = dict_get_bool(dict, "resize", -1); if (nr != -1) { @@ -1320,6 +1333,320 @@ popup_extra_width(win_T *wp) + wp->w_has_scrollbar; } +/* + * Return the host window used to clip popup "wp" when POPF_CLIPWINDOW is set, + * or NULL when no clipping should be applied (option off, or the host window + * is no longer valid). The textprop window is used as the host; popups not + * anchored to a textprop are not clipped. + */ + static win_T * +popup_get_clipwin(win_T *wp) +{ + if (!(wp->w_popup_flags & POPF_CLIPWINDOW)) + return NULL; + if (win_valid(wp->w_popup_prop_win)) + return wp->w_popup_prop_win; + return NULL; +} + +// Per-popup clip geometry derived from w_popup_{top,bottom}off and +// w_popup_{left,right}clip. Filled by popup_compute_clip(). +// +// *_extra : original border+padding at each edge. +// clip_*_content : how many *content* rows/cols are clipped at each edge +// (border/padding is consumed first; the rest comes off +// w_height/w_width). >= 0. +// eff_*_extra : 0 when that edge is clipped (border+padding gone), +// otherwise the original *_extra. +// eff_border[], +// eff_padding[] : per-edge border/padding sizes (indexed [top,right,bot,left] +// matching wp->w_popup_border / wp->w_popup_padding). At a +// clipped edge they collapse to 0; elsewhere they keep the +// original size. Drawing code can replace +// `wp->w_popup_border[N] > 0 && wp->w_popup_*clip == 0` +// with a single `cl.eff_border[N] > 0` test. +// eff_height : drawn extent = eff_top_extra + visible content + eff_bot_extra. +// eff_width : drawn extent = eff_left_extra + visible content + eff_right_extra +// (does NOT include w_leftcol or scrollbar; see callers). +typedef struct { + int top_extra; + int bot_extra; + int left_extra; + int right_extra; + + int clip_top_content; + int clip_bot_content; + int clip_left_content; + int clip_right_content; + + int eff_top_extra; + int eff_bot_extra; + int eff_left_extra; + int eff_right_extra; + + int eff_border[4]; + int eff_padding[4]; + + int eff_height; + int eff_width; +} popup_clip_T; + + static void +popup_compute_clip(win_T *wp, popup_clip_T *cl) +{ + int h, w; + + cl->top_extra = popup_top_extra(wp); + cl->bot_extra = wp->w_popup_padding[2] + wp->w_popup_border[2]; + cl->left_extra = wp->w_popup_border[3] + wp->w_popup_padding[3]; + cl->right_extra = wp->w_popup_border[1] + wp->w_popup_padding[1]; + + cl->clip_top_content = wp->w_popup_topoff - cl->top_extra; + if (cl->clip_top_content < 0) + cl->clip_top_content = 0; + cl->clip_bot_content = wp->w_popup_bottomoff - cl->bot_extra; + if (cl->clip_bot_content < 0) + cl->clip_bot_content = 0; + cl->clip_left_content = wp->w_popup_leftclip - cl->left_extra; + if (cl->clip_left_content < 0) + cl->clip_left_content = 0; + cl->clip_right_content = wp->w_popup_rightclip - cl->right_extra; + if (cl->clip_right_content < 0) + cl->clip_right_content = 0; + + cl->eff_top_extra = wp->w_popup_topoff > 0 ? 0 : cl->top_extra; + cl->eff_bot_extra = wp->w_popup_bottomoff > 0 ? 0 : cl->bot_extra; + cl->eff_left_extra = wp->w_popup_leftclip > 0 ? 0 : cl->left_extra; + cl->eff_right_extra = wp->w_popup_rightclip > 0 ? 0 : cl->right_extra; + + cl->eff_border[0] = wp->w_popup_topoff > 0 ? 0 : wp->w_popup_border[0]; + cl->eff_border[1] = wp->w_popup_rightclip > 0 ? 0 : wp->w_popup_border[1]; + cl->eff_border[2] = wp->w_popup_bottomoff > 0 ? 0 : wp->w_popup_border[2]; + cl->eff_border[3] = wp->w_popup_leftclip > 0 ? 0 : wp->w_popup_border[3]; + + cl->eff_padding[0] = wp->w_popup_topoff > 0 ? 0 : wp->w_popup_padding[0]; + cl->eff_padding[1] = wp->w_popup_rightclip > 0 ? 0 : wp->w_popup_padding[1]; + cl->eff_padding[2] = wp->w_popup_bottomoff > 0 ? 0 : wp->w_popup_padding[2]; + cl->eff_padding[3] = wp->w_popup_leftclip > 0 ? 0 : wp->w_popup_padding[3]; + + h = wp->w_height - cl->clip_top_content - cl->clip_bot_content; + if (h < 0) + h = 0; + cl->eff_height = cl->eff_top_extra + h + cl->eff_bot_extra; + + w = wp->w_width - cl->clip_left_content - cl->clip_right_content; + if (w < 0) + w = 0; + cl->eff_width = cl->eff_left_extra + w + cl->eff_right_extra; +} + +// Snapshot of the popup window geometry that update_popups() temporarily +// mutates so that win_update() draws within the host-window clip rectangle. +// Saved before the clip is applied, restored after win_update() returns so +// callers continue to see the popup's logical geometry. +// Field names omit the "w_" prefix to avoid clashing with struct-field +// macros like w_p_wrap (= w_onebuf_opt.wo_wrap). +typedef struct { + int height; + int width; + int winrow; + int wincol; + int leftcol; + int p_wrap; + linenr_T topline; +} popup_geom_save_T; + + static void +popup_geom_save(win_T *wp, popup_geom_save_T *sv) +{ + sv->height = wp->w_height; + sv->width = wp->w_width; + sv->winrow = wp->w_winrow; + sv->wincol = wp->w_wincol; + sv->leftcol = wp->w_leftcol; + sv->p_wrap = wp->w_p_wrap; + sv->topline = wp->w_topline; +} + + static void +popup_geom_restore(win_T *wp, popup_geom_save_T *sv) +{ + wp->w_p_wrap = sv->p_wrap; + wp->w_leftcol = sv->leftcol; + wp->w_wincol = sv->wincol; + wp->w_winrow = sv->winrow; + wp->w_topline = sv->topline; + wp->w_width = sv->width; + wp->w_height = sv->height; +} + +/* + * Compute a screen row for a textprop that has scrolled above the host + * window's top. textpos2screenpos() cannot return a row above topline, so we + * probe at topline to fill the screen_{scol,ccol,ecol} column mapping, then + * extrapolate a (possibly-negative) row by counting how many buffer lines lie + * between the prop and topline. The popup_topoff clip path then turns the + * negative row into a top-clip animation as the prop rolls off the top edge. + */ + static void +popup_screenpos_above_top( + win_T *prop_win, + pos_T *pos, + linenr_T prop_lnum, + int *screen_row, + int *screen_scol, + int *screen_ccol, + int *screen_ecol) +{ + pos_T probe = *pos; + + probe.lnum = prop_win->w_topline; + textpos2screenpos(prop_win, &probe, + screen_row, screen_scol, screen_ccol, screen_ecol); + *screen_row = prop_win->w_winrow + 1 + - (int)(prop_win->w_topline - prop_lnum); +} + +/* + * Hide popup "wp" because its anchoring textprop is no longer reachable. + * Marks the popup as POPF_HIDDEN (no-op when already hidden) and schedules a + * redraw of the host window so any leftover decorations are cleared. + */ + static void +popup_hide_for_textprop(win_T *wp) +{ + if ((wp->w_popup_flags & POPF_HIDDEN) != 0) + return; + wp->w_popup_flags |= POPF_HIDDEN; + if (win_valid(wp->w_popup_prop_win)) + redraw_win_later(wp->w_popup_prop_win, UPD_SOME_VALID); +} + +/* + * For "clipwindow" popups: search the lines above prop_win->w_topline for the + * popup's anchoring textprop and report whether one was found. When + * "max_reach" is > 0, only the last "max_reach" lines before topline are + * scanned; pass 0 to scan all lines from line 1. Returns false when the + * popup is not "clipwindow", topline is already at line 1, or no prop matches. + */ + static bool +popup_find_prop_above_top( + win_T *wp, + win_T *prop_win, + int max_reach, + textprop_T *prop, + linenr_T *found_lnum) +{ + linenr_T first; + + if (!(wp->w_popup_flags & POPF_CLIPWINDOW) || prop_win->w_topline <= 1) + return false; + + first = max_reach > 0 ? prop_win->w_topline - max_reach : 1; + if (first < 1) + first = 1; + return find_prop_in_lines(prop_win, + wp->w_popup_prop_type, wp->w_popup_prop_id, + prop, found_lnum, first, prop_win->w_topline - 1); +} + +/* + * Compute and assign w_popup_topoff/bottomoff/leftclip/rightclip from the + * host (textprop) window's content rectangle when POPF_CLIPWINDOW is set. + * The popup's logical geometry (w_winrow, w_height, w_width) is preserved; + * only the *off/clip fields record how much of each edge falls outside. + * Returns true when the popup has scrolled completely past one of the host + * edges, in which case the caller must hide it. + */ + static bool +popup_compute_clipwindow_offsets(win_T *wp) +{ + win_T *cw = popup_get_clipwin(wp); + int extra_h, extra_w; + int popup_top, popup_bottom, popup_left, popup_right; + int total_h, total_w; + + if (cw == NULL) + return false; + + extra_h = popup_top_extra(wp) + + wp->w_popup_padding[2] + wp->w_popup_border[2]; + extra_w = popup_extra_width(wp); + + popup_top = wp->w_winrow; + popup_bottom = wp->w_winrow + wp->w_height + extra_h; + popup_left = wp->w_wincol; + popup_right = wp->w_wincol + wp->w_width + extra_w; + total_h = wp->w_height + extra_h; + total_w = wp->w_width + extra_w; + + if (popup_top < cw->w_winrow) + wp->w_popup_topoff = cw->w_winrow - popup_top; + if (popup_bottom > cw->w_winrow + cw->w_height) + wp->w_popup_bottomoff = popup_bottom - (cw->w_winrow + cw->w_height); + if (popup_left < cw->w_wincol) + wp->w_popup_leftclip = cw->w_wincol - popup_left; + if (popup_right > cw->w_wincol + cw->w_width) + wp->w_popup_rightclip = popup_right - (cw->w_wincol + cw->w_width); + + return wp->w_popup_topoff >= total_h + || wp->w_popup_bottomoff >= total_h + || wp->w_popup_leftclip >= total_w + || wp->w_popup_rightclip >= total_w; +} + +/* + * Mutate "wp"'s window geometry so win_update() draws only the rows/columns + * that fit within the host-window clip rectangle for "clipwindow" popups. + * The caller must save the original geometry with popup_geom_save() before + * this call and restore it with popup_geom_restore() after win_update(). + * + * Vertical clip: shrink w_height by the clipped content rows; advance + * w_topline and w_winrow when rows are cut off the top so the first visible + * content row lands on the host's top edge. + * + * Horizontal clip: when the right side is clipped, just shrink w_width. + * When the left side is clipped, advance w_leftcol so the hidden buffer + * columns scroll off and shift w_wincol so the first visible column lands on + * the host's left edge. Disable wrap so the transient w_width reduction does + * not reflow wrapped lines: the popup's logical width is unchanged, we just + * want to truncate cells that fall outside the host at draw time. + */ + static void +popup_apply_winupdate_clip(win_T *wp, popup_clip_T *cl) +{ + if (wp->w_popup_topoff > 0 || wp->w_popup_bottomoff > 0) + { + wp->w_height -= cl->clip_top_content + cl->clip_bot_content; + if (wp->w_height < 0) + wp->w_height = 0; + if (cl->clip_top_content > 0) + { + wp->w_topline += cl->clip_top_content; + wp->w_winrow += cl->clip_top_content; + } + } + if (wp->w_popup_leftclip > 0 || wp->w_popup_rightclip > 0) + { + if (cl->clip_left_content > 0 || cl->clip_right_content > 0) + wp->w_p_wrap = FALSE; + if (cl->clip_right_content > 0) + { + wp->w_width -= cl->clip_right_content; + if (wp->w_width < 0) + wp->w_width = 0; + } + if (cl->clip_left_content > 0) + { + wp->w_leftcol += cl->clip_left_content; + wp->w_wincol += cl->clip_left_content; + wp->w_width -= cl->clip_left_content; + if (wp->w_width < 0) + wp->w_width = 0; + } + } +} + /* * Adjust the position and size of the popup to fit on the screen. */ @@ -1361,6 +1688,10 @@ popup_adjust_position(win_T *wp) wp->w_leftcol = 0; wp->w_popup_leftoff = 0; wp->w_popup_rightoff = 0; + wp->w_popup_topoff = 0; + wp->w_popup_bottomoff = 0; + wp->w_popup_leftclip = 0; + wp->w_popup_rightclip = 0; // May need to update the "cursorline" highlighting, which may also change // "topline" @@ -1378,20 +1709,24 @@ popup_adjust_position(win_T *wp) int screen_ccol; int screen_ecol; - // Popup window is positioned relative to a text property. + // Popup window is positioned relative to a text property. With + // "clipwindow", keep the popup visible while the textprop has just + // scrolled above the host's top: extrapolate a negative screen_row + // from a prop above topline so the top-clip path can roll the popup + // off the top edge. Unhiding is done in check_popup_unhidden(). + bool prop_above_top = false; if (!find_visible_prop(prop_win, wp->w_popup_prop_type, wp->w_popup_prop_id, &prop, &prop_lnum)) { - // Text property is no longer visible, hide the popup. - // Unhiding the popup is done in check_popup_unhidden(). - if ((wp->w_popup_flags & POPF_HIDDEN) == 0) + if (popup_find_prop_above_top(wp, prop_win, 0, + &prop, &prop_lnum)) + prop_above_top = true; + else { - wp->w_popup_flags |= POPF_HIDDEN; - if (win_valid(wp->w_popup_prop_win)) - redraw_win_later(wp->w_popup_prop_win, UPD_SOME_VALID); + popup_hide_for_textprop(wp); + return; } - return; } // Compute the desired position from the position of the text @@ -1401,7 +1736,11 @@ popup_adjust_position(win_T *wp) if (wp->w_popup_pos == POPPOS_TOPLEFT || wp->w_popup_pos == POPPOS_BOTLEFT) pos.col += prop.tp_len - 1; - textpos2screenpos(prop_win, &pos, &screen_row, + if (prop_above_top) + popup_screenpos_above_top(prop_win, &pos, prop_lnum, &screen_row, + &screen_scol, &screen_ccol, &screen_ecol); + else + textpos2screenpos(prop_win, &pos, &screen_row, &screen_scol, &screen_ccol, &screen_ecol); if (screen_scol == 0) @@ -1771,8 +2110,11 @@ popup_adjust_position(win_T *wp) else if (wp->w_popup_pos == POPPOS_BOTRIGHT || wp->w_popup_pos == POPPOS_BOTLEFT) { - if ((wp->w_height + extra_height) <= wantline) - // bottom aligned: may move down + if ((wp->w_height + extra_height) <= wantline + || (wp->w_popup_flags & POPF_CLIPWINDOW)) + // bottom aligned: may move down. With "clipwindow" the popup + // keeps its natural position even if it overflows the screen, + // because the clip logic handles the overflow. wp->w_winrow = wantline - (wp->w_height + extra_height); else if (wantline * 2 >= Rows || !(wp->w_popup_flags & POPF_POSINVERT)) { @@ -1838,7 +2180,7 @@ popup_adjust_position(win_T *wp) // make sure w_winrow is valid if (wp->w_winrow >= Rows) wp->w_winrow = Rows - 1; - else if (wp->w_winrow < 0) + else if (wp->w_winrow < 0 && !(wp->w_popup_flags & POPF_CLIPWINDOW)) wp->w_winrow = 0; if (wp->w_wincol + wp->w_width + extra_width @@ -1863,25 +2205,48 @@ popup_adjust_position(win_T *wp) // Same for the bottom edge: shift up so the border/padding/shadow stays // on screen, and clip the height if the popup is taller than the screen. - if (wp->w_winrow + wp->w_height + extra_height > Rows) - wp->w_winrow = Rows - wp->w_height - extra_height; - if (wp->w_winrow < 0) - wp->w_winrow = 0; - if (wp->w_winrow + wp->w_height + extra_height > Rows) - { - int avail = Rows - wp->w_winrow - extra_height; - wp->w_height = avail > 0 ? avail : 0; + // For "clipwindow" popups the host-window clip below handles overflow, so + // skip these screen-edge clamps -- otherwise a synthesised negative + // w_winrow (popup partially above the host's top edge) would be snapped + // back to 0 and defeat the top-clip animation. + if (!(wp->w_popup_flags & POPF_CLIPWINDOW)) + { + if (wp->w_winrow + wp->w_height + extra_height > Rows) + wp->w_winrow = Rows - wp->w_height - extra_height; + if (wp->w_winrow < 0) + wp->w_winrow = 0; + if (wp->w_winrow + wp->w_height + extra_height > Rows) + { + int avail = Rows - wp->w_winrow - extra_height; + wp->w_height = avail > 0 ? avail : 0; + } } if (wp->w_height != org_layout.height) win_comp_scroll(wp); + // Confine the popup to its host window for "clipwindow". The popup's + // logical geometry stays untouched; only w_popup_topoff/bottomoff/ + // leftclip/rightclip record how many rows/columns of each edge fall + // outside the host so the drawing code can skip them. When the popup + // has fully scrolled past one of the host edges, hide it instead of + // leaving stray decorations behind. + if (popup_compute_clipwindow_offsets(wp)) + { + popup_hide_for_textprop(wp); + return; + } + wp->w_popup_last_changedtick = CHANGEDTICK(wp->w_buffer); if (win_valid(wp->w_popup_prop_win)) { wp->w_popup_prop_changedtick = CHANGEDTICK(wp->w_popup_prop_win->w_buffer); wp->w_popup_prop_topline = wp->w_popup_prop_win->w_topline; + wp->w_popup_prop_winrow = wp->w_popup_prop_win->w_winrow; + wp->w_popup_prop_wincol = wp->w_popup_prop_win->w_wincol; + wp->w_popup_prop_width = wp->w_popup_prop_win->w_width; + wp->w_popup_prop_winheight = wp->w_popup_prop_win->w_height; } // Need to update popup_mask if the position or size changed. @@ -3554,6 +3919,10 @@ popup_save_layout(win_T *wp, popup_layout_T *layout) layout->leftcol = wp->w_leftcol; layout->leftoff = wp->w_popup_leftoff; layout->has_scrollbar = wp->w_has_scrollbar; + layout->topoff = wp->w_popup_topoff; + layout->bottomoff = wp->w_popup_bottomoff; + layout->leftclip = wp->w_popup_leftclip; + layout->rightclip = wp->w_popup_rightclip; } /* @@ -3568,7 +3937,11 @@ popup_layout_changed(win_T *wp, popup_layout_T *layout) || layout->leftoff != wp->w_popup_leftoff || layout->width != wp->w_width || layout->height != wp->w_height - || layout->has_scrollbar != wp->w_has_scrollbar; + || layout->has_scrollbar != wp->w_has_scrollbar + || layout->topoff != wp->w_popup_topoff + || layout->bottomoff != wp->w_popup_bottomoff + || layout->leftclip != wp->w_popup_leftclip + || layout->rightclip != wp->w_popup_rightclip; } /* @@ -4298,6 +4671,8 @@ f_popup_getoptions(typval_T *argvars, typval_T *rettv) dict_add_number(dict, "resize", (wp->w_popup_flags & POPF_RESIZE) != 0); dict_add_number(dict, "posinvert", (wp->w_popup_flags & POPF_POSINVERT) != 0); + dict_add_number(dict, "clipwindow", + (wp->w_popup_flags & POPF_CLIPWINDOW) != 0); // Return opacity (0-100) by converting from internal blend value dict_add_number(dict, "opacity", (wp->w_popup_flags & POPF_OPACITY) ? 100 - wp->w_popup_blend : 100); @@ -4765,11 +5140,23 @@ check_popup_unhidden(win_T *wp) { textprop_T prop; linenr_T lnum; + bool found = false; - if ((wp->w_popup_flags & POPF_HIDDEN_FORCE) == 0 - && find_visible_prop(wp->w_popup_prop_win, - wp->w_popup_prop_type, wp->w_popup_prop_id, - &prop, &lnum)) + if ((wp->w_popup_flags & POPF_HIDDEN_FORCE) != 0) + return FALSE; + if (find_visible_prop(wp->w_popup_prop_win, + wp->w_popup_prop_type, wp->w_popup_prop_id, + &prop, &lnum)) + found = true; + // The textprop may have scrolled just above the host window's top. + // Unhide the popup so popup_adjust_position() can roll it partially + // onto the host's top edge via the top-clip path. Limit the search + // to the popup's own height so we do not resurrect a popup whose + // prop is already further off-screen than the popup can extend. + else if (popup_find_prop_above_top(wp, wp->w_popup_prop_win, + popup_height(wp), &prop, &lnum)) + found = true; + if (found) { wp->w_popup_flags &= ~POPF_HIDDEN; wp->w_popup_prop_topline = 0; // force repositioning @@ -4793,7 +5180,11 @@ popup_need_position_adjust(win_T *wp) if (win_valid(wp->w_popup_prop_win) && (wp->w_popup_prop_changedtick != CHANGEDTICK(wp->w_popup_prop_win->w_buffer) - || wp->w_popup_prop_topline != wp->w_popup_prop_win->w_topline)) + || wp->w_popup_prop_topline != wp->w_popup_prop_win->w_topline + || wp->w_popup_prop_winrow != wp->w_popup_prop_win->w_winrow + || wp->w_popup_prop_wincol != wp->w_popup_prop_win->w_wincol + || wp->w_popup_prop_width != wp->w_popup_prop_win->w_width + || wp->w_popup_prop_winheight != wp->w_popup_prop_win->w_height)) return TRUE; // May need to adjust the width if the cursor moved. @@ -5086,7 +5477,18 @@ may_update_popup_mask(int type) } width = popup_width(wp); - height = popup_height(wp); + // Match the drawn extent computed by update_popups so that cells + // outside the clipped popup are not marked as popup-owned and the + // background window can draw through them. + if (wp->w_popup_topoff > 0 || wp->w_popup_bottomoff > 0) + { + popup_clip_T cl; + + popup_compute_clip(wp, &cl); + height = cl.eff_height; + } + else + height = popup_height(wp); popup_update_mask(wp, width, height); // Popup with partial transparency do not block lower layers from @@ -5095,18 +5497,25 @@ may_update_popup_mask(int type) if ((wp->w_popup_flags & POPF_OPACITY) && wp->w_popup_blend > 0) continue; - for (line = wp->w_winrow; - line < wp->w_winrow + height && line < screen_Rows; ++line) - for (col = wp->w_wincol; - col < wp->w_wincol + width - wp->w_popup_leftoff - && col < screen_Columns; ++col) - if (wp->w_zindex < POPUPMENU_ZINDEX - && pum_visible() - && pum_under_menu(line, col, FALSE)) - mask[line * screen_Columns + col] = POPUPMENU_ZINDEX; - else if (wp->w_popup_mask_cells == NULL - || !popup_masked(wp, width, height, col, line)) - mask[line * screen_Columns + col] = wp->w_zindex; + { + int mask_start = wp->w_winrow + wp->w_popup_topoff; + int mask_end = mask_start + height; + int mask_col_start = wp->w_wincol + wp->w_popup_leftclip; + int mask_col_end = wp->w_wincol + width - wp->w_popup_leftoff + - wp->w_popup_rightclip; + + for (line = mask_start; + line < mask_end && line < screen_Rows; ++line) + for (col = mask_col_start; + col < mask_col_end && col < screen_Columns; ++col) + if (wp->w_zindex < POPUPMENU_ZINDEX + && pum_visible() + && pum_under_menu(line, col, FALSE)) + mask[line * screen_Columns + col] = POPUPMENU_ZINDEX; + else if (wp->w_popup_mask_cells == NULL + || !popup_masked(wp, width, height, col, line)) + mask[line * screen_Columns + col] = wp->w_zindex; + } } // Only check which lines are to be updated if not already @@ -5528,6 +5937,14 @@ update_popups(void (*win_update)(win_T *wp)) { int title_len = 0; int title_wincol; + popup_clip_T cl; + + // Compute the clip geometry once per iteration; w_popup_*off/clip, + // w_height, w_width, w_popup_border and w_popup_padding are stable + // for the duration of this iteration (popup_apply_winupdate_clip() + // mutates w_height/w_width temporarily but the result is restored + // before any code below reads cl again). + popup_compute_clip(wp, &cl); override_success = push_highlight_overrides(wp->w_hl, wp->w_hl_len); @@ -5613,13 +6030,25 @@ update_popups(void (*win_update)(win_T *wp)) // Draw the popup text, unless it's off screen. if (wp->w_winrow < screen_Rows && wp->w_wincol < screen_Columns) { + popup_geom_save_T saved; + + popup_geom_save(wp, &saved); + // May need to update the "cursorline" highlighting, which may also // change "topline" if (wp->w_popup_last_curline != wp->w_cursor.lnum) popup_highlight_curline(wp); + // Clip the buffer's drawn extent to the host window when + // "clipwindow" is set. The transient mutations are reverted by + // popup_geom_restore() so callers continue to see the popup's + // logical geometry via popup_getoptions/popup_getpos. + popup_apply_winupdate_clip(wp, &cl); + win_update(wp); + popup_geom_restore(wp, &saved); + // move the cursor into the visible lines, otherwise executing // commands with win_execute() may cause the text to jump. if (wp->w_cursor.lnum < wp->w_topline) @@ -5631,6 +6060,12 @@ update_popups(void (*win_update)(win_T *wp)) wp->w_winrow -= top_off; wp->w_wincol -= left_extra; + // "clipwindow" with top-clip shifts all popup decorations down so the + // first visible row of the popup lands at the host window's top edge. + // Apply the shift before drawing borders/padding/etc. and restore at + // the end of this popup's iteration. + wp->w_winrow += wp->w_popup_topoff; + // Add offset for border and padding if not done already. if ((wp->w_flags & WFLAG_WCOL_OFF_ADDED) == 0) { @@ -5643,8 +6078,22 @@ update_popups(void (*win_update)(win_T *wp)) wp->w_flags |= WFLAG_WROW_OFF_ADDED; } - total_width = popup_width(wp) - wp->w_popup_rightoff; - total_height = popup_height(wp); + // When clipped by "clipwindow", drop the border/padding slot at the + // clipped edge that we will not render, so the popup ends exactly on + // the last visible content row (no empty trailing side-border row) + // and starts on the first visible row when top-clipped. When + // unclipped, fall back to the full popup geometry (cl.eff_width + // excludes w_leftcol and the scrollbar, which popup_width() folds in). + if (wp->w_popup_leftclip > 0 || wp->w_popup_rightclip > 0) + total_width = cl.eff_width; + else + total_width = popup_width(wp) - wp->w_popup_rightoff; + if (total_width < 0) + total_width = 0; + if (wp->w_popup_topoff > 0 || wp->w_popup_bottomoff > 0) + total_height = cl.eff_height; + else + total_height = popup_height(wp); popup_attr = get_win_attr(wp); if (wp->w_winrow + total_height > cmdline_row) @@ -5723,16 +6172,16 @@ update_popups(void (*win_update)(win_T *wp)) wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr); } - wincol = wp->w_wincol - wp->w_popup_leftoff; - top_padding = wp->w_popup_padding[0]; - if (wp->w_popup_border[0] > 0) + wincol = wp->w_wincol - wp->w_popup_leftoff + wp->w_popup_leftclip; + top_padding = cl.eff_padding[0]; + if (cl.eff_border[0] > 0) { // top border; do not draw over the title if (title_len > 0) { screen_fill(wp->w_winrow, wp->w_winrow + 1, wincol < 0 ? 0 : wincol, title_wincol, - wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0 + cl.eff_border[3] != 0 && wp->w_popup_leftoff == 0 ? border_char[4] : border_char[0], border_char[0], border_attr[0]); screen_fill(wp->w_winrow, wp->w_winrow + 1, @@ -5743,18 +6192,19 @@ update_popups(void (*win_update)(win_T *wp)) { screen_fill(wp->w_winrow, wp->w_winrow + 1, wincol < 0 ? 0 : wincol, wincol + total_width, - wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0 + cl.eff_border[3] != 0 && wp->w_popup_leftoff == 0 ? border_char[4] : border_char[0], border_char[0], border_attr[0]); } - if (wp->w_popup_border[1] > 0) + if (cl.eff_border[1] > 0) { buf[mb_char2bytes(border_char[5], buf)] = NUL; screen_puts(buf, wp->w_winrow, wincol + total_width - 1, border_attr[1]); } } - else if (wp->w_popup_padding[0] == 0 && popup_top_extra(wp) > 0) + else if (cl.eff_padding[0] == 0 && popup_top_extra(wp) > 0 + && wp->w_popup_topoff == 0) top_padding = 1; if (top_padding > 0 || wp->w_popup_padding[2] > 0) @@ -5844,25 +6294,26 @@ update_popups(void (*win_update)(win_T *wp)) attr_thumb = highlight_attr[HLF_PST]; } - for (i = wp->w_popup_border[0]; - i < total_height - wp->w_popup_border[2]; ++i) + // The side-border loop spans the popup's drawn extent. cl.eff_border + // and cl.eff_padding collapse the clipped edges to 0 so the loop + // covers the full visible area without leaving an empty trailing row. + for (i = cl.eff_border[0]; i < total_height - cl.eff_border[2]; ++i) { int pad_left; // left and right padding only needed next to the body int do_padding = - i >= wp->w_popup_border[0] + wp->w_popup_padding[0] - && i < total_height - wp->w_popup_border[2] - - wp->w_popup_padding[2]; + i >= cl.eff_border[0] + cl.eff_padding[0] + && i < total_height - cl.eff_border[2] - cl.eff_padding[2]; row = wp->w_winrow + i; // left border - if (wp->w_popup_border[3] > 0 && wincol >= 0) + if (cl.eff_border[3] > 0 && wincol >= 0) { buf[mb_char2bytes(border_char[3], buf)] = NUL; screen_puts(buf, row, wincol, border_attr[3]); } - if (do_padding && wp->w_popup_padding[3] > 0) + if (do_padding && cl.eff_padding[3] > 0) { int col = wincol + wp->w_popup_border[3]; @@ -5899,13 +6350,13 @@ update_popups(void (*win_update)(win_T *wp)) screen_putchar(' ', row, scroll_col, popup_attr); } // right border - if (wp->w_popup_border[1] > 0) + if (cl.eff_border[1] > 0) { buf[mb_char2bytes(border_char[1], buf)] = NUL; screen_puts(buf, row, wincol + total_width - 1, border_attr[1]); } // right padding - if (do_padding && wp->w_popup_padding[1] > 0) + if (do_padding && cl.eff_padding[1] > 0) { int pad_col_start = wincol + wp->w_popup_border[3] + wp->w_popup_padding[3] + wp->w_width + wp->w_leftcol; @@ -5932,7 +6383,7 @@ update_popups(void (*win_update)(win_T *wp)) } } - if (wp->w_popup_padding[2] > 0) + if (cl.eff_padding[2] > 0) { // bottom padding row = wp->w_winrow + wp->w_popup_border[0] @@ -5945,24 +6396,24 @@ update_popups(void (*win_update)(win_T *wp)) padcol, padendcol, ' ', ' ', popup_attr); } - if (wp->w_popup_border[2] > 0) + if (cl.eff_border[2] > 0) { // bottom border row = wp->w_winrow + total_height - 1; screen_fill(row, row + 1, wincol < 0 ? 0 : wincol, wincol + total_width, - wp->w_popup_border[3] != 0 && wp->w_popup_leftoff == 0 + cl.eff_border[3] != 0 && wp->w_popup_leftoff == 0 ? border_char[7] : border_char[2], border_char[2], border_attr[2]); - if (wp->w_popup_border[1] > 0) + if (cl.eff_border[1] > 0) { buf[mb_char2bytes(border_char[6], buf)] = NUL; screen_puts(buf, row, wincol + total_width - 1, border_attr[2]); } } - if (wp->w_popup_shadow) + if (wp->w_popup_shadow && wp->w_popup_bottomoff == 0) { // bottom shadow row = wp->w_winrow + total_height; @@ -5996,6 +6447,10 @@ update_popups(void (*win_update)(win_T *wp)) if (override_success) pop_highlight_overrides(); + + // Undo the topoff shift applied before drawing the borders so the + // next iteration sees the popup's logical winrow. + wp->w_winrow -= wp->w_popup_topoff; } #ifdef FEAT_PROP_POPUP diff --git a/src/proto/textprop.pro b/src/proto/textprop.pro index d3ecf6d14c..be9d80c6e3 100644 --- a/src/proto/textprop.pro +++ b/src/proto/textprop.pro @@ -16,6 +16,7 @@ int get_text_props(buf_T *buf, linenr_T lnum, char_u **props, int will_change); int prop_count_above_below(buf_T *buf, linenr_T lnum); int count_props(linenr_T lnum, int only_starting, int last_line); void sort_text_props(buf_T *buf, textprop_T *props, int *idxs, int count); +bool find_prop_in_lines(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum, linenr_T first_lnum, linenr_T last_lnum); bool find_visible_prop(win_T *wp, int type_id, int id, textprop_T *prop, linenr_T *found_lnum); char_u *props_add_count_header(char_u *line, int line_len, int textlen, int *new_len); void add_text_props(linenr_T lnum, textprop_T *text_props, int text_prop_count); diff --git a/src/structs.h b/src/structs.h index 4d92ca75a1..6c6ea8c0e4 100644 --- a/src/structs.h +++ b/src/structs.h @@ -4197,6 +4197,14 @@ struct window_S int w_popup_leftoff; // columns left of the screen int w_popup_rightoff; // columns right of the screen + int w_popup_topoff; // rows above the host window's top + // when "clipwindow" is set + int w_popup_bottomoff; // rows below the host window's bottom + // when "clipwindow" is set + int w_popup_leftclip; // columns left of the host window's left + // when "clipwindow" is set + int w_popup_rightclip; // columns right of the host window's right + // when "clipwindow" is set varnumber_T w_popup_last_changedtick; // b:changedtick of popup buffer // when position was computed varnumber_T w_popup_prop_changedtick; // b:changedtick of buffer with @@ -4205,6 +4213,14 @@ struct window_S int w_popup_prop_topline; // w_topline of window with // w_popup_prop_type when position was // computed + int w_popup_prop_winrow; // w_winrow of host window when + // position was computed + int w_popup_prop_wincol; // w_wincol of host window when + // position was computed + int w_popup_prop_width; // w_width of host window when + // position was computed + int w_popup_prop_winheight; // w_height of host window when + // position was computed linenr_T w_popup_last_curline; // last known w_cursor.lnum of window // with "cursorline" set callback_T w_close_cb; // popup close callback diff --git a/src/testdir/dumps/Test_popup_clipwindow_bottom_clip.dump b/src/testdir/dumps/Test_popup_clipwindow_bottom_clip.dump new file mode 100644 index 0000000000..b5d79480fb --- /dev/null +++ b/src/testdir/dumps/Test_popup_clipwindow_bottom_clip.dump @@ -0,0 +1,14 @@ +>h+0&#ffffff0|o|s|t| |l|i|n|e| |1| @28 +|h|o|s|t| |l|i|n|e| |2| @28 +|h|o|s|t| |l|i|n|e| |3| @28 +|h|o|s|t|╔+0#0000001#e0e0e08|═@8|╗| +0#0000000#ffffff0@24 +|h|o|s|t|║+0#0000001#e0e0e08| |p|o|p|u|p| |A| |║| +0#0000000#ffffff0@24 +|h|o|s|t|║+0#0000001#e0e0e08| |p|o|p|u|p| |B| |║| +0#0000000#ffffff0@24 +|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @8|1|,|1| @11|T|o|p +| +0&&@39 +|~+0#4040ff13&| @38 +|~| @38 +|~| @38 +|~| @38 +|[+1#0000000&|N|o| |N|a|m|e|]| @12|0|,|0|-|1| @9|A|l@1 +| +0&&@39 diff --git a/src/testdir/dumps/Test_popup_clipwindow_hidden.dump b/src/testdir/dumps/Test_popup_clipwindow_hidden.dump new file mode 100644 index 0000000000..c5f8930695 --- /dev/null +++ b/src/testdir/dumps/Test_popup_clipwindow_hidden.dump @@ -0,0 +1,14 @@ +|h+0&#ffffff0|o|s|t| |l|i|n|e| |4|5| @27 +|h|o|s|t| |l|i|n|e| |4|6| @27 +|h|o|s|t| |l|i|n|e| |4|7| @27 +|h|o|s|t| |l|i|n|e| |4|8| @27 +|h|o|s|t| |l|i|n|e| |4|9| @27 +>h|o|s|t| |l|i|n|e| |5|0| @27 +|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @8|5|0|,|1| @10|B|o|t +| +0&&@39 +|~+0#4040ff13&| @38 +|~| @38 +|~| @38 +|~| @38 +|[+1#0000000&|N|o| |N|a|m|e|]| @12|0|,|0|-|1| @9|A|l@1 +| +0&&@39 diff --git a/src/testdir/dumps/Test_popup_clipwindow_left_clip.dump b/src/testdir/dumps/Test_popup_clipwindow_left_clip.dump new file mode 100644 index 0000000000..5d38f98d2a --- /dev/null +++ b/src/testdir/dumps/Test_popup_clipwindow_left_clip.dump @@ -0,0 +1,14 @@ +| +0&#ffffff0@26||+1&&>h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&|═+0#0000001#e0e0e08@7|╗| +0#0000000#ffffff0|n|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&| +0&&|p+0#0000001#e0e0e08|o|p|u|p| |A|║| |n+0#0000000#ffffff0|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&| +0&&|p+0#0000001#e0e0e08|o|p|u|p| |B|║| |n+0#0000000#ffffff0|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&| +0&&|p+0#0000001#e0e0e08|o|p|u|p| |C|║| |n+0#0000000#ffffff0|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&|═+0#0000001#e0e0e08@7|╝| +0#0000000#ffffff0|n|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d +|~+0#4040ff13&| @25||+1#0000000&|h+0&&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d +| @31|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_popup_clipwindow_right_clip.dump b/src/testdir/dumps/Test_popup_clipwindow_right_clip.dump new file mode 100644 index 0000000000..6427f94a5a --- /dev/null +++ b/src/testdir/dumps/Test_popup_clipwindow_right_clip.dump @@ -0,0 +1,14 @@ +>h+0&#ffffff0|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&| +0&&@26 +|h|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|╔+0#0000001#e0e0e08|═@3||+1#0000000#ffffff0|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|║+0#0000001#e0e0e08| |p|o|p||+1#0000000#ffffff0|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|║+0#0000001#e0e0e08| |p|o|p||+1#0000000#ffffff0|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|║+0#0000001#e0e0e08| |p|o|p||+1#0000000#ffffff0|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e|╚+0#0000001#e0e0e08|═@3||+1#0000000#ffffff0|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25 +|h+0#0000000&|o|s|t| |c|o|n|t|e|n|t| |l|i|n|e| |a|b|c|d||+1&&|~+0#4040ff13&| @25 +| +0#0000000&@31|1|,|1| @10|T|o|p| diff --git a/src/testdir/dumps/Test_popup_clipwindow_top_clip.dump b/src/testdir/dumps/Test_popup_clipwindow_top_clip.dump new file mode 100644 index 0000000000..9876f15cce --- /dev/null +++ b/src/testdir/dumps/Test_popup_clipwindow_top_clip.dump @@ -0,0 +1,14 @@ +| +0&#ffffff0@39 +|~+0#4040ff13&| @38 +|~| @38 +|~| @38 +|~| @38 +|[+1#0000000&|N|o| |N|a|m|e|]| @12|0|,|0|-|1| @9|A|l@1 +>h+0&&|o|s|t|║+0#0000001#e0e0e08| |p|o|p|u|p| |B| |║| +0#0000000#ffffff0@24 +|h|o|s|t|║+0#0000001#e0e0e08| |p|o|p|u|p| |C| |║| +0#0000000#ffffff0@24 +|h|o|s|t|╚+0#0000001#e0e0e08|═@8|╝| +0#0000000#ffffff0@24 +|h|o|s|t| |l|i|n|e| |4| @28 +|h|o|s|t| |l|i|n|e| |5| @28 +|h|o|s|t| |l|i|n|e| |6| @28 +|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @8|1|,|1| @11|T|o|p +| +0&&@39 diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index d6aa634c72..873840d8e8 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -4522,6 +4522,222 @@ func Test_popup_setoptions_other_tab() call prop_type_delete('textprop') endfunc +func Test_popup_clipwindow_option() + " Default: clipwindow is off. + let id = popup_create('TEST', #{}) + call assert_equal(0, popup_getoptions(id).clipwindow) + call popup_close(id) + + " popup_create() honours the option. + let id = popup_create('TEST', #{clipwindow: v:true}) + call assert_equal(1, popup_getoptions(id).clipwindow) + + " popup_setoptions() can toggle it off and on. + call popup_setoptions(id, #{clipwindow: v:false}) + call assert_equal(0, popup_getoptions(id).clipwindow) + call popup_setoptions(id, #{clipwindow: v:true}) + call assert_equal(1, popup_getoptions(id).clipwindow) + + call popup_close(id) +endfunc + +func Test_popup_clipwindow_hide_when_prop_off_screen() + " A "clipwindow" popup attached to a textprop should be hidden once the + " host window scrolls so the textprop is far enough off-screen that even + " the partially-clipped popup would no longer overlap, and unhidden again + " when the prop scrolls back into reach. + call prop_type_add('clipprop', {}) + new + call setline(1, range(1, 200)->mapnew({_, v -> 'line ' .. v})) + call prop_add(5, 1, #{type: 'clipprop', length: 5}) + let host = win_getid() + + let id = popup_create('attached', #{ + \ textprop: 'clipprop', + \ textpropwin: host, + \ line: -1, + \ wrap: v:false, + \ fixed: v:true, + \ clipwindow: v:true, + \ }) + redraw + call assert_equal(1, popup_getpos(id).visible) + + " Scroll the host so the prop is far below topline; popup hides. + call win_execute(host, 'normal! Gzb') + redraw + call assert_equal(0, popup_getpos(id).visible) + + " Scroll back so the prop is on the first visible line; popup unhides. + call win_execute(host, 'normal! ggzt') + redraw + call assert_equal(1, popup_getpos(id).visible) + + call popup_close(id) + bwipe! + call prop_type_delete('clipprop') +endfunc + +func Test_popup_clipwindow_top_clip() + CheckScreendump + + let lines =<< trim END + vim9script + set nowrap + :botright new + :resize 6 + setline(1, range(1, 30)->mapnew((_, v) => 'host line ' .. v)) + prop_type_add('clipprop', {}) + prop_add(2, 1, {type: 'clipprop', length: 4}) + popup_create(['popup A', 'popup B', 'popup C'], { + textprop: 'clipprop', + line: -4, + col: 0, + border: [], + padding: [0, 1, 0, 1], + highlight: 'PmenuSel', + wrap: false, + fixed: true, + posinvert: false, + clipwindow: true, + }) + END + call writefile(lines, 'XtestPopupClipwindowTop', 'D') + let buf = RunVimInTerminal('-S XtestPopupClipwindowTop', #{rows: 14, cols: 40}) + call VerifyScreenDump(buf, 'Test_popup_clipwindow_top_clip', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_popup_clipwindow_bottom_clip() + CheckScreendump + + let lines =<< trim END + vim9script + set nowrap + :topleft new + :resize 6 + setline(1, range(1, 30)->mapnew((_, v) => 'host line ' .. v)) + prop_type_add('clipprop', {}) + prop_add(2, 1, {type: 'clipprop', length: 4}) + popup_create(['popup A', 'popup B', 'popup C'], { + textprop: 'clipprop', + line: 1, + col: 0, + border: [], + padding: [0, 1, 0, 1], + highlight: 'PmenuSel', + wrap: false, + fixed: true, + posinvert: false, + clipwindow: true, + }) + END + call writefile(lines, 'XtestPopupClipwindowBottom', 'D') + let buf = RunVimInTerminal('-S XtestPopupClipwindowBottom', #{rows: 14, cols: 40}) + call VerifyScreenDump(buf, 'Test_popup_clipwindow_bottom_clip', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_popup_clipwindow_left_clip() + CheckScreendump + + let lines =<< trim END + vim9script + set nowrap + :vert botright new + :vert resize 22 + set laststatus=0 + setline(1, repeat(['host content line abcdef'], 20)) + prop_type_add('clipprop', {}) + prop_add(5, 6, {type: 'clipprop', length: 4}) + popup_create(['popup A', 'popup B', 'popup C'], { + textprop: 'clipprop', + line: 0, + col: -10, + border: [], + padding: [0, 1, 0, 1], + highlight: 'PmenuSel', + wrap: false, + fixed: true, + posinvert: false, + clipwindow: true, + }) + END + call writefile(lines, 'XtestPopupClipwindowLeft', 'D') + let buf = RunVimInTerminal('-S XtestPopupClipwindowLeft', #{rows: 14, cols: 50}) + call VerifyScreenDump(buf, 'Test_popup_clipwindow_left_clip', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_popup_clipwindow_right_clip() + CheckScreendump + + let lines =<< trim END + vim9script + set nowrap + :vert topleft new + :vert resize 22 + set laststatus=0 + setline(1, repeat(['host content line abcdef'], 20)) + prop_type_add('clipprop', {}) + prop_add(5, 14, {type: 'clipprop', length: 4}) + popup_create(['popup A', 'popup B', 'popup C'], { + textprop: 'clipprop', + line: 0, + col: 0, + border: [], + padding: [0, 1, 0, 1], + highlight: 'PmenuSel', + wrap: false, + fixed: true, + posinvert: false, + clipwindow: true, + }) + END + call writefile(lines, 'XtestPopupClipwindowRight', 'D') + let buf = RunVimInTerminal('-S XtestPopupClipwindowRight', #{rows: 14, cols: 50}) + call VerifyScreenDump(buf, 'Test_popup_clipwindow_right_clip', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_popup_clipwindow_hidden() + CheckScreendump + + let lines =<< trim END + vim9script + set nowrap + :topleft new + :resize 6 + setline(1, range(1, 50)->mapnew((_, v) => 'host line ' .. v)) + prop_type_add('clipprop', {}) + prop_add(2, 1, {type: 'clipprop', length: 4}) + popup_create(['popup A', 'popup B', 'popup C'], { + textprop: 'clipprop', + line: -4, + col: 0, + border: [], + padding: [0, 1, 0, 1], + highlight: 'PmenuSel', + wrap: false, + fixed: true, + posinvert: false, + clipwindow: true, + }) + # Scroll the host so the textprop is far below topline; the popup is + # then hidden because the prop has scrolled out of the host window. + win_execute(win_getid(), 'normal! Gzb') + END + call writefile(lines, 'XtestPopupClipwindowHidden', 'D') + let buf = RunVimInTerminal('-S XtestPopupClipwindowHidden', #{rows: 14, cols: 40}) + call VerifyScreenDump(buf, 'Test_popup_clipwindow_hidden', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_popup_prop_not_visible() CheckScreendump diff --git a/src/textprop.c b/src/textprop.c index 48cbad4b6a..a049edec9b 100644 --- a/src/textprop.c +++ b/src/textprop.c @@ -1396,25 +1396,28 @@ sort_text_props( } /* - * Find text property "type_id" in the visible lines of window "wp". - * Match "id" when it is > 0. - * Returns false when not found. + * Find text property "type_id" in lines [first_lnum, last_lnum] of window + * "wp"'s buffer. Match "id" when it is > 0. Returns false when not found. */ bool -find_visible_prop( +find_prop_in_lines( win_T *wp, int type_id, int id, textprop_T *prop, - linenr_T *found_lnum) + linenr_T *found_lnum, + linenr_T first_lnum, + linenr_T last_lnum) { - // return when "type_id" no longer exists if (text_prop_type_by_id(wp->w_buffer, type_id) == NULL) return false; - // w_botline may not have been updated yet. - validate_botline_win(wp); - for (linenr_T lnum = wp->w_topline; lnum < wp->w_botline; ++lnum) + if (first_lnum < 1) + first_lnum = 1; + if (last_lnum > wp->w_buffer->b_ml.ml_line_count) + last_lnum = wp->w_buffer->b_ml.ml_line_count; + + for (linenr_T lnum = first_lnum; lnum <= last_lnum; ++lnum) { char_u *props; int count = get_text_props(wp->w_buffer, lnum, &props, FALSE); @@ -1432,6 +1435,25 @@ find_visible_prop( return false; } +/* + * Find text property "type_id" in the visible lines of window "wp". + * Match "id" when it is > 0. + * Returns false when not found. + */ + bool +find_visible_prop( + win_T *wp, + int type_id, + int id, + textprop_T *prop, + linenr_T *found_lnum) +{ + // w_botline may not have been updated yet. + validate_botline_win(wp); + return find_prop_in_lines(wp, type_id, id, prop, found_lnum, + wp->w_topline, wp->w_botline - 1); +} + /* * Set the text properties for line "lnum" to "tps" array with "count" entries. * If "count" is zero text properties are removed. diff --git a/src/version.c b/src/version.c index f1e40ddb20..2882be5795 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 469, /**/ 468, /**/ diff --git a/src/vim.h b/src/vim.h index ee67e4078a..d9a22ef018 100644 --- a/src/vim.h +++ b/src/vim.h @@ -690,6 +690,7 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring); #define POPF_INFO_MENU 0x400 // align info popup with popup menu #define POPF_POSINVERT 0x800 // vertical position can be inverted #define POPF_OPACITY 0x1000 // popup has opacity/transparency setting +#define POPF_CLIPWINDOW 0x2000 // confine popup to its host window's rect // flags used in w_popup_handled #define POPUP_HANDLED_1 0x01 // used by mouse_find_win() From e0781bd5bfd1acc2c4d5d01ec60fecafc99ee3e2 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Sun, 10 May 2026 19:12:22 +0000 Subject: [PATCH 207/545] patch 9.2.0470: No way to hook into put commands Problem: No way to hook into put commands (yochem) Solution: Introduce TextPutPre and TextPutPost autocommands (Foxe Chen). fixes: #18701 closes: #20144 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- runtime/doc/autocmd.txt | 43 ++++++++- runtime/doc/tags | 2 + runtime/doc/version9.txt | 2 + runtime/syntax/vim.vim | 6 +- src/autocmd.c | 21 +++++ src/getchar.c | 5 + src/globals.h | 8 ++ src/proto/autocmd.pro | 2 + src/register.c | 178 ++++++++++++++++++++++++++++++----- src/testdir/test_autocmd.vim | 125 ++++++++++++++++++++++++ src/version.c | 2 + src/vim.h | 2 + 12 files changed, 368 insertions(+), 28 deletions(-) diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index eba6e43542..62cd761e19 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1,4 +1,4 @@ -*autocmd.txt* For Vim version 9.2. Last change: 2026 Feb 25 +*autocmd.txt* For Vim version 9.2. Last change: 2026 May 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -421,6 +421,8 @@ Name triggered by ~ |TextChangedP| after a change was made to the text in Insert mode when popup menu visible |TextChangedT| after a change was made to the text in Terminal mode +|TextPutPost| after text has been put +|TextPutPre| before text is put |TextYankPost| after text has been yanked or deleted |SafeState| nothing pending, going to wait for the user to type a @@ -1359,6 +1361,45 @@ TextChangedP After a change was made to the text in the TextChangedT After a change was made to the text in the current buffer in Terminal mode. Otherwise the same as TextChanged. + *TextPutPost* +TextPutPost After text has been put in the current buffer. + The following values in |v:event| are mostly + the same as |TextYankPost|: + operator The operation performed, + either 'p' or 'P'. + regcontents Text that was put. For + |quote_=|, this is the result + of the expression. + regname Name of the register or empty + string for the unnamed + register. + regtype Type of the register, see + |getregtype()|. + visual True if the operation is + performed in |Visual| mode. + Not triggered when |quote_| is used nor when + called recursively. + It is not allowed to change the buffer text, + see |textlock|. + Note that for the |quote_.| register, since + the last inserted text is buffered into the + input buffer (buffer isn't modified directly), + this autocommand is called directly after + |TextPutPre|. + {only when compiled with the +eval feature} + *TextPutPre* +TextPutPre Before text has been put in the current buffer. + Same values as |TextPutPost| in |v:event|. It + is valid to call |setreg()| in this + autocommand, allowing you to process and + modify the text in "regcontents" before it is + put. However this does not apply to |quote_#|, + |quote_=|, |quote_%|, |quote_:|, |quote_/| or |quote_.|. + Not triggered when |quote_| is used nor when + called recursively. + It is not allowed to change the buffer text, + see |textlock|. + {only when compiled with the +eval feature} *TextYankPost* TextYankPost After text has been yanked or deleted in the current buffer. The following values of diff --git a/runtime/doc/tags b/runtime/doc/tags index 8eef8cf28b..d5a8a1b30c 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -5969,6 +5969,8 @@ TextChanged autocmd.txt /*TextChanged* TextChangedI autocmd.txt /*TextChangedI* TextChangedP autocmd.txt /*TextChangedP* TextChangedT autocmd.txt /*TextChangedT* +TextPutPost autocmd.txt /*TextPutPost* +TextPutPre autocmd.txt /*TextPutPre* TextYankPost autocmd.txt /*TextYankPost* Transact-SQL ft_sql.txt /*Transact-SQL* Tuple eval.txt /*Tuple* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index f4cdc702b7..45a25b08bb 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52669,6 +52669,8 @@ Functions: ~ Autocommands: ~ |SessionLoadPre| before loading a |Session| file +|TextPutPost| after putting text +|TextPutPre| before putting text Options: ~ diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 698e551a7d..1cf3b9940e 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -2,7 +2,7 @@ " Language: Vim script " Maintainer: Hirohito Higashi " Doug Kearns -" Last Change: 2026 May 08 +" Last Change: 2026 May 10 " Former Maintainer: Charles E. Campbell " DO NOT CHANGE DIRECTLY. @@ -134,8 +134,8 @@ syn keyword vimErrSetting contained invakm invaltkeymap invanti invantialias inv syn case ignore " GEN_SYN_VIM: vimAutoEvent, START_STR='syn keyword vimAutoEvent contained', END_STR='skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern' syn keyword vimAutoEvent contained BufAdd BufCreate BufDelete BufEnter BufFilePost BufFilePre BufHidden BufLeave BufNew BufNewFile BufRead BufReadCmd BufReadPost BufReadPre BufUnload BufWinEnter BufWinLeave BufWipeout BufWrite BufWriteCmd BufWritePost BufWritePre CmdlineChanged CmdlineEnter CmdlineLeave CmdlineLeavePre CmdUndefined CmdwinEnter CmdwinLeave ColorScheme ColorSchemePre CompleteChanged CompleteDone CompleteDonePre CursorHold CursorHoldI CursorMoved CursorMovedC CursorMovedI DiffUpdated DirChanged DirChangedPre EncodingChanged ExitPre FileAppendCmd FileAppendPost FileAppendPre FileChangedRO FileChangedShell FileChangedShellPost FileEncoding FileReadCmd FileReadPost FileReadPre FileType FileWriteCmd FileWritePost FileWritePre FilterReadPost FilterReadPre skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern -syn keyword vimAutoEvent contained FilterWritePost FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave InsertLeavePre KeyInputPre MenuPopup ModeChanged OptionSet QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SafeState SafeStateAgain SessionLoadPost SessionLoadPre SessionWritePost ShellCmdPost ShellFilterPost SigUSR1 SourceCmd SourcePost SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabClosed TabClosedPre TabEnter TabLeave TabNew TermChanged TerminalOpen TerminalWinOpen TermResponse TermResponseAll TextChanged TextChangedI TextChangedP TextChangedT TextYankPost VimEnter VimLeave VimLeavePre VimResized VimResume VimSuspend WinClosed WinEnter WinLeave WinNew WinNewPre skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern -syn keyword vimAutoEvent contained WinResized WinScrolled skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern +syn keyword vimAutoEvent contained FilterWritePost FilterWritePre FocusGained FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre InsertEnter InsertLeave InsertLeavePre KeyInputPre MenuPopup ModeChanged OptionSet QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SafeState SafeStateAgain SessionLoadPost SessionLoadPre SessionWritePost ShellCmdPost ShellFilterPost SigUSR1 SourceCmd SourcePost SourcePre SpellFileMissing StdinReadPost StdinReadPre SwapExists Syntax TabClosed TabClosedPre TabEnter TabLeave TabNew TermChanged TerminalOpen TerminalWinOpen TermResponse TermResponseAll TextChanged TextChangedI TextChangedP TextChangedT TextPutPost TextPutPre TextYankPost VimEnter VimLeave VimLeavePre VimResized VimResume VimSuspend WinClosed WinEnter skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern +syn keyword vimAutoEvent contained WinLeave WinNew WinNewPre WinResized WinScrolled skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern syn keyword vimAutoEvent contained User skipwhite nextgroup=vimUserAutoEvent syn match vimUserAutoEvent contained "\<\h\w*\>" skipwhite nextgroup=vimUserAutoEventSep,vimAutocmdMod,vimAutocmdBlock diff --git a/src/autocmd.c b/src/autocmd.c index 78965529db..3b16a6b5b3 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -195,6 +195,8 @@ static keyvalue_T event_tab[NUM_EVENTS] = { KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDI, "TextChangedI"), KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDP, "TextChangedP"), KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDT, "TextChangedT"), + KEYVALUE_ENTRY(-EVENT_TEXTPUTPOST, "TextPutPost"), + KEYVALUE_ENTRY(-EVENT_TEXTPUTPRE, "TextPutPre"), KEYVALUE_ENTRY(-EVENT_TEXTYANKPOST, "TextYankPost"), KEYVALUE_ENTRY(EVENT_USER, "User"), KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"), @@ -2023,6 +2025,25 @@ has_cmdundefined(void) } #if defined(FEAT_EVAL) + +/* + * Return TRUE when there is a TextPutPost autocommand defined. + */ + int +has_textputpost(void) +{ + return (first_autopat[(int)EVENT_TEXTPUTPOST] != NULL); +} + +/* + * Return TRUE when there is a TextPutPre autocommand defined. + */ + int +has_textputpre(void) +{ + return (first_autopat[(int)EVENT_TEXTPUTPRE] != NULL); +} + /* * Return TRUE when there is a TextYankPost autocommand defined. */ diff --git a/src/getchar.c b/src/getchar.c index 1fe155dcd9..2e3079a1e4 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -714,6 +714,11 @@ stuffRedoReadbuff(char_u *s) void stuffReadbuffLen(char_u *s, long len) { +#ifdef FEAT_EVAL + if (add_last_insert == 1) // Only add if this is the first call, for + // recursive calls, ignore. + ga_concat_len(&last_insert_ga, s, (size_t)len); +#endif add_buff(&readbuf1, s, len); } diff --git a/src/globals.h b/src/globals.h index 9a6c285937..2fdc944648 100644 --- a/src/globals.h +++ b/src/globals.h @@ -2165,3 +2165,11 @@ EXTERN bool inside_redraw_on_start_cb INIT(= false); // If greater than zero, then silence the W23/W24 warning. EXTERN int silence_w23_w24_msg INIT( = 0); + +#ifdef FEAT_EVAL +// Used by TextPutPost/TextPutPre autocommands for the '.' register. If +// "add_last_insert" is == 1, then "stuff_inserted" will add the last inserted +// text to "last_insert_ga". +EXTERN garray_T last_insert_ga INIT5(0, 0, 1, 64, NULL); +EXTERN int add_last_insert INIT(= 0); +#endif diff --git a/src/proto/autocmd.pro b/src/proto/autocmd.pro index ce1f75910a..abff7fd414 100644 --- a/src/proto/autocmd.pro +++ b/src/proto/autocmd.pro @@ -29,6 +29,8 @@ int has_textchangedP(void); int has_insertcharpre(void); int has_keyinputpre(void); int has_cmdundefined(void); +int has_textputpost(void); +int has_textputpre(void); int has_textyankpost(void); int has_completechanged(void); int has_modechanged(void); diff --git a/src/register.c b/src/register.c index 9f0e06b546..44e6aba6f5 100644 --- a/src/register.c +++ b/src/register.c @@ -1081,6 +1081,36 @@ shift_delete_registers(void) } #if defined(FEAT_EVAL) + static void +add_regtype_to_dict(int regname, dict_T *dict, char_u *buf, int bufsize) +{ + size_t buflen; + long reglen; + // Register type + switch (get_reg_type(regname, ®len)) + { + case MLINE: + buf[0] = 'V'; + buf[1] = NUL; + buflen = 1; + break; + case MCHAR: + buf[0] = 'v'; + buf[1] = NUL; + buflen = 1; + break; + case MBLOCK: + buflen = vim_snprintf_safelen((char *)buf, bufsize, + "%c%ld", Ctrl_V, reglen + 1); + break; + default: + buf[0] = NUL; + buflen = 0; + break; + } + (void)dict_add_string_len(dict, "regtype", buf, (int)buflen); +} + void yank_do_autocmd(oparg_T *oap, yankreg_T *reg) { @@ -1090,7 +1120,6 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg) int n; char_u buf[NUMBUFLEN + 2]; size_t buflen; - long reglen = 0; save_v_event_T save_v_event; if (recursive) @@ -1124,29 +1153,7 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg) buflen = (buf[0] == NUL) ? 0 : (buf[1] == NUL) ? 1 : 2; (void)dict_add_string_len(v_event, "operator", buf, (int)buflen); - // register type - switch (get_reg_type(oap->regname, ®len)) - { - case MLINE: - buf[0] = 'V'; - buf[1] = NUL; - buflen = 1; - break; - case MCHAR: - buf[0] = 'v'; - buf[1] = NUL; - buflen = 1; - break; - case MBLOCK: - buflen = vim_snprintf_safelen((char *)buf, sizeof(buf), - "%c%ld", Ctrl_V, reglen + 1); - break; - default: - buf[0] = NUL; - buflen = 0; - break; - } - (void)dict_add_string_len(v_event, "regtype", buf, (int)buflen); + add_regtype_to_dict(oap->regname, v_event, buf, sizeof(buf)); // selection type - visual or not (void)dict_add_bool(v_event, "visual", oap->is_VIsual); @@ -1163,6 +1170,85 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg) // Empty the dictionary, v:event is still valid restore_v_event(v_event, &save_v_event); } + + static void +put_do_autocmd( + int regname, + yankreg_T *reg, // May be NULL, if special register + string_T *insert, // Not NULL if special register, except '.' + bool post, // If Post or Pre + int dir) // BACKWARD for 'P', FORWARD for 'p' +{ + static bool recursive = false; + dict_T *v_event; + list_T *list; + int n; + char_u buf[NUMBUFLEN + 2]; + size_t buflen; + save_v_event_T save_v_event; + + if (recursive || regname == '_') + return; + + v_event = get_v_event(&save_v_event); + + list = list_alloc(); + if (list == NULL) + return; + + if (regname == '.') + { + if (last_insert_ga.ga_data != NULL) + // Get the last inserted text to place in "regcontents" + list_append_string(list, last_insert_ga.ga_data, + (int)last_insert_ga.ga_len); + } + else if (insert != NULL) + { + list_append_string(list, insert->string, (int)insert->length); + } + else + { + assert(reg != NULL); + for (n = 0; n < reg->y_size; n++) + list_append_string(list, reg->y_array[n].string, + (int)reg->y_array[n].length); + } + + list->lv_lock = VAR_FIXED; + (void)dict_add_list(v_event, "regcontents", list); + + // register name or empty string for unnamed operation + buf[0] = (char_u)regname; + buf[1] = NUL; + buflen = (buf[0] == NUL) ? 0 : 1; + (void)dict_add_string_len(v_event, "regname", buf, (int)buflen); + + // kind of operation (P, p) + buf[0] = dir == BACKWARD ? 'P' : 'p'; + buf[1] = NUL; + buflen = 1; + (void)dict_add_string_len(v_event, "operator", buf, (int)buflen); + + add_regtype_to_dict(regname, v_event, buf, sizeof(buf)); + + (void)dict_add_bool(v_event, "visual", VIsual_active); + + // Lock the dictionary and its keys + dict_set_items_ro(v_event); + + recursive = true; + textlock++; + if (post) + apply_autocmds(EVENT_TEXTPUTPOST, NULL, NULL, FALSE, curbuf); + else + apply_autocmds(EVENT_TEXTPUTPRE, NULL, NULL, FALSE, curbuf); + textlock--; + recursive = false; + + // Empty the dictionary, v:event is still valid + restore_v_event(v_event, &save_v_event); +} #endif /* @@ -1648,8 +1734,28 @@ do_put( { if (VIsual_active) stuffcharReadbuff(VIsual_mode); + +#ifdef FEAT_EVAL + if (has_textputpre() || has_textputpost()) + add_last_insert++; +#endif + (void)stuff_inserted((dir == FORWARD ? (count == -1 ? 'o' : 'a') : (count == -1 ? 'O' : 'i')), count, FALSE); + +#ifdef FEAT_EVAL + // Since the text is not inserted into the buffer immediately, just call + // TextPutPost after TextPutPre. + if (has_textputpre()) + put_do_autocmd('.', NULL, NULL, false, dir); + + if (has_textputpost()) + put_do_autocmd('.', NULL, NULL, true, dir); + + if (--add_last_insert == 0) + ga_clear(&last_insert_ga); +#endif + // Putting the text is done later, so can't really move the cursor to // the next character. Use "l" to simulate it. if ((flags & PUT_CURSEND) && gchar_cursor() != NUL) @@ -1733,9 +1839,22 @@ do_put( y_size = 1; // use fake one-line yank register y_array = &insert_string; } +#ifdef FEAT_EVAL + if (has_textputpre()) + put_do_autocmd(regname, NULL, &insert_string, false, dir); +#endif } else { +#ifdef FEAT_EVAL + if (has_textputpre()) + { + // Make sure to call this before we set the variables, as setreg() + // may be called and invalidate them. + get_yank_register(regname, FALSE); + put_do_autocmd(regname, y_current, NULL, false, dir); + } +#endif get_yank_register(regname, FALSE); y_type = y_current->y_type; @@ -2401,6 +2520,17 @@ do_put( curbuf->b_op_start = orig_start; curbuf->b_op_end = orig_end; } + +#ifdef FEAT_EVAL + if (has_textputpost()) + { + if (insert_string.string == NULL) + put_do_autocmd(regname, y_current, NULL, true, dir); + else + put_do_autocmd(regname, NULL, &insert_string, true, dir); + } +#endif + if (allocated) vim_free(insert_string.string); if (regname == '=') diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 592dd47150..2a3616893f 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -5967,4 +5967,129 @@ func Test_autocmd_add_secure() call assert_fails('sandbox call autocmd_delete([{"event": "BufRead"}])', 'E48:') endfunc +func Test_TextPutX() + enew! + + let g:pre_event = [] + let g:post_event = [] + au TextPutPre * let g:pre_event = copy(v:event) + au TextPutPost * let g:post_event = copy(v:event) + + call setreg('a', ['foo'], 'v') + norm "ap + call assert_equal( + \ #{regcontents: ['foo'], regname: 'a', operator: 'p', + \ visual: v:false, regtype: 'v'}, + \ g:pre_event) + call assert_equal(g:pre_event, g:post_event) + + call setreg('', ['hello'], 'V') + norm P + call assert_equal( + \ #{regcontents: ['hello'], regname: '', operator: 'P', + \ visual: v:false, regtype: 'V'}, + \ g:pre_event) + call assert_equal(g:pre_event, g:post_event) + + call setreg('', ['maybe', 'true'], 'V') + norm Vp + call assert_equal( + \ #{regcontents: ['maybe', 'true'], regname: '', operator: 'P', + \ regtype: 'V', visual: v:true}, + \ g:pre_event) + call assert_equal(g:pre_event, g:post_event) + call assert_equal({}, v:event) + + call feedkeys("iinserted text\below\", 'x') + norm ".p + call assert_equal( + \ #{regcontents: ["inserted text\nbelow"], regname: '.', + \ operator: 'p', regtype: 'v', visual: v:false}, + \ g:pre_event) + call assert_equal(g:pre_event, g:post_event) + + call feedkeys("\"=201\p", 'x') + call assert_equal( + \ #{regcontents: ["201"], regname: '=', + \ operator: 'p', regtype: 'v', visual: v:false}, + \ g:pre_event) + call assert_equal(g:pre_event, g:post_event) + + vsplit some.txt + wincmd l + norm "#p + call assert_equal( + \ #{regcontents: ["some.txt"], regname: '#', + \ operator: 'p', regtype: 'v', visual: v:false}, + \ g:pre_event) + call assert_equal(g:pre_event, g:post_event) + wincmd h + bw! + + if has('clipboard_working') + let @+ = 'clipboard' + + norm "+p + call assert_equal( + \ #{regcontents: ["clipboard"], regname: '+', + \ operator: 'p', regtype: 'v', visual: v:false}, + \ g:pre_event) + call assert_equal(g:pre_event, g:post_event) + endif + + %delete " Clear buffer + + au! TextPutPre + au! TextPutPost + let g:pre_event = [] + let g:post_event = [] + + au TextPutPre * call setreg(v:event['regname'], + \ getreg('', 0, v:true) + ['!']) " Unnamed register should be same as regname + + call setreg('', ['hello', 'world']) + norm p + call assert_equal(['', 'hello', 'world', '!'], getline(1, '$')) + + au! TextPutPre + + " Test that special registers cannot be modified + %delete + au TextPutPre * call setreg('=', '"modified"') | let g:pre_event = copy(v:event) + + " Set up the expression register to evaluate to a known value. + call feedkeys("\"=\"original\"\p", 'x') + + " The original value is what got put, not the modified one. + call assert_equal(['original'], getline(1, '$')) + + " v:event still reports the original value. + call assert_equal(['original'], g:pre_event['regcontents']) + + au! TextPutPre + let g:pre_event = [] + + " Test that recursive ". register calls have the same contents for post and + " pre + au TextPutPre * put . | let g:pre_event = copy(v:event) + au TextPutPost * let g:post_event = copy(v:event) + + call feedkeys("iinserted\", 'x') + norm! ".p + + call assert_equal( + \ #{regcontents: ["inserted"], regname: '.', + \ operator: 'p', regtype: 'v', visual: v:false}, + \ g:pre_event) + call assert_equal(g:pre_event, g:post_event) + + au! TextPutPre + au! TextPutPost + + unlet g:post_event + unlet g:pre_event + bwipe! + +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 2882be5795..5e5f17f85a 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 470, /**/ 469, /**/ diff --git a/src/vim.h b/src/vim.h index d9a22ef018..25c81f2f8b 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1484,6 +1484,8 @@ enum auto_event EVENT_TEXTCHANGEDI, // text was modified in Insert mode EVENT_TEXTCHANGEDP, // TextChangedI with popup menu visible EVENT_TEXTCHANGEDT, // text was modified in Terminal mode + EVENT_TEXTPUTPOST, // after some text was put + EVENT_TEXTPUTPRE, // before some text was put EVENT_TEXTYANKPOST, // after some text was yanked EVENT_USER, // user defined autocommand EVENT_VIMENTER, // after starting Vim From bfebd1209bd39b3c8afe5dceae37a20152efac35 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 11 May 2026 10:15:02 +0200 Subject: [PATCH 208/545] runtime(javacc): Check for existence of javaFuncDef syn group before clearing it fixes: #20190 Signed-off-by: Christian Brabandt --- runtime/syntax/javacc.vim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/syntax/javacc.vim b/runtime/syntax/javacc.vim index a80572d510..f81e12d29b 100644 --- a/runtime/syntax/javacc.vim +++ b/runtime/syntax/javacc.vim @@ -3,6 +3,7 @@ " Maintainer: Claudio Fleiner " URL: http://www.fleiner.com/vim/syntax/javacc.vim " Last Change: 2012 Oct 05 +" 2026 May 11 by Vim project: check for existence of javaFuncDef before clearing it " Uses java.vim, and adds a few special things for JavaCC Parser files. " Those files usually have the extension *.jj @@ -33,7 +34,9 @@ syn clear javaError2 " remove function definitions (they look different) (first define in " in case it was not defined in java.vim) "syn match javaFuncDef "--" -syn clear javaFuncDef +if hlexists('javaFuncDef') + syn clear javaFuncDef +endif syn match javaFuncDef "[$_a-zA-Z][$_a-zA-Z0-9_. \[\]]*([^-+*/()]*)[ \t]*:" contains=javaType syn keyword javaccPackages options DEBUG_PARSER DEBUG_LOOKAHEAD DEBUG_TOKEN_MANAGER From 852f4f43caa08ae6a49cab8dcf93b1a0739f6743 Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Mon, 11 May 2026 16:18:56 +0000 Subject: [PATCH 209/545] runtime(doc): Fix manpage typo in description of '--ttyfail' This seems to have been accidentally introduced in ce6fe84db21f00fd30b226200dbb02709514871a. closes: #20186 Signed-off-by: Matthew Fernandez Signed-off-by: Christian Brabandt --- runtime/doc/vim.1 | 2 +- runtime/doc/vim.man | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/doc/vim.1 b/runtime/doc/vim.1 index df39fb2b24..cf9a080482 100644 --- a/runtime/doc/vim.1 +++ b/runtime/doc/vim.1 @@ -515,7 +515,7 @@ GTK GUI only: Use the GtkPlug mechanism to run gVim in another window. During startup write timing messages to the file {fname}. .TP \-\-ttyfail -When stdin or stdout is not a a terminal (tty) then exit right away. +When stdin or stdout is not a terminal (tty) then exit right away. .TP \-\-version Print version information and exit. diff --git a/runtime/doc/vim.man b/runtime/doc/vim.man index c1484bebb0..ab21a4b825 100644 --- a/runtime/doc/vim.man +++ b/runtime/doc/vim.man @@ -396,7 +396,7 @@ OPTIONS --startuptime {file} During startup write timing messages to the file {fname}. - --ttyfail When stdin or stdout is not a a terminal (tty) then exit + --ttyfail When stdin or stdout is not a terminal (tty) then exit right away. --version Print version information and exit. From 1b65cfbac5c5bbad108548908e77697111ad4a3f Mon Sep 17 00:00:00 2001 From: John Marriott Date: Mon, 11 May 2026 16:22:05 +0000 Subject: [PATCH 210/545] patch 9.2.0471: vimvars di_key initialized at runtime Problem: evalvars_init() copies each vimvar's name into di_key at startup and runtime-checks that the name fits in DICTITEM16_KEY_LEN, even though all names are known at compile time. Solution: Embed the name in di_key via the VV_NAME macro so the initialization happens at compile time. Drop the runtime length check and the STRCPY loop (John Marriott). closes: #20185 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/evalvars.c | 8 +------- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/evalvars.c b/src/evalvars.c index 1df4af9972..9b98ae7310 100644 --- a/src/evalvars.c +++ b/src/evalvars.c @@ -37,7 +37,7 @@ static hashtab_T compat_hashtab; #define VV_RO 2 // read-only #define VV_RO_SBX 4 // read-only in the sandbox -#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {0}} +#define VV_NAME(s, t) s, {{t, 0, {0}}, 0, {s}} typedef struct vimvar vimvar_T; @@ -216,12 +216,6 @@ evalvars_init(void) for (i = 0; i < VV_LEN; ++i) { p = &vimvars[i]; - if (STRLEN(p->vv_name) > DICTITEM16_KEY_LEN) - { - iemsg("Name too long, increase size of dictitem16_T"); - getout(1); - } - STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; else if (p->vv_flags & VV_RO_SBX) diff --git a/src/version.c b/src/version.c index 5e5f17f85a..875c6f6740 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 471, /**/ 470, /**/ From 51f1447d2a094ae0a7e1749da6e2dafe6fdc1883 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 11 May 2026 16:29:27 +0000 Subject: [PATCH 211/545] patch 9.2.0472: popup: column jitters when scrolled outside viewport Problem: popup: column jitters horizontally when textprop is scrolled above the host window's top (after v9.2.0469) Solution: Compute the virtual column from the prop's actual line via getvcol() and translate it through prop_win's win_col_off / leftcol / wincol (Yasuhiro Matsumoto). popup_screenpos_above_top() probed textpos2screenpos() at prop_win->w_topline using the prop's own tp_col, so the returned screen_scol picked up topline's tab stops and multi-byte widths instead of the prop line's own. Once the textprop scrolled above the host's top, the popup's wincol jittered left/right every time a wider or narrower line rotated into the topmost slot. Compute the virtual column from the prop's actual line via getvcol() and translate it through prop_win's win_col_off / leftcol / wincol. Row extrapolation from topline is unchanged. closes: #20187 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/popupwin.c | 26 ++++++++++++++++++-------- src/version.c | 2 ++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/popupwin.c b/src/popupwin.c index 5bd156367e..685c3cae7c 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -1482,11 +1482,15 @@ popup_geom_restore(win_T *wp, popup_geom_save_T *sv) /* * Compute a screen row for a textprop that has scrolled above the host - * window's top. textpos2screenpos() cannot return a row above topline, so we - * probe at topline to fill the screen_{scol,ccol,ecol} column mapping, then - * extrapolate a (possibly-negative) row by counting how many buffer lines lie - * between the prop and topline. The popup_topoff clip path then turns the + * window's top. textpos2screenpos() cannot return a row above topline, so + * compute the virtual column directly from the prop's *own* line and then + * extrapolate a (possibly-negative) row by counting how many buffer lines + * lie between the prop and topline. The popup_topoff clip path turns the * negative row into a top-clip animation as the prop rolls off the top edge. + * + * Probing at topline with the prop's tp_col would inherit topline's tab + * stops / multi-byte widths, so the popup's wincol would jitter every time + * a wider/narrower line scrolled into the topmost position. */ static void popup_screenpos_above_top( @@ -1499,10 +1503,16 @@ popup_screenpos_above_top( int *screen_ecol) { pos_T probe = *pos; - - probe.lnum = prop_win->w_topline; - textpos2screenpos(prop_win, &probe, - screen_row, screen_scol, screen_ccol, screen_ecol); + colnr_T scol = 0, ccol = 0, ecol = 0; + int coloff; + + probe.lnum = prop_lnum; + getvcol(prop_win, &probe, &scol, &ccol, &ecol, 0); + coloff = (int)win_col_off(prop_win) - (int)prop_win->w_leftcol + + prop_win->w_wincol + 1; + *screen_scol = (int)scol + coloff; + *screen_ccol = (int)ccol + coloff; + *screen_ecol = (int)ecol + coloff; *screen_row = prop_win->w_winrow + 1 - (int)(prop_win->w_topline - prop_lnum); } diff --git a/src/version.c b/src/version.c index 875c6f6740..3aff29a296 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 472, /**/ 471, /**/ From a70b7a85afad9dc8eeae2a97142dc744cf3205e6 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Mon, 11 May 2026 16:49:19 +0000 Subject: [PATCH 212/545] patch 9.2.0473: Pasting ". register without autocommands breaks TextPut* Problem: Pasting ". register without TextPut* autocommands breaks subsequent TextPut* autocommands (after 9.2.0470). Solution: Only decrement add_last_insert if it has been incremented (zeertzjq). closes: #20192 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/register.c | 6 +++--- src/testdir/test_autocmd.vim | 31 ++++++++++++++++++------------- src/version.c | 2 ++ 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/register.c b/src/register.c index 44e6aba6f5..86ac02cfc1 100644 --- a/src/register.c +++ b/src/register.c @@ -1736,7 +1736,8 @@ do_put( stuffcharReadbuff(VIsual_mode); #ifdef FEAT_EVAL - if (has_textputpre() || has_textputpost()) + bool has_textput_events = has_textputpre() || has_textputpost(); + if (has_textput_events) add_last_insert++; #endif @@ -1748,11 +1749,10 @@ do_put( // TextPutPost after TextPutPre. if (has_textputpre()) put_do_autocmd('.', NULL, NULL, false, dir); - if (has_textputpost()) put_do_autocmd('.', NULL, NULL, true, dir); - if (--add_last_insert == 0) + if (has_textput_events && --add_last_insert == 0) ga_clear(&last_insert_ga); #endif diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index 2a3616893f..ecd5791c90 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -6069,22 +6069,27 @@ func Test_TextPutX() au! TextPutPre let g:pre_event = [] - " Test that recursive ". register calls have the same contents for post and - " pre - au TextPutPre * put . | let g:pre_event = copy(v:event) - au TextPutPost * let g:post_event = copy(v:event) + for round in range(2) + " Recursive ". register calls have the same contents for post and pre. + au TextPutPre * put . | let g:pre_event = copy(v:event) + au TextPutPost * let g:post_event = copy(v:event) - call feedkeys("iinserted\", 'x') - norm! ".p + call feedkeys("iinserted\", 'x') + norm! ".p - call assert_equal( - \ #{regcontents: ["inserted"], regname: '.', - \ operator: 'p', regtype: 'v', visual: v:false}, - \ g:pre_event) - call assert_equal(g:pre_event, g:post_event) + call assert_equal( + \ #{regcontents: ["inserted"], regname: '.', + \ operator: 'p', regtype: 'v', visual: v:false}, + \ g:pre_event) + call assert_equal(g:pre_event, g:post_event) - au! TextPutPre - au! TextPutPost + au! TextPutPre + au! TextPutPost + + " Pasting ". register without TextPutPre/TextPutPost autocommands should + " not interfere with these autocommands in the next round. + norm! ".p + endfor unlet g:post_event unlet g:pre_event diff --git a/src/version.c b/src/version.c index 3aff29a296..f96e170e4e 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 473, /**/ 472, /**/ From 7784fac15f7ae729b4422c5efc2db48bd15a8548 Mon Sep 17 00:00:00 2001 From: "K.Takata" Date: Mon, 11 May 2026 16:56:33 +0000 Subject: [PATCH 213/545] patch 9.2.0474: MS-Windows: hard to tell which Visual Studio version was selected with MSVC Problem: When running msvc*.bat there is no indication of which Visual Studio version and target architecture got selected. Solution: After vcvarsall.bat returns, echo the VS version, VC tools version and target architecture, and set the Command Prompt title accordingly (Ken Takata). closes: #20193 Signed-off-by: K.Takata Signed-off-by: Christian Brabandt --- src/msvc-latest.bat | 25 +++++++++++++++---------- src/msvc2015.bat | 14 ++++++++++++++ src/msvc2017.bat | 4 ++++ src/msvc2019.bat | 4 ++++ src/msvc2022.bat | 4 ++++ src/version.c | 2 ++ 6 files changed, 43 insertions(+), 10 deletions(-) diff --git a/src/msvc-latest.bat b/src/msvc-latest.bat index 4529fa02eb..93ac705702 100644 --- a/src/msvc-latest.bat +++ b/src/msvc-latest.bat @@ -39,18 +39,16 @@ if "%VSVEROPT%"=="" ( ) rem Search Visual Studio Community, Professional or above. +set InstallDir= for /f "usebackq tokens=*" %%i in (`"%VSWHERE%" %VSVEROPT% -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do ( set InstallDir=%%i ) -if exist "%InstallDir%\VC\Auxiliary\Build\vcvarsall.bat" ( - call "%InstallDir%\VC\Auxiliary\Build\vcvarsall.bat" %* - goto done -) - -rem Search Visual Studio 2017 Express. -rem (Visual Studio 2017 Express uses different component IDs.) -for /f "usebackq tokens=*" %%i in (`"%VSWHERE%" %VSVEROPT% -products Microsoft.VisualStudio.Product.WDExpress -property installationPath`) do ( - set InstallDir=%%i +if not defined InstallDir ( + rem Search Visual Studio 2017 Express. + rem (Visual Studio 2017 Express uses different component IDs.) + for /f "usebackq tokens=*" %%i in (`"%VSWHERE%" %VSVEROPT% -products Microsoft.VisualStudio.Product.WDExpress -property installationPath`) do ( + set InstallDir=%%i + ) ) if exist "%InstallDir%\VC\Auxiliary\Build\vcvarsall.bat" ( call "%InstallDir%\VC\Auxiliary\Build\vcvarsall.bat" %* @@ -60,7 +58,14 @@ if exist "%InstallDir%\VC\Auxiliary\Build\vcvarsall.bat" ( call ) -:done +if defined VCToolsVersion ( + if "%VSVEROPT%"=="-latest" ( + echo VCTools %VCToolsVersion% %VSCMD_ARG_TGT_ARCH% + for /f "tokens=1,2 delims=." %%I in ("%VCToolsVersion%") do ( + title VCTools %%I.%%J %VSCMD_ARG_TGT_ARCH% + ) + ) +) if "%VSWHERE_SET%"=="yes" ( set VSWHERE= set VSWHERE_SET= diff --git a/src/msvc2015.bat b/src/msvc2015.bat index b541719ac2..2ee864ca6a 100644 --- a/src/msvc2015.bat +++ b/src/msvc2015.bat @@ -12,4 +12,18 @@ rem If you use Community (or Professional) edition, you can also use "x64" rem option: rem msvc2015 x64 +set Platform= +if not exist "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" ( + echo Error: vcvarsall.bat not found. + exit /b 1 +) call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" %* +if defined VisualStudioVersion ( + if defined Platform ( + echo VS 2015 ^(%VisualStudioVersion%^) %Platform% + title VS 2015 %Platform% + ) else ( + echo VS 2015 ^(%VisualStudioVersion%^) x86 + title VS 2015 x86 + ) +) diff --git a/src/msvc2017.bat b/src/msvc2017.bat index c7731c92c9..569eddd153 100644 --- a/src/msvc2017.bat +++ b/src/msvc2017.bat @@ -11,3 +11,7 @@ rem msvc2017 x86_amd64 set "VSVEROPT=-version [15.0^,16.0^)" call "%~dp0msvc-latest.bat" %* set VSVEROPT= +if defined VCToolsVersion ( + echo VS 2017 ^(%VCToolsVersion%^) %VSCMD_ARG_TGT_ARCH% + title VS 2017 %VSCMD_ARG_TGT_ARCH% +) diff --git a/src/msvc2019.bat b/src/msvc2019.bat index a45ef2c564..16a893f1dc 100644 --- a/src/msvc2019.bat +++ b/src/msvc2019.bat @@ -11,3 +11,7 @@ rem msvc2019 x64 set "VSVEROPT=-version [16.0^,17.0^)" call "%~dp0msvc-latest.bat" %* set VSVEROPT= +if defined VCToolsVersion ( + echo VS 2019 ^(%VCToolsVersion%^) %VSCMD_ARG_TGT_ARCH% + title VS 2019 %VSCMD_ARG_TGT_ARCH% +) diff --git a/src/msvc2022.bat b/src/msvc2022.bat index b707410070..67a9491b67 100644 --- a/src/msvc2022.bat +++ b/src/msvc2022.bat @@ -11,3 +11,7 @@ rem msvc2022 x64 set "VSVEROPT=-version [17.0^,18.0^)" call "%~dp0msvc-latest.bat" %* set VSVEROPT= +if defined VCToolsVersion ( + echo VS 2022 ^(%VCToolsVersion%^) %VSCMD_ARG_TGT_ARCH% + title VS 2022 %VSCMD_ARG_TGT_ARCH% +) diff --git a/src/version.c b/src/version.c index f96e170e4e..9240b09958 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 474, /**/ 473, /**/ From 74019bea8c97077bfd66cd63dc0ffa279fd729ac Mon Sep 17 00:00:00 2001 From: "J. Paulo Seibt" Date: Mon, 11 May 2026 17:08:48 +0000 Subject: [PATCH 214/545] patch 9.2.0475: runtime(netrw): bookmark paths not normalized Problem: the bookmarks list can have duplicate entries, more often in win32 (due to mixed slashes and capitalization) and when g:netrw_keepdir=0 (which could introduce relative paths). Duplicate entries could be: C:\foo\BAR\baz.file c:\foo\bar\baz.file c:/foo\BAR/baz.file BAR/baz.file Solution: Normalize the paths and make sure they are always absolute (J. Paulo Seibt). closes: #20194 Signed-off-by: J. Paulo Seibt Signed-off-by: Christian Brabandt --- runtime/doc/pi_netrw.txt | 2 ++ .../pack/dist/opt/netrw/autoload/netrw.vim | 31 ++++++++++++------- runtime/pack/dist/opt/netrw/doc/netrw.txt | 2 ++ src/testdir/test_plugin_netrw.vim | 12 +++++-- src/version.c | 2 ++ 5 files changed, 36 insertions(+), 13 deletions(-) diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 2d98a8407b..3f744bc8f5 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1142,6 +1142,8 @@ One may easily "bookmark" the currently browsed directory by using > mb < +Note: Bookmarked paths are normalized and stored as absolute paths. + *.netrwbook* Bookmarks are retained in between sessions of vim in a file called .netrwbook as a |List|, which is typically stored in the first directory on the user's diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index 7d73af8fae..fbd396566a 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -1,7 +1,7 @@ " Creator: Charles E Campbell " Previous Maintainer: Luca Saccarola " Maintainer: This runtime file is looking for a new maintainer. -" Last Change: 2026 May 10 +" Last Change: 2026 May 11 " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -8979,21 +8979,30 @@ function s:MakeSshCmd(sshcmd) return sshcmd endfunction -" s:MakeBookmark: enters a bookmark into Netrw's bookmark system {{{2 +" s:MakeBookmark: enters a bookmark into Netrw's bookmark system {{{2 +" Note that bookmark paths should always be absolute. function s:MakeBookmark(fname) if !exists("g:netrw_bookmarklist") - let g:netrw_bookmarklist= [] + let g:netrw_bookmarklist = [] endif - if index(g:netrw_bookmarklist,a:fname) == -1 - " curdir not currently in g:netrw_bookmarklist, so include it - if isdirectory(s:NetrwFile(a:fname)) && a:fname !~ '/$' - " Directory without a trailing slash - call add(g:netrw_bookmarklist, s:NetrwFile(a:fname) . '/') - else - call add(g:netrw_bookmarklist, s:NetrwFile(a:fname)) - endif + " Normalize path to prevent duplicate entries + let bookmark_path = netrw#fs#AbsPath(s:NetrwFile(a:fname)) + let ignore_case = 0 + if has('win32') + let bookmark_path = substitute(bookmark_path, '\\', '/', 'ge') + let ignore_case = 1 + endif + let bookmark_path = simplify(bookmark_path) + + if isdirectory(bookmark_path) && bookmark_path !~ '/$' + let bookmark_path .= '/' + endif + + if index(g:netrw_bookmarklist, bookmark_path, 0, ignore_case) == -1 + " Not currently in the bookmarks list, so include it + call add(g:netrw_bookmarklist, bookmark_path) call sort(g:netrw_bookmarklist) endif diff --git a/runtime/pack/dist/opt/netrw/doc/netrw.txt b/runtime/pack/dist/opt/netrw/doc/netrw.txt index 144bab5fb3..168deed345 100644 --- a/runtime/pack/dist/opt/netrw/doc/netrw.txt +++ b/runtime/pack/dist/opt/netrw/doc/netrw.txt @@ -1142,6 +1142,8 @@ One may easily "bookmark" the currently browsed directory by using > mb < +Note: Bookmarked paths are normalized and stored as absolute paths. + *.netrwbook* Bookmarks are retained in between sessions of vim in a file called .netrwbook as a |List|, which is typically stored in the first directory on the user's diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim index 5a86c29b7b..c6bd589a43 100644 --- a/src/testdir/test_plugin_netrw.vim +++ b/src/testdir/test_plugin_netrw.vim @@ -692,7 +692,7 @@ func Test_netrw_bookmark_marked_file() " Make sure Vim's working directory diverge from Netrw's let g:netrw_keepdir = 1 let g:netrw_bookmarklist = [] - let test_workdir = 'Xtest_workdir' + let test_workdir = getcwd() . '/Xtest_workdir' let test_netrw_curdir = test_workdir . '/Xtest_netrw_curdir' call mkdir(test_netrw_curdir, 'p') call writefile([], test_netrw_curdir . '/test_file') @@ -700,8 +700,16 @@ func Test_netrw_bookmark_marked_file() execute 'cd ' . test_workdir call Test_MakeBookmark(test_netrw_curdir, 'test_file') + " Bookmark paths should be absolute and normalized to prevent duplicates on win32 + let expected_path = netrw#fs#AbsPath(test_netrw_curdir . '/test_file') + if has('win32') + let expected_path = substitute(expected_path, '\\', '/', 'ge') + endif + let expected_path = simplify(expected_path) + call assert_equal(1, len(g:netrw_bookmarklist)) - call assert_match(test_netrw_curdir . '/test_file', g:netrw_bookmarklist[0]) + call assert_equal(expected_path, g:netrw_bookmarklist[0]) + call assert_true(isabsolutepath(g:netrw_bookmarklist[0]), 'Bookmark paths should be absolute') " Tear down execute 'cd ' . save_workdir diff --git a/src/version.c b/src/version.c index 9240b09958..9e794b7ed3 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 475, /**/ 474, /**/ From f45ed6465dd6d219b72c61758158dd4038a5ef95 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Mon, 11 May 2026 17:19:19 +0000 Subject: [PATCH 215/545] runtime(hlyamk): Allow to highlight put regions using TextPutPost closes: #20196 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- runtime/doc/usr_05.txt | 12 +++++++- runtime/doc/version9.txt | 4 ++- .../pack/dist/opt/hlyank/plugin/hlyank.vim | 28 ++++++++++++++++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index 56e7fb9e78..4a3444b61a 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -1,4 +1,4 @@ -*usr_05.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*usr_05.txt* For Vim version 9.2. Last change: 2026 May 11 VIM USER MANUAL by Bram Moolenaar @@ -509,6 +509,16 @@ To highlight in visual mode, use: > To disable the effect of the plugin after it has been loaded: > au! hlyank +Additionally, the plugin can also highlight regions that are put using the +|TextPutPost| autocommand. This is by default disabled and can be enabled +using: > + :let g:hlput_enable = v:true +< +The following configuration variables can be used are "g:hlput_hlgroup" and +"g:hlput_duration", which have the same effect as their yank counterparts: > + :let g:hlput_hlgroup = 'IncSearch' + :let g:hlput_duration = 300 + ------------------------------------------------------------------------------ Adding the osc52 package *osc52-install* *package-osc52* ------------------------------------------------------------------------------ diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 45a25b08bb..5121fe90c6 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 May 10 +*version9.txt* For Vim version 9.2. Last change: 2026 May 11 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52627,6 +52627,8 @@ Other ~ - |:command-completion-customlist| can return a list of dictionaries with kind/menu/info/abbr for the popup menu. - |C-indenting| detects comments better. +- The |package-hlyank| can now optionally highlight the last put region as + well. Platform specific ~ ----------------- diff --git a/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim b/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim index 3e0cdb9bfb..27e2a032ed 100644 --- a/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim +++ b/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim @@ -1,7 +1,7 @@ vim9script # Highlight Yank plugin -# Last Change: 2026 Apr 11 +# Last Change: 2026 May 11 def HighlightedYank() @@ -36,8 +36,34 @@ def HighlightedYank() endif enddef +export def HighlightedPut() + if !get(g:, "hlput_enable", false) + return + endif + + var hlgroup = get(g:, "hlput_hlgroup", "IncSearch") + var duration = min([get(g:, "hlput_duration", 300), 3000]) + + var [beg, end] = [getpos("'["), getpos("']")] + var type = v:event.regtype ?? 'v' + var pos = getregionpos(beg, end, {type: type, exclusive: false}) + + var m = matchaddpos(hlgroup, pos->mapnew((_, v) => { + var col_beg = v[0][2] + v[0][3] + var col_end = v[1][2] + v[1][3] + 1 + return [v[0][1], col_beg, col_end - col_beg] + })) + var winid = win_getid() + timer_start(duration, (_) => { + if winbufnr(winid) != -1 + m->matchdelete(winid) + endif + }) +enddef + augroup hlyank autocmd! autocmd TextYankPost * HighlightedYank() + autocmd TextPutPost * HighlightedPut() augroup END # vim:sts=2:sw=2:et: From 78302b7b4aaa4449b21cc8b966b6baed63f7531e Mon Sep 17 00:00:00 2001 From: Antonio Giovanni Colombo Date: Tue, 12 May 2026 17:29:22 +0000 Subject: [PATCH 216/545] translation(it): Update Italian manpage Signed-off-by: Christian Brabandt --- runtime/doc/vim-it.1 | 288 ++++++++++++++++++++----------------- runtime/doc/vim-it.UTF-8.1 | 288 ++++++++++++++++++++----------------- 2 files changed, 306 insertions(+), 270 deletions(-) mode change 100644 => 100755 runtime/doc/vim-it.1 mode change 100644 => 100755 runtime/doc/vim-it.UTF-8.1 diff --git a/runtime/doc/vim-it.1 b/runtime/doc/vim-it.1 old mode 100644 new mode 100755 index 09e1730208..329f1e64f1 --- a/runtime/doc/vim-it.1 +++ b/runtime/doc/vim-it.1 @@ -36,8 +36,9 @@ Pu Particolarmente utile per editare programmi. .PP Ci sono parecchi miglioramenti rispetto a Vi: undo multipli, finestre e buffer -multipli, evidenziazione sintattica, possibilità di modificare la riga di comando, -completamento nomi file, help in linea, selezione testi in Modo Visual, etc.. +multipli, evidenziazione sintattica, possibilità di modificare la riga di +comando, completamento nomi file, help online, selezione testi in Modo +Visual, etc. Vedere ":help vi_diff.txt" per un sommario delle differenze fra .B Vim e Vi. @@ -67,9 +68,10 @@ essere usata per scegliere uno o pi nome_file .. Una lista di nomi di file. Il primo di questi sarà il file corrente, e verrà letto nel buffer. -Il cursore sarà posizionato sulla prima linea del buffer. +Il cursore sarà posizionato sulla prima riga del buffer. Si può arrivare agli altri file col comando ":next". -Per editare un file il cui nome inizia per "\-" premettete "\-\-" alla lista_file. +Per editare un file il cui nome inizia per "\-" premettete "\-\-" alla +lista_file. .TP \- Il file da editare è letto dallo stdin. @@ -77,7 +79,7 @@ I comandi sono letti da stderr, che dovrebbe essere un terminale [tty]. .TP \-t {tag} Il file da editare e la posizione iniziale del cursore dipendono da "tag", -una specie di "etichetta" a cui saltare. +una "etichetta" a cui saltare. {tag} viene cercata nel file "tags", e il file a essa associato diventa quello corrente, e il comando a essa associato viene eseguito. Di solito si usa per programmi C, nel qual caso {tag} potrebbe essere un @@ -87,7 +89,7 @@ e il cursore Vedere ":help tag\-commands". .TP \-q [file_errori] -Inizia in Modo QuickFix [correzione veloce]. +Iniziare in Modo QuickFix [correzione veloce]. Il file [file_errori] è letto e il primo errore è visualizzato. Se [file_errori] non è indicato, il suo nome è ottenuto dal valore dell'opzione 'errorfile' (che, se non specificata, vale "AztecC.Err" @@ -97,28 +99,28 @@ Vedere ":help quickfix". .PP .B Vim si comporta in modo diverso se invocato con nomi differenti (il programma -eseguibile "soggiacente" può essere sempre lo stesso). +eseguibile "sottostante" può essere sempre lo stesso). .TP 10 vim -Modo Normal, comportamento predefinito. +Iniziare in Modo Normal, comportamento predefinito. .TP ex -Inizia in Modo "Ex". +Iniziare in Modo "Ex". Si può passare in Modo Normal col comando ":vi". Si può invocare il Modo "Ex" anche con l'argomento "\-e". .TP view -Inizia in Modo Read-only (Sola Lettura). Non si possono modificare i file. +Iniziare in Modo Read-only (sola-lettura). Non si possono modificare i file. Si può invocare il Modo Read-only anche con l'argomento "\-R". .TP gvim gview -La versione GUI [Graphical User Interface]. -Apre una nuova finestra. +Usare la versione GUI [Graphical User Interface]. +Viene aperta una nuova finestra. Si può invocare il Modo GUI anche con l'argomento "\-g". .TP evim eview -La versione GUI in Modo Easy (semplificata). -Apre una nuova finestra. +Usare la versione GUI in Modo Easy (semplificato). +Viene aperta una nuova finestra. Si può invocare il Modo Easy anche con l'argomento "\-y". .TP rvim rview rgvim rgview @@ -128,21 +130,21 @@ della shell o sospendere Si può chiedere la stessa cosa anche con l'argomento "\-Z". .SH OPZIONI Le opzioni possono essere in un ordine qualsiasi, prima o dopo i nomi di file. -Opzioni che non hanno un argomento si possono specificare dietro a un solo "\-". +Opzioni che non hanno un argomento si possono specificare insieme dietro a un +solo "\-". .TP 12 +[numero] -Nel primo file il cursore sarà posizionato sulla linea "numero". -Se "numero" manca, il cursore sarà posizionato sull'ultima linea del file. +Nel primo file, posizionare il cursore sulla riga numero "numero". +Se "numero" manca, il cursore sarà posizionato sull'ultima riga del file. .TP +/{espressione} -Nel primo file il cursore sarà posizionato alla -prima occorrenza di {espressione}. +Nel primo file posizionare il cursore alla prima occorrenza di {espressione}. Vedere ":help search\-pattern" per come specificare l'espressione. .TP +{comando} .TP \-c {comando} -{comando} sarà eseguito dopo che il primo file è stato letto. +Eseguire {comando} dopo che il primo file è stato letto. {comando} è interpretato come un comando Ex. Se il {comando} contiene spazi deve essere incluso fra doppi apici (o altro delimitatore, a seconda della shell che si sta usando). @@ -154,7 +156,7 @@ Note: Si possono avere fino a 10 comandi "+" o "\-c". Se .B Vim è stato compilato con supporto ARABIC per editare file con orientamento -destra-sinistra e tastiera con mappatura araba, quest'opzione inizia +destra-sinistra e tastiera con mappatura araba, quest'opzione fa iniziare .B Vim in Modo Arabic, cioè impostando 'arabic'. Altrimenti viene dato un messaggio di errore e @@ -162,32 +164,32 @@ Altrimenti viene dato un messaggio di errore e termina in modo anormale. .TP \-b -Modo Binary (binario). -Vengono impostate alcune opzioni che permettono di modificare un file -binario o un file che contiene un programma eseguibile. +Eseguire Vim in Modo Binary (binario). +Vengono impostate alcune opzioni che permettono di modificare un file binario +o un file che contiene un programma eseguibile. .TP \-C -Compatibile. Imposta l'opzione 'compatible'. +Compatibile. Impostare l'opzione 'compatible'. In questo modo .B Vim ha quasi lo stesso comportamento di Vi, anche in presenza di un file .vimrc. .TP \-d -Inizia in Modo Diff [differenze]. -Dovrebbero esserci come argomenti da due o otto nomi di file. +Iniziare in Modo Diff [differenze]. +Dovrebbero esserci come argomenti da due fino a otto nomi di file. .B Vim aprirà tutti i file evidenziando le differenze fra gli stessi. Funziona come vimdiff(1). .TP \-d {dispositivo}, \-dev {dispositivo} -Apre {dispositivo} per usarlo come terminale. +Aprire {dispositivo} per usarlo come terminale. Solo per l'Amiga. Esempio: "\-d con:20/30/600/150". .TP \-D -Debugging. Vim si mette in Modo "debugging" a partire -dall'esecuzione del primo comando da uno script. +Debugging. Entrare in Modo "debugging" a partire dall'esecuzione del primo +comando di uno script. .TP \-e Eseguire @@ -202,15 +204,15 @@ in Modo Ex migliorato, come se il programma eseguito sia "exim". \-f Direttamente [Foreground]. Per la versione GUI, .B Vim -non crea [fork] una nuova finestra, indipendente dalla shell di invocazione. -Per l'Amiga, +non creare [fork] una nuova finestra, indipendente dalla shell di invocazione. +Per l'Amiga, non fa ripartire .B Vim -non è fatto ripartire per aprire una nuova finestra. +per aprire una nuova finestra. Opzione da usare quando .B Vim -è eseguito da un programma che attende la fine della -sessione di edit (p.es., mail). -Sull'Amiga i comandi ":sh" e ":!" non sono disponibili. +è eseguito da un programma che attende la fine della sessione di edit (p.es., +mail). +In ambiente Amiga, i comandi ":sh" e ":!" non sono disponibili. .TP \-F Se @@ -229,7 +231,8 @@ Nota: Il supporto Farsi Se .B Vim è stato compilato con supporto GUI, quest'opzione chiede di usarla. -Se Vim è stato compilato senza supporto GUI viene dato un messaggio di errore e +Se Vim è stato compilato senza supporto GUI viene dato un messaggio di +errore e .B Vim termina in modo anormale. .TP @@ -246,145 +249,148 @@ termina in modo anormale. .TP \-i {viminfo} Se è abilitato l'uso di un file viminfo, quest'opzione indica il nome -del file da usare invece di quello predefinito "~/.viminfo". +del file da usare invece del file predefinito "~/.viminfo". Si può anche evitare l'uso di un file .viminfo, dando come nome "NONE". .TP \-l -Modo Lisp. -Imposta le opzioni 'lisp' e 'showmatch'. +Eseguire Vim in Modo Lisp. +Vengono impostate le opzioni 'lisp' e 'showmatch'. .TP \-L Equivalente a \-r. .TP \-m -Inibisce modifica file. -Inibisce l'opzione 'write'. +Inibire modifica file. +Viene inibita l'opzione 'write'. È ancora possibile modificare un buffer, ma non riscriverlo. .TP \-M Modifiche non permesse. Le opzioni 'modifiable' e 'write' sono annullate, -in modo da impedire sia modifiche che riscritture. Da notare che queste -opzioni possono essere abilitate in seguito, permettendo così modifiche. +in modo da impedire sia modifiche che riscritture. Si noti che queste opzioni +possono essere cambiate in seguito, per consentire modifiche. .TP \-n -Inibisce l'uso di un file di swap. -Il recupero dopo una caduta di macchina sarà impossibile. +Inibire l'uso di un file di swap. +Ripristinare il file in caso di fine anormale sarà impossibile. Utile per editare un file su un supporto molto lento (p.es., floppy). Il comando ":set uc=0" ha lo stesso effetto. -Per abilitare il recupero usare ":set uc=200". +Per abilitare il ripristino usare ":set uc=200". .TP \-N -Modo "Non-compatibile". Annulla l'opzione 'compatible'. +Eseguire Vim in Modo "Non-compatibile". Annulla l'opzione 'compatible'. Così .B Vim -si comporta un po' meglio, ma è meno compatibile con Vi, anche in assenza di un -file .vimrc. +si comporta un po' meglio, ma è meno compatibile con Vi, anche in assenza di +un file .vimrc. .TP \-nb -Diviene un Editor server per NetBeans. Vedere la documentazione per dettagli. +Fare di Vim un Editor server per NetBeans. Vedere la documentazione per +dettagli. .TP \-o[N] -Apre N finestre in orizzontale. -Se N manca, apre una finestra per ciascun file. +Aprire N finestre in orizzontale. +Se N manca, aprire una finestra per ciascun file. .TP \-O[N] -Apre N finestre, in verticale. -Se N manca, apre una finestra per ciascun file. +Aprire N finestre, in verticale. +Se N manca, aprire una finestra per ciascun file. .TP \-p[N] -Apre N pagine di linguette. -Quando N è omesso, apre una pagine di linguette per ciascun file. +Aprire N pagine di schede. +Quando N è omesso, aprire una pagine di schede per ciascun file. .TP \-P {titolo-padre} -Solo per GUI Win32: Specifica il titolo dell'applicazione-padre. Se possibile, -Vim viene eseguito in una finestra MDI (Multiple-Document Interface). -{titolo-padre} deve apparire nel titolo della applicazione-padre. Accertatevi +Solo per GUI Win32: Specificare il titolo dell'applicazione-padre. Se +possibile, Vim viene eseguito in una finestra MDI +(Multiple-Document Interface). +{titolo-padre} deve apparire nel titolo dell'applicazione-padre. Accertarsi che sia sufficientemente esplicativo. Notare che l'implementazione è ancora rudimentale. Non funziona per tutte le applicazioni, e il menù non funziona. .TP \-r -Lista file di swap, e informazioni su come usarli per ripristinare file. +Listare file di swap, con informazioni su come usarli per ripristinare file. .TP \-r {file} -Modo Recovery (ripristino). +Eseguire Vim in Modo Recovery (ripristino). Il file di swap è usato per recuperare una sessione di edit finita male. -Il file di swap è un file con lo stesso nome file del file di testo -editato, col suffisso ".swp". +Il file di swap è un file con lo stesso nome file del file di testo editato, +col prefisso "." e col suffisso ".swp". Vedere ":help recovery". .TP \-R -Modo Read-only (Sola Lettura). -Imposta l'opzione 'readonly'. -Si può ancora modificare il buffer, ma il file è protetto da una riscrittura -involontaria. -Se si vuole davvero riscrivere il file, occorre aggiungere un punto esclamativo -al comando Ex, come in ":w!". +Eseguire Vim in Modo Read-only (Sola Lettura). +Viene impostata l'opzione 'readonly'. +È ancora possibile modificare il buffer, ma il file è protetto da una +riscrittura involontaria. +Se si vuole davvero riscrivere il file, occorre aggiungere un punto +esclamativo al comando Ex, come in ":w!". L'opzione \-R implica anche l'opzione \-n (vedere sopra). L'opzione 'readonly' può essere annullata con ":set noro". Vedere ":help 'readonly'". .TP \-s -Modo silenzioso. Solo quando invocato come "Ex" o quando l'opzione -"\-e" è stata data prima dell'opzione "\-s". +Eseguire Vim in Modo silenzioso. Solo quando invocato come "Ex" o quando +l'opzione "\-e" è stata specificata prima dell'opzione "\-s". .TP \-s {script_in_input} -Lo script file {script_in_input} è letto. +Eseguire lo script file {script_in_input}. I caratteri nel file sono interpretati come se immessi da terminale. Lo stesso risultato si può ottenere col comando ":source! {script_in_input}". Se la fine del file di input viene raggiunta prima che Vim termini, l'ulteriore input verrà preso dalla tastiera. .TP \-S {file} -I comandi contenuti in {file} sono eseguiti dopo la lettura del primo file. +Eseguire i comandi contenuti in {file} dopo la lettura del primo file. Equivalente a \-c "source {file}". {file} non può avere un nome che inizia per '\-'. Se {file} è omesso si usa "Session.vim" (funziona solo se \-S è l'ultimo argomento specificato). .TP \-T {terminale} -Dice a +Specificare a .B Vim -quale tipo di terminale state usando. +il tipo di terminale che si sta usando. Utile solo se il terminale non viene riconosciuto correttamente da Vim. Dovrebbe essere un terminale noto a .B Vim (predefinito) o definito nei file termcap o terminfo. .TP \-u {vimrc} -Usa i comandi nel file {vimrc} per inizializzazioni. +Usare i comandi nel file {vimrc} per inizializzazioni. Tutte le altre inizializzazioni non sono eseguite. Usare quest'opzione per editare qualche file di tipo speciale. Si possono anche omettere tutte le inizializzazioni dando come nome "NONE". Vedere ":help initialization" da vim per ulteriori dettagli. .TP \-U {gvimrc} -Usa i comandi nel file {gvimrc} per inizializzazioni GUI. +Usare i comandi nel file {gvimrc} per inizializzare la GUI. Tutte le altre inizializzazioni GUI non sono eseguite. -Si possono anche omettere tutte le inizializzazioni GUI dando come nome "NONE". +Si possono anche omettere tutte le inizializzazioni GUI dando come nome +"NONE". Vedere ":help gui\-init" da vim per ulteriori dettagli. .TP \-v -Inizia +Iniziare .B Vim in Modo Vi, come se il programma eseguibile fosse "vi". Questo ha effetto solo quando Vim viene invocato con il nome "ex". .TP \-V[N] -Verboso. Vim manda messaggi relativi ai file di script che esegue -e quando legge o scrive un file viminfo. Il numero opzionale N è il valore -dell'opzione 'verbose'. Il valore predefinito è 10. +Verboso. Fare inviare a Vim messaggi relativi ai file di script eseguiti +e alla lettura/scrittura di un file viminfo. Il numero opzionale N è il +valore dell'opzione 'verbose'. Il valore predefinito è 10. .TP \-V[N]{nome_file} -Come \-V imposta 'verbosefile' a {nome_file}. Il risultato è che i messaggi -non sono visualizzati, ma scritti sul file {nome_file}. Il {nome_file} non -deve iniziare con un numero. +Come \-V, e si imposta 'verbosefile' a {nome_file}. Il risultato è che i +messaggi non sono visualizzati, ma scritti sul file {nome_file}. Il +{nome_file} non deve iniziare con un numero. .TP \-w{numero} -Imposta l'opzione 'window' a {numero}. +Impostare l'opzione 'window' a {numero}. .TP \-w {script_file} -Ogni carattere immesso viene registrato nel file {script_file}, +Registrare ogni carattere immesso nel file {script_file}, finché non si esce da .B Vim. Utile se si vuole creare uno script file da usare con "vim \-s" o @@ -402,7 +408,7 @@ nella scrittura dei file. Verr .TP \-X Non connettersi al server X. Vim parte più rapidamente, -ma il titolo della finestra e la clipboard non sono usati. +ma il titolo della finestra e gli appunti non sono disponibili. .TP \-Y Non connettersi al compositore Wayland. @@ -410,17 +416,19 @@ Non connettersi al compositore Wayland. \-y Eseguire .B Vim -in Modo Easy (semplificato), come se l'eseguibile invocato sia "evim" o "eview". +in Modo Easy (semplificato), come se l'eseguibile invocato sia "evim" o +"eview". Fa sì che .B Vim si comporti come un editor che usa solo il mouse e i caratteri. .TP \-Z -Modo ristretto. Vim si comporta come se invocato con un nome che inizia per "r". +Eseguire Vim in Modo ristretto. Vim si comporta come se invocato con un nome +che inizia per "r". .TP \-\- -Specifica la fine delle opzioni. -Argomenti specificati dopo questo sono considerati nomi file. +In questo modo si specifica la fine delle opzioni. +Argomenti aggiunti dopo questo sono considerati nomi file. Si può usare per editare un file il cui nome inizi per '-'. .TP \-\-clean @@ -428,40 +436,43 @@ Richiede di non usare alcun file di personalizzazione (vimrc, plugin, etc.). Utile per verificare se un problema persiste invocando Vim "originale". .TP \-\-cmd {comando} -Come "\-c", ma il comando è eseguito subito PRIMA -di eseguire qualsiasi file vimrc. -Si possono usare fino a 10 di questi comandi, indipendentemente dai comandi "\-c". +Come "\-c", ma eseguire il comando subito PRIMA di eseguire qualsiasi file +vimrc. +Si possono usare fino a 10 di questi comandi, indipendentemente dai comandi +"\-c". .TP \-\-echo\-wid -Solo per GUI GTK: Visualizza ID di Window su stdout. +Solo per GUI GTK: Visualizzare ID di Window su stdout. .TP \-\-gui-dialog-file {nome} Quando si usa la GUI, invece di visualizzare un dialogo, il titolo e il messaggio del dialogo sono scritti sul file {nome}. Il file viene creato o, se già esistente, viene esteso. Quest'opzione serve solo in fase di test, -per evitare di restare bloccati da un dialogo che non si riesce a visualizzare. +per evitare di restare bloccati da un dialogo che non si riesce a +visualizzare. Se si sta lavorando senza la GUI l'argomento viene ignorato. .TP \-\-help, \-h, \-? -Un po' di aiuto su opzioni e argomenti che si possono dare invocando Vim. +Visualizzare opzioni e argomenti che si possono specificare invocando Vim. Subito dopo .B Vim esce. .TP \-\-literal -Considera i nomi passati come argomenti letterali, senza espandere metacaratteri. +Considerare i nomi passati come argomenti letterali, senza espandere +metacaratteri. Non ha effetto in Unix, dove la shell espande comunque i metacaratteri. .TP \-\-log {nome_file} Se .B Vim -è stato compilato con le funzionalità eval e channel, inizia a registrare -e scrive le registrazioni a {nome_file}. Ciò equivale a chiamare +è stato compilato con le funzionalità eval e channel, iniziare a registrare +e scrivere le registrazioni a {nome_file}. Ciò equivale a chiamare .I ch_logfile({nome_file}, 'ao') in una fase molto iniziale dell'avvio del programma. .TP \-\-nofork -Direttamente [Foreground]. Per la versione GUI, +Eseguire Vim irettamente [Foreground]. Per la versione GUI, .B Vim non crea [fork] una nuova finestra, indipendente dalla shell di invocazione. .TP @@ -471,17 +482,19 @@ Non caricare plugin. Implicito se si specifica \-u NONE. \-\-not\-a\-term Da usare per specificare a .B Vim -che l'utente è consapevole che l'input e l'output non avvengono con un terminale -vero e proprio. Ciò serve per evitare il messaggio di avvertimento e il ritardo -di due secondi che avverrebbero in assenza di questo argomento. +che l'utente è consapevole che l'input e l'output non avvengono con un +terminale vero e proprio. Ciò serve per evitare il messaggio di avvertimento +e il ritardo di due secondi, ossia il comportamento previsto in assenza di +questo argomento. .TP \-\-remote -Connettersi a un server Vim e chiedere di editare i file elencati come altri -argomenti. Se non si trova un server viene dato un messaggio e i file sono -editati nel Vim corrente. +Connettersi a un server Vim a cui chiedere di editare i file elencati come +altri argomenti. Se non si trova un server viene dato un messaggio e i file +sono editati nel Vim corrente. .TP -\-\-remote\-expr {expr} -Connettersi a un server Vim, valutare {expr} e stampare il risultato su stdout. +\-\-remote\-expr {espressione} +Connettersi a un server Vim, valutare {espressione} e stampare il risultato su +stdout. .TP \-\-remote\-send {chiavi} Connettersi a un server Vim e spedirgli {chiavi}. @@ -490,16 +503,16 @@ Connettersi a un server Vim e spedirgli {chiavi}. Come \-\-remote, ma senza avvisare se non si trova un server. .TP \-\-remote-wait -Come \-\-remote, ma Vim non termina finché i file non sono stati editati. +Come \-\-remote, ma non terminare Vim finché i file non sono stati editati. .TP \-\-remote\-wait\-silent Come \-\-remote\-wait, ma senza avvisare se non si trova un server. .TP \-\-serverlist -Elenca i nomi di tutti i server Vim disponibili. +Elencare i nomi di tutti i server Vim disponibili. .TP \-\-servername {nome} -Usa {nome} come nome del server usato per il Vim corrente, a meno che sia +Usare {nome} come nome del server usato per il Vim corrente, a meno che sia usato con l'argomento \-\-remote, nel qual caso indica il server a cui connettersi. Quando si sta usando il server a socket [socketserver backend], se il nome @@ -512,19 +525,20 @@ rispettivamente, o "socket" o "x11". Disponibile solo se Vim compilato con le due funzionalità socketserver e X11. .TP \-\-socketid {id} -Solo per GUI GTK: Usa meccanismo GtkPlug per eseguire gVim in un'altra finestra. +Solo per GUI GTK: Usare meccanismo GtkPlug per eseguire gVim in un'altra +finestra. .TP \-\-startuptime {nome_file} -Durante la fase iniziale, scrive messaggi di log al file {nome_file}. +Durante la fase iniziale, scrivere messaggi di log al file {nome_file}. .TP \-\-ttyfail -Quando stdin o stdout non sono un terminale (tty) esce subito da Vim. +Quando stdin o stdout non sono un terminale (tty) uscire subito da Vim. .TP \-\-version -Stampa la versione di Vim ed esce. +Stampare la versione di Vim e uscire. .TP \-\-windowid {id} -Solo per GUI Win32: Chiede a gVim di provare a user l'ID di window {id} +Solo per GUI Win32: Chiedere a gVim di provare a user l'ID di window {id} come padre, in modo da venir eseguito all'interno della finestra specificata. .SH AIUTO ONLINE Battere ":help" in @@ -532,10 +546,11 @@ Battere ":help" in per iniziare. Battere ":help argomento" per ricevere aiuto su uno specifico argomento. Per esempio: ":help ZZ" per ricevere aiuto sul comando "ZZ". -Usare e CTRL\-D per completare gli argomenti (":help cmdline\-completion"). +Usare e CTRL\-D per completare gli argomenti +(":help cmdline\-completion"). Ci sono "tag" nei file di help per passare da un argomento a un altro (simili a legami ipertestuali, vedere ":help"). -Tutti i file di documentazione possono essere navigati così. Ad es.: +Tutti i file di documentazione possono essere navigati così. P.es.: ":help syntax.txt". .SH FILE .TP 15 @@ -546,11 +561,11 @@ I file di documentazione di Usare ":help doc\-file\-list" per avere la lista completa. .br .I vim?? -è il numero di versione corto, p.es., vim91 per indicare -.B Vim 9.1 +è il numero di versione corto, p.es., vim92 per indicare +.B Vim 9.2 .TP /usr/local/share/vim/vim??/doc/tags -Il file di tags usato per trovare informazioni nei file di documentazione. +Il file di tag usato per trovare informazioni nei file di documentazione. .TP /usr/local/share/vim/vim??/syntax/syntax.vim Inizializzazioni sintattiche a livello di sistema. @@ -586,18 +601,21 @@ Inizializzazioni del men a livello di sistema. .TP /usr/local/share/vim/vim??/bugreport.vim -Script Vim per generare una segnalazione di errore. Vedere ":help bugs". +Script Vim per generare una segnalazione di errore. +Vedere ":help bugs". .TP /usr/local/share/vim/vim??/filetype.vim -Script Vim per determinare il tipo di un file dal suo nome. Vedere ":help 'filetype'". +Script Vim per determinare il tipo di un file dal suo nome. +Vedere ":help 'filetype'". .TP /usr/local/share/vim/vim??/scripts.vim -Script Vim per determinare il tipo di un file dal suo contenuto. Vedere ":help 'filetype'". +Script Vim per determinare il tipo di un file dal suo contenuto. +Vedere ":help 'filetype'". .TP /usr/local/share/vim/vim??/print/*.ps File usati per stampa PostScript. .PP -Per informazioni aggiornate [in inglese \- NdT] vedere la home page di Vim: +Per informazioni aggiornate [in inglese] vedere la home page di Vim: .br .SH VEDERE ANCHE @@ -614,12 +632,12 @@ Vedere ":help credits" in Tony Andrews e G.R. (Fred) Walter. In verità, poco o nulla è rimasto del loro codice originale. .SH BUG -Probabilmente. +È probabile che ce ne siano. Vedere ":help todo" per una lista di problemi noti. .PP -Si noti che un certo numero di comportamenti che possono essere considerati errori -da qualcuno, sono in effetti causati da una riproduzione fin troppo fedele del -comportamento di Vi. Se si ritiene che altre cose siano errori "perché Vi si comporta -diversamente", si dia prima un'occhiata al file vi_diff.txt (o si immetta -:help vi_diff.txt da Vim). -Un'occhiata va data anche alle opzioni 'compatible' e 'cpoptions. +Si noti che un certo numero di comportamenti che possono essere visti come +errori, sono in effetti causati da una riproduzione fin troppo fedele del +comportamento di Vi. Se si ritiene che altre cose siano errori "perché Vi si +comporta diversamente", si dia prima un'occhiata al file vi_diff.txt +(o si immetta :help vi_diff.txt da Vim). +Un'occhiata va data anche alle opzioni 'compatible' e 'cpoptions'. diff --git a/runtime/doc/vim-it.UTF-8.1 b/runtime/doc/vim-it.UTF-8.1 old mode 100644 new mode 100755 index caff716602..ffe30b3cbb --- a/runtime/doc/vim-it.UTF-8.1 +++ b/runtime/doc/vim-it.UTF-8.1 @@ -36,8 +36,9 @@ Può essere usato per editare qualsiasi file di testo. Particolarmente utile per editare programmi. .PP Ci sono parecchi miglioramenti rispetto a Vi: undo multipli, finestre e buffer -multipli, evidenziazione sintattica, possibilità di modificare la riga di comando, -completamento nomi file, help in linea, selezione testi in Modo Visual, etc.. +multipli, evidenziazione sintattica, possibilità di modificare la riga di +comando, completamento nomi file, help online, selezione testi in Modo +Visual, etc. Vedere ":help vi_diff.txt" per un sommario delle differenze fra .B Vim e Vi. @@ -67,9 +68,10 @@ essere usata per scegliere uno o più file da modificare. nome_file .. Una lista di nomi di file. Il primo di questi sarà il file corrente, e verrà letto nel buffer. -Il cursore sarà posizionato sulla prima linea del buffer. +Il cursore sarà posizionato sulla prima riga del buffer. Si può arrivare agli altri file col comando ":next". -Per editare un file il cui nome inizia per "\-" premettete "\-\-" alla lista_file. +Per editare un file il cui nome inizia per "\-" premettete "\-\-" alla +lista_file. .TP \- Il file da editare è letto dallo stdin. @@ -77,7 +79,7 @@ I comandi sono letti da stderr, che dovrebbe essere un terminale [tty]. .TP \-t {tag} Il file da editare e la posizione iniziale del cursore dipendono da "tag", -una specie di "etichetta" a cui saltare. +una "etichetta" a cui saltare. {tag} viene cercata nel file "tags", e il file a essa associato diventa quello corrente, e il comando a essa associato viene eseguito. Di solito si usa per programmi C, nel qual caso {tag} potrebbe essere un @@ -87,7 +89,7 @@ e il cursore è posizionato all'inizio della funzione. Vedere ":help tag\-commands". .TP \-q [file_errori] -Inizia in Modo QuickFix [correzione veloce]. +Iniziare in Modo QuickFix [correzione veloce]. Il file [file_errori] è letto e il primo errore è visualizzato. Se [file_errori] non è indicato, il suo nome è ottenuto dal valore dell'opzione 'errorfile' (che, se non specificata, vale "AztecC.Err" @@ -97,28 +99,28 @@ Vedere ":help quickfix". .PP .B Vim si comporta in modo diverso se invocato con nomi differenti (il programma -eseguibile "soggiacente" può essere sempre lo stesso). +eseguibile "sottostante" può essere sempre lo stesso). .TP 10 vim -Modo Normal, comportamento predefinito. +Iniziare in Modo Normal, comportamento predefinito. .TP ex -Inizia in Modo "Ex". +Iniziare in Modo "Ex". Si può passare in Modo Normal col comando ":vi". Si può invocare il Modo "Ex" anche con l'argomento "\-e". .TP view -Inizia in Modo Read-only (Sola Lettura). Non si possono modificare i file. +Iniziare in Modo Read-only (sola-lettura). Non si possono modificare i file. Si può invocare il Modo Read-only anche con l'argomento "\-R". .TP gvim gview -La versione GUI [Graphical User Interface]. -Apre una nuova finestra. +Usare la versione GUI [Graphical User Interface]. +Viene aperta una nuova finestra. Si può invocare il Modo GUI anche con l'argomento "\-g". .TP evim eview -La versione GUI in Modo Easy (semplificata). -Apre una nuova finestra. +Usare la versione GUI in Modo Easy (semplificato). +Viene aperta una nuova finestra. Si può invocare il Modo Easy anche con l'argomento "\-y". .TP rvim rview rgvim rgview @@ -128,21 +130,21 @@ della shell o sospendere Si può chiedere la stessa cosa anche con l'argomento "\-Z". .SH OPZIONI Le opzioni possono essere in un ordine qualsiasi, prima o dopo i nomi di file. -Opzioni che non hanno un argomento si possono specificare dietro a un solo "\-". +Opzioni che non hanno un argomento si possono specificare insieme dietro a un +solo "\-". .TP 12 +[numero] -Nel primo file il cursore sarà posizionato sulla linea "numero". -Se "numero" manca, il cursore sarà posizionato sull'ultima linea del file. +Nel primo file, posizionare il cursore sulla riga numero "numero". +Se "numero" manca, il cursore sarà posizionato sull'ultima riga del file. .TP +/{espressione} -Nel primo file il cursore sarà posizionato alla -prima occorrenza di {espressione}. +Nel primo file posizionare il cursore alla prima occorrenza di {espressione}. Vedere ":help search\-pattern" per come specificare l'espressione. .TP +{comando} .TP \-c {comando} -{comando} sarà eseguito dopo che il primo file è stato letto. +Eseguire {comando} dopo che il primo file è stato letto. {comando} è interpretato come un comando Ex. Se il {comando} contiene spazi deve essere incluso fra doppi apici (o altro delimitatore, a seconda della shell che si sta usando). @@ -154,7 +156,7 @@ Note: Si possono avere fino a 10 comandi "+" o "\-c". Se .B Vim è stato compilato con supporto ARABIC per editare file con orientamento -destra-sinistra e tastiera con mappatura araba, quest'opzione inizia +destra-sinistra e tastiera con mappatura araba, quest'opzione fa iniziare .B Vim in Modo Arabic, cioè impostando 'arabic'. Altrimenti viene dato un messaggio di errore e @@ -162,32 +164,32 @@ Altrimenti viene dato un messaggio di errore e termina in modo anormale. .TP \-b -Modo Binary (binario). -Vengono impostate alcune opzioni che permettono di modificare un file -binario o un file che contiene un programma eseguibile. +Eseguire Vim in Modo Binary (binario). +Vengono impostate alcune opzioni che permettono di modificare un file binario +o un file che contiene un programma eseguibile. .TP \-C -Compatibile. Imposta l'opzione 'compatible'. +Compatibile. Impostare l'opzione 'compatible'. In questo modo .B Vim ha quasi lo stesso comportamento di Vi, anche in presenza di un file .vimrc. .TP \-d -Inizia in Modo Diff [differenze]. -Dovrebbero esserci come argomenti da due o otto nomi di file. +Iniziare in Modo Diff [differenze]. +Dovrebbero esserci come argomenti da due fino a otto nomi di file. .B Vim aprirà tutti i file evidenziando le differenze fra gli stessi. Funziona come vimdiff(1). .TP \-d {dispositivo}, \-dev {dispositivo} -Apre {dispositivo} per usarlo come terminale. +Aprire {dispositivo} per usarlo come terminale. Solo per l'Amiga. Esempio: "\-d con:20/30/600/150". .TP \-D -Debugging. Vim si mette in Modo "debugging" a partire -dall'esecuzione del primo comando da uno script. +Debugging. Entrare in Modo "debugging" a partire dall'esecuzione del primo +comando di uno script. .TP \-e Eseguire @@ -202,15 +204,15 @@ in Modo Ex migliorato, come se il programma eseguito sia "exim". \-f Direttamente [Foreground]. Per la versione GUI, .B Vim -non crea [fork] una nuova finestra, indipendente dalla shell di invocazione. -Per l'Amiga, +non creare [fork] una nuova finestra, indipendente dalla shell di invocazione. +Per l'Amiga, non fa ripartire .B Vim -non è fatto ripartire per aprire una nuova finestra. +per aprire una nuova finestra. Opzione da usare quando .B Vim -è eseguito da un programma che attende la fine della -sessione di edit (p.es., mail). -Sull'Amiga i comandi ":sh" e ":!" non sono disponibili. +è eseguito da un programma che attende la fine della sessione di edit (p.es., +mail). +In ambiente Amiga, i comandi ":sh" e ":!" non sono disponibili. .TP \-F Se @@ -229,7 +231,8 @@ Nota: Il supporto Farsi è stato rimosso a partire dalla patch 8.1.0932. Se .B Vim è stato compilato con supporto GUI, quest'opzione chiede di usarla. -Se Vim è stato compilato senza supporto GUI viene dato un messaggio di errore e +Se Vim è stato compilato senza supporto GUI viene dato un messaggio di +errore e .B Vim termina in modo anormale. .TP @@ -246,145 +249,148 @@ termina in modo anormale. .TP \-i {viminfo} Se è abilitato l'uso di un file viminfo, quest'opzione indica il nome -del file da usare invece di quello predefinito "~/.viminfo". +del file da usare invece del file predefinito "~/.viminfo". Si può anche evitare l'uso di un file .viminfo, dando come nome "NONE". .TP \-l -Modo Lisp. -Imposta le opzioni 'lisp' e 'showmatch'. +Eseguire Vim in Modo Lisp. +Vengono impostate le opzioni 'lisp' e 'showmatch'. .TP \-L Equivalente a \-r. .TP \-m -Inibisce modifica file. -Inibisce l'opzione 'write'. +Inibire modifica file. +Viene inibita l'opzione 'write'. È ancora possibile modificare un buffer, ma non riscriverlo. .TP \-M Modifiche non permesse. Le opzioni 'modifiable' e 'write' sono annullate, -in modo da impedire sia modifiche che riscritture. Da notare che queste -opzioni possono essere abilitate in seguito, permettendo così modifiche. +in modo da impedire sia modifiche che riscritture. Si noti che queste opzioni +possono essere cambiate in seguito, per consentire modifiche. .TP \-n -Inibisce l'uso di un file di swap. -Il recupero dopo una caduta di macchina sarà impossibile. +Inibire l'uso di un file di swap. +Ripristinare il file in caso di fine anormale sarà impossibile. Utile per editare un file su un supporto molto lento (p.es., floppy). Il comando ":set uc=0" ha lo stesso effetto. -Per abilitare il recupero usare ":set uc=200". +Per abilitare il ripristino usare ":set uc=200". .TP \-N -Modo "Non-compatibile". Annulla l'opzione 'compatible'. +Eseguire Vim in Modo "Non-compatibile". Annulla l'opzione 'compatible'. Così .B Vim -si comporta un po' meglio, ma è meno compatibile con Vi, anche in assenza di un -file .vimrc. +si comporta un po' meglio, ma è meno compatibile con Vi, anche in assenza di +un file .vimrc. .TP \-nb -Diviene un Editor server per NetBeans. Vedere la documentazione per dettagli. +Fare di Vim un Editor server per NetBeans. Vedere la documentazione per +dettagli. .TP \-o[N] -Apre N finestre in orizzontale. -Se N manca, apre una finestra per ciascun file. +Aprire N finestre in orizzontale. +Se N manca, aprire una finestra per ciascun file. .TP \-O[N] -Apre N finestre, in verticale. -Se N manca, apre una finestra per ciascun file. +Aprire N finestre, in verticale. +Se N manca, aprire una finestra per ciascun file. .TP \-p[N] -Apre N pagine di linguette. -Quando N è omesso, apre una pagine di linguette per ciascun file. +Aprire N pagine di schede. +Quando N è omesso, aprire una pagine di schede per ciascun file. .TP \-P {titolo-padre} -Solo per GUI Win32: Specifica il titolo dell'applicazione-padre. Se possibile, -Vim viene eseguito in una finestra MDI (Multiple-Document Interface). -{titolo-padre} deve apparire nel titolo della applicazione-padre. Accertatevi +Solo per GUI Win32: Specificare il titolo dell'applicazione-padre. Se +possibile, Vim viene eseguito in una finestra MDI +(Multiple-Document Interface). +{titolo-padre} deve apparire nel titolo dell'applicazione-padre. Accertarsi che sia sufficientemente esplicativo. Notare che l'implementazione è ancora rudimentale. Non funziona per tutte le applicazioni, e il menù non funziona. .TP \-r -Lista file di swap, e informazioni su come usarli per ripristinare file. +Listare file di swap, con informazioni su come usarli per ripristinare file. .TP \-r {file} -Modo Recovery (ripristino). +Eseguire Vim in Modo Recovery (ripristino). Il file di swap è usato per recuperare una sessione di edit finita male. -Il file di swap è un file con lo stesso nome file del file di testo -editato, col suffisso ".swp". +Il file di swap è un file con lo stesso nome file del file di testo editato, +col prefisso "." e col suffisso ".swp". Vedere ":help recovery". .TP \-R -Modo Read-only (Sola Lettura). -Imposta l'opzione 'readonly'. -Si può ancora modificare il buffer, ma il file è protetto da una riscrittura -involontaria. -Se si vuole davvero riscrivere il file, occorre aggiungere un punto esclamativo -al comando Ex, come in ":w!". +Eseguire Vim in Modo Read-only (Sola Lettura). +Viene impostata l'opzione 'readonly'. +È ancora possibile modificare il buffer, ma il file è protetto da una +riscrittura involontaria. +Se si vuole davvero riscrivere il file, occorre aggiungere un punto +esclamativo al comando Ex, come in ":w!". L'opzione \-R implica anche l'opzione \-n (vedere sopra). L'opzione 'readonly' può essere annullata con ":set noro". Vedere ":help 'readonly'". .TP \-s -Modo silenzioso. Solo quando invocato come "Ex" o quando l'opzione -"\-e" è stata data prima dell'opzione "\-s". +Eseguire Vim in Modo silenzioso. Solo quando invocato come "Ex" o quando +l'opzione "\-e" è stata specificata prima dell'opzione "\-s". .TP \-s {script_in_input} -Lo script file {script_in_input} è letto. +Eseguire lo script file {script_in_input}. I caratteri nel file sono interpretati come se immessi da terminale. Lo stesso risultato si può ottenere col comando ":source! {script_in_input}". Se la fine del file di input viene raggiunta prima che Vim termini, l'ulteriore input verrà preso dalla tastiera. .TP \-S {file} -I comandi contenuti in {file} sono eseguiti dopo la lettura del primo file. +Eseguire i comandi contenuti in {file} dopo la lettura del primo file. Equivalente a \-c "source {file}". {file} non può avere un nome che inizia per '\-'. Se {file} è omesso si usa "Session.vim" (funziona solo se \-S è l'ultimo argomento specificato). .TP \-T {terminale} -Dice a +Specificare a .B Vim -quale tipo di terminale state usando. +il tipo di terminale che si sta usando. Utile solo se il terminale non viene riconosciuto correttamente da Vim. Dovrebbe essere un terminale noto a .B Vim (predefinito) o definito nei file termcap o terminfo. .TP \-u {vimrc} -Usa i comandi nel file {vimrc} per inizializzazioni. +Usare i comandi nel file {vimrc} per inizializzazioni. Tutte le altre inizializzazioni non sono eseguite. Usare quest'opzione per editare qualche file di tipo speciale. Si possono anche omettere tutte le inizializzazioni dando come nome "NONE". Vedere ":help initialization" da vim per ulteriori dettagli. .TP \-U {gvimrc} -Usa i comandi nel file {gvimrc} per inizializzazioni GUI. +Usare i comandi nel file {gvimrc} per inizializzare la GUI. Tutte le altre inizializzazioni GUI non sono eseguite. -Si possono anche omettere tutte le inizializzazioni GUI dando come nome "NONE". +Si possono anche omettere tutte le inizializzazioni GUI dando come nome +"NONE". Vedere ":help gui\-init" da vim per ulteriori dettagli. .TP \-v -Inizia +Iniziare .B Vim in Modo Vi, come se il programma eseguibile fosse "vi". Questo ha effetto solo quando Vim viene invocato con il nome "ex". .TP \-V[N] -Verboso. Vim manda messaggi relativi ai file di script che esegue -e quando legge o scrive un file viminfo. Il numero opzionale N è il valore -dell'opzione 'verbose'. Il valore predefinito è 10. +Verboso. Fare inviare a Vim messaggi relativi ai file di script eseguiti +e alla lettura/scrittura di un file viminfo. Il numero opzionale N è il +valore dell'opzione 'verbose'. Il valore predefinito è 10. .TP \-V[N]{nome_file} -Come \-V imposta 'verbosefile' a {nome_file}. Il risultato è che i messaggi -non sono visualizzati, ma scritti sul file {nome_file}. Il {nome_file} non -deve iniziare con un numero. +Come \-V, e si imposta 'verbosefile' a {nome_file}. Il risultato è che i +messaggi non sono visualizzati, ma scritti sul file {nome_file}. Il +{nome_file} non deve iniziare con un numero. .TP \-w{numero} -Imposta l'opzione 'window' a {numero}. +Impostare l'opzione 'window' a {numero}. .TP \-w {script_file} -Ogni carattere immesso viene registrato nel file {script_file}, +Registrare ogni carattere immesso nel file {script_file}, finché non si esce da .B Vim. Utile se si vuole creare uno script file da usare con "vim \-s" o @@ -402,7 +408,7 @@ nella scrittura dei file. Verrà chiesta una chiave di cifratura. .TP \-X Non connettersi al server X. Vim parte più rapidamente, -ma il titolo della finestra e la clipboard non sono usati. +ma il titolo della finestra e gli appunti non sono disponibili. .TP \-Y Non connettersi al compositore Wayland. @@ -410,17 +416,19 @@ Non connettersi al compositore Wayland. \-y Eseguire .B Vim -in Modo Easy (semplificato), come se l'eseguibile invocato sia "evim" o "eview". +in Modo Easy (semplificato), come se l'eseguibile invocato sia "evim" o +"eview". Fa sì che .B Vim si comporti come un editor che usa solo il mouse e i caratteri. .TP \-Z -Modo ristretto. Vim si comporta come se invocato con un nome che inizia per "r". +Eseguire Vim in Modo ristretto. Vim si comporta come se invocato con un nome +che inizia per "r". .TP \-\- -Specifica la fine delle opzioni. -Argomenti specificati dopo questo sono considerati nomi file. +In questo modo si specifica la fine delle opzioni. +Argomenti aggiunti dopo questo sono considerati nomi file. Si può usare per editare un file il cui nome inizi per '-'. .TP \-\-clean @@ -428,40 +436,43 @@ Richiede di non usare alcun file di personalizzazione (vimrc, plugin, etc.). Utile per verificare se un problema persiste invocando Vim "originale". .TP \-\-cmd {comando} -Come "\-c", ma il comando è eseguito subito PRIMA -di eseguire qualsiasi file vimrc. -Si possono usare fino a 10 di questi comandi, indipendentemente dai comandi "\-c". +Come "\-c", ma eseguire il comando subito PRIMA di eseguire qualsiasi file +vimrc. +Si possono usare fino a 10 di questi comandi, indipendentemente dai comandi +"\-c". .TP \-\-echo\-wid -Solo per GUI GTK: Visualizza ID di Window su stdout. +Solo per GUI GTK: Visualizzare ID di Window su stdout. .TP \-\-gui-dialog-file {nome} Quando si usa la GUI, invece di visualizzare un dialogo, il titolo e il messaggio del dialogo sono scritti sul file {nome}. Il file viene creato o, se già esistente, viene esteso. Quest'opzione serve solo in fase di test, -per evitare di restare bloccati da un dialogo che non si riesce a visualizzare. +per evitare di restare bloccati da un dialogo che non si riesce a +visualizzare. Se si sta lavorando senza la GUI l'argomento viene ignorato. .TP \-\-help, \-h, \-? -Un po' di aiuto su opzioni e argomenti che si possono dare invocando Vim. +Visualizzare opzioni e argomenti che si possono specificare invocando Vim. Subito dopo .B Vim esce. .TP \-\-literal -Considera i nomi passati come argomenti letterali, senza espandere metacaratteri. +Considerare i nomi passati come argomenti letterali, senza espandere +metacaratteri. Non ha effetto in Unix, dove la shell espande comunque i metacaratteri. .TP \-\-log {nome_file} Se .B Vim -è stato compilato con le funzionalità eval e channel, inizia a registrare -e scrive le registrazioni a {nome_file}. Ciò equivale a chiamare +è stato compilato con le funzionalità eval e channel, iniziare a registrare +e scrivere le registrazioni a {nome_file}. Ciò equivale a chiamare .I ch_logfile({nome_file}, 'ao') in una fase molto iniziale dell'avvio del programma. .TP \-\-nofork -Direttamente [Foreground]. Per la versione GUI, +Eseguire Vim irettamente [Foreground]. Per la versione GUI, .B Vim non crea [fork] una nuova finestra, indipendente dalla shell di invocazione. .TP @@ -471,17 +482,19 @@ Non caricare plugin. Implicito se si specifica \-u NONE. \-\-not\-a\-term Da usare per specificare a .B Vim -che l'utente è consapevole che l'input e l'output non avvengono con un terminale -vero e proprio. Ciò serve per evitare il messaggio di avvertimento e il ritardo -di due secondi che avverrebbero in assenza di questo argomento. +che l'utente è consapevole che l'input e l'output non avvengono con un +terminale vero e proprio. Ciò serve per evitare il messaggio di avvertimento +e il ritardo di due secondi, ossia il comportamento previsto in assenza di +questo argomento. .TP \-\-remote -Connettersi a un server Vim e chiedere di editare i file elencati come altri -argomenti. Se non si trova un server viene dato un messaggio e i file sono -editati nel Vim corrente. +Connettersi a un server Vim a cui chiedere di editare i file elencati come +altri argomenti. Se non si trova un server viene dato un messaggio e i file +sono editati nel Vim corrente. .TP -\-\-remote\-expr {expr} -Connettersi a un server Vim, valutare {expr} e stampare il risultato su stdout. +\-\-remote\-expr {espressione} +Connettersi a un server Vim, valutare {espressione} e stampare il risultato su +stdout. .TP \-\-remote\-send {chiavi} Connettersi a un server Vim e spedirgli {chiavi}. @@ -490,16 +503,16 @@ Connettersi a un server Vim e spedirgli {chiavi}. Come \-\-remote, ma senza avvisare se non si trova un server. .TP \-\-remote-wait -Come \-\-remote, ma Vim non termina finché i file non sono stati editati. +Come \-\-remote, ma non terminare Vim finché i file non sono stati editati. .TP \-\-remote\-wait\-silent Come \-\-remote\-wait, ma senza avvisare se non si trova un server. .TP \-\-serverlist -Elenca i nomi di tutti i server Vim disponibili. +Elencare i nomi di tutti i server Vim disponibili. .TP \-\-servername {nome} -Usa {nome} come nome del server usato per il Vim corrente, a meno che sia +Usare {nome} come nome del server usato per il Vim corrente, a meno che sia usato con l'argomento \-\-remote, nel qual caso indica il server a cui connettersi. Quando si sta usando il server a socket [socketserver backend], se il nome @@ -512,19 +525,20 @@ rispettivamente, o "socket" o "x11". Disponibile solo se Vim è stato compilato con le due funzionalità socketserver e X11. .TP \-\-socketid {id} -Solo per GUI GTK: Usa meccanismo GtkPlug per eseguire gVim in un'altra finestra. +Solo per GUI GTK: Usare meccanismo GtkPlug per eseguire gVim in un'altra +finestra. .TP \-\-startuptime {nome_file} -Durante la fase iniziale, scrive messaggi di log al file {nome_file}. +Durante la fase iniziale, scrivere messaggi di log al file {nome_file}. .TP \-\-ttyfail -Quando stdin o stdout non sono un terminale (tty) esce subito da Vim. +Quando stdin o stdout non sono un terminale (tty) uscire subito da Vim. .TP \-\-version -Stampa la versione di Vim ed esce. +Stampare la versione di Vim e uscire. .TP \-\-windowid {id} -Solo per GUI Win32: Chiede a gVim di provare a user l'ID di window {id} +Solo per GUI Win32: Chiedere a gVim di provare a user l'ID di window {id} come padre, in modo da venir eseguito all'interno della finestra specificata. .SH AIUTO ONLINE Battere ":help" in @@ -532,10 +546,11 @@ Battere ":help" in per iniziare. Battere ":help argomento" per ricevere aiuto su uno specifico argomento. Per esempio: ":help ZZ" per ricevere aiuto sul comando "ZZ". -Usare e CTRL\-D per completare gli argomenti (":help cmdline\-completion"). +Usare e CTRL\-D per completare gli argomenti +(":help cmdline\-completion"). Ci sono "tag" nei file di help per passare da un argomento a un altro (simili a legami ipertestuali, vedere ":help"). -Tutti i file di documentazione possono essere navigati così. Ad es.: +Tutti i file di documentazione possono essere navigati così. P.es.: ":help syntax.txt". .SH FILE .TP 15 @@ -546,11 +561,11 @@ I file di documentazione di Usare ":help doc\-file\-list" per avere la lista completa. .br .I vim?? -è il numero di versione corto, p.es., vim91 per indicare -.B Vim 9.1 +è il numero di versione corto, p.es., vim92 per indicare +.B Vim 9.2 .TP /usr/local/share/vim/vim??/doc/tags -Il file di tags usato per trovare informazioni nei file di documentazione. +Il file di tag usato per trovare informazioni nei file di documentazione. .TP /usr/local/share/vim/vim??/syntax/syntax.vim Inizializzazioni sintattiche a livello di sistema. @@ -586,18 +601,21 @@ Inizializzazioni del menù a livello di sistema. .TP /usr/local/share/vim/vim??/bugreport.vim -Script Vim per generare una segnalazione di errore. Vedere ":help bugs". +Script Vim per generare una segnalazione di errore. +Vedere ":help bugs". .TP /usr/local/share/vim/vim??/filetype.vim -Script Vim per determinare il tipo di un file dal suo nome. Vedere ":help 'filetype'". +Script Vim per determinare il tipo di un file dal suo nome. +Vedere ":help 'filetype'". .TP /usr/local/share/vim/vim??/scripts.vim -Script Vim per determinare il tipo di un file dal suo contenuto. Vedere ":help 'filetype'". +Script Vim per determinare il tipo di un file dal suo contenuto. +Vedere ":help 'filetype'". .TP /usr/local/share/vim/vim??/print/*.ps File usati per stampa PostScript. .PP -Per informazioni aggiornate [in inglese \- NdT] vedere la home page di Vim: +Per informazioni aggiornate [in inglese] vedere la home page di Vim: .br .SH VEDERE ANCHE @@ -614,12 +632,12 @@ Vedere ":help credits" in Tony Andrews e G.R. (Fred) Walter. In verità, poco o nulla è rimasto del loro codice originale. .SH BUG -Probabilmente. +È probabile che ce ne siano. Vedere ":help todo" per una lista di problemi noti. .PP -Si noti che un certo numero di comportamenti che possono essere considerati errori -da qualcuno, sono in effetti causati da una riproduzione fin troppo fedele del -comportamento di Vi. Se si ritiene che altre cose siano errori "perché Vi si comporta -diversamente", si dia prima un'occhiata al file vi_diff.txt (o si immetta -:help vi_diff.txt da Vim). -Un'occhiata va data anche alle opzioni 'compatible' e 'cpoptions. +Si noti che un certo numero di comportamenti che possono essere visti come +errori, sono in effetti causati da una riproduzione fin troppo fedele del +comportamento di Vi. Se si ritiene che altre cose siano errori "perché Vi si +comporta diversamente", si dia prima un'occhiata al file vi_diff.txt +(o si immetta :help vi_diff.txt da Vim). +Un'occhiata va data anche alle opzioni 'compatible' e 'cpoptions'. From 38237411e4e202a96b0efe41567505331114bfa8 Mon Sep 17 00:00:00 2001 From: glepnir Date: Tue, 12 May 2026 17:40:19 +0000 Subject: [PATCH 217/545] patch 9.2.0476: pattern completion leaks memory on alloc failures Problem: copy_substring_from_pos() leaked on ga_grow() failures, expand_pattern_in_buf() leaked "match" on ga_grow() failure, fuzzy_match_str_with_pos() ignored ga_grow() failures Solution: Route failures through cleanup paths, check ga_grow before writing to ga_data (glepnir) closes: #20203 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- src/cmdexpand.c | 9 ++++++--- src/fuzzy.c | 7 ++++++- src/version.c | 2 ++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/cmdexpand.c b/src/cmdexpand.c index f313981984..19ae319981 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -4914,7 +4914,7 @@ copy_substring_from_pos(pos_T *start, pos_T *end, char_u **match, line = ml_get(lnum); linelen = (int)ml_get_len(lnum); if (ga_grow(&ga, linelen + 2) != OK) - return FAIL; + goto fail; ga_concat_len(&ga, line, linelen); if (exacttext) GA_CONCAT_LITERAL(&ga, "\\n"); @@ -5141,8 +5141,8 @@ expand_pattern_in_buf( } // Extract the matching text prepended to completed word - if (!copy_substring_from_pos(&cur_match_pos, &end_match_pos, &full_match, - &word_end_pos)) + if (copy_substring_from_pos(&cur_match_pos, &end_match_pos, &full_match, + &word_end_pos) == FAIL) break; if (exacttext) @@ -5183,7 +5183,10 @@ expand_pattern_in_buf( if (match != NULL) { if (ga_grow(&ga, 1) == FAIL) + { + VIM_CLEAR(match); goto cleanup; + } ((char_u **)ga.ga_data)[ga.ga_len++] = match; if (ga.ga_len > TAG_MANY) break; diff --git a/src/fuzzy.c b/src/fuzzy.c index 9a94acea20..bb140fcace 100644 --- a/src/fuzzy.c +++ b/src/fuzzy.c @@ -690,7 +690,12 @@ fuzzy_match_str_with_pos(char_u *str UNUSED, char_u *pat UNUSED) { if (!VIM_ISWHITE(PTR2CHAR(p))) { - ga_grow(match_positions, 1); + if (ga_grow(match_positions, 1) == FAIL) + { + ga_clear(match_positions); + vim_free(match_positions); + return NULL; + } ((int_u *)match_positions->ga_data)[match_positions->ga_len] = matches[j]; match_positions->ga_len++; diff --git a/src/version.c b/src/version.c index 9e794b7ed3..6a8735c6bf 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 476, /**/ 475, /**/ From 3a9e1bb7e245f862f506b02845fc6132013ef6e7 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 12 May 2026 17:47:41 +0000 Subject: [PATCH 218/545] patch 9.2.0477: popup: leftover content after popup_free under layout change Problem: popup_mask still marks the freed popup's cells as covered until may_update_popup_mask() runs inside the next update_screen. Any screen_fill / screen_puts called in between (for example msg_clr_eos triggered by a status message from :copen) hits skip_for_popup() and silently drops writes to those cells, so the popup's chars survive on screen until those cells happen to be redrawn for another reason. Solution: Add popup_clear_mask_for() and call it from popup_hide() and popup_free() when the popup was visible, so the upcoming writes take effect immediately (Yasuhiro Matsumoto) Note: The test is limited to MS-Windows because the original report (#20178) was reproduced there and the redraw timing required to surface the bug differs on other platforms. fixes: #20178 closes: #20188 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/popupwin.c | 35 +++++++++++++++++++++++++ src/testdir/test_popupwin.vim | 48 +++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 85 insertions(+) diff --git a/src/popupwin.c b/src/popupwin.c index 685c3cae7c..0767fa9138 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -3522,10 +3522,38 @@ f_popup_close(typval_T *argvars, typval_T *rettv UNUSED) popup_close_and_callback(wp, &argvars[1]); } +/* + * Clear popup_mask entries for the cells covered by "wp" so that + * screen_fill / screen_puts calls made before the next update_screen() + * (e.g. msg_clr_eos triggered by a status message) are not silently + * dropped by skip_for_popup(). Without this the popup's chars survive + * on screen until may_update_popup_mask() runs and the affected cells + * happen to be redrawn. + */ + static void +popup_clear_mask_for(win_T *wp) +{ + int r, c; + int row_start, col_start, row_end, col_end; + + if (popup_mask == NULL || !popup_visible) + return; + + row_start = MAX(wp->w_winrow, 0); + col_start = MAX(wp->w_wincol, 0); + row_end = MIN(wp->w_winrow + popup_height(wp), (int)screen_Rows); + col_end = MIN(wp->w_wincol + popup_width(wp), (int)screen_Columns); + + for (r = row_start; r < row_end; ++r) + for (c = col_start; c < col_end; ++c) + popup_mask[r * screen_Columns + c] = 0; +} + void popup_hide(win_T *wp) { popup_area_T old_area; + int was_visible = (wp->w_popup_flags & POPF_HIDDEN) == 0; #ifdef FEAT_TERMINAL if (error_if_term_popup_window()) @@ -3541,6 +3569,9 @@ popup_hide(win_T *wp) if (wp->w_winrow + popup_height(wp) >= cmdline_row) clear_cmdline = TRUE; + if (was_visible) + popup_clear_mask_for(wp); + if (old_area.active) popup_redraw_exposed_area(&old_area); else @@ -3725,6 +3756,7 @@ f_popup_setbuf(typval_T *argvars, typval_T *rettv UNUSED) popup_free(win_T *wp) { popup_area_T old_area; + int was_visible = (wp->w_popup_flags & POPF_HIDDEN) == 0; popup_save_area(wp, &old_area); @@ -3733,6 +3765,9 @@ popup_free(win_T *wp) if (wp->w_winrow + popup_height(wp) >= cmdline_row) clear_cmdline = TRUE; + if (was_visible) + popup_clear_mask_for(wp); + popup_redraw_exposed_area(&old_area); win_free_popup(wp); diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 873840d8e8..56cce4fb4e 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -5408,4 +5408,52 @@ func Test_popupwin_close_status_redraw() call StopVimInTerminal(buf) endfunc +func Test_popupwin_close_copen_redraw() + CheckMSWindows + CheckFeature quickfix + + func! s:OpenPopup() + call popup_create(repeat(['ZZZZZZZZZ'], 10), #{ + \ pos: 'botright', + \ col: &columns, + \ line: &lines, + \ filter: function('s:PopupFilter'), + \ }) + endfunc + func! s:PopupFilter(winid, key) + if a:key ==# 'q' + call popup_close(a:winid) + copen + endif + return 1 + endfunc + + enew! + call setline(1, range(1, 30)) + call setqflist(map(range(1, 20), + \ {_, v -> {'bufnr': bufnr('%'), 'lnum': v, 'col': 1, 'text': 'item ' .. v}})) + + call s:OpenPopup() + redraw + call feedkeys('q', 'xt') + redraw + cclose + redraw + + call s:OpenPopup() + redraw + call feedkeys('q', 'xt') + redraw + + for row in range(&lines - 9, &lines) + let line = join(map(range(1, &columns), 'screenstring(row, v:val)'), '') + call assert_notmatch('Z', line) + endfor + + cclose + bwipe! + delfunc s:OpenPopup + delfunc s:PopupFilter +endfunc + " vim: shiftwidth=2 sts=2 diff --git a/src/version.c b/src/version.c index 6a8735c6bf..d52a2c4680 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 477, /**/ 476, /**/ From 8aecd377f08e9fbdf0092478ab7ec3ce5e0f04ec Mon Sep 17 00:00:00 2001 From: John Marriott Date: Tue, 12 May 2026 17:58:21 +0000 Subject: [PATCH 219/545] patch 9.2.0478: channel: redundant str/length assignments in channel_part_info() Problem: channel: redundant str/length assignments in channel_part_info Solution: Use the STR_LITERAL_SET() macro (John Marriott) closes: #20199 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/channel.c | 24 ++++++++---------------- src/version.c | 2 ++ 2 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/channel.c b/src/channel.c index e703a82089..579087b374 100644 --- a/src/channel.c +++ b/src/channel.c @@ -3708,35 +3708,27 @@ channel_part_info(channel_T *channel, dict_T *dict, char *name, ch_part_T part) STRCPY(namebuf + tail, "io"); if (part == PART_SOCK) - { - s.string = (char_u *)"socket"; - s.length = STRLEN_LITERAL("socket"); - } + STR_LITERAL_SET(s, "socket"); else switch (chanpart->ch_io) { case JIO_NULL: - s.string = (char_u *)"null"; - s.length = STRLEN_LITERAL("null"); + STR_LITERAL_SET(s, "null"); break; case JIO_PIPE: - s.string = (char_u *)"pipe"; - s.length = STRLEN_LITERAL("pipe"); + STR_LITERAL_SET(s, "pipe"); break; case JIO_FILE: - s.string = (char_u *)"file"; - s.length = STRLEN_LITERAL("file"); + STR_LITERAL_SET(s, "file"); break; case JIO_BUFFER: - s.string = (char_u *)"buffer"; - s.length = STRLEN_LITERAL("buffer"); + STR_LITERAL_SET(s, "buffer"); break; case JIO_OUT: - s.string = (char_u *)"out"; - s.length = STRLEN_LITERAL("out"); + STR_LITERAL_SET(s, "out"); break; default: - s.string = (char_u *)""; - s.length = 0; + STR_LITERAL_SET(s, ""); + break; } dict_add_string_len(dict, namebuf, s.string, (int)s.length); diff --git a/src/version.c b/src/version.c index d52a2c4680..8f46a438d9 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 478, /**/ 477, /**/ From 702d32e16168be35cedd1519de981163010f193b Mon Sep 17 00:00:00 2001 From: yilisharcs Date: Tue, 12 May 2026 18:11:55 +0000 Subject: [PATCH 220/545] runtime(compiler): set zig errorformat includes a new zig_cc file to catch warnings closes: #20198 Signed-off-by: yilisharcs Signed-off-by: Christian Brabandt --- runtime/compiler/zig.vim | 30 ++++++++++++++++++++++++------ runtime/compiler/zig_build.vim | 11 +++++------ runtime/compiler/zig_build_exe.vim | 7 +++---- runtime/compiler/zig_cc.vim | 18 ++++++++++++++++++ runtime/compiler/zig_test.vim | 7 +++---- 5 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 runtime/compiler/zig_cc.vim diff --git a/runtime/compiler/zig.vim b/runtime/compiler/zig.vim index 44014a3775..5f08423daf 100644 --- a/runtime/compiler/zig.vim +++ b/runtime/compiler/zig.vim @@ -1,6 +1,8 @@ " Vim compiler file " Compiler: Zig Compiler " Upstream: https://github.com/ziglang/zig.vim +" Last Change: +" 2026 May 12 by the Vim project (set errormformat) if exists("current_compiler") finish @@ -11,13 +13,29 @@ let s:save_cpo = &cpo set cpo&vim " a subcommand must be provided for the this compiler (test, build-exe, etc) -if has('patch-7.4.191') - CompilerSet makeprg=zig\ \$*\ \%:S -else - CompilerSet makeprg=zig\ \$*\ \"%\" -endif +CompilerSet makeprg=zig\ \$*\ \%:S + +CompilerSet errorformat= + \%-G, + \%-G\ %#+-\ %.%#, + \%-Ginstall, + \%-Ginstall\ transitive\ failure, + \%-Grun, + \%-Grun\ transitive\ failure, + \%-Gtest, + \%-Gtest\ transitive\ failure, + \%-Gfailed\ command:\ %.%#, + \%-Gerror:\ %*\\d\ compilation\ errors, + \%-GBuild\ Summary:\ %.%#, + \%-Gerror:\ the\ following\ build\ command\ failed\ with\ exit\ code\ %*\\d:, + \%-G.zig-cache%.%#, + \%E%f:%l:%c:\ error:\ %m, + \%I%f:%l:%c:\ note:\ %m -" TODO: improve errorformat as needed. +" zig has no warnings, but zig cc and zig c++ do +CompilerSet errorformat+= + \%W%f:%l:%c:\ warning:\ %m, + \%-G%*\\d\ warnings\ generated. let &cpo = s:save_cpo unlet s:save_cpo diff --git a/runtime/compiler/zig_build.vim b/runtime/compiler/zig_build.vim index 5a61c9f423..79308afe34 100644 --- a/runtime/compiler/zig_build.vim +++ b/runtime/compiler/zig_build.vim @@ -1,7 +1,8 @@ " Vim compiler file " Compiler: Zig Compiler (zig build) " Upstream: https://github.com/ziglang/zig.vim -" Last Change: 2024 Apr 05 by The Vim Project (removed :CompilerSet definition) +" Last Change: 2024 Apr 05 by the Vim Project (removed :CompilerSet definition) +" 2026 May 12 by the Vim Project (removed comment) if exists('current_compiler') finish @@ -13,13 +14,11 @@ let s:save_cpo = &cpo set cpo&vim if exists('g:zig_build_makeprg_params') - execute 'CompilerSet makeprg=zig\ build\ '.escape(g:zig_build_makeprg_params, ' \|"').'\ $*' + execute 'CompilerSet makeprg=zig\ build\ '.escape(g:zig_build_makeprg_params, ' \|"').'\ $*' else - CompilerSet makeprg=zig\ build\ $* + CompilerSet makeprg=zig\ build\ $* endif -" TODO: anything to add to errorformat for zig build specifically? - let &cpo = s:save_cpo unlet s:save_cpo -" vim: tabstop=8 shiftwidth=4 softtabstop=4 expandtab +" vim: tabstop=8 shiftwidth=2 softtabstop=2 expandtab diff --git a/runtime/compiler/zig_build_exe.vim b/runtime/compiler/zig_build_exe.vim index 440eff7885..ab63bfffe8 100644 --- a/runtime/compiler/zig_build_exe.vim +++ b/runtime/compiler/zig_build_exe.vim @@ -1,7 +1,8 @@ " Vim compiler file " Compiler: Zig Compiler (zig build-exe) " Upstream: https://github.com/ziglang/zig.vim -" Last Change: 2025 Nov 16 by The Vim Project (set errorformat) +" Last Change: 2025 Nov 16 by the Vim Project (set errorformat) +" 2026 May 12 by the Vim project (remove errorformat) if exists('current_compiler') finish @@ -13,9 +14,7 @@ let s:save_cpo = &cpo set cpo&vim CompilerSet makeprg=zig\ build-exe\ \%:S\ \$* -" CompilerSet errorformat=%f:%l:%c: %t%*[^:]: %m, %f:%l:%c: %m, %f:%l: %m -CompilerSet errorformat& let &cpo = s:save_cpo unlet s:save_cpo -" vim: tabstop=8 shiftwidth=4 softtabstop=4 expandtab +" vim: tabstop=8 shiftwidth=2 softtabstop=2 expandtab diff --git a/runtime/compiler/zig_cc.vim b/runtime/compiler/zig_cc.vim new file mode 100644 index 0000000000..331d44751a --- /dev/null +++ b/runtime/compiler/zig_cc.vim @@ -0,0 +1,18 @@ +" Vim compiler file +" Compiler: Zig Compiler (zig cc) +" Last Change: 2026 May 12 + +if exists('current_compiler') + finish +endif +runtime compiler/zig.vim +let current_compiler = 'zig_cc' + +let s:save_cpo = &cpo +set cpo&vim + +CompilerSet makeprg=zig\ cc\ \%:S\ \$* + +let &cpo = s:save_cpo +unlet s:save_cpo +" vim: tabstop=8 shiftwidth=2 softtabstop=2 expandtab diff --git a/runtime/compiler/zig_test.vim b/runtime/compiler/zig_test.vim index afe57ad4d3..6dee38b2f1 100644 --- a/runtime/compiler/zig_test.vim +++ b/runtime/compiler/zig_test.vim @@ -1,7 +1,8 @@ " Vim compiler file " Compiler: Zig Compiler (zig test) " Upstream: https://github.com/ziglang/zig.vim -" Last Change: 2025 Nov 16 by The Vim Project (set errorformat) +" Last Change: 2025 Nov 16 by the Vim Project (set errorformat) +" 2026 May 12 by the Vim Project (remove error format) if exists('current_compiler') finish @@ -13,9 +14,7 @@ let s:save_cpo = &cpo set cpo&vim CompilerSet makeprg=zig\ test\ \%:S\ \$* -" CompilerSet errorformat=%f:%l:%c: %t%*[^:]: %m, %f:%l:%c: %m, %f:%l: %m -CompilerSet errorformat& let &cpo = s:save_cpo unlet s:save_cpo -" vim: tabstop=8 shiftwidth=4 softtabstop=4 expandtab +" vim: tabstop=8 shiftwidth=2 softtabstop=2 expandtab From 950f501a1894219001b572e461c7b20d063071f5 Mon Sep 17 00:00:00 2001 From: "K.Takata" Date: Thu, 14 May 2026 08:39:03 +0000 Subject: [PATCH 221/545] runtime(doc): Fix file mode for the Italian manpage closes: #20210 Signed-off-by: K.Takata Signed-off-by: Christian Brabandt --- runtime/doc/vim-it.1 | 0 runtime/doc/vim-it.UTF-8.1 | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 runtime/doc/vim-it.1 mode change 100755 => 100644 runtime/doc/vim-it.UTF-8.1 diff --git a/runtime/doc/vim-it.1 b/runtime/doc/vim-it.1 old mode 100755 new mode 100644 diff --git a/runtime/doc/vim-it.UTF-8.1 b/runtime/doc/vim-it.UTF-8.1 old mode 100755 new mode 100644 From 3fb5e58fbc63d86a3e65f1a141b0d67af2aa38a1 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 14 May 2026 15:35:28 +0000 Subject: [PATCH 222/545] patch 9.2.0479: [security]: runtime(tar): command injection in tar plugin Problem: [security]: runtime(tar): command injection in tar plugin (Christopher Lusk) Solution: Use the correct shellescape(args, 1) form for a :! command Github Advisory: https://github.com/vim/vim/security/advisories/GHSA-2fpv-9ff7-xg5w Signed-off-by: Christian Brabandt --- runtime/autoload/tar.vim | 5 +++-- src/testdir/test_plugin_tar.vim | 19 +++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/tar.vim b/runtime/autoload/tar.vim index 1347371b51..3c899f85f6 100644 --- a/runtime/autoload/tar.vim +++ b/runtime/autoload/tar.vim @@ -25,6 +25,7 @@ " 2026 Apr 09 by Vim Project: fix bug with dotted filename (#19930) " 2026 Apr 15 by Vim Project: fix more path traversal issues (#19981) " 2026 Apr 16 by Vim Project: use g:tar_secure in tar#Extract() +" 2026 May 14 by Vim Project: use correct shellescape() call in Vimuntar() " " Contains many ideas from Michael Toren's " @@ -832,9 +833,9 @@ fun! tar#Vimuntar(...) " if necessary, decompress the tarball; then, extract it if tartail =~ '\.tgz' if executable("gunzip") - silent exe "!gunzip ".shellescape(tartail) + silent exe "!gunzip ".shellescape(tartail, 1) elseif executable("gzip") - silent exe "!gzip -d ".shellescape(tartail) + silent exe "!gzip -d ".shellescape(tartail, 1) else echoerr "unable to decompress<".tartail."> on this system" if simplify(curdir) != simplify(tarhome) diff --git a/src/testdir/test_plugin_tar.vim b/src/testdir/test_plugin_tar.vim index 56f25df2ae..e19e5d809c 100644 --- a/src/testdir/test_plugin_tar.vim +++ b/src/testdir/test_plugin_tar.vim @@ -318,3 +318,22 @@ def g:Test_extract_with_dotted_filename() delete('X.txt') bw! enddef + +def g:Test_extract_command_injection() + CheckExecutable gunzip + CheckExecutable touch + var tgz = eval('0z1F8B08087795056A000364756D6D792E74617200EDCE2B12C2300004D01C254' .. + '7480269CE534080A8495BD1DBF3996106C3A08A7ACFACD8157B59A7690BFB4A0FC3707C666E357D' .. + 'E65BC8B5A47CC8A5D61A522EA5B510D3CEBF5ED679197B8CE17CEDB7F9D4C76FBB5F3D000000000' .. + '000000000FCD11D32415E2C00280000') + var dirname = tempname() + + mkdir(dirname, 'R') + var tar = dirname .. "/';%$(touch pwned)'.tgz" + writefile(tgz, tar) + new + exe "e " .. fnameescape(tar) + exe ":Vimuntar " .. dirname + assert_false(filereadable(dirname .. "/pwned")) + bw! +enddef diff --git a/src/version.c b/src/version.c index 8f46a438d9..c98028a80c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 479, /**/ 478, /**/ From 8af0f098c3a42a28661d0295364e6e0fd7dbc92c Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 14 May 2026 16:43:15 +0000 Subject: [PATCH 223/545] patch 9.2.0480: [security]: runtime(netrw): code injection via mf command Problem: [security]: runtime(netrw): code injection via mf command (Christopher Lusk, Zdenek Dohnal) Solution: Do not use string concatenation inside the filter() commands (Zdenek Dohnal) Github Security Advisory: https://github.com/vim/vim/security/advisories/GHSA-66hr-7p6x-x5j3 Signed-off-by: Christian Brabandt --- runtime/pack/dist/opt/netrw/autoload/netrw.vim | 9 ++++----- src/testdir/test_plugin_netrw.vim | 15 +++++++++++++++ src/version.c | 2 ++ 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index fbd396566a..8681091f09 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -1,7 +1,7 @@ " Creator: Charles E Campbell " Previous Maintainer: Luca Saccarola " Maintainer: This runtime file is looking for a new maintainer. -" Last Change: 2026 May 11 +" Last Change: 2026 May 14 " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -5153,7 +5153,7 @@ function s:NetrwMarkFile(islocal,fname) else " remove filename from buffer's markfilelist - call filter(s:netrwmarkfilelist_{curbufnr},'v:val != a:fname') + call filter(s:netrwmarkfilelist_{curbufnr}, {_, v -> v !=# a:fname}) if s:netrwmarkfilelist_{curbufnr} == [] " local markfilelist is empty; remove it entirely call s:NetrwUnmarkList(curbufnr,curdir) @@ -5174,7 +5174,6 @@ function s:NetrwMarkFile(islocal,fname) else " initialize new markfilelist - let s:netrwmarkfilelist_{curbufnr}= [] call add(s:netrwmarkfilelist_{curbufnr},substitute(a:fname,'[|@]$','','')) @@ -5194,7 +5193,7 @@ function s:NetrwMarkFile(islocal,fname) call add(s:netrwmarkfilelist,netrw#fs#ComposePath(b:netrw_curdir,a:fname)) else " remove new filename from global markfilelist - call filter(s:netrwmarkfilelist,'v:val != "'.dname.'"') + call filter(s:netrwmarkfilelist, {_, v -> v !=# dname}) if s:netrwmarkfilelist == [] unlet s:netrwmarkfilelist endif @@ -7217,7 +7216,7 @@ function s:NetrwTreeDisplay(dir,depth) " hide given patterns let listhide= split(g:netrw_list_hide,',') for pat in listhide - call filter(w:netrw_treedict[dir],'v:val !~ "'.escape(pat,'\\').'"') + call filter(w:netrw_treedict[dir], {_, v -> v !~# pat}) endfor elseif g:netrw_hide == 2 diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim index c6bd589a43..72bf8300e6 100644 --- a/src/testdir/test_plugin_netrw.vim +++ b/src/testdir/test_plugin_netrw.vim @@ -725,4 +725,19 @@ func Test_netrw_bookmark_marked_file() bw! endfunc +func Test_netrw_mf_command_injection() + CheckUnix + CheckExecutable touch + let path = tempname() + let fname = 'x" . execute("silent! !touch poc") . "' + call mkdir(path, 'R') + exe "cd " path + call writefile([], fname) + Explore . + call search('^x') + :norm mf + :norm mf + call assert_false(filereadable('poc'), 'Command injection via mf command') +endfunc + " vim:ts=8 sts=2 sw=2 et diff --git a/src/version.c b/src/version.c index c98028a80c..df580d3de1 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 480, /**/ 479, /**/ From 8e41c34aba0e775d2e3bf6f0e2da1b1c5317f3df Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 14 May 2026 18:46:56 +0000 Subject: [PATCH 224/545] patch 9.2.0481: runtime(netrw): command injection possible via maps Problem: runtime(netrw): command injection possible via crafted directory names in NetrwMaps() (Christopher Lusk) Solution: Temporarily remove B flag in NetrwMaps() to prevent command injection Signed-off-by: Christian Brabandt --- .../pack/dist/opt/netrw/autoload/netrw.vim | 7 +++++++ src/testdir/test_plugin_netrw.vim | 20 +++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 29 insertions(+) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index 8681091f09..176cdcb87f 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -4810,6 +4810,12 @@ endfunction " s:NetrwMaps: {{{2 function s:NetrwMaps(islocal) + " remove B flag from 'cpo' so that \, \, etc. inside + " interpolated path names play back as literal text rather than + " the actual key — without this, a crafted directory name can + " inject keystrokes into the cmdline the mapping is typing + let _cpo = &cpo + set cpo-=B " mouse maps: {{{3 if g:netrw_mousemaps && g:netrw_retmap @@ -5054,6 +5060,7 @@ function s:NetrwMaps(islocal) " support user-specified maps call netrw#UserMaps(0) endif " }}}3 + let &cpo = _cpo endfunction " s:NetrwCommands: set up commands {{{2 diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim index 72bf8300e6..4256dc9d2a 100644 --- a/src/testdir/test_plugin_netrw.vim +++ b/src/testdir/test_plugin_netrw.vim @@ -731,6 +731,7 @@ func Test_netrw_mf_command_injection() let path = tempname() let fname = 'x" . execute("silent! !touch poc") . "' call mkdir(path, 'R') + let _cwd = getcwd() exe "cd " path call writefile([], fname) Explore . @@ -738,6 +739,25 @@ func Test_netrw_mf_command_injection() :norm mf :norm mf call assert_false(filereadable('poc'), 'Command injection via mf command') + exe "cd " _cwd + bw! endfunc +function Test_netrw_NetrwMaps_CR_dirname() + CheckNotMSWindows + + let tmpdir = tempname() . '/evil:let g:netrw_pwn=1' + call mkdir(tmpdir, 'pR') + call assert_true(isdirectory(tmpdir)) + exe ":Explore " tmpdir + " Fire D + " If the commands are injected successfully, + " this fails with + " Vim(let):E488: Trailing characters: \ @ command line script + call feedkeys("D\\", "xt") + call assert_false(exists("g:netrw_pwn")) + + unlet! g:netrw_pwn + bw! +endfunction " vim:ts=8 sts=2 sw=2 et diff --git a/src/version.c b/src/version.c index df580d3de1..10a787cacb 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 481, /**/ 480, /**/ From 29fa9343444754f6976a91663fd7d4de3a8e1c8e Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Fri, 15 May 2026 02:09:24 +0000 Subject: [PATCH 225/545] patch 9.2.0482: runtime(osc52): triggered twice with TextPutPoste autocmd Problem: runtime(osc52): triggered twice with TextPutPoste autocmd Solution: Detect recursive trigger and return null (Foxe Chen) closes: #20216 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- .../pack/dist/opt/osc52/autoload/osc52.vim | 17 +++++++++++++++- src/testdir/dumps/Test_osc52_paste_05.dump | 20 +++++++++++++++++++ src/testdir/test_plugin_osc52.vim | 14 +++++++++++++ src/version.c | 2 ++ 4 files changed, 52 insertions(+), 1 deletion(-) create mode 100644 src/testdir/dumps/Test_osc52_paste_05.dump diff --git a/runtime/pack/dist/opt/osc52/autoload/osc52.vim b/runtime/pack/dist/opt/osc52/autoload/osc52.vim index 3471329d7f..bad4bc388d 100644 --- a/runtime/pack/dist/opt/osc52/autoload/osc52.vim +++ b/runtime/pack/dist/opt/osc52/autoload/osc52.vim @@ -16,13 +16,20 @@ def OSCMessage(id: number) sent_message = true enddef -export def Paste(reg: string): tuple> +var loop_timerid: number = -1 + +export def Paste(reg: string): any # Check if user has indicated that the terminal does not support OSC 52 paste # (or has disabled it) if get(g:, 'osc52_disable_paste', 0) return ("c", []) endif + if loop_timerid != -1 + # This will result in the register being unchanged + return null + endif + # Some terminals like Kitty respect the selection type parameter on both X11 # and Wayland. If the terminal doesn't then the selection type parameter # should be ignored (no-op) @@ -61,6 +68,14 @@ export def Paste(reg: string): tuple> endif endtry + # A TextPutPost autocmd may cause this function to be called twice, which is + # technically intended behaviour, however it is not necessary for this plugin. + # To prevent this, return immediately if we have not returned back to the main + # loop since the last "paste" call. + loop_timerid = timer_start(0, (_) => { + loop_timerid = -1 + }) + if interrupt echo "Interrupted while waiting for OSC 52 response" return ("c", [""]) diff --git a/src/testdir/dumps/Test_osc52_paste_05.dump b/src/testdir/dumps/Test_osc52_paste_05.dump new file mode 100644 index 0000000000..32448367fb --- /dev/null +++ b/src/testdir/dumps/Test_osc52_paste_05.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@74 +|h|e|l@1|o| @69 +>t|e|s|t| @70 +|w|o|r|l|d|!| @68 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|3|,|1| @10|A|l@1| diff --git a/src/testdir/test_plugin_osc52.vim b/src/testdir/test_plugin_osc52.vim index f14d3b8c16..afb13ef908 100644 --- a/src/testdir/test_plugin_osc52.vim +++ b/src/testdir/test_plugin_osc52.vim @@ -90,6 +90,20 @@ func Test_osc52_paste() call VerifyScreenDump(buf, 'Test_osc52_paste_04', {}) + " Test that TextPutPost (e.g. from hlyank plugin) doesn't make "paste" + " callback be called twice for osc52. + call term_sendkeys(buf, "\:au TextPutPost * let g:test = 1\") + call TermWait(buf) + + call term_sendkeys(buf, "\"+p") + call TermWait(buf) + + call term_sendkeys(buf, "\]52;c;" .. + \ base64_encode(str2blob(["test"])) .. "\\\") + call TermWait(buf) + + call VerifyScreenDump(buf, 'Test_osc52_paste_05', {}) + call StopVimInTerminal(buf) endfunc diff --git a/src/version.c b/src/version.c index 10a787cacb..692c510e6b 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 482, /**/ 481, /**/ From 37d61dae7272752269a57da34bff2278e5304f4e Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Fri, 15 May 2026 09:46:47 +0000 Subject: [PATCH 226/545] runtime(doc): update doc for clipboard provider closes: #20217 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- runtime/doc/eval.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index a73055b098..71585da898 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*eval.txt* For Vim version 9.2. Last change: 2026 May 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -5367,6 +5367,9 @@ order: is an empty string, then the type is automatically chosen. 2. A |list| of strings to return to Vim, each representing a line. +If an invalid value is returned, then this is not an error. Instead the +register will be left unchanged (not cleared). + *clipboard-providers-copy* The "copy" callback returns nothing and takes the following arguments in the following order: From f281493c491c58e2452111e0e9b78edce486f07e Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Fri, 15 May 2026 15:02:48 +0000 Subject: [PATCH 227/545] patch 9.2.0483: popup: terminal embedded in an opacity popup freezes Vim on input Problem: When a terminal buffer is shown inside a popup with 'opacity' set to a value other than 100, typing into it freezes Vim. Only the first keystroke is drawn; afterwards no input is processed and the screen stops updating. Solution: When marking background lines for redraw to keep opacity blend cells fresh, do not raise must_redraw. This marking happens from inside update_screen() (via may_update_popup_mask()), so raising must_redraw makes terminal_loop()'s "while (must_redraw != 0) update_screen()" loop never terminate. Add redraw_win_range_now() that updates only the per-window state and use it from redraw_win_under_opacity_popup() (Hirohito Higashi) fixes: #20214 closes: #20220 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/drawscreen.c | 22 ++++++++++++++++++++++ src/popupwin.c | 4 +++- src/proto/drawscreen.pro | 1 + src/testdir/test_popupwin.vim | 26 ++++++++++++++++++++++++++ src/version.c | 2 ++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/drawscreen.c b/src/drawscreen.c index 751e09aa3f..94b616214d 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -3497,6 +3497,28 @@ redraw_win_range_later( } } +/* + * Like redraw_win_range_later() but do not raise the global must_redraw. + * Use this from inside an update_screen() pass (where the redraw will be + * picked up this cycle), to avoid triggering an extra full redraw cycle. + */ + void +redraw_win_range_now( + 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; + if (wp->w_redr_type < UPD_VALID) + wp->w_redr_type = UPD_VALID; + } +} + #ifdef FEAT_EVAL static bool redraw_cb_in_progress = false; diff --git a/src/popupwin.c b/src/popupwin.c index 0767fa9138..772148f61d 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -5311,7 +5311,9 @@ redraw_win_under_opacity_popup(win_T *wp) linenr_T lnum; (void)mouse_comp_pos(twp, &line_cp, &col_cp, &lnum, NULL); - redrawWinline(twp, lnum); + // Called from inside update_screen(); raising must_redraw + // would loop the outer redraw indefinitely. + redraw_win_range_now(twp, lnum, lnum); } else if (line_cp == twp->w_height) // Status bar line: mark for redraw to prevent diff --git a/src/proto/drawscreen.pro b/src/proto/drawscreen.pro index dcf98cb6fb..da2662aca7 100644 --- a/src/proto/drawscreen.pro +++ b/src/proto/drawscreen.pro @@ -25,6 +25,7 @@ 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); +void redraw_win_range_now(win_T *wp, linenr_T first, linenr_T last); void f_redraw_listener_add(typval_T *argvars, typval_T *rettv); void f_redraw_listener_remove(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim index 56cce4fb4e..783138b7e5 100644 --- a/src/testdir/test_popupwin.vim +++ b/src/testdir/test_popupwin.vim @@ -5180,6 +5180,32 @@ func Test_popup_opacity_zero() call StopVimInTerminal(buf) endfunc +func Test_popup_opacity_terminal_no_freeze() + CheckFeature terminal + CheckUnix + let g:test_is_flaky = 1 + + let origwin = win_getid() + let termbuf = term_start(&shell, #{hidden: 1}) + let winid = popup_create(termbuf, #{minwidth: 40, minheight: 10, + \ border: [1, 1, 1, 1], opacity: 10}) + call WaitForAssert({-> assert_equal("run", job_status(term_getjob(termbuf)))}) + call WaitForAssert({-> assert_equal(' ', screenstring(screenrow(), screencol() - 1))}) + + " Before the fix typing froze Vim: redraw under an opacity popup raised + " must_redraw every cycle, trapping terminal_loop in its redraw loop. + call feedkeys('x', 'xt') + call term_wait(termbuf) + redraw + call WaitForAssert({-> assert_equal('x', screenstring(screenrow(), screencol() - 1))}) + + call feedkeys("\", 'xt') + call feedkeys("exit\", 'xt') + call WaitForAssert({-> assert_equal("dead", job_status(term_getjob(termbuf)))}) + call feedkeys(":quit\", 'xt') + call assert_equal(origwin, win_getid()) +endfunc + func Test_popup_getwininfo_tabnr() tab split let winid1 = popup_create('sup', #{tabpage: 1}) diff --git a/src/version.c b/src/version.c index 692c510e6b..acf47f67a3 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 483, /**/ 482, /**/ From 77b8a12f8249a2f87cc2b20d8a4279da6bf5b11a Mon Sep 17 00:00:00 2001 From: Jonathan Demme Date: Fri, 15 May 2026 15:37:31 +0000 Subject: [PATCH 228/545] runtime(doc): Update 'softtabstop' documentation closes: #20218 Signed-off-by: Jonathan Demme Signed-off-by: Christian Brabandt --- runtime/doc/usr_30.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/doc/usr_30.txt b/runtime/doc/usr_30.txt index a541c77d21..7b9dbf1cb4 100644 --- a/runtime/doc/usr_30.txt +++ b/runtime/doc/usr_30.txt @@ -1,4 +1,4 @@ -*usr_30.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*usr_30.txt* For Vim version 9.2. Last change: 2026 May 15 VIM USER MANUAL by Bram Moolenaar @@ -531,9 +531,9 @@ they do when using only tab characters. Vim 5.4 introduced the 'softtabstop' option. On top of the (hard) tab stops used to display the horizontal tab characters in the text, Vim adds extra soft tab stops dedicated only to the cursor. When 'softtabstop' is set to a -positive value, and the key will push the cursor to the next soft tab +positive value, the key will push the cursor to the next soft tab stop. Vim will insert the correct combination of tab characters and spaces to -make the effect visually. Likewise pressing will have the cursor try to +achieve this effect. Likewise pressing will have the cursor try to reach the nearest soft tab stop. The following example uses `:set softtabstop=4` From bec23ef65c9a488959ae31e6d372f0c33abc7920 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Fri, 15 May 2026 15:45:11 +0000 Subject: [PATCH 229/545] patch 9.2.0484: TextPutPre triggers clipboard provider callback twice Problem: TextPutPre triggers clipboard provider callback twice when do_put() runs autocommands that themselves request the clipboard. Solution: Guard do_put() and put_do_autocmd() with inc_clip_provider()/dec_clip_provider() so the provider is queried at most once per put operation (Foxe Chen). closes: #20215 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/register.c | 32 +++++++++++++++++++++-- src/testdir/test_eval_stuff.vim | 46 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/register.c b/src/register.c index 86ac02cfc1..9daa7729e8 100644 --- a/src/register.c +++ b/src/register.c @@ -1196,6 +1196,16 @@ put_do_autocmd( if (list == NULL) return; + // Make sure regcontents will be up to date +# ifdef FEAT_CLIPBOARD_PROVIDER + inc_clip_provider(); + call_clip_provider_request(regname); +# endif +# ifdef FEAT_CLIPBOARD + if (clipmethod != CLIPMETHOD_PROVIDER) + regname = may_get_selection(regname); +# endif + if (regname == '.') { if (last_insert_ga.ga_data != NULL) @@ -1231,6 +1241,9 @@ put_do_autocmd( (void)dict_add_string_len(v_event, "operator", buf, (int)buflen); add_regtype_to_dict(regname, v_event, buf, sizeof(buf)); +# ifdef FEAT_CLIPBOARD_PROVIDER + dec_clip_provider(); +# endif (void)dict_add_bool(v_event, "visual", VIsual_active); @@ -1713,6 +1726,7 @@ do_put( adjust_clip_reg(®name); #endif #ifdef FEAT_CLIPBOARD_PROVIDER + inc_clip_provider(); call_clip_provider_request(regname); #endif #ifdef FEAT_CLIPBOARD @@ -1749,6 +1763,11 @@ do_put( // TextPutPost after TextPutPre. if (has_textputpre()) put_do_autocmd('.', NULL, NULL, false, dir); +#endif +#ifdef FEAT_CLIPBOARD_PROVIDER + dec_clip_provider(); +#endif +#ifdef FEAT_EVAL if (has_textputpost()) put_do_autocmd('.', NULL, NULL, true, dir); @@ -1771,7 +1790,12 @@ do_put( insert_string.string = expr_result; else if (get_spec_reg(regname, &insert_string.string, &allocated, TRUE) && insert_string.string == NULL) + { +#ifdef FEAT_CLIPBOARD_PROVIDER + dec_clip_provider(); +#endif return; + } // Autocommands may be executed when saving lines for undo. This might // make "y_array" invalid, so we start undo now to avoid that. @@ -2001,8 +2025,8 @@ do_put( // move to start of next multi-byte character curwin->w_cursor.col += (*mb_ptr2len)(ml_get_cursor()); else - if (c != TAB || cur_ve_flags != VE_ALL) - ++curwin->w_cursor.col; + if (c != TAB || cur_ve_flags != VE_ALL) + ++curwin->w_cursor.col; ++col; } else @@ -2521,6 +2545,10 @@ do_put( curbuf->b_op_end = orig_end; } +#ifdef FEAT_CLIPBOARD_PROVIDER + dec_clip_provider(); +#endif + #ifdef FEAT_EVAL if (has_textputpost()) { diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim index 39b1227aef..c3d98b96e3 100644 --- a/src/testdir/test_eval_stuff.vim +++ b/src/testdir/test_eval_stuff.vim @@ -1127,6 +1127,52 @@ func Test_clipboard_provider_accessed_once() bw! + new + " Emitting TextPutPre/TextPutPost/TextYankPost may cause a clipboard access + " + " Note that TextPutPost will always cause a second clipboard access, since a + " TextPutPre may have changed the clipboard, meaning another "paste" call is + " needed to make sure everything is up to date. + augroup TextAutocmd + autocmd! + autocmd TextPutPost * let g:putpost = 1 + autocmd TextPutPre * let g:putpre = 1 + autocmd TextYankPost * let g:yankpost = 1 + augroup END + + let g:putpost = 0 + let g:putpre = 0 + let g:yankpost = 0 + + let g:vim_paste_count = {'*': 0, '+': 0} + let g:vim_copy_count = {'*': 0, '+': 0} + + call setline(1, "hello world!") + + yank + + + yank * + + put + + + put * + + call assert_equal(2, g:vim_paste_count['+']) + call assert_equal(1, g:vim_copy_count['+']) + + call assert_equal(2, g:vim_paste_count['*']) + call assert_equal(1, g:vim_copy_count['*']) + + call assert_equal(1, g:putpost) + call assert_equal(1, g:putpre) + call assert_equal(1, g:yankpost) + + bw! + unlet g:putpost + unlet g:putpre + unlet g:yankpost + autocmd! TextAutocmd + set clipmethod& set clipboard& endfunc diff --git a/src/version.c b/src/version.c index acf47f67a3..2602bccd37 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 484, /**/ 483, /**/ From e85e3e5d8547cf83375fcb88803940e74df15bae Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Fri, 15 May 2026 16:00:04 +0000 Subject: [PATCH 230/545] patch 9.2.0485: clipboard provider callback can be called recursively Problem: clipboard provider callback can be called recursively, leading to E132: Function call depth is higher than 'maxfuncdepth' Solution: Prevent recursive calls of clip_provider_copy()/clip_provider_paste() (Foxe Chen). closes: #20213 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- runtime/doc/eval.txt | 3 +++ src/clipboard.c | 12 +++++++++++ src/testdir/test_eval_stuff.vim | 36 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 53 insertions(+) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 71585da898..2bcad93a39 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -5346,6 +5346,9 @@ a |lambda| expression. With the exception of the "available" callback if a callback is not provided, Vim will not invoke anything, and this is not an error. +If the "paste" or "copy" callbacks are triggered recursively, then they will +not be called. + *clipboard-providers-textlock* In both the "paste" and "copy" callbacks, it is not allowed to change the buffer text, see |textlock|. diff --git a/src/clipboard.c b/src/clipboard.c index e47c9a5adf..745a8c47a2 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -3428,6 +3428,7 @@ clip_provider_get_callback( static void clip_provider_copy(char_u *reg, char_u *provider) { + static bool recursive = false; callback_T callback; typval_T rettv; typval_T argvars[4]; @@ -3435,6 +3436,9 @@ clip_provider_copy(char_u *reg, char_u *provider) char_u type[2 + NUMBUFLEN] = {0}; list_T *list = NULL; + if (recursive) + return; + if (clip_provider_get_callback( reg, provider, @@ -3497,7 +3501,9 @@ clip_provider_copy(char_u *reg, char_u *provider) argvars[3].v_type = VAR_UNKNOWN; textlock++; + recursive = true; call_callback(&callback, -1, &rettv, 3, argvars); + recursive = false; clear_tv(&rettv); textlock--; @@ -3508,6 +3514,7 @@ clip_provider_copy(char_u *reg, char_u *provider) static void clip_provider_paste(char_u *reg, char_u *provider) { + static bool recursive = false; callback_T callback; typval_T argvars[2]; typval_T rettv; @@ -3515,6 +3522,9 @@ clip_provider_paste(char_u *reg, char_u *provider) char_u *reg_type; list_T *lines; + if (recursive) + return; + if (clip_provider_get_callback( reg, provider, @@ -3528,7 +3538,9 @@ clip_provider_paste(char_u *reg, char_u *provider) argvars[1].v_type = VAR_UNKNOWN; textlock++; + recursive = true; ret = call_callback(&callback, -1, &rettv, 1, argvars); + recursive = false; textlock--; if (ret == FAIL) diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim index c3d98b96e3..e1afbfbe2c 100644 --- a/src/testdir/test_eval_stuff.vim +++ b/src/testdir/test_eval_stuff.vim @@ -765,7 +765,10 @@ func s:Paste(reg) else return ("c", []) endif + endif + if exists("g:vim_paste_recursive") + call getreg(a:reg) endif endfunc @@ -773,6 +776,9 @@ func s:Copy(reg, type, lines) if exists("g:vim_copy_count") let g:vim_copy_count[a:reg] += 1 endif + if exists("g:vim_copy_recursive") + call setreg(a:reg, a:lines) + endif let g:vim_copy = { \ "reg": a:reg, @@ -1349,4 +1355,34 @@ func Test_clipboard_provider_clipboard_option() set clipboard& endfunc +" Test that callback aren't called recursively +func Test_clipboard_provider_recursive() + let v:clipproviders["test"] = { + \ "paste": { + \ '+': function("s:Paste"), + \ '*': function("s:Paste") + \ }, + \ "copy": { + \ '+': function("s:Copy"), + \ '*': function("s:Copy") + \ } + \ } + set clipmethod=test + + let g:vim_paste = "count" + let g:vim_paste_count = {'*': 0, '+': 0} + let g:vim_copy_count = {'*': 0, '+': 0} + let g:vim_paste_recursive = 1 + let g:vim_copy_recursive = 1 + + call getreg('+') + call assert_equal(1, g:vim_paste_count['+']) + call setreg('+', 'test') + call assert_equal(1, g:vim_copy_count['+']) + + set clipmethod& + unlet g:vim_paste_recursive + unlet g:vim_copy_recursive +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 2602bccd37..fa746a9ae6 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 485, /**/ 484, /**/ From de7a5b5425811169f589a7a3492696e8dedf8d68 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Fri, 15 May 2026 16:31:14 +0000 Subject: [PATCH 231/545] patch 9.2.0486: out-of-bound read when recovering swap files Problem: out-of-bound read when recovering corrupted swap files (Rahul Hoysala) Solution: Validate the db_txt_start field when recovering a swap file. Supported by AI Signed-off-by: Christian Brabandt --- runtime/doc/recover.txt | 4 ++++ src/memline.c | 9 +++++++++ src/po/vim.pot | 5 ++++- src/testdir/test_recover.vim | 14 ++++++++++++++ src/version.c | 2 ++ 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/runtime/doc/recover.txt b/runtime/doc/recover.txt index c52910c1f7..d52aacd880 100644 --- a/runtime/doc/recover.txt +++ b/runtime/doc/recover.txt @@ -195,6 +195,10 @@ recovered file. Or use |:DiffOrig|. Once you are sure the recovery is ok delete the swap file. Otherwise, you will continue to get warning messages that the ".swp" file already exists. +Note: Recovering swap files is best-effort. Vim attempts to validate fields +and skip corrupted sections, but the swap file format is intended for files +you trust. A crafted swap file may trigger parser bugs; such reports are +treated as robustness issues rather than security vulnerabilities. ENCRYPTION AND THE SWAP FILE *:recover-crypt* diff --git a/src/memline.c b/src/memline.c index 2887ebbf9d..ffd3fbd72a 100644 --- a/src/memline.c +++ b/src/memline.c @@ -1690,6 +1690,15 @@ ml_recover(int checkext) dp->db_txt_end = page_count * mfp->mf_page_size; } + if (dp->db_txt_start < HEADER_SIZE + || dp->db_txt_start > dp->db_txt_end) + { + ml_append(lnum++, (char_u *)_("??? block header corrupted"), + (colnr_T)0, TRUE); + ++error; + has_error = TRUE; + dp->db_txt_start = dp->db_txt_end; + } // Make sure there is a NUL at the end of the block so we // don't go over the end when copying text. *((char_u *)dp + dp->db_txt_end - 1) = NUL; diff --git a/src/po/vim.pot b/src/po/vim.pot index 5db2850389..3c07464955 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-05-07 19:25+0000\n" +"POT-Creation-Date: 2026-05-15 16:34+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1969,6 +1969,9 @@ msgstr "" msgid "??? from here until ???END lines may be messed up" msgstr "" +msgid "??? block header corrupted" +msgstr "" + msgid "??? from here until ???END lines may have been inserted/deleted" msgstr "" diff --git a/src/testdir/test_recover.vim b/src/testdir/test_recover.vim index b36d23e18d..69caa89135 100644 --- a/src/testdir/test_recover.vim +++ b/src/testdir/test_recover.vim @@ -344,7 +344,21 @@ func Test_recover_corrupted_swap_file() call assert_fails('recover Xfile1', 'E312:') call assert_equal('Xfile1', @%) call assert_equal(['???MANY LINES MISSING'], getline(1, '$')) + bw! + " use an out-of-bounds db_txt_start in the data block (would cause OOB + " read past dp->db_index[] in ml_recover() without the bounds check) + let b = copy(save_b) + let b[8200:8203] = s:little_endian ? 0zFEFFFFFF : 0zFFFFFFFE + if s:system_64bit + let b[8208:8215] = 0z00FFFFFF.FFFFFF00 + else + let b[8208:8211] = 0z00FFFF00 + endif + call writefile(b, sn) + call assert_fails('recover Xfile1', 'E312:') + call assert_equal('Xfile1', @%) + call assert_match('??? block header corrupted', getline(1)) bw! call delete(sn) endfunc diff --git a/src/version.c b/src/version.c index fa746a9ae6..046cc83425 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 486, /**/ 485, /**/ From ee49669e8f3b5ecdcfd2fdd08aeaac0e3de26ea8 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 15 May 2026 16:44:46 +0000 Subject: [PATCH 232/545] patch 9.2.0487: viminfo: possible signed int overflow in register array Problem: viminfo: possible signed int overflow in register array growth Solution: Cast to size_t (Yasuhiro Matsumoto) The expression `limit * 2 * sizeof(string_T)` in read_viminfo_register() multiplies in int and overflows once limit exceeds INT_MAX/2. Cast to size_t first so the size computation stays unsigned. Defensive only; reaching this path requires registers consuming many gigabytes. closes: #20207 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/version.c | 2 ++ src/viminfo.c | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/version.c b/src/version.c index 046cc83425..0714ec1ac0 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 487, /**/ 486, /**/ diff --git a/src/viminfo.c b/src/viminfo.c index d05900544a..bb84726c8f 100644 --- a/src/viminfo.c +++ b/src/viminfo.c @@ -1706,7 +1706,7 @@ read_viminfo_register(vir_T *virp, int force) if (size == limit) { string_T *new_array = (string_T *) - alloc(limit * 2 * sizeof(string_T)); + alloc((size_t)limit * 2 * sizeof(string_T)); if (new_array == NULL) { From b467b56ef064c952e5014fcd8b0888ddbc8c8a56 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Fri, 15 May 2026 16:50:27 +0000 Subject: [PATCH 233/545] patch 9.2.0488: statusline: status line highlight blends into adjacent vsep cells Problem: When two windows are placed side by side with vsplit and their status lines are connected (the cell between them is drawn with the 'stl' / 'stlnc' fillchar, not the 'vert' character), that connecting cell still uses the VertSplit highlight. The status line bar therefore looks broken at the separator column, and any custom edge highlight set in 'statusline' (%#XX# / %N*) is cut off there. Solution: Make that connecting cell take the highlight from the neighbouring status line edge instead of VertSplit: - Next to the current window, use the current window's edge highlight, so the StatusLine bar (and any %#... at the edge) extends into the column without a seam. - Between two non-current windows whose status fillchar is a space, use the left window's right-edge highlight, so the StatusLineNC bar is continuous across the column too. Cells drawn with the 'vert' character (the two windows do not share a status line) keep the VertSplit highlight as before. Add Test_statusline_vsep_borrow_hl with two layouts (NC | cur | NC | NC and NC | NC | cur | NC) so all three cases above are covered. closes: #20182 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/syntax.txt | 11 ++- src/drawscreen.c | 81 +++++++++++++++++++ src/testdir/dumps/Test_VertSplitNC_2.dump | 2 +- src/testdir/dumps/Test_VertSplitNC_3.dump | 2 +- src/testdir/dumps/Test_VertSplitNC_whl2.dump | 2 +- .../dumps/Test_VertSplitNC_winbar_1.dump | 2 +- src/testdir/dumps/Test_diff_anchors_07.dump | 2 +- ...st_diff_anchors_scrollbind_topline_01.dump | 2 +- ...st_diff_anchors_scrollbind_topline_02.dump | 2 +- ...st_diff_anchors_scrollbind_topline_03.dump | 2 +- ...st_diff_anchors_scrollbind_topline_04.dump | 2 +- ...st_diff_anchors_scrollbind_topline_05.dump | 2 +- ...st_diff_anchors_scrollbind_topline_06.dump | 2 +- ...st_diff_anchors_scrollbind_topline_07.dump | 2 +- ...st_diff_anchors_scrollbind_topline_08.dump | 2 +- ...st_diff_anchors_scrollbind_topline_13.dump | 2 +- ...st_diff_anchors_scrollbind_topline_14.dump | 2 +- ...st_diff_anchors_scrollbind_topline_15.dump | 2 +- ...st_diff_anchors_scrollbind_topline_16.dump | 2 +- ...st_diff_anchors_scrollbind_topline_17.dump | 2 +- .../dumps/Test_diff_get_put_linematch_16.dump | 2 +- .../dumps/Test_diff_get_put_linematch_17.dump | 2 +- .../dumps/Test_diff_get_put_linematch_18.dump | 2 +- .../dumps/Test_diff_get_put_linematch_19.dump | 2 +- .../dumps/Test_diff_get_put_linematch_3.dump | 2 +- .../dumps/Test_diff_get_put_linematch_4.dump | 2 +- .../dumps/Test_diff_inline_multiline_07.dump | 2 +- .../dumps/Test_diff_inline_multiline_08.dump | 2 +- .../dumps/Test_diff_inline_multiline_09.dump | 2 +- .../dumps/Test_diff_inline_multiline_10.dump | 2 +- src/testdir/dumps/Test_diff_rnu_01.dump | 2 +- src/testdir/dumps/Test_diff_rnu_02.dump | 2 +- src/testdir/dumps/Test_diff_rnu_03.dump | 2 +- src/testdir/dumps/Test_diff_scroll_1.dump | 2 +- src/testdir/dumps/Test_diff_scroll_2.dump | 2 +- .../dumps/Test_diff_scroll_change_03.dump | 2 +- src/testdir/dumps/Test_diff_topline_1.dump | 2 +- src/testdir/dumps/Test_diff_topline_2.dump | 2 +- src/testdir/dumps/Test_diff_topline_4.dump | 2 +- .../dumps/Test_diff_with_cul_bri_01.dump | 2 +- .../dumps/Test_diff_with_cul_bri_02.dump | 2 +- .../dumps/Test_diff_with_cul_bri_03.dump | 2 +- .../dumps/Test_diff_with_cul_bri_04.dump | 2 +- .../dumps/Test_diff_with_cursorline_01.dump | 2 +- .../dumps/Test_diff_with_cursorline_02.dump | 2 +- .../dumps/Test_diff_with_cursorline_03.dump | 2 +- src/testdir/dumps/Test_hor_scroll_1.dump | 2 +- src/testdir/dumps/Test_hor_scroll_2.dump | 2 +- src/testdir/dumps/Test_hor_scroll_3.dump | 2 +- src/testdir/dumps/Test_hor_scroll_4.dump | 2 +- src/testdir/dumps/Test_hor_scroll_5.dump | 2 +- src/testdir/dumps/Test_linematch_3diffs1.dump | 2 +- src/testdir/dumps/Test_listchars_01.dump | 2 +- src/testdir/dumps/Test_listchars_02.dump | 2 +- src/testdir/dumps/Test_listchars_03.dump | 2 +- src/testdir/dumps/Test_listchars_04.dump | 2 +- src/testdir/dumps/Test_listchars_05.dump | 2 +- src/testdir/dumps/Test_listchars_06.dump | 2 +- src/testdir/dumps/Test_listchars_07.dump | 2 +- .../dumps/Test_popup_prop_not_visible_01.dump | 2 +- .../Test_popup_prop_not_visible_01a.dump | 2 +- .../Test_popup_prop_not_visible_01b.dump | 2 +- .../dumps/Test_popup_prop_not_visible_02.dump | 2 +- .../dumps/Test_popup_prop_not_visible_03.dump | 2 +- src/testdir/dumps/Test_prop_diff_mode_2.dump | 2 +- .../dumps/Test_smooth_diff_change_line_1.dump | 2 +- .../dumps/Test_smooth_diff_change_line_2.dump | 2 +- .../dumps/Test_smooth_diff_change_line_3.dump | 2 +- .../Test_smooth_diff_change_line_3a.dump | 2 +- .../dumps/Test_smooth_diff_change_line_4.dump | 2 +- .../Test_statusline_vsep_borrow_hl_01.dump | 6 ++ .../Test_statusline_vsep_borrow_hl_02.dump | 6 ++ .../dumps/Test_switchwin_clear_pum_02.dump | 2 +- .../Test_tabpanel_drawing_scrolling_3.dump | 2 +- src/testdir/dumps/Test_winhighlight_14.dump | 2 +- src/testdir/dumps/Test_winhighlight_15.dump | 2 +- src/testdir/dumps/Test_winhighlight_2.dump | 2 +- src/testdir/dumps/Test_winhighlight_6.dump | 2 +- .../dumps/Test_winhighlight_hlsearch_2.dump | 2 +- .../dumps/Test_winscrolled_once_only_1.dump | 2 +- src/testdir/test_statusline.vim | 36 +++++++++ src/version.c | 2 + 82 files changed, 215 insertions(+), 79 deletions(-) create mode 100644 src/testdir/dumps/Test_statusline_vsep_borrow_hl_01.dump create mode 100644 src/testdir/dumps/Test_statusline_vsep_borrow_hl_02.dump diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 6c3888508a..c74f95e808 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 May 08 +*syntax.txt* For Vim version 9.2. Last change: 2026 May 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6115,9 +6115,14 @@ SpellRare Word that is recognized by the spellchecker as one that is hardly ever used. |spell| This will be combined with the highlighting used otherwise. *hl-StatusLine* -StatusLine Status line of current window. +StatusLine Status line of current window. The highlight at the status + line's edge (StatusLine, or any %#... / %N* in 'statusline') + also extends into the adjacent vertical separator cell when + the status lines are connected (no 'vert' character drawn). *hl-StatusLineNC* -StatusLineNC status lines of not-current windows. +StatusLineNC status lines of not-current windows. Like |hl-StatusLine|, + the edge highlight also extends into the adjacent vertical + separator cell. Note: If this is equal to "StatusLine", Vim will use "^^^" in the status line of the current window. *hl-StatusLineTerm* diff --git a/src/drawscreen.c b/src/drawscreen.c index 94b616214d..c110041fe6 100644 --- a/src/drawscreen.c +++ b/src/drawscreen.c @@ -69,6 +69,7 @@ static void win_update(win_T *wp); #ifdef FEAT_STL_OPT static void redraw_custom_statusline(win_T *wp); #endif +static void borrow_stl_vsep_hl(void); #if defined(FEAT_SEARCH_EXTRA) || defined(FEAT_CLIPBOARD) static int did_update_one_window; #endif @@ -386,6 +387,8 @@ update_screen(int type_arg) draw_vsep_win(wp, 0); } + borrow_stl_vsep_hl(); + // Reset b_mod_set flags. Going through all windows is probably faster // than going through all buffers (there could be many buffers). FOR_ALL_WINDOWS(wp) @@ -630,6 +633,83 @@ win_redr_status(win_T *wp, int ignore_pum UNUSED) busy = FALSE; } +/* + * Borrow status line edge highlight to adjacent vsep cells. + * - When the pair involves curwin: borrow curwin's edge attr so custom + * statusline highlights flow into the vsep cell. + * - When both windows are non-current: borrow the left window's right-edge + * attr only if the status fillchar is a space, so StatusLineNC blends + * over the join without changing visible characters. + * - Cells where the vsep char is drawn (stl_connected == FALSE) are left + * untouched so the VertSplit highlight is preserved. + */ + static void +borrow_stl_vsep_hl(void) +{ + win_T *left = NULL; + win_T *right = NULL; + + if (!redrawing()) + return; + + FOR_ALL_WINDOWS(left) + { + if (left->w_status_height == 0 || left->w_vsep_width == 0) + continue; + if (!stl_connected(left)) + continue; + + // Find a right neighbour whose status line rows overlap. + win_T *neighbour = NULL; + int start = 0; + int end = 0; + + FOR_ALL_WINDOWS(right) + { + if (right == left || right->w_status_height == 0) + continue; + if (right->w_wincol != W_ENDCOL(left) + 1) + continue; + int l_stl_row = W_WINROW(left) + left->w_height; + int r_stl_row = W_WINROW(right) + right->w_height; + + start = l_stl_row > r_stl_row ? l_stl_row : r_stl_row; + end = l_stl_row + left->w_status_height + < r_stl_row + right->w_status_height + ? l_stl_row + left->w_status_height + : r_stl_row + right->w_status_height; + if (start < end) + { + neighbour = right; + break; + } + } + if (neighbour == NULL) + continue; + + // For non-current pairs only borrow when the status fillchar is a + // space; otherwise the visible character would be repainted with a + // foreign highlight. + int hl; + if (left != curwin && neighbour != curwin + && fillchar_status(&hl, left) != ' ') + continue; + + // Source: prefer curwin's side; otherwise left window's right edge. + int dst_col = W_ENDCOL(left); + int src_col = (neighbour == curwin) + ? neighbour->w_wincol : W_ENDCOL(left) - 1; + + for (int r = start; r < end; r++) + { + unsigned dst_off = LineOffset[r] + dst_col; + + ScreenAttrs[dst_off] = ScreenAttrs[LineOffset[r] + src_col]; + screen_char(dst_off, r, dst_col); + } + } +} + #ifdef FEAT_STL_OPT /* * Redraw the status line according to 'statusline' and take care of any @@ -3434,6 +3514,7 @@ redraw_statuslines(void) if (ret) pop_highlight_overrides(); } + borrow_stl_vsep_hl(); if (redraw_tabline) draw_tabline(); diff --git a/src/testdir/dumps/Test_VertSplitNC_2.dump b/src/testdir/dumps/Test_VertSplitNC_2.dump index c0da105f94..8afa526ac0 100644 --- a/src/testdir/dumps/Test_VertSplitNC_2.dump +++ b/src/testdir/dumps/Test_VertSplitNC_2.dump @@ -8,5 +8,5 @@ |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 -|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#ffffff16#0000e05| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p +|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| +0#ffffff16#0000e05|<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#0000001#a8a8a8255| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p | +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_3.dump b/src/testdir/dumps/Test_VertSplitNC_3.dump index 0922ede594..614de055ea 100644 --- a/src/testdir/dumps/Test_VertSplitNC_3.dump +++ b/src/testdir/dumps/Test_VertSplitNC_3.dump @@ -8,5 +8,5 @@ |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 |V|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @8||+0#6c6c6c255&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7||+0#40ff4011&|V+0#0000000&|e|r|t|S|p|l|i|t|N|C| |t|e|s|t| @7 -|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<+0#ffffff16#0000e05| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p +|<+0#0000001#a8a8a8255|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| |<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p| +0#ffffff16#0000e05|<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|T|o|p | +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_whl2.dump b/src/testdir/dumps/Test_VertSplitNC_whl2.dump index 9701df25b4..825775e591 100644 --- a/src/testdir/dumps/Test_VertSplitNC_whl2.dump +++ b/src/testdir/dumps/Test_VertSplitNC_whl2.dump @@ -8,5 +8,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| @9|0|,|0|-|1| @9|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump b/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump index ce880fa309..4fbee462da 100644 --- a/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump +++ b/src/testdir/dumps/Test_VertSplitNC_winbar_1.dump @@ -8,5 +8,5 @@ |w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 |w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 |w|i|n|b|a|r| |t|e|s|t| @25||+0#40ff4011&|w+0#0000000&|i|n|b|a|r| |t|e|s|t| @25 -|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p| |[+0#ffffff16#0000e05|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p +|[+0#0000001#a8a8a8255|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p| +0#ffffff16#0000e05|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|T|o|p | +0#0000000#ffffff0@74 diff --git a/src/testdir/dumps/Test_diff_anchors_07.dump b/src/testdir/dumps/Test_diff_anchors_07.dump index 310fa6544b..eda58e9fd7 100644 --- a/src/testdir/dumps/Test_diff_anchors_07.dump +++ b/src/testdir/dumps/Test_diff_anchors_07.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@22||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255| @20||+1&#ffffff0| +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@21 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@22||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|3+0#0000000#5fd7ff255| @20||+1&#ffffff0| +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@21 |~+0&#ffffff0| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump index a0ffa9f301..5616d5f371 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_01.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump index 9e5c9dc94e..3d66db8d6e 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump index 76f8617683..fdab541440 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump index 1c069d8ded..705446a3dd 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_04.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump index 87bbc572e5..5daf6573d3 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_05.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump index 1f5917cf47..6cae25e1d4 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_06.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump index 1c069d8ded..705446a3dd 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_07.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| |X+3&&|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @10|3|,|1| @11|B|o|t| +3&&|X|d|i|f|i|l|e|2| @10|3|,|1| @11|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump index aadabe077d..a2ade506e4 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_08.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump index 99cb0ded0f..af99924276 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_13.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|1| @5|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|1| @5|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump index 3c35c7741a..c6a418a4b4 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_14.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump index 432cadb94d..cf6bce8d0d 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_15.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump index b95c2661a6..4bff316f42 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_16.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump index 7daaf7a9ac..9496933153 100644 --- a/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump +++ b/src/testdir/dumps/Test_diff_anchors_scrollbind_topline_17.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| |X+3&&|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|1| @5|A|l@1| |X|d|i|f|i|l|e|2| @3|5|,|1| @5|B|o|t| +3&&|X|d|i|f|i|l|e|3| @3|5|,|1| @5|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_16.dump b/src/testdir/dumps/Test_diff_get_put_linematch_16.dump index d4649d2052..eeb984d682 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_16.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_16.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|D+0#0000000#5fd7ff255|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|6|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|6|,|1| @11|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_17.dump b/src/testdir/dumps/Test_diff_get_put_linematch_17.dump index 69737fa58c..b8426a39a4 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_17.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_17.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|D+0#0000000#5fd7ff255|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|8|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|9|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|8|,|1| @11|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_18.dump b/src/testdir/dumps/Test_diff_get_put_linematch_18.dump index 49ab243871..2b94da9551 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_18.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_18.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|D+0#0000000#5fd7ff255|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|6|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|9|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|6|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|9|,|1| @11|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_19.dump b/src/testdir/dumps/Test_diff_get_put_linematch_19.dump index cbea3e5602..8fdd9f608a 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_19.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_19.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|D+0#0000000#ffffff0|E|F| @31||+1&&| +0#0000e05#a8a8a8255@1|D+0#0000000#ffffff0|E|F| @31 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25||+1&&| +0#0000e05#a8a8a8255@1>s+0#0000000#ffffff0|o|m|e|t|h|i|n|g| @25 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|1|3|,|1| @10|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|7|,|1| @10|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| |[|+|]| @6|1|3|,|1| @10|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|7|,|1| @10|A|l@1 |:+0&&|d|i|f@1|p|u|t| @66 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_3.dump b/src/testdir/dumps/Test_diff_get_put_linematch_3.dump index cb62a9ef71..ce905130e9 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_3.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_3.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 |:+0&&|5|,|1|0|d|i|f@1|g|e|t| @62 diff --git a/src/testdir/dumps/Test_diff_get_put_linematch_4.dump b/src/testdir/dumps/Test_diff_get_put_linematch_4.dump index 16c2cd874d..c6ea6ddc85 100644 --- a/src/testdir/dumps/Test_diff_get_put_linematch_4.dump +++ b/src/testdir/dumps/Test_diff_get_put_linematch_4.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|5|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| |[|+|]| @6|5|,|1| @11|A|l@1 |:+0&&|4|,|1|7|d|i|f@1|g|e|t| @62 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_07.dump b/src/testdir/dumps/Test_diff_inline_multiline_07.dump index de58a6ac30..b76d549b27 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_07.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_07.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_08.dump b/src/testdir/dumps/Test_diff_inline_multiline_08.dump index 901ea4699d..51c35f5afd 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_08.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_08.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_09.dump b/src/testdir/dumps/Test_diff_inline_multiline_09.dump index 91f59cda25..dc36ed8ffc 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_09.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_09.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_inline_multiline_10.dump b/src/testdir/dumps/Test_diff_inline_multiline_10.dump index 212c247975..ad86c4684d 100644 --- a/src/testdir/dumps/Test_diff_inline_multiline_10.dump +++ b/src/testdir/dumps/Test_diff_inline_multiline_10.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| |X+3&&|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @10|1|,|1| @11|A|l@1| +3&&|X|d|i|f|i|l|e|2| @10|1|,|1| @11|A|l@1 |:+0&&> @73 diff --git a/src/testdir/dumps/Test_diff_rnu_01.dump b/src/testdir/dumps/Test_diff_rnu_01.dump index dc7c0f9101..d51d66674c 100644 --- a/src/testdir/dumps/Test_diff_rnu_01.dump +++ b/src/testdir/dumps/Test_diff_rnu_01.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_rnu_02.dump b/src/testdir/dumps/Test_diff_rnu_02.dump index 32ffc4003d..d33a2ee0ab 100644 --- a/src/testdir/dumps/Test_diff_rnu_02.dump +++ b/src/testdir/dumps/Test_diff_rnu_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_rnu_03.dump b/src/testdir/dumps/Test_diff_rnu_03.dump index 8c8b938449..0d29b84081 100644 --- a/src/testdir/dumps/Test_diff_rnu_03.dump +++ b/src/testdir/dumps/Test_diff_rnu_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_scroll_1.dump b/src/testdir/dumps/Test_diff_scroll_1.dump index ee53c35bbb..9713461012 100644 --- a/src/testdir/dumps/Test_diff_scroll_1.dump +++ b/src/testdir/dumps/Test_diff_scroll_1.dump @@ -8,5 +8,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|d+0#0000000#5fd7ff255|o|l|o|r| @29 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|s+0#0000000#5fd7ff255|i|t| @31 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|a+0#0000000#5fd7ff255|m|e|t|,| @29 -|X+1&#ffffff0|l|e|f|t| @13|5|,|0|-|1| @9|T|o|p| |X+3&&|r|i|g|h|t| @12|5|,|0|-|1| @9|T|o|p +|X+1&#ffffff0|l|e|f|t| @13|5|,|0|-|1| @9|T|o|p| +3&&|X|r|i|g|h|t| @12|5|,|0|-|1| @9|T|o|p |"+0&&|X|r|i|g|h|t|"| |3@1|L|,| |2|6|3|B| @56 diff --git a/src/testdir/dumps/Test_diff_scroll_2.dump b/src/testdir/dumps/Test_diff_scroll_2.dump index 4cac386a6c..d6cd1b9c99 100644 --- a/src/testdir/dumps/Test_diff_scroll_2.dump +++ b/src/testdir/dumps/Test_diff_scroll_2.dump @@ -8,5 +8,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|d+0#0000000#5fd7ff255|o|l|o|r| @29 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|s+0#0000000#5fd7ff255|i|t| @31 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|a+0#0000000#5fd7ff255|m|e|t|,| @29 -|X+1&#ffffff0|l|e|f|t| @13|6|,|1| @11|T|o|p| |X+3&&|r|i|g|h|t| @12|6|,|1| @11|T|o|p +|X+1&#ffffff0|l|e|f|t| @13|6|,|1| @11|T|o|p| +3&&|X|r|i|g|h|t| @12|6|,|1| @11|T|o|p |"+0&&|X|r|i|g|h|t|"| |3@1|L|,| |2|6|3|B| @56 diff --git a/src/testdir/dumps/Test_diff_scroll_change_03.dump b/src/testdir/dumps/Test_diff_scroll_change_03.dump index 6d0c8600f6..5a4088975b 100644 --- a/src/testdir/dumps/Test_diff_scroll_change_03.dump +++ b/src/testdir/dumps/Test_diff_scroll_change_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|7|,|3| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|5|,|3| @10|B|o|t +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|7|,|3| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|5|,|3| @10|B|o|t | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_topline_1.dump b/src/testdir/dumps/Test_diff_topline_1.dump index 1d4c9145f0..5b5122621c 100644 --- a/src/testdir/dumps/Test_diff_topline_1.dump +++ b/src/testdir/dumps/Test_diff_topline_1.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|4| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|5| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|6| @32 -|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% +|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_topline_2.dump b/src/testdir/dumps/Test_diff_topline_2.dump index 2d28bd373a..a0f3db79c0 100644 --- a/src/testdir/dumps/Test_diff_topline_2.dump +++ b/src/testdir/dumps/Test_diff_topline_2.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|4| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|5| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|6| @32 -|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% +|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|4|2|%| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|1|,|1| @10|1|9|% |9+0&&| @73 diff --git a/src/testdir/dumps/Test_diff_topline_4.dump b/src/testdir/dumps/Test_diff_topline_4.dump index 6043d7ad73..94414b1e79 100644 --- a/src/testdir/dumps/Test_diff_topline_4.dump +++ b/src/testdir/dumps/Test_diff_topline_4.dump @@ -16,5 +16,5 @@ | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|1+0#0000000#5fd7ff255|9| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|0| @32 | +0#0000e05#a8a8a8255@1|-+0#4040ff13#afffff255@34||+1#0000000#ffffff0| +0#0000e05#a8a8a8255@1|2+0#0000000#5fd7ff255|1| @32 -|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p +|[+1&#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|0|,|1| @10|T|o|p | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_01.dump b/src/testdir/dumps/Test_diff_with_cul_bri_01.dump index 8151088e04..3855fa17c5 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_01.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_01.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |1|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_02.dump b/src/testdir/dumps/Test_diff_with_cul_bri_02.dump index 62126f6b39..c1ae50d6d4 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_02.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_02.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |2|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_03.dump b/src/testdir/dumps/Test_diff_with_cul_bri_03.dump index f32be7385b..bdf8be065e 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_03.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_03.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |3|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cul_bri_04.dump b/src/testdir/dumps/Test_diff_with_cul_bri_04.dump index 9489f9ab04..aa3ce8e5d4 100644 --- a/src/testdir/dumps/Test_diff_with_cul_bri_04.dump +++ b/src/testdir/dumps/Test_diff_with_cul_bri_04.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| |<+3&&| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 +|<+1#0000000&|o| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| +3&&|<| |N|a|m|e|]| |[|+|]| |4|,|1| @5|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @2|0|,|0|-|1| @3|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cursorline_01.dump b/src/testdir/dumps/Test_diff_with_cursorline_01.dump index 31d8b2a189..0b9bcb9bda 100644 --- a/src/testdir/dumps/Test_diff_with_cursorline_01.dump +++ b/src/testdir/dumps/Test_diff_with_cursorline_01.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cursorline_02.dump b/src/testdir/dumps/Test_diff_with_cursorline_02.dump index 9aef7d1fa3..68014acb2c 100644 --- a/src/testdir/dumps/Test_diff_with_cursorline_02.dump +++ b/src/testdir/dumps/Test_diff_with_cursorline_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_diff_with_cursorline_03.dump b/src/testdir/dumps/Test_diff_with_cursorline_03.dump index 5bc9e13167..595de46ce1 100644 --- a/src/testdir/dumps/Test_diff_with_cursorline_03.dump +++ b/src/testdir/dumps/Test_diff_with_cursorline_03.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|4|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|4|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_hor_scroll_1.dump b/src/testdir/dumps/Test_hor_scroll_1.dump index 615afb8e12..65b16f3ccc 100644 --- a/src/testdir/dumps/Test_hor_scroll_1.dump +++ b/src/testdir/dumps/Test_hor_scroll_1.dump @@ -4,5 +4,5 @@ @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_hor_scroll_2.dump b/src/testdir/dumps/Test_hor_scroll_2.dump index dfe2e98ae0..2b6afa0ecd 100644 --- a/src/testdir/dumps/Test_hor_scroll_2.dump +++ b/src/testdir/dumps/Test_hor_scroll_2.dump @@ -4,5 +4,5 @@ @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_hor_scroll_3.dump b/src/testdir/dumps/Test_hor_scroll_3.dump index db9fd6ff1b..93f94a0ecc 100644 --- a/src/testdir/dumps/Test_hor_scroll_3.dump +++ b/src/testdir/dumps/Test_hor_scroll_3.dump @@ -4,5 +4,5 @@ @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 @19| +0&#e0e0e08||+1&#ffffff0| +0&&@19| +0&#e0e0e08| +0&#ffffff0@32 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|2|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|2|1| @10|A|l@1 |:+0&&|w|i|n|d|o| |:|s|e|t| |c|u|r|s|o|r|l|i|n|e| @52 diff --git a/src/testdir/dumps/Test_hor_scroll_4.dump b/src/testdir/dumps/Test_hor_scroll_4.dump index 81821604d0..830423ad35 100644 --- a/src/testdir/dumps/Test_hor_scroll_4.dump +++ b/src/testdir/dumps/Test_hor_scroll_4.dump @@ -4,5 +4,5 @@ @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 @10| +0&#e0e0e08| +0&#ffffff0@8||+1&&| +0&&@29| +0&#e0e0e08| +0&#ffffff0@22 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|3|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|3|1| @10|A|l@1 |:+0&&|w|i|n|d|o| |:|s|e|t| |c|u|r|s|o|r|l|i|n|e| @52 diff --git a/src/testdir/dumps/Test_hor_scroll_5.dump b/src/testdir/dumps/Test_hor_scroll_5.dump index f2986f3796..d706910fc0 100644 --- a/src/testdir/dumps/Test_hor_scroll_5.dump +++ b/src/testdir/dumps/Test_hor_scroll_5.dump @@ -4,5 +4,5 @@ @20||+1&&| +0&&@53 @20||+1&&| +0&&@53 |~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @52 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|4|1| @2|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|4|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|4|1| @2|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @22|1|,|4|1| @10|A|l@1 |:+0&&|w|i|n|d|o| |:|s|e|t| |n|o|c|u|r|s|o|r|l|i|n|e| |n|o|c|u|r|s|o|r|c|o|l|u|m|n| @35 diff --git a/src/testdir/dumps/Test_linematch_3diffs1.dump b/src/testdir/dumps/Test_linematch_3diffs1.dump index d8f23e84bd..a010d27617 100644 --- a/src/testdir/dumps/Test_linematch_3diffs1.dump +++ b/src/testdir/dumps/Test_linematch_3diffs1.dump @@ -16,5 +16,5 @@ |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 |~| @23||+1#0000000&|~+0#4040ff13&| @22||+1#0000000&|~+0#4040ff13&| @22 -|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|0|-|1| @3|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|0|-|1| @3|A|l@1| |X+3&&|d|i|f|i|l|e|3| @3|1|,|0|-|1| @3|A|l@1 +|X+1#0000000&|d|i|f|i|l|e|1| @4|1|,|0|-|1| @3|A|l@1| |X|d|i|f|i|l|e|2| @3|1|,|0|-|1| @3|A|l@1| +3&&|X|d|i|f|i|l|e|3| @3|1|,|0|-|1| @3|A|l@1 |"+0&&|X|d|i|f|i|l|e|3|"| |5|L|,| |4|5|B| @56 diff --git a/src/testdir/dumps/Test_listchars_01.dump b/src/testdir/dumps/Test_listchars_01.dump index 72fff585c2..a7de310690 100644 --- a/src/testdir/dumps/Test_listchars_01.dump +++ b/src/testdir/dumps/Test_listchars_01.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|]| |1|,| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|]| |1|,| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_02.dump b/src/testdir/dumps/Test_listchars_02.dump index b7be52e890..d7e05df9ae 100644 --- a/src/testdir/dumps/Test_listchars_02.dump +++ b/src/testdir/dumps/Test_listchars_02.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @2||+1#0000000&|~+0#4040ff13&| @32 |~| @18||+1#0000000&|~+0#4040ff13&| @2||+1#0000000&|~+0#4040ff13&| @32 |~| @18||+1#0000000&|~+0#4040ff13&| @2||+1#0000000&|~+0#4040ff13&| @32 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1|,| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1|,| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @10|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_03.dump b/src/testdir/dumps/Test_listchars_03.dump index 13aca6d2f7..d54b3e50fa 100644 --- a/src/testdir/dumps/Test_listchars_03.dump +++ b/src/testdir/dumps/Test_listchars_03.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @1||+1#0000000&|~+0#4040ff13&| @33 |~| @18||+1#0000000&|~+0#4040ff13&| @1||+1#0000000&|~+0#4040ff13&| @33 |~| @18||+1#0000000&|~+0#4040ff13&| @1||+1#0000000&|~+0#4040ff13&| @33 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @10|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @10|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_04.dump b/src/testdir/dumps/Test_listchars_04.dump index e62c5aff20..694e6eadca 100644 --- a/src/testdir/dumps/Test_listchars_04.dump +++ b/src/testdir/dumps/Test_listchars_04.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| ||+1#0000000&|~+0#4040ff13&| @34 |~| @18||+1#0000000&|~+0#4040ff13&| ||+1#0000000&|~+0#4040ff13&| @34 |~| @18||+1#0000000&|~+0#4040ff13&| ||+1#0000000&|~+0#4040ff13&| @34 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @11|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<|1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @4|1|,|1| @11|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_05.dump b/src/testdir/dumps/Test_listchars_05.dump index 89b759fb42..7ecb79738a 100644 --- a/src/testdir/dumps/Test_listchars_05.dump +++ b/src/testdir/dumps/Test_listchars_05.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 | +0&&@59 diff --git a/src/testdir/dumps/Test_listchars_06.dump b/src/testdir/dumps/Test_listchars_06.dump index 9f4b5d30e2..9176c5fded 100644 --- a/src/testdir/dumps/Test_listchars_06.dump +++ b/src/testdir/dumps/Test_listchars_06.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 |~| @18||+1#0000000&|~+0#4040ff13&| @3||+1#0000000&|~+0#4040ff13&| @31 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<+3&&|]| |1|,| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| +3&&|<|]| |1|,| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @3|1|,|1| @9|A|l@1 |:+0&&|s|e|t| |n|o|w|r|a|p| |f|o|l|d|c|o|l|u|m|n|=|4| @35 diff --git a/src/testdir/dumps/Test_listchars_07.dump b/src/testdir/dumps/Test_listchars_07.dump index ad81656a50..572bf248ff 100644 --- a/src/testdir/dumps/Test_listchars_07.dump +++ b/src/testdir/dumps/Test_listchars_07.dump @@ -6,5 +6,5 @@ |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 |~| @18||+1#0000000&|~+0#4040ff13&||+1#0000000&|~+0#4040ff13&| @35 -|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| |<+3&&| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|<+1#0000000&|a|m|e|]| |[|+|]| |1|,|1| @3|A|l@1| +3&&|<| |[+1&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 |:+0&&|s|e|t| |n|o|w|r|a|p| |f|o|l|d|c|o|l|u|m|n|=|4| @35 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_01.dump b/src/testdir/dumps/Test_popup_prop_not_visible_01.dump index dbcfda1161..20f4d46586 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_01.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_01.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 -|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump b/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump index 0b97cb832d..e048949de9 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_01a.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 -|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 |:+0&&|c|a|l@1| |p|o|p|u|p|_|h|i|d|e|(|g|:|s|o|m|e|_|i|d|)| @47 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump b/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump index e8a5c2e353..c91b201003 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_01b.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 |~| @41||+1#0000000&|~+0#4040ff13&| @29 -|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| @15|0|,|0|-|1| @9|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @2|1|,|0|-|1| @6|A|l@1 |:+0&&|c|a|l@1| |p|o|p|u|p|_|s|h|o|w|(|g|:|s|o|m|e|_|i|d|)| @47 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_02.dump b/src/testdir/dumps/Test_popup_prop_not_visible_02.dump index 1d1a5531c9..e4b2aec90f 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_02.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_02.dump @@ -6,5 +6,5 @@ |~+0#4040ff13#ffffff0| @55||+1#0000000&|~+0#4040ff13&| @15 |~| @55||+1#0000000&|~+0#4040ff13&| @15 |~| @55||+1#0000000&|~+0#4040ff13&| @15 -|[+1#0000000&|N|o| |N|a|m|e|]| @29|0|,|0|-|1| @9|A|l@1| |<+3&&|m|e|]| |[|+|]| |1|,|0|-|1| @2 +|[+1#0000000&|N|o| |N|a|m|e|]| @29|0|,|0|-|1| @9|A|l@1| +3&&|<|m|e|]| |[|+|]| |1|,|0|-|1| @2 |:+0&&|v|e|r|t| |r|e|s|i|z|e| |-|1|4| @58 diff --git a/src/testdir/dumps/Test_popup_prop_not_visible_03.dump b/src/testdir/dumps/Test_popup_prop_not_visible_03.dump index caaa881fe3..a056cd867a 100644 --- a/src/testdir/dumps/Test_popup_prop_not_visible_03.dump +++ b/src/testdir/dumps/Test_popup_prop_not_visible_03.dump @@ -6,5 +6,5 @@ |~+0#4040ff13&| @63||+1#0000000&|~+0#4040ff13&| @7 |~| @63||+1#0000000&|~+0#4040ff13&| @7 |~| @63||+1#0000000&|~+0#4040ff13&| @7 -|[+1#0000000&|N|o| |N|a|m|e|]| @37|0|,|0|-|1| @9|A|l@1| |<+3&&|[|+|]| |1|,|0|- +|[+1#0000000&|N|o| |N|a|m|e|]| @37|0|,|0|-|1| @9|A|l@1| +3&&|<|[|+|]| |1|,|0|- |:+0&&|v|e|r|t| |r|e|s|i|z|e| |-|8| @59 diff --git a/src/testdir/dumps/Test_prop_diff_mode_2.dump b/src/testdir/dumps/Test_prop_diff_mode_2.dump index 243ec0dc18..453665d998 100644 --- a/src/testdir/dumps/Test_prop_diff_mode_2.dump +++ b/src/testdir/dumps/Test_prop_diff_mode_2.dump @@ -6,5 +6,5 @@ |~+0#4040ff13#ffffff0| @28||+1#0000000&| +0#af5f00255&@1|5| |0+0#0000000#ffd7ff255@2|9+2&#ff404010| +0&#ffd7ff255@20 |~+0#4040ff13#ffffff0| @28||+1#0000000&| +0#af5f00255&@3|<+0#0000000#ffd7ff255|t|e|x|t|>| @18 |~+0#4040ff13#ffffff0| @28||+1#0000000&|~+0#4040ff13&| @27 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1|-|7| @5|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1|-|7| @5|A|l@1 |:+0&&|w|i|n|d|o| |s|e|t| |n|u|m|b|e|r| @42 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_1.dump b/src/testdir/dumps/Test_smooth_diff_change_line_1.dump index c634f1dd31..28f2e1d995 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_1.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_1.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_2.dump b/src/testdir/dumps/Test_smooth_diff_change_line_2.dump index 691fe289fd..4c2d28d0e6 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_2.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_2.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|7| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|7| @11|A|l@1 |-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@62 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_3.dump b/src/testdir/dumps/Test_smooth_diff_change_line_3.dump index c4178404d8..e47fb5d34b 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_3.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_3.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump b/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump index d13141eb28..0c7e70c273 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_3a.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|4| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|6| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_smooth_diff_change_line_4.dump b/src/testdir/dumps/Test_smooth_diff_change_line_4.dump index b789df3fb7..37ce70a758 100644 --- a/src/testdir/dumps/Test_smooth_diff_change_line_4.dump +++ b/src/testdir/dumps/Test_smooth_diff_change_line_4.dump @@ -16,5 +16,5 @@ |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|1| @11|A|l@1 | +0&&@74 diff --git a/src/testdir/dumps/Test_statusline_vsep_borrow_hl_01.dump b/src/testdir/dumps/Test_statusline_vsep_borrow_hl_01.dump new file mode 100644 index 0000000000..ee71fb0591 --- /dev/null +++ b/src/testdir/dumps/Test_statusline_vsep_borrow_hl_01.dump @@ -0,0 +1,6 @@ +| +0&#ffffff0@19||+1&&> +0&&@19||+1&&| +0&&@16||+1&&| +0&&@17 +|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| @16 +|~| @18||+1#0000000&|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| @16 +|~| @18||+1#0000000&|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @15||+1#0000000&|~+0#4040ff13&| @16 +|L+2#ff404010#ffff4012| @17|R+2#4040ff13#40ff4011| +0#ff404010#ffff4012|L| @17|R+0#4040ff13#40ff4011| |L+2#ff404010#ffff4012| @14|R+2#4040ff13#40ff4011| |L+2#ff404010#ffff4012| @15|R+2#4040ff13#40ff4011 +| +0#0000000#ffffff0@77 diff --git a/src/testdir/dumps/Test_statusline_vsep_borrow_hl_02.dump b/src/testdir/dumps/Test_statusline_vsep_borrow_hl_02.dump new file mode 100644 index 0000000000..60703a35f0 --- /dev/null +++ b/src/testdir/dumps/Test_statusline_vsep_borrow_hl_02.dump @@ -0,0 +1,6 @@ +| +0&#ffffff0@19||+1&&| +0&&@19||+1&&> +0&&@19||+1&&| +0&&@14 +|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @13 +|~| @18||+1#0000000&|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @13 +|~| @18||+1#0000000&|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @18||+1#0000000&|~+0#4040ff13&| @13 +|L+2#ff404010#ffff4012| @17|R+2#4040ff13#40ff4011| |L+2#ff404010#ffff4012| @17|R+2#4040ff13#40ff4011| +0#ff404010#ffff4012|L| @17|R+0#4040ff13#40ff4011| |L+2#ff404010#ffff4012| @12|R+2#4040ff13#40ff4011 +| +0#0000000#ffffff0@77 diff --git a/src/testdir/dumps/Test_switchwin_clear_pum_02.dump b/src/testdir/dumps/Test_switchwin_clear_pum_02.dump index 39013c09fd..cf5f9dbc9c 100644 --- a/src/testdir/dumps/Test_switchwin_clear_pum_02.dump +++ b/src/testdir/dumps/Test_switchwin_clear_pum_02.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|w+1#0000000&|i|n|_|b| |[|+|]| @9|1|,|1|0| @10|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|9| @11|A|l@1 +|w+1#0000000&|i|n|_|b| |[|+|]| @9|1|,|1|0| @10|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|9| @11|A|l@1 |-+2&&@1| |I|N|S|E|R|T| |-@1| +0&&@62 diff --git a/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump b/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump index b041b672f6..0ee2efe9db 100644 --- a/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump +++ b/src/testdir/dumps/Test_tabpanel_drawing_scrolling_3.dump @@ -6,5 +6,5 @@ | +1&&@19| +0#af5f00255&@1|6| ||+1#0000000&| +0#af5f00255&|3|0| |t+0#0000000&|e|x|t| |t|e|x|t| |t|e|x|t| |t | +1&&@19| +0#af5f00255&@1|7| ||+1#0000000&| +0#af5f00255&|3|1| |t+0#0000000&|e|x|t| |t|e|x|t| |t|e|x|t| |t | +1&&@19| +0#af5f00255&@1|8| ||+1#0000000&| +0#af5f00255&|3|2| |t+0#0000000&|e|x|t| |t|e|x|t| |t|e|x|t| |t -| +1&&@19|<| |1|,| |<+3&&|.|t|x|t| |[|+|]| |2|8|,|1| @2|2|6|% +| +1&&@19|<| |1|,| +3&&|<|.|t|x|t| |[|+|]| |2|8|,|1| @2|2|6|% | +1&&@19| +0&&@24 diff --git a/src/testdir/dumps/Test_winhighlight_14.dump b/src/testdir/dumps/Test_winhighlight_14.dump index 1ea869d656..176bf6ea99 100644 --- a/src/testdir/dumps/Test_winhighlight_14.dump +++ b/src/testdir/dumps/Test_winhighlight_14.dump @@ -4,5 +4,5 @@ |S+8&&|i|x| @33||+1&&|S+0&&|i|x| @33 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 |:+0&&|w|i|n|c|m|d| |l| @65 diff --git a/src/testdir/dumps/Test_winhighlight_15.dump b/src/testdir/dumps/Test_winhighlight_15.dump index a90302a86f..7d69f21b56 100644 --- a/src/testdir/dumps/Test_winhighlight_15.dump +++ b/src/testdir/dumps/Test_winhighlight_15.dump @@ -4,5 +4,5 @@ |S+8#0000000#ffffff0|i|x| @33||+1&&|S+0#ffffff16#e000002|i|x| @33 |~+0#4040ff13#ffffff0| @35||+1#0000000&|~+0#4040ff13#e000002| @35 |~+0&#ffffff0| @35||+1#0000000&|~+0#4040ff13#e000002| @35 -|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 +|[+1#0000000#ffffff0|N|o| |N|a|m|e|]| |[|+|]| @5|3|,|3| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|5| @11|A|l@1 |:+0&&|s|e|t|l|o|c|a|l| |w|h|l|=|N|o|r|m|a|l|:|E|r@1|o|r|M|s|g| @45 diff --git a/src/testdir/dumps/Test_winhighlight_2.dump b/src/testdir/dumps/Test_winhighlight_2.dump index b84ef70531..72f754114e 100644 --- a/src/testdir/dumps/Test_winhighlight_2.dump +++ b/src/testdir/dumps/Test_winhighlight_2.dump @@ -4,5 +4,5 @@ |S|i|x| @33||+1&&|S+0&&|i|x| @33 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 |:+0&&|w|i|n|c|m|d| |l| @65 diff --git a/src/testdir/dumps/Test_winhighlight_6.dump b/src/testdir/dumps/Test_winhighlight_6.dump index f5753e1187..e482465ca4 100644 --- a/src/testdir/dumps/Test_winhighlight_6.dump +++ b/src/testdir/dumps/Test_winhighlight_6.dump @@ -4,5 +4,5 @@ |S|i|x| @33||+1&&|S+0&&|i|x| @33 |~+0#4040ff13&| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1 |:+0&&|s|e|t|l|o|c|a|l| |w|h|l|=|V|e|r|t|S|p|l|i|t|:|E|r@1|o|r|M|s|g| @42 diff --git a/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump b/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump index 30d643854e..37a9087bba 100644 --- a/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump +++ b/src/testdir/dumps/Test_winhighlight_hlsearch_2.dump @@ -16,5 +16,5 @@ |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 |~| @35||+1#0000000&|~+0#4040ff13&| @35 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @5|1|,|1| @11|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @5|2|,|1| @11|A|l@1 |/+0&&|F> @72 diff --git a/src/testdir/dumps/Test_winscrolled_once_only_1.dump b/src/testdir/dumps/Test_winscrolled_once_only_1.dump index 56d640107e..7407f46863 100644 --- a/src/testdir/dumps/Test_winscrolled_once_only_1.dump +++ b/src/testdir/dumps/Test_winscrolled_once_only_1.dump @@ -5,6 +5,6 @@ |a+0#0000000&@2| @26||+1&&|~+0#4040ff13&| @27 |b+0#0000000&@2| @26||+1&&|~+0#4040ff13&| @27 |~| @28||+1#0000000&|~+0#4040ff13&| @27 -|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| |[+3&&|N|o| |N|a|m|e|]| |[|+|]| @1|2|,|1| @7|B|o|t +|[+1#0000000&|N|o| |N|a|m|e|]| |[|+|]| @1|1|,|1| @8|A|l@1| +3&&|[|N|o| |N|a|m|e|]| |[|+|]| @1|2|,|1| @7|B|o|t |1+0&&| |1|0@2| |[|'|r|o|w|'|,| |[@1|'|c|o|l|'|,| |[@1|'|l|e|a|f|'|,| |1|0@1|2|]|,| |[|'|l|e|a|f|'|,| |1|0@1|1|]@2|,| |[ |'|l|e|a|f|'|,| |1|0@2|]@2| @44 diff --git a/src/testdir/test_statusline.vim b/src/testdir/test_statusline.vim index 0b1359cd53..34aa94e4c5 100644 --- a/src/testdir/test_statusline.vim +++ b/src/testdir/test_statusline.vim @@ -978,4 +978,40 @@ func Test_statusline_empty() set statusline& endfunc +func Test_statusline_vsep_borrow_hl() + CheckScreendump + + " borrow_stl_vsep_hl(): vsep cells adjacent to a status line should + " borrow the highlight of the neighbouring window's status line edge so + " that custom highlights flow into the separator column. + " - vsep next to curwin: curwin's edge hl is borrowed. + " - vsep between two non-current windows: the left window's right-edge + " hl is borrowed when the status fillchar is a space. + let lines =<< trim END + hi User1 ctermfg=Red ctermbg=Yellow + hi User2 ctermfg=Blue ctermbg=Green + set laststatus=2 + set statusline=%1*L%=%2*R + vsplit + vsplit + vsplit + " curwin is now the leftmost window; move to the second one so the + " layout becomes: NC | cur | NC | NC. + 2wincmd w + END + call writefile(lines, 'XTest_statusline_vsep_borrow_hl', 'D') + + let buf = RunVimInTerminal('-S XTest_statusline_vsep_borrow_hl', + \ {'rows': 6, 'cols': 78}) + call term_sendkeys(buf, "\") + call VerifyScreenDump(buf, 'Test_statusline_vsep_borrow_hl_01', {}) + + " Move curwin to the third window: NC | NC | cur | NC. + " Now the leftmost vsep is between two non-current windows. + call term_sendkeys(buf, "\l\") + call VerifyScreenDump(buf, 'Test_statusline_vsep_borrow_hl_02', {}) + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 0714ec1ac0..9820655148 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 488, /**/ 487, /**/ From 7cb86f46cb27541faedb14292b319f5937a0bd97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 16 May 2026 07:40:21 +0000 Subject: [PATCH 234/545] CI: Bump the github-actions group across 1 directory with 2 updates Bumps the github-actions group with 2 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action) and [actions/labeler](https://github.com/actions/labeler). Updates `github/codeql-action` from 4.35.3 to 4.35.4 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v4.35.3...v4.35.4) Updates `actions/labeler` from 6.0.1 to 6.1.0 - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/v6.0.1...v6.1.0) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.35.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: actions/labeler dependency-version: 6.1.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... closes: #20226 Signed-off-by: dependabot[bot] Signed-off-by: Christian Brabandt --- .github/workflows/codeql-analysis.yml | 6 +++--- .github/workflows/label.yml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 17a12b97c9..856ce7f241 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -48,7 +48,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4.35.3 + uses: github/codeql-action/init@v4.35.4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -59,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v4.35.3 + uses: github/codeql-action/autobuild@v4.35.4 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -73,4 +73,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4.35.3 + uses: github/codeql-action/analyze@v4.35.4 diff --git a/.github/workflows/label.yml b/.github/workflows/label.yml index 6be0b2f8dd..4172d28e0d 100644 --- a/.github/workflows/label.yml +++ b/.github/workflows/label.yml @@ -17,6 +17,6 @@ jobs: pull-requests: write steps: - - uses: actions/labeler@v6.0.1 + - uses: actions/labeler@v6.1.0 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" From bc7f736a393123c1349fe3fbe4fb48bfc56754b1 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Sat, 16 May 2026 07:46:25 +0000 Subject: [PATCH 235/545] patch 9.2.0489: filetype: some Objective-C files are not recognized Problem: filetype: some Objective-C files are not recognized Solution: Add g:filetype_mm override variable, improve the objective c pattern detection (Keith Smiley). closes: #20221 Signed-off-by: Keith Smiley Signed-off-by: Christian Brabandt --- runtime/autoload/dist/ft.vim | 9 +++++++-- runtime/doc/filetype.txt | 3 ++- src/testdir/test_filetype.vim | 31 +++++++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 17b2618231..3085f3575c 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -3,7 +3,7 @@ vim9script # Vim functions for file type detection # # Maintainer: The Vim Project -# Last Change: 2026 Apr 23 +# Last Change: 2026 May 16 # Former Maintainer: Bram Moolenaar # These functions are moved here from runtime/filetype.vim to make startup @@ -798,9 +798,14 @@ export def FTnroff(): number enddef export def FTmm() + if exists("g:filetype_mm") + exe "setf " .. g:filetype_mm + return + endif + var n = 1 while n < 20 - if getline(n) =~ '^\s*\(#\s*\(include\|import\)\>\|@import\>\|/\*\)' + if getline(n) =~ '^\s*\(//\|#\s*\(include\|import\)\>\|@import\>\|/\*\)' setf objcpp return endif diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 2c978ebc9a..5078913e67 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -1,4 +1,4 @@ -*filetype.txt* For Vim version 9.2. Last change: 2026 Apr 13 +*filetype.txt* For Vim version 9.2. Last change: 2026 May 16 VIM REFERENCE MANUAL by Bram Moolenaar @@ -164,6 +164,7 @@ variables can be used to overrule the filetype used for certain extensions: *.int g:filetype_int *.lsl g:filetype_lsl *.m g:filetype_m |ft-mathematica-syntax| + *.mm g:filetype_mm *.mac g:filetype_mac *[mM]makefile,*.mk,*.mak,[mM]akefile* g:make_flavor |ft-make-syntax| diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 5040ac0e61..9aa99a97d7 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -2068,6 +2068,37 @@ func Test_m4_file() endtry endfunc +func Test_mm_file() + filetype on + + call writefile(['#import "test.h"'], 'Xfile.mm', 'D') + split Xfile.mm + call assert_equal('objcpp', &filetype) + bwipe! + + call writefile(['// Objective-C++ line comment'], 'Xfile.mm', 'D') + split Xfile.mm + call assert_equal('objcpp', &filetype) + bwipe! + + call writefile(['.TH VIM 1 "YYYY Mth DD"'], 'Xfile.mm', 'D') + split Xfile.mm + call assert_equal('nroff', &filetype) + bwipe! + + try + let g:filetype_mm = 'objcpp' + call writefile(['.TH VIM 1 "YYYY Mth DD"'], 'Xfile_override.mm', 'D') + split Xfile_override.mm + call assert_equal('objcpp', &filetype) + bwipe! + finally + unlet! g:filetype_mm + endtry + + filetype off +endfunc + func Test_mod_file() filetype on diff --git a/src/version.c b/src/version.c index 9820655148..fb89edd04e 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 489, /**/ 488, /**/ From f7e239bd0e8eccc78bf93c44cc9e241691153842 Mon Sep 17 00:00:00 2001 From: Doug Kearns Date: Sat, 16 May 2026 07:54:54 +0000 Subject: [PATCH 236/545] runtime(sh): Update syntax, don't include parens in function name highlighting Parentheses are not part of the function name so highlight them differently. closes: #20219 Signed-off-by: Doug Kearns Signed-off-by: Christian Brabandt --- runtime/syntax/sh.vim | 48 ++++++++++--------- runtime/syntax/testdir/dumps/sh_06_00.dump | 2 +- runtime/syntax/testdir/dumps/sh_06_01.dump | 2 +- runtime/syntax/testdir/dumps/sh_06_03.dump | 2 +- runtime/syntax/testdir/dumps/sh_07_00.dump | 2 +- runtime/syntax/testdir/dumps/sh_07_01.dump | 2 +- runtime/syntax/testdir/dumps/sh_07_02.dump | 2 +- runtime/syntax/testdir/dumps/sh_07_03.dump | 4 +- .../testdir/dumps/sh_functions_bash_00.dump | 10 ++-- .../testdir/dumps/sh_functions_bash_01.dump | 10 ++-- .../testdir/dumps/sh_functions_bash_02.dump | 4 +- .../testdir/dumps/sh_functions_bash_03.dump | 2 +- .../testdir/dumps/sh_functions_dash_00.dump | 8 ++-- .../testdir/dumps/sh_functions_dash_01.dump | 10 ++-- .../testdir/dumps/sh_functions_dash_02.dump | 2 +- .../dumps/sh_functions_ksh2020_00.dump | 10 ++-- .../dumps/sh_functions_ksh2020_01.dump | 10 ++-- .../dumps/sh_functions_ksh2020_02.dump | 4 +- .../testdir/dumps/sh_functions_ksh88_00.dump | 10 ++-- .../testdir/dumps/sh_functions_ksh88_01.dump | 10 ++-- .../testdir/dumps/sh_functions_ksh88_02.dump | 2 +- .../testdir/dumps/sh_functions_mksh_00.dump | 10 ++-- .../testdir/dumps/sh_functions_mksh_01.dump | 10 ++-- .../testdir/dumps/sh_functions_mksh_02.dump | 4 +- .../syntax/testdir/dumps/sh_generic_07.dump | 2 +- .../syntax/testdir/dumps/sh_generic_10.dump | 4 +- .../syntax/testdir/dumps/sh_generic_11.dump | 4 +- .../syntax/testdir/dumps/sh_ksh2020_07.dump | 2 +- .../syntax/testdir/dumps/sh_ksh2020_10.dump | 4 +- .../syntax/testdir/dumps/sh_ksh2020_11.dump | 4 +- runtime/syntax/testdir/dumps/sh_ksh88_07.dump | 2 +- runtime/syntax/testdir/dumps/sh_ksh88_10.dump | 2 +- .../testdir/dumps/sh_ksh93_generic_07.dump | 2 +- .../testdir/dumps/sh_ksh93_generic_10.dump | 4 +- .../testdir/dumps/sh_ksh93_generic_11.dump | 4 +- .../syntax/testdir/dumps/sh_ksh93u_07.dump | 2 +- .../syntax/testdir/dumps/sh_ksh93u_10.dump | 4 +- .../syntax/testdir/dumps/sh_ksh93u_11.dump | 4 +- .../syntax/testdir/dumps/sh_ksh93v_07.dump | 2 +- .../syntax/testdir/dumps/sh_ksh93v_10.dump | 4 +- .../syntax/testdir/dumps/sh_ksh93v_11.dump | 4 +- runtime/syntax/testdir/dumps/sh_mksh_07.dump | 2 +- runtime/syntax/testdir/dumps/sh_mksh_10.dump | 6 +-- runtime/syntax/testdir/dumps/sh_mksh_11.dump | 4 +- .../testdir/dumps/sh_mksh_gibberish_00.dump | 8 ++-- .../testdir/dumps/sh_mksh_gibberish_01.dump | 10 ++-- .../testdir/dumps/sh_mksh_gibberish_02.dump | 10 ++-- .../testdir/dumps/sh_mksh_gibberish_03.dump | 10 ++-- .../testdir/dumps/sh_mksh_gibberish_04.dump | 2 +- .../dumps/Test_matchparen_sh_case_1.dump | 2 +- .../dumps/Test_matchparen_sh_case_2.dump | 2 +- 51 files changed, 147 insertions(+), 143 deletions(-) diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim index d022059d72..41570cce4f 100644 --- a/runtime/syntax/sh.vim +++ b/runtime/syntax/sh.vim @@ -25,6 +25,7 @@ " 2026 Mar 23 improve matching of function definitions #19638 " 2026 Apr 02 improve matching of function definitions #19849 " 2026 Apr 19 improve detection of special variables #20016 +" 2026 May 14 don't highlight parens as part of the function name #20219 " }}} " Version: 208 " Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH @@ -665,40 +666,42 @@ endif ShFoldFunctions syn region shFunctionExpr matchgroup=shFunctionExprRegion start="{" end="}" contains=@shFunctionList contained skipwhite skipnl nextgroup=shQuickComment ShFoldFunctions syn region shFunctionSubSh matchgroup=shFunctionSubShRegion start="(" end=")" contains=@shFunctionList contained skipwhite skipnl nextgroup=shQuickComment +syn match shFunctionParens "()" contained + if exists("b:is_bash") syn keyword shFunctionKey coproc - syn match shFunctionCmdOne "\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" skipwhite skipnl nextgroup=@shFunctionCmds - syn match shFunctionCmdTwo "\%#=1\%(\%(\<\k\+\>\|[^()<>|&$;\t ]\+\)\+\)\@>\ze\s*\%(()\ze\)\=\_s*\%(\<\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" contained skipwhite skipnl nextgroup=@shFunctionCmds - syn match shFunctionOne "\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr - syn match shFunctionTwo "\%#=1\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\ze\s*\%(()\ze\)\=\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr - syn match shFunctionThree "\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\s*()\ze\_s*((\@!" skipwhite skipnl nextgroup=shFunctionSubSh - syn match shFunctionFour "\%#=1\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\ze\s*\%(\%(()\ze\)\=\)\@>\_s*((\@!" contained skipwhite skipnl nextgroup=shFunctionSubSh + syn match shFunctionCmdOne "\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens + syn match shFunctionCmdTwo "\%#=1\%(\%(\<\k\+\>\|[^()<>|&$;\t ]\+\)\+\)\@>\ze\s*\%(()\ze\)\=\_s*\%(\<\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" contained skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens + syn match shFunctionOne "\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens + syn match shFunctionTwo "\%#=1\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\ze\s*\%(()\ze\)\=\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens + syn match shFunctionThree "\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\s*()\ze\_s*((\@!" skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens + syn match shFunctionFour "\%#=1\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\ze\s*\%(\%(()\ze\)\=\)\@>\_s*((\@!" contained skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens elseif exists("b:is_ksh88") " AT&T ksh88 - syn match shFunctionCmdOne "^\s*\zs\h\w*\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" skipwhite skipnl nextgroup=@shFunctionCmds - syn match shFunctionOne "^\s*\zs\h\w*\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr + syn match shFunctionCmdOne "^\s*\zs\h\w*\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens + syn match shFunctionOne "^\s*\zs\h\w*\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens syn match shFunctionTwo "\<\h\w*\>\ze\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr - syn match shFunctionThree "^\s*\zs\h\w*\s*()\ze\_s*((\@!" skipwhite skipnl nextgroup=shFunctionSubSh + syn match shFunctionThree "^\s*\zs\h\w*\s*()\ze\_s*((\@!" skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens elseif exists("b:is_mksh") " MirBSD ksh is the wild west of absurd and abstruse function names... - syn match shFunctionCmdOne "^\s*\zs[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" skipwhite skipnl nextgroup=@shFunctionCmds - syn match shFunctionOne "^\s*\zs[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr - syn match shFunctionTwo "\%#=1\%(\%(\<\w\+\|[@!+.%,:-]\+\)*[-A-Za-z_.%,0-9:]\)\@>\ze\s*\%(()\ze\)\=\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr - syn match shFunctionThree "^\s*\zs[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\s*()\ze\_s*((\@!" skipwhite skipnl nextgroup=shFunctionSubSh + syn match shFunctionCmdOne "^\s*\zs[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens + syn match shFunctionOne "^\s*\zs[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens + syn match shFunctionTwo "\%#=1\%(\%(\<\w\+\|[@!+.%,:-]\+\)*[-A-Za-z_.%,0-9:]\)\@>\ze\s*\%(()\ze\)\=\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens + syn match shFunctionThree "^\s*\zs[-A-Za-z_@!+.%,0-9:]*[-A-Za-z_.%,0-9:]\s*()\ze\_s*((\@!" skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens elseif exists("b:is_kornshell") " ksh93 - syn match shFunctionCmdOne "^\s*\zs[A-Za-z_.][A-Za-z_.0-9]*\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" skipwhite skipnl nextgroup=@shFunctionCmds - syn match shFunctionOne "^\s*\zs[A-Za-z_.][A-Za-z_.0-9]*\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr + syn match shFunctionCmdOne "^\s*\zs[A-Za-z_.][A-Za-z_.0-9]*\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens + syn match shFunctionOne "^\s*\zs[A-Za-z_.][A-Za-z_.0-9]*\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens syn match shFunctionTwo "\%(\<\h\+\|\.\)[A-Za-z_.0-9]*\ze\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr - syn match shFunctionThree "^\s*\zs[A-Za-z_.][A-Za-z_.0-9]*\s*()\ze\_s*((\@!" skipwhite skipnl nextgroup=shFunctionSubSh + syn match shFunctionThree "^\s*\zs[A-Za-z_.][A-Za-z_.0-9]*\s*()\ze\_s*((\@!" skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens syn match shNamespaceOne "\<\h\w*\>\ze\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr else - syn match shFunctionCmdOne "^\s*\zs\h\w*\s*()\ze\_s*\%(for\|case\|if\|while\|until\)\>" skipwhite skipnl nextgroup=@shFunctionCmds - syn match shFunctionCmdTwo "\<\h\w*\s*()\ze\_s*\%(for\|case\|if\|while\|until\)\>" contained skipwhite skipnl nextgroup=@shFunctionCmds - syn match shFunctionOne "^\s*\zs\h\w*\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr - syn match shFunctionTwo "\<\h\w*\>\s*()\ze\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr - syn match shFunctionThree "^\s*\zs\h\w*\s*()\ze\_s*(" skipwhite skipnl nextgroup=shFunctionSubSh - syn match shFunctionFour "\<\h\w*\>\s*()\ze\_s*(" contained skipwhite skipnl nextgroup=shFunctionSubSh + syn match shFunctionCmdOne "^\s*\zs\h\w*\s*()\ze\_s*\%(for\|case\|if\|while\|until\)\>" skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens + syn match shFunctionCmdTwo "\<\h\w*\s*()\ze\_s*\%(for\|case\|if\|while\|until\)\>" contained skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens + syn match shFunctionOne "^\s*\zs\h\w*\s*()\ze\_s*{" skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens + syn match shFunctionTwo "\<\h\w*\>\s*()\ze\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens + syn match shFunctionThree "^\s*\zs\h\w*\s*()\ze\_s*(" skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens + syn match shFunctionFour "\<\h\w*\>\s*()\ze\_s*(" contained skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens endif if !exists("g:sh_no_error") @@ -987,6 +990,7 @@ if !exists("skip_sh_syntax_inits") hi def link shCtrlSeq Special hi def link shExprRegion Delimiter hi def link shFunctionKey Keyword + hi def link shFunctionParens Delimiter hi def link shFunctionOne Function hi def link shFunctionTwo shFunctionOne hi def link shFunctionThree shFunctionOne diff --git a/runtime/syntax/testdir/dumps/sh_06_00.dump b/runtime/syntax/testdir/dumps/sh_06_00.dump index b21039227e..8005105917 100644 --- a/runtime/syntax/testdir/dumps/sh_06_00.dump +++ b/runtime/syntax/testdir/dumps/sh_06_00.dump @@ -11,7 +11,7 @@ |#+0#0000e05&| +0#0000000&@73 |#+0#0000e05&| |D|i|s|p|l|a|y| |s|o|m|e| |H|e|l|p| +0#0000000&@55 |#+0#0000e05&| +0#0000000&@73 -|U+0#00e0e07&|s|a|g|e| |(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@64 +|U+0#00e0e07&|s|a|g|e| |(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@64 @75 |#+0#0000e05&| |d|o|e|s| |t|h|i|s| |c|o|m@1|e|n|t| |w|o|r|k|?| +0#0000000&@49 |V+0#00e0e07&|a|r|i|a|b|l|e|N|a|m|e|=+0#0000000&|"+0#af5f00255&|$+0#e000e06&|{|B|a|s|i|c|C|o|n|f|i|g|N|a|m|e|}|_+0#e000002&|*|"+0#af5f00255&| +0#0000000&@39 diff --git a/runtime/syntax/testdir/dumps/sh_06_01.dump b/runtime/syntax/testdir/dumps/sh_06_01.dump index e7cedb4b8e..409fea9b6e 100644 --- a/runtime/syntax/testdir/dumps/sh_06_01.dump +++ b/runtime/syntax/testdir/dumps/sh_06_01.dump @@ -1,4 +1,4 @@ -|U+0#00e0e07#ffffff0|s|a|g|e| |(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@64 +|U+0#00e0e07#ffffff0|s|a|g|e| |(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@64 @75 |#+0#0000e05&| |d|o|e|s| |t|h|i|s| |c|o|m@1|e|n|t| |w|o|r|k|?| +0#0000000&@49 |V+0#00e0e07&|a|r|i|a|b|l|e|N|a|m|e|=+0#0000000&|"+0#af5f00255&|$+0#e000e06&|{|B|a|s|i|c|C|o|n|f|i|g|N|a|m|e|}|_+0#e000002&|*|"+0#af5f00255&| +0#0000000&@39 diff --git a/runtime/syntax/testdir/dumps/sh_06_03.dump b/runtime/syntax/testdir/dumps/sh_06_03.dump index 6a54bcd9af..98bad3092d 100644 --- a/runtime/syntax/testdir/dumps/sh_06_03.dump +++ b/runtime/syntax/testdir/dumps/sh_06_03.dump @@ -4,7 +4,7 @@ |#+0#0000e05&| +0#0000000&@73 |#+0#0000e05&| |C|r|e|a|t|e| |a| |b|a|c|k|u|p| |u|s|i|n|g| |f|b|a|c|k|u|p|/|f|r|e|c|o|v|e|r| +0#0000000&@34 >#+0#0000e05&| +0#0000000&@73 -|E+0#00e0e07&|x|e|c|u|t|e|F|b|a|c|k|u|p| |(|)| +0#0000000&|{+0#e000e06&| +0#0000000&|#+0#0000e05&| |T|E|S|T|I|N|G| +0#0000000&@45 +|E+0#00e0e07&|x|e|c|u|t|e|F|b|a|c|k|u|p| |(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&|#+0#0000e05&| |T|E|S|T|I|N|G| +0#0000000&@45 @75 |[+0#af5f00255&| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|D|e|b|u|g|S|c|r|i|p|t|"+0#af5f00255&| +0#0000000&|]+0#af5f00255&| +0#0000000&@3|&+0#af5f00255&@1| +0#0000000&|s+0#af5f00255&|e|t| +0#00e0e07&|-+0#e000e06&|x| +0#00e0e07&||+0#af5f00255&@1| +0#0000000&|s+0#af5f00255&|e|t| +0#00e0e07&|++0#e000e06&|x| +0#0000000&@33 @75 diff --git a/runtime/syntax/testdir/dumps/sh_07_00.dump b/runtime/syntax/testdir/dumps/sh_07_00.dump index f6dbd2b798..0629360f5e 100644 --- a/runtime/syntax/testdir/dumps/sh_07_00.dump +++ b/runtime/syntax/testdir/dumps/sh_07_00.dump @@ -14,7 +14,7 @@ | | +0#0000000&@73 |#+0#0000e05&| |a|v|a|i|l|a|b|l|e|!| +0#0000000&@62 |#+0#0000e05&| +0#0000000&@73 -|F+0#00e0e07&|u|n|c|t|i|o|n|1| |(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 +|F+0#00e0e07&|u|n|c|t|i|o|n|1| |(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 @75 |e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|F+0#e000002&|u|n|c|t|i|o|n|1|:| |f|o|r| |l|o@1|p| |i|n|s|i|d|e| |a| |f|u|n|c|t|i|o|n|:|\+0#e000e06&|t|\|c|"+0#af5f00255&| +0#0000000&@25 |i|s|_|d|a|s|h|:| |1|,| |i|s|_|p|o|s|i|x|:| |1|,| |i|s|_|s|h|:| |1|,| @22|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/sh_07_01.dump b/runtime/syntax/testdir/dumps/sh_07_01.dump index 4c4fbcb902..d07fa5a55a 100644 --- a/runtime/syntax/testdir/dumps/sh_07_01.dump +++ b/runtime/syntax/testdir/dumps/sh_07_01.dump @@ -2,7 +2,7 @@ | | +0#0000000&@73 |#+0#0000e05&| |a|v|a|i|l|a|b|l|e|!| +0#0000000&@62 |#+0#0000e05&| +0#0000000&@73 -|F+0#00e0e07&|u|n|c|t|i|o|n|1| |(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 +|F+0#00e0e07&|u|n|c|t|i|o|n|1| |(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 @75 >e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|F+0#e000002&|u|n|c|t|i|o|n|1|:| |f|o|r| |l|o@1|p| |i|n|s|i|d|e| |a| |f|u|n|c|t|i|o|n|:|\+0#e000e06&|t|\|c|"+0#af5f00255&| +0#0000000&@25 |[+0#af5f00255&| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|*|"+0#af5f00255&| +0#0000000&|]+0#af5f00255&| +0#0000000&||+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|n+0#e000002&|o|n|e|\+0#e000e06&|c|"+0#af5f00255&| +0#0000000&@49 diff --git a/runtime/syntax/testdir/dumps/sh_07_02.dump b/runtime/syntax/testdir/dumps/sh_07_02.dump index 61842b5ebf..8776cdb13e 100644 --- a/runtime/syntax/testdir/dumps/sh_07_02.dump +++ b/runtime/syntax/testdir/dumps/sh_07_02.dump @@ -3,7 +3,7 @@ |#+0#0000e05&| +0#0000000&@73 |#+0#0000e05&| |F|o|r| |l|o@1|p| |w|i|t|h| |'|i|n| |l|i|s|t|'| |$|*| +0#0000000&@46 |#+0#0000e05&| +0#0000000&@73 ->F+0#00e0e07&|u|n|c|t|i|o|n|2| |(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 +>F+0#00e0e07&|u|n|c|t|i|o|n|2| |(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 @75 |e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|F+0#e000002&|u|n|c|t|i|o|n|2|:| |f|o|r| |l|o@1|p| |i|n|s|i|d|e| |a| |f|u|n|c|t|i|o|n|:|\+0#e000e06&|t|\|c|"+0#af5f00255&| +0#0000000&@25 |f+0#af5f00255&|o|r| +0#0000000&|V|a|r| |i+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|*| +0#0000000&@61 diff --git a/runtime/syntax/testdir/dumps/sh_07_03.dump b/runtime/syntax/testdir/dumps/sh_07_03.dump index a42f25710c..4e9308cd25 100644 --- a/runtime/syntax/testdir/dumps/sh_07_03.dump +++ b/runtime/syntax/testdir/dumps/sh_07_03.dump @@ -1,6 +1,6 @@ |#+0#0000e05#ffffff0| |F|o|r| |l|o@1|p| |w|i|t|h| |'|i|n| |l|i|s|t|'| |$|@|.| |W|o|r|k|s| |t|h|e| |s|a|m|e| |w|a|y| |a|s| |$|*| +0#0000000&@20 |#+0#0000e05&| +0#0000000&@73 -|F+0#00e0e07&|u|n|c|t|i|o|n|3| |(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 +|F+0#00e0e07&|u|n|c|t|i|o|n|3| |(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 @75 |e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|F+0#e000002&|u|n|c|t|i|o|n|3|:| |f|o|r| |l|o@1|p| |i|n|s|i|d|e| |a| |f|u|n|c|t|i|o|n|:|\+0#e000e06&|t|\|c|"+0#af5f00255&| +0#0000000&@25 >f+0#af5f00255&|o|r| +0#0000000&|V|a|r| |i+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|@| +0#0000000&@61 @@ -15,6 +15,6 @@ |#+0#0000e05&| +0#0000000&@73 |#+0#0000e05&| |F|o|r| |l|o@1|p| |w|i|t|h| |'|i|n| |l|i|s|t|'| |"|$|@|"|.| |S|p|e|c|i|a|l| |c|a|s|e|.| |W|o|r|k|s| |l|i|k|e| |"|$|1|"| |"|$|2|"| |.@2| +0#0000000&@4 |#+0#0000e05&| +0#0000000&@73 -|F+0#00e0e07&|u|n|c|t|i|o|n|4| |(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 +|F+0#00e0e07&|u|n|c|t|i|o|n|4| |(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 @75 @57|4|8|,|1| @9|5|6|%| diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_00.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_00.dump index 7045450954..e78273f4f3 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_bash_00.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_bash_00.dump @@ -4,17 +4,17 @@ | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1|t+0#af5f00255#ffffff0|y|p|e|s|e|t| +0#0000000&|-+0#e000e06&|i| +0#0000000&|n+0#00e0e07&|=+0#0000000&|0+0#e000002&| +0#0000000&@58 -| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o@1|s|i|e|(|)| +0#0000000&|(+0#e000e06&@1|n+0#0000000&|++0#af5f00255&|=|1+0#e000002&|)+0#e000e06&@1|;+0#0000000&| |d|o@1|s|i|e| @47 -| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o|n|e@1|(|)| +0#0000000&|[+0#e000e06&@1| +0#0000000&|-+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|#| +0#0000000&|]+0#e000e06&@1|;+0#0000000&| |d|o|n|e@1| @46 +| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o@1|s|i|e|(+0#e000e06&|)| +0#0000000&|(+0#e000e06&@1|n+0#0000000&|++0#af5f00255&|=|1+0#e000002&|)+0#e000e06&@1|;+0#0000000&| |d|o@1|s|i|e| @47 +| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o|n|e@1|(+0#e000e06&|)| +0#0000000&|[+0#e000e06&@1| +0#0000000&|-+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|#| +0#0000000&|]+0#e000e06&@1|;+0#0000000&| |d|o|n|e@1| @46 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(|)| +0#0000000&@64 +| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(+0#e000e06&|)| +0#0000000&@64 | +0#0000e05#a8a8a8255@1|u+0#af5f00255#ffffff0|n|t|i|l| |:| +0#0000000&@65 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:| @67 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|e|n|c|e| @66 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 +| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(+0#e000e06&|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 |i|s|_|b|a|s|h|:| |1|,| @45|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_01.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_01.dump index a7ee351d9f..de0d7d6f12 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_bash_01.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_bash_01.dump @@ -1,19 +1,19 @@ ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|e|n|c|e| @66 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 +| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(+0#e000e06&|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| >e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| >e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 ||+0#0000e05#a8a8a8255| |t+0#af5f00255#ffffff0|h|e|n| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|f+0#af5f00255&|i|;+0#0000000&| |e|l|s|e|w|h|e|r|e| @51 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |s+0#00e0e07#ffffff0|e|l|e|c|t|o|r|(|)| +0#0000000&|s+0#af5f00255&|e|l|e|c|t| |x+0#0000000&| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&|;+0#0000000&| |d+0#af5f00255&|o| +0#0000000&@42 +|-+0#0000e05#a8a8a8255| |s+0#00e0e07#ffffff0|e|l|e|c|t|o|r|(+0#e000e06&|)| +0#0000000&|s+0#af5f00255&|e|l|e|c|t| |x+0#0000000&| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&|;+0#0000000&| |d+0#af5f00255&|o| +0#0000000&@42 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|b+0#af5f00255&|r|e|a|k| +0#0000000&@63 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|e|l|e|c|t|o|r| |0+0#e000002&|<+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1| |2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1| ||@1| |:| @35 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 +| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(+0#e000e06&|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(|)| +0#0000000&@66 +| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(+0#e000e06&|)| +0#0000000&@66 | +0#0000e05#a8a8a8255@1|f+0#af5f00255#ffffff0|o|r| +0#0000000&|x| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&| +0#0000000&@60 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:| @67 diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_02.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_02.dump index c11eb911e2..8016beea4f 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_bash_02.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_bash_02.dump @@ -2,14 +2,14 @@ ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o|r|e| @68 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|i+0#00e0e07#ffffff0|f@1|y|(|)| +0#0000000&|f+0#af5f00255&|o|r| |(@1|;@1|)@1| +0#0000000&@55 +| +0#0000e05#a8a8a8255@1|i+0#00e0e07#ffffff0|f@1|y|(+0#e000e06&|)| +0#0000000&|f+0#af5f00255&|o|r| |(@1|;@1|)@1| +0#0000000&@55 |-+0#0000e05#a8a8a8255| >d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|b+0#af5f00255&|r|e|a|k| +0#0000000&@63 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|i+0#0000000#ffffff0|f@1|y| @68 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 |-+0#0000e05#a8a8a8255| |i+0#af5f00255#ffffff0|f| |:+0#0000000&|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@62 -||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|!+0#00e0e07&|?|#|(|)| +0#0000000&@54 +||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|!+0#00e0e07&|?|#|(+0#e000e06&|)| +0#0000000&@54 |-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|(+0#af5f00255&| +0#0000000&@67 |-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|@+0#00e0e07&|α|!| +0#0000000&|{+0#e000e06&| +0#0000000&@50 |3+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@11|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@51 diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_03.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_03.dump index a18749ada0..a90642e1dc 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_bash_03.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_bash_03.dump @@ -1,7 +1,7 @@ ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|e+0#af5f00255&|v|a|l| +0#0000000&|!+0#af5f00255&|?+0#0000000&|\+0#e000e06&|#| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@54 ||+0#0000e05#a8a8a8255| |f+0#af5f00255#ffffff0|i| +0#0000000&@70 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|n+0#00e0e07#ffffff0|a|m|e|s|p|a|c|e| |(|)| +0#0000000&@60 +| +0#0000e05#a8a8a8255@1|n+0#00e0e07#ffffff0|a|m|e|s|p|a|c|e| |(+0#e000e06&|)| +0#0000000&@60 |-+0#0000e05#a8a8a8255| |{+0#e000e06#ffffff0| +0#0000000&|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|#|;+0#af5f00255&| +0#0000000&@62 ||+0#0000e05#a8a8a8255| >}+0#e000e06#ffffff0|;+0#0000000&| |n|a|m|e|s|p|a|c|e| |$+0#e000e06&|@| +0#0000000&@57 |~+0#4040ff13&| @73 diff --git a/runtime/syntax/testdir/dumps/sh_functions_dash_00.dump b/runtime/syntax/testdir/dumps/sh_functions_dash_00.dump index f4465f4ea0..a7f92e5165 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_dash_00.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_dash_00.dump @@ -3,18 +3,18 @@ | +0#0000e05#a8a8a8255@1|#+0&#ffffff0| |V|I|M|_|T|E|S|T|_|S|E|T|U|P| |l|e|t| |g|:|s|h|_|f|o|l|d|_|e|n|a|b|l|e|d| |=| |1| |+| |2| |+| |4| +0#0000000&@22 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(|)| +0#0000000&@64 +| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(+0#e000e06&|)| +0#0000000&@64 | +0#0000e05#a8a8a8255@1|u+0#af5f00255#ffffff0|n|t|i|l| |:| +0#0000000&@65 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:+0#0000e05&| +0#0000000&@67 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|e|n|c|e| @66 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 +| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(+0#e000e06&|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 ||+0#0000e05#a8a8a8255| |t+0#af5f00255#ffffff0|h|e|n| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|f+0#af5f00255&|i|;+0#0000000&| |e|l|s|e|w|h|e|r|e| @51 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 +| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(+0#e000e06&|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 |i|s|_|d|a|s|h|:| |1|,| |i|s|_|p|o|s|i|x|:| |1|,| |i|s|_|s|h|:| |1|,| @22|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/sh_functions_dash_01.dump b/runtime/syntax/testdir/dumps/sh_functions_dash_01.dump index 5762b7be64..1294700034 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_dash_01.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_dash_01.dump @@ -1,10 +1,10 @@ | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 ||+0#0000e05#a8a8a8255| |t+0#af5f00255#ffffff0|h|e|n| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|f+0#af5f00255&|i|;+0#0000000&| |e|l|s|e|w|h|e|r|e| @51 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 +| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(+0#e000e06&|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 | +0#0000e05#a8a8a8255@1> +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(|)| +0#0000000&@66 +| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(+0#e000e06&|)| +0#0000000&@66 | +0#0000e05#a8a8a8255@1|f+0#af5f00255#ffffff0|o|r| +0#0000000&|x| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&| +0#0000000&@60 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:+0#0000e05&| +0#0000000&@67 @@ -12,9 +12,9 @@ | +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o|r|e| @68 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 |-+0#0000e05#a8a8a8255| |i+0#af5f00255#ffffff0|f| |:+0#0000000&|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@62 -||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|i+0#00e0e07&|d|2|(|)| +0#0000000&@63 +||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|i+0#00e0e07&|d|2|(+0#e000e06&|)| +0#0000000&@63 |-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|(+0#af5f00255&| +0#0000000&@67 -|-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|i+0#00e0e07&|d|1|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 +|-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|i+0#00e0e07&|d|1|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 |3+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@11|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@51 |3+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|}+0#e000e06&| +0#0000000&@63 @57|1|9|,|0|-|1| @7|6|1|%| diff --git a/runtime/syntax/testdir/dumps/sh_functions_dash_02.dump b/runtime/syntax/testdir/dumps/sh_functions_dash_02.dump index e7d2985902..e1aa1d46d6 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_dash_02.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_dash_02.dump @@ -4,7 +4,7 @@ ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|i|d|2| |"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@60 ||+0#0000e05#a8a8a8255| |f+0#af5f00255#ffffff0|i| +0#0000000&@70 | +0#0000e05#a8a8a8255@1> +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|u|n|c|t|i|o|n| |(|)| +0#0000000&@61 +| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|u|n|c|t|i|o|n| |(+0#e000e06&|)| +0#0000000&@61 |-+0#0000e05#a8a8a8255| |{+0#e000e06#ffffff0| +0#0000000&|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|#|;+0#af5f00255&| +0#0000000&@62 ||+0#0000e05#a8a8a8255| |}+0#e000e06#ffffff0|;+0#0000000&| |f|u|n|c|t|i|o|n| |$+0#e000e06&|@| +0#0000000&@58 |~+0#4040ff13&| @73 diff --git a/runtime/syntax/testdir/dumps/sh_functions_ksh2020_00.dump b/runtime/syntax/testdir/dumps/sh_functions_ksh2020_00.dump index 8c4edd229c..e1d5e01558 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_ksh2020_00.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_ksh2020_00.dump @@ -4,17 +4,17 @@ | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1|t+0#af5f00255#ffffff0|y|p|e|s|e|t| +0#0000000&|-+0#e000e06&|i| +0#0000000&|1+0#e000002&|0| +0#0000000&|n+0#00e0e07&|=+0#0000000&|0+0#e000002&| +0#0000000&@55 -| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o@1|s|i|e|(|)| +0#0000000&|(+0#e000e06&@1|n+0#0000000&|++0#af5f00255&|=|1+0#e000002&|)+0#e000e06&@1|;+0#0000000&| |d|o@1|s|i|e| @47 -| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o|n|e@1|(|)| +0#0000000&|[+0#e000e06&@1| +0#0000000&|-+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|#| +0#0000000&|]+0#e000e06&@1|;+0#0000000&| |d|o|n|e@1| @46 +| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o@1|s|i|e|(+0#e000e06&|)| +0#0000000&|(+0#e000e06&@1|n+0#0000000&|++0#af5f00255&|=|1+0#e000002&|)+0#e000e06&@1|;+0#0000000&| |d|o@1|s|i|e| @47 +| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o|n|e@1|(+0#e000e06&|)| +0#0000000&|[+0#e000e06&@1| +0#0000000&|-+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|#| +0#0000000&|]+0#e000e06&@1|;+0#0000000&| |d|o|n|e@1| @46 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(|)| +0#0000000&@64 +| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(+0#e000e06&|)| +0#0000000&@64 | +0#0000e05#a8a8a8255@1|u+0#af5f00255#ffffff0|n|t|i|l| |:| +0#0000000&@65 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:+0#0000e05&| +0#0000000&@67 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|e|n|c|e| @66 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 +| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(+0#e000e06&|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 |i|s|_|k|o|r|n|s|h|e|l@1|:| |1|,| |i|s|_|k|s|h|2|0|2|0|:| |1|,| @25|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/sh_functions_ksh2020_01.dump b/runtime/syntax/testdir/dumps/sh_functions_ksh2020_01.dump index 6e74c342dc..c466ead0f4 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_ksh2020_01.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_ksh2020_01.dump @@ -1,19 +1,19 @@ ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|e|n|c|e| @66 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 +| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(+0#e000e06&|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| >e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| >e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 ||+0#0000e05#a8a8a8255| |t+0#af5f00255#ffffff0|h|e|n| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|f+0#af5f00255&|i|;+0#0000000&| |e|l|s|e|w|h|e|r|e| @51 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |s+0#00e0e07#ffffff0|e|l|e|c|t|o|r|(|)| +0#0000000&|s+0#af5f00255&|e|l|e|c|t| |x+0#0000000&| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&|;+0#0000000&| |d+0#af5f00255&|o| +0#0000000&@42 +|-+0#0000e05#a8a8a8255| |s+0#00e0e07#ffffff0|e|l|e|c|t|o|r|(+0#e000e06&|)| +0#0000000&|s+0#af5f00255&|e|l|e|c|t| |x+0#0000000&| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&|;+0#0000000&| |d+0#af5f00255&|o| +0#0000000&@42 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|b+0#af5f00255&|r|e|a|k| +0#0000000&@63 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|e|l|e|c|t|o|r| |0+0#e000002&|<+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1| |2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1| ||@1| |:| @35 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 +| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(+0#e000e06&|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(|)| +0#0000000&@66 +| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(+0#e000e06&|)| +0#0000000&@66 | +0#0000e05#a8a8a8255@1|f+0#af5f00255#ffffff0|o|r| +0#0000000&|x| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&| +0#0000000&@60 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:+0#0000e05&| +0#0000000&@67 diff --git a/runtime/syntax/testdir/dumps/sh_functions_ksh2020_02.dump b/runtime/syntax/testdir/dumps/sh_functions_ksh2020_02.dump index cca29de74d..0fb4817dab 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_ksh2020_02.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_ksh2020_02.dump @@ -2,14 +2,14 @@ ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o|r|e| @68 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|i+0#00e0e07#ffffff0|f@1|y|(|)| +0#0000000&|f+0#af5f00255&|o|r| |(@1|;@1|)@1| +0#0000000&@55 +| +0#0000e05#a8a8a8255@1|i+0#00e0e07#ffffff0|f@1|y|(+0#e000e06&|)| +0#0000000&|f+0#af5f00255&|o|r| |(@1|;@1|)@1| +0#0000000&@55 |-+0#0000e05#a8a8a8255| >d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|b+0#af5f00255&|r|e|a|k| +0#0000000&@63 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|i+0#0000000#ffffff0|f@1|y| @68 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 |-+0#0000e05#a8a8a8255| |i+0#af5f00255#ffffff0|f| |:+0#0000000&|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@62 -||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|i+0#00e0e07&|d|_|(|)| +0#0000000&@63 +||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|i+0#00e0e07&|d|_|(+0#e000e06&|)| +0#0000000&@63 |-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|(+0#af5f00255&| +0#0000000&@67 |-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|n+0#00e0e07&|.|s|e|t| +0#0000000&|{+0#e000e06&| +0#0000000&@48 |3+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@11|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|n|"+0#af5f00255&| +0#0000000&@51 diff --git a/runtime/syntax/testdir/dumps/sh_functions_ksh88_00.dump b/runtime/syntax/testdir/dumps/sh_functions_ksh88_00.dump index f4a6e0311c..799e458df2 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_ksh88_00.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_ksh88_00.dump @@ -4,17 +4,17 @@ | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1|t+0#af5f00255#ffffff0|y|p|e|s|e|t| +0#0000000&|-+0#e000e06&|i| +0#0000000&|1+0#e000002&|0| +0#0000000&|n+0#00e0e07&|=+0#0000000&|0+0#e000002&| +0#0000000&@55 -| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o@1|s|i|e|(|)| +0#0000000&|(+0#e000e06&@1|n+0#0000000&|++0#af5f00255&|=|1+0#e000002&|)+0#e000e06&@1|;+0#0000000&| |d|o@1|s|i|e| @47 -| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o|n|e@1|(|)| +0#0000000&|[+0#e000e06&@1| +0#0000000&|-+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|#| +0#0000000&|]+0#e000e06&@1|;+0#0000000&| |d|o|n|e@1| @46 +| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o@1|s|i|e|(+0#e000e06&|)| +0#0000000&|(+0#e000e06&@1|n+0#0000000&|++0#af5f00255&|=|1+0#e000002&|)+0#e000e06&@1|;+0#0000000&| |d|o@1|s|i|e| @47 +| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o|n|e@1|(+0#e000e06&|)| +0#0000000&|[+0#e000e06&@1| +0#0000000&|-+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|#| +0#0000000&|]+0#e000e06&@1|;+0#0000000&| |d|o|n|e@1| @46 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(|)| +0#0000000&@64 +| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(+0#e000e06&|)| +0#0000000&@64 | +0#0000e05#a8a8a8255@1|u+0#af5f00255#ffffff0|n|t|i|l| |:| +0#0000000&@65 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:+0#0000e05&| +0#0000000&@67 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|e|n|c|e| @66 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 +| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(+0#e000e06&|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 |i|s|_|k|o|r|n|s|h|e|l@1|:| |1|,| |i|s|_|k|s|h|8@1|:| |1|,| @27|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/sh_functions_ksh88_01.dump b/runtime/syntax/testdir/dumps/sh_functions_ksh88_01.dump index 3426afb1af..e72cee2b18 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_ksh88_01.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_ksh88_01.dump @@ -1,19 +1,19 @@ ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|e|n|c|e| @66 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 +| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(+0#e000e06&|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| >e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| >e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 ||+0#0000e05#a8a8a8255| |t+0#af5f00255#ffffff0|h|e|n| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|f+0#af5f00255&|i|;+0#0000000&| |e|l|s|e|w|h|e|r|e| @51 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |s+0#00e0e07#ffffff0|e|l|e|c|t|o|r|(|)| +0#0000000&|s+0#af5f00255&|e|l|e|c|t| |x+0#0000000&| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&|;+0#0000000&| |d+0#af5f00255&|o| +0#0000000&@42 +|-+0#0000e05#a8a8a8255| |s+0#00e0e07#ffffff0|e|l|e|c|t|o|r|(+0#e000e06&|)| +0#0000000&|s+0#af5f00255&|e|l|e|c|t| |x+0#0000000&| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&|;+0#0000000&| |d+0#af5f00255&|o| +0#0000000&@42 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|b+0#af5f00255&|r|e|a|k| +0#0000000&@63 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|e|l|e|c|t|o|r| |0+0#e000002&|<+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1| |2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1| ||@1| |:| @35 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 +| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(+0#e000e06&|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(|)| +0#0000000&@66 +| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(+0#e000e06&|)| +0#0000000&@66 | +0#0000e05#a8a8a8255@1|f+0#af5f00255#ffffff0|o|r| +0#0000000&|x| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&| +0#0000000&@60 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:+0#0000e05&| +0#0000000&@67 diff --git a/runtime/syntax/testdir/dumps/sh_functions_ksh88_02.dump b/runtime/syntax/testdir/dumps/sh_functions_ksh88_02.dump index 4df14699ad..c7fe98e65f 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_ksh88_02.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_ksh88_02.dump @@ -3,7 +3,7 @@ | +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o|r|e| @68 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 |-+0#0000e05#a8a8a8255| |i+0#af5f00255#ffffff0|f| |:+0#0000000&|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@62 -||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3>i+0#00e0e07&|d|2|(|)| +0#0000000&@63 +||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3>i+0#00e0e07&|d|2|(+0#e000e06&|)| +0#0000000&@63 |-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|(+0#af5f00255&| +0#0000000&@67 |-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|i+0#00e0e07&|d|1| +0#0000000&|{+0#e000e06&| +0#0000000&@50 |3+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@11|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@51 diff --git a/runtime/syntax/testdir/dumps/sh_functions_mksh_00.dump b/runtime/syntax/testdir/dumps/sh_functions_mksh_00.dump index ac300ee600..5cb6c2fd49 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_mksh_00.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_mksh_00.dump @@ -4,17 +4,17 @@ | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1|t+0#af5f00255#ffffff0|y|p|e|s|e|t| +0#0000000&|-+0#e000e06&|i|1|0| +0#0000000&|n+0#00e0e07&|=+0#0000000&|0+0#e000002&| +0#0000000&@56 -| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o@1|s|i|e|(|)| +0#0000000&|(+0#e000e06&@1|n+0#0000000&|++0#af5f00255&|=|1+0#e000002&|)+0#e000e06&@1|;+0#0000000&| |d|o@1|s|i|e| @47 -| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o|n|e@1|(|)| +0#0000000&|[+0#e000e06&@1| +0#0000000&|-+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|#| +0#0000000&|]+0#e000e06&@1|;+0#0000000&| |d|o|n|e@1| @46 +| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o@1|s|i|e|(+0#e000e06&|)| +0#0000000&|(+0#e000e06&@1|n+0#0000000&|++0#af5f00255&|=|1+0#e000002&|)+0#e000e06&@1|;+0#0000000&| |d|o@1|s|i|e| @47 +| +0#0000e05#a8a8a8255@1|d+0#00e0e07#ffffff0|o|n|e@1|(+0#e000e06&|)| +0#0000000&|[+0#e000e06&@1| +0#0000000&|-+0#af5f00255&|n| +0#0000000&|$+0#e000e06&|#| +0#0000000&|]+0#e000e06&@1|;+0#0000000&| |d|o|n|e@1| @46 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(|)| +0#0000000&@64 +| +0#0000e05#a8a8a8255@1|t+0#00e0e07#ffffff0|h|e|n|c|e|(+0#e000e06&|)| +0#0000000&@64 | +0#0000e05#a8a8a8255@1|u+0#af5f00255#ffffff0|n|t|i|l| |:| +0#0000000&@65 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:+0#0000e05&| +0#0000000&@67 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|e|n|c|e| @66 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 +| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(+0#e000e06&|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| |e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 |i|s|_|k|o|r|n|s|h|e|l@1|:| |1|,| |i|s|_|m|k|s|h|:| |1|,| @28|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/sh_functions_mksh_01.dump b/runtime/syntax/testdir/dumps/sh_functions_mksh_01.dump index 3426afb1af..e72cee2b18 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_mksh_01.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_mksh_01.dump @@ -1,19 +1,19 @@ ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|t+0#0000000#ffffff0|h|e|n|c|e| @66 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 +| +0#0000e05#a8a8a8255@1|w+0#00e0e07#ffffff0|h|i|l|e|s|(+0#e000e06&|)| +0#0000000&|w+0#af5f00255&|h|i|l|e| |f|a|l|s|e|;| |d|o| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|d+0#af5f00255&|o|n|e|;+0#0000000&| |w|h|i|l|e|s| @32 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| >e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 +|-+0#0000e05#a8a8a8255| >e+0#00e0e07#ffffff0|l|s|e|w|h|e|r|e|(+0#e000e06&|)| +0#0000000&|i+0#af5f00255&|f| |:+0#0000000&| @56 ||+0#0000e05#a8a8a8255| |t+0#af5f00255#ffffff0|h|e|n| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|f+0#af5f00255&|i|;+0#0000000&| |e|l|s|e|w|h|e|r|e| @51 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -|-+0#0000e05#a8a8a8255| |s+0#00e0e07#ffffff0|e|l|e|c|t|o|r|(|)| +0#0000000&|s+0#af5f00255&|e|l|e|c|t| |x+0#0000000&| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&|;+0#0000000&| |d+0#af5f00255&|o| +0#0000000&@42 +|-+0#0000e05#a8a8a8255| |s+0#00e0e07#ffffff0|e|l|e|c|t|o|r|(+0#e000e06&|)| +0#0000000&|s+0#af5f00255&|e|l|e|c|t| |x+0#0000000&| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&|;+0#0000000&| |d+0#af5f00255&|o| +0#0000000&@42 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|b+0#af5f00255&|r|e|a|k| +0#0000000&@63 ||+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o|n|e| +0#0000000&@68 | +0#0000e05#a8a8a8255@1|s+0#0000000#ffffff0|e|l|e|c|t|o|r| |0+0#e000002&|<+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1| |2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1| ||@1| |:| @35 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 +| +0#0000e05#a8a8a8255@1|c+0#00e0e07#ffffff0|a|s|e|d|(+0#e000e06&|)| +0#0000000&|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|#|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&|*|)+0#af5f00255&| +0#0000000&|:|;+0#af5f00255&@1| +0#0000000&|e+0#af5f00255&|s|a|c|;+0#0000000&| |c|a|s|e|d| @33 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 -| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(|)| +0#0000000&@66 +| +0#0000e05#a8a8a8255@1|f+0#00e0e07#ffffff0|o|r|e|(+0#e000e06&|)| +0#0000000&@66 | +0#0000e05#a8a8a8255@1|f+0#af5f00255#ffffff0|o|r| +0#0000000&|x| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&| +0#0000000&@60 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:+0#0000e05&| +0#0000000&@67 diff --git a/runtime/syntax/testdir/dumps/sh_functions_mksh_02.dump b/runtime/syntax/testdir/dumps/sh_functions_mksh_02.dump index 5fed5e6358..aa72e6f518 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_mksh_02.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_mksh_02.dump @@ -3,9 +3,9 @@ | +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o|r|e| @68 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 |-+0#0000e05#a8a8a8255| |i+0#af5f00255#ffffff0|f| |:+0#0000000&|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@62 -||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3>%+0#00e0e07&@2|(|)| +0#0000000&@63 +||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3>%+0#00e0e07&@2|(+0#e000e06&|)| +0#0000000&@63 |-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|(+0#af5f00255&| +0#0000000&@67 -|-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|@+0#00e0e07&|a|:|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@48 +|-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|@+0#00e0e07&|a|:|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@48 |3+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@11|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@51 |3+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|}+0#e000e06&| +0#0000000&@63 |2+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|@|a|:| |"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@56 diff --git a/runtime/syntax/testdir/dumps/sh_generic_07.dump b/runtime/syntax/testdir/dumps/sh_generic_07.dump index 7589946109..6c7c45c6c1 100644 --- a/runtime/syntax/testdir/dumps/sh_generic_07.dump +++ b/runtime/syntax/testdir/dumps/sh_generic_07.dump @@ -3,7 +3,7 @@ |#+0#0000e05&| |V|a|l|u|e| |s|u|b|s|t|i|t|u|t|i|o|n|s| |o|f| |t|h|e| |f|o|r|m| |$|{|||c|o|m@1|a|n|d|}| |a|r|e| |o|n|l|y| +0#0000000&@20 |#+0#0000e05&| |s|u|p@1|o|r|t|e|d| |b|y| |m|k|s|h|,| |n|o|t| |k|s|h|9|3|.| +0#0000000&@43 |i+0#af5f00255&|f| |!| +0#0000000&|c+0#af5f00255&|o|m@1|a|n|d| +0#0000000&|e+0#af5f00255&|v|a|l| +0#0000000&|'+0#af5f00255&|(+0#e000002&@1|.|s|h|.|v|e|r|s|i|o|n| |>|=| |2|0@1|7|0|7|0|3|)@1|'+0#af5f00255&| +0#0000000&|2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@9 -@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 +@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 @16|R+0#e000e06&|E|P|L|Y|=+0#af5f00255&|$+0#e000e06&|1| +0#0000000&@50 @8|}+0#e000e06&| +0#0000000&@65 @8|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{|||v|a|l|s|u|b|f|u|n|c| |t|e|n|}| +0#0000000&@43 diff --git a/runtime/syntax/testdir/dumps/sh_generic_10.dump b/runtime/syntax/testdir/dumps/sh_generic_10.dump index cf4178f5e5..dbd1f398df 100644 --- a/runtime/syntax/testdir/dumps/sh_generic_10.dump +++ b/runtime/syntax/testdir/dumps/sh_generic_10.dump @@ -1,5 +1,5 @@ | +0&#ffffff0@74 -|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 +|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 @8|t+0#af5f00255&|r|u|e| +0#0000000&@62 |}+0#e000e06&| +0#0000000&@73 @75 @@ -15,6 +15,6 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |f|u|n|c|t|i|o|n| +0#0000000&@37 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|3|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 +|f+0#00e0e07&|o@1|3|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 @57|1|5|4|,|1| @8|7|9|%| diff --git a/runtime/syntax/testdir/dumps/sh_generic_11.dump b/runtime/syntax/testdir/dumps/sh_generic_11.dump index f1eb6e296d..fcb44b04ca 100644 --- a/runtime/syntax/testdir/dumps/sh_generic_11.dump +++ b/runtime/syntax/testdir/dumps/sh_generic_11.dump @@ -1,7 +1,7 @@ | +0&#ffffff0@7|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|4|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 +|f+0#00e0e07&|o@1|4|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@24 >}+0#e000e06&| +0#0000000&@73 @75 @@ -9,7 +9,7 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@20 |}+0#e000e06&| +0#0000000&@73 @75 -|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 +|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |s|u|b|s|h|e|l@1| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@21 |)+0#af5f00255&| +0#0000000&@73 @75 diff --git a/runtime/syntax/testdir/dumps/sh_ksh2020_07.dump b/runtime/syntax/testdir/dumps/sh_ksh2020_07.dump index 187550a491..20ebec5e04 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh2020_07.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh2020_07.dump @@ -3,7 +3,7 @@ |#+0#0000e05&| |V|a|l|u|e| |s|u|b|s|t|i|t|u|t|i|o|n|s| |o|f| |t|h|e| |f|o|r|m| |$|{|||c|o|m@1|a|n|d|}| |a|r|e| |o|n|l|y| +0#0000000&@20 |#+0#0000e05&| |s|u|p@1|o|r|t|e|d| |b|y| |m|k|s|h|,| |n|o|t| |k|s|h|9|3|.| +0#0000000&@43 |i+0#af5f00255&|f| |!| +0#0000000&|c+0#af5f00255&|o|m@1|a|n|d| +0#0000000&|e+0#af5f00255&|v|a|l| +0#0000000&|'+0#af5f00255&|(+0#e000002&@1|.|s|h|.|v|e|r|s|i|o|n| |>|=| |2|0@1|7|0|7|0|3|)@1|'+0#af5f00255&| +0#0000000&|2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@9 -@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 +@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 @16|R+0#e000e06&|E|P|L|Y|=+0#af5f00255&|$+0#e000e06&|1| +0#0000000&@50 @8|}+0#e000e06&| +0#0000000&@65 @8|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{||+0#ffffff16#ff404010|v|a|l|s|u|b|f|u|n|c| |t|e|n|}+0#e000e06#ffffff0| +0#0000000&@43 diff --git a/runtime/syntax/testdir/dumps/sh_ksh2020_10.dump b/runtime/syntax/testdir/dumps/sh_ksh2020_10.dump index cf4178f5e5..dbd1f398df 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh2020_10.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh2020_10.dump @@ -1,5 +1,5 @@ | +0&#ffffff0@74 -|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 +|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 @8|t+0#af5f00255&|r|u|e| +0#0000000&@62 |}+0#e000e06&| +0#0000000&@73 @75 @@ -15,6 +15,6 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |f|u|n|c|t|i|o|n| +0#0000000&@37 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|3|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 +|f+0#00e0e07&|o@1|3|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 @57|1|5|4|,|1| @8|7|9|%| diff --git a/runtime/syntax/testdir/dumps/sh_ksh2020_11.dump b/runtime/syntax/testdir/dumps/sh_ksh2020_11.dump index f1eb6e296d..fcb44b04ca 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh2020_11.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh2020_11.dump @@ -1,7 +1,7 @@ | +0&#ffffff0@7|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|4|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 +|f+0#00e0e07&|o@1|4|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@24 >}+0#e000e06&| +0#0000000&@73 @75 @@ -9,7 +9,7 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@20 |}+0#e000e06&| +0#0000000&@73 @75 -|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 +|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |s|u|b|s|h|e|l@1| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@21 |)+0#af5f00255&| +0#0000000&@73 @75 diff --git a/runtime/syntax/testdir/dumps/sh_ksh88_07.dump b/runtime/syntax/testdir/dumps/sh_ksh88_07.dump index f2b2a7f5d2..f70edcc3d8 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh88_07.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh88_07.dump @@ -3,7 +3,7 @@ |#+0#0000e05&| |V|a|l|u|e| |s|u|b|s|t|i|t|u|t|i|o|n|s| |o|f| |t|h|e| |f|o|r|m| |$|{|||c|o|m@1|a|n|d|}| |a|r|e| |o|n|l|y| +0#0000000&@20 |#+0#0000e05&| |s|u|p@1|o|r|t|e|d| |b|y| |m|k|s|h|,| |n|o|t| |k|s|h|9|3|.| +0#0000000&@43 |i+0#af5f00255&|f| |!| +0#0000000&|c+0#af5f00255&|o|m@1|a|n|d| +0#0000000&|e+0#af5f00255&|v|a|l| +0#0000000&|'+0#af5f00255&|(+0#e000002&@1|.|s|h|.|v|e|r|s|i|o|n| |>|=| |2|0@1|7|0|7|0|3|)@1|'+0#af5f00255&| +0#0000000&|2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@9 -@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 +@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 @16|R+0#e000e06&|E|P|L|Y|=+0#af5f00255&|$+0#e000e06&|1| +0#0000000&@50 @8|}+0#e000e06&| +0#0000000&@65 @8|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{||+0#ffffff16#ff404010|v|a|l|s|u|b|f|u|n|c| |t|e|n|}+0#e000e06#ffffff0| +0#0000000&@43 diff --git a/runtime/syntax/testdir/dumps/sh_ksh88_10.dump b/runtime/syntax/testdir/dumps/sh_ksh88_10.dump index 54507b7d6e..b7fefdea5d 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh88_10.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh88_10.dump @@ -15,6 +15,6 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |f|u|n|c|t|i|o|n| +0#0000000&@37 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|3|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 +|f+0#00e0e07&|o@1|3|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 @57|1|5|4|,|1| @8|7|9|%| diff --git a/runtime/syntax/testdir/dumps/sh_ksh93_generic_07.dump b/runtime/syntax/testdir/dumps/sh_ksh93_generic_07.dump index 187550a491..20ebec5e04 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh93_generic_07.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh93_generic_07.dump @@ -3,7 +3,7 @@ |#+0#0000e05&| |V|a|l|u|e| |s|u|b|s|t|i|t|u|t|i|o|n|s| |o|f| |t|h|e| |f|o|r|m| |$|{|||c|o|m@1|a|n|d|}| |a|r|e| |o|n|l|y| +0#0000000&@20 |#+0#0000e05&| |s|u|p@1|o|r|t|e|d| |b|y| |m|k|s|h|,| |n|o|t| |k|s|h|9|3|.| +0#0000000&@43 |i+0#af5f00255&|f| |!| +0#0000000&|c+0#af5f00255&|o|m@1|a|n|d| +0#0000000&|e+0#af5f00255&|v|a|l| +0#0000000&|'+0#af5f00255&|(+0#e000002&@1|.|s|h|.|v|e|r|s|i|o|n| |>|=| |2|0@1|7|0|7|0|3|)@1|'+0#af5f00255&| +0#0000000&|2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@9 -@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 +@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 @16|R+0#e000e06&|E|P|L|Y|=+0#af5f00255&|$+0#e000e06&|1| +0#0000000&@50 @8|}+0#e000e06&| +0#0000000&@65 @8|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{||+0#ffffff16#ff404010|v|a|l|s|u|b|f|u|n|c| |t|e|n|}+0#e000e06#ffffff0| +0#0000000&@43 diff --git a/runtime/syntax/testdir/dumps/sh_ksh93_generic_10.dump b/runtime/syntax/testdir/dumps/sh_ksh93_generic_10.dump index cf4178f5e5..dbd1f398df 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh93_generic_10.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh93_generic_10.dump @@ -1,5 +1,5 @@ | +0&#ffffff0@74 -|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 +|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 @8|t+0#af5f00255&|r|u|e| +0#0000000&@62 |}+0#e000e06&| +0#0000000&@73 @75 @@ -15,6 +15,6 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |f|u|n|c|t|i|o|n| +0#0000000&@37 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|3|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 +|f+0#00e0e07&|o@1|3|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 @57|1|5|4|,|1| @8|7|9|%| diff --git a/runtime/syntax/testdir/dumps/sh_ksh93_generic_11.dump b/runtime/syntax/testdir/dumps/sh_ksh93_generic_11.dump index f1eb6e296d..fcb44b04ca 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh93_generic_11.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh93_generic_11.dump @@ -1,7 +1,7 @@ | +0&#ffffff0@7|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|4|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 +|f+0#00e0e07&|o@1|4|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@24 >}+0#e000e06&| +0#0000000&@73 @75 @@ -9,7 +9,7 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@20 |}+0#e000e06&| +0#0000000&@73 @75 -|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 +|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |s|u|b|s|h|e|l@1| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@21 |)+0#af5f00255&| +0#0000000&@73 @75 diff --git a/runtime/syntax/testdir/dumps/sh_ksh93u_07.dump b/runtime/syntax/testdir/dumps/sh_ksh93u_07.dump index 187550a491..20ebec5e04 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh93u_07.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh93u_07.dump @@ -3,7 +3,7 @@ |#+0#0000e05&| |V|a|l|u|e| |s|u|b|s|t|i|t|u|t|i|o|n|s| |o|f| |t|h|e| |f|o|r|m| |$|{|||c|o|m@1|a|n|d|}| |a|r|e| |o|n|l|y| +0#0000000&@20 |#+0#0000e05&| |s|u|p@1|o|r|t|e|d| |b|y| |m|k|s|h|,| |n|o|t| |k|s|h|9|3|.| +0#0000000&@43 |i+0#af5f00255&|f| |!| +0#0000000&|c+0#af5f00255&|o|m@1|a|n|d| +0#0000000&|e+0#af5f00255&|v|a|l| +0#0000000&|'+0#af5f00255&|(+0#e000002&@1|.|s|h|.|v|e|r|s|i|o|n| |>|=| |2|0@1|7|0|7|0|3|)@1|'+0#af5f00255&| +0#0000000&|2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@9 -@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 +@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 @16|R+0#e000e06&|E|P|L|Y|=+0#af5f00255&|$+0#e000e06&|1| +0#0000000&@50 @8|}+0#e000e06&| +0#0000000&@65 @8|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{||+0#ffffff16#ff404010|v|a|l|s|u|b|f|u|n|c| |t|e|n|}+0#e000e06#ffffff0| +0#0000000&@43 diff --git a/runtime/syntax/testdir/dumps/sh_ksh93u_10.dump b/runtime/syntax/testdir/dumps/sh_ksh93u_10.dump index cf4178f5e5..dbd1f398df 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh93u_10.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh93u_10.dump @@ -1,5 +1,5 @@ | +0&#ffffff0@74 -|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 +|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 @8|t+0#af5f00255&|r|u|e| +0#0000000&@62 |}+0#e000e06&| +0#0000000&@73 @75 @@ -15,6 +15,6 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |f|u|n|c|t|i|o|n| +0#0000000&@37 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|3|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 +|f+0#00e0e07&|o@1|3|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 @57|1|5|4|,|1| @8|7|9|%| diff --git a/runtime/syntax/testdir/dumps/sh_ksh93u_11.dump b/runtime/syntax/testdir/dumps/sh_ksh93u_11.dump index f1eb6e296d..fcb44b04ca 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh93u_11.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh93u_11.dump @@ -1,7 +1,7 @@ | +0&#ffffff0@7|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|4|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 +|f+0#00e0e07&|o@1|4|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@24 >}+0#e000e06&| +0#0000000&@73 @75 @@ -9,7 +9,7 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@20 |}+0#e000e06&| +0#0000000&@73 @75 -|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 +|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |s|u|b|s|h|e|l@1| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@21 |)+0#af5f00255&| +0#0000000&@73 @75 diff --git a/runtime/syntax/testdir/dumps/sh_ksh93v_07.dump b/runtime/syntax/testdir/dumps/sh_ksh93v_07.dump index 187550a491..20ebec5e04 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh93v_07.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh93v_07.dump @@ -3,7 +3,7 @@ |#+0#0000e05&| |V|a|l|u|e| |s|u|b|s|t|i|t|u|t|i|o|n|s| |o|f| |t|h|e| |f|o|r|m| |$|{|||c|o|m@1|a|n|d|}| |a|r|e| |o|n|l|y| +0#0000000&@20 |#+0#0000e05&| |s|u|p@1|o|r|t|e|d| |b|y| |m|k|s|h|,| |n|o|t| |k|s|h|9|3|.| +0#0000000&@43 |i+0#af5f00255&|f| |!| +0#0000000&|c+0#af5f00255&|o|m@1|a|n|d| +0#0000000&|e+0#af5f00255&|v|a|l| +0#0000000&|'+0#af5f00255&|(+0#e000002&@1|.|s|h|.|v|e|r|s|i|o|n| |>|=| |2|0@1|7|0|7|0|3|)@1|'+0#af5f00255&| +0#0000000&|2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@9 -@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 +@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 @16|R+0#e000e06&|E|P|L|Y|=+0#af5f00255&|$+0#e000e06&|1| +0#0000000&@50 @8|}+0#e000e06&| +0#0000000&@65 @8|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{||+0#ffffff16#ff404010|v|a|l|s|u|b|f|u|n|c| |t|e|n|}+0#e000e06#ffffff0| +0#0000000&@43 diff --git a/runtime/syntax/testdir/dumps/sh_ksh93v_10.dump b/runtime/syntax/testdir/dumps/sh_ksh93v_10.dump index cf4178f5e5..dbd1f398df 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh93v_10.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh93v_10.dump @@ -1,5 +1,5 @@ | +0&#ffffff0@74 -|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 +|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 @8|t+0#af5f00255&|r|u|e| +0#0000000&@62 |}+0#e000e06&| +0#0000000&@73 @75 @@ -15,6 +15,6 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |f|u|n|c|t|i|o|n| +0#0000000&@37 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|3|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 +|f+0#00e0e07&|o@1|3|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 @57|1|5|4|,|1| @8|7|9|%| diff --git a/runtime/syntax/testdir/dumps/sh_ksh93v_11.dump b/runtime/syntax/testdir/dumps/sh_ksh93v_11.dump index f1eb6e296d..fcb44b04ca 100644 --- a/runtime/syntax/testdir/dumps/sh_ksh93v_11.dump +++ b/runtime/syntax/testdir/dumps/sh_ksh93v_11.dump @@ -1,7 +1,7 @@ | +0&#ffffff0@7|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|4|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 +|f+0#00e0e07&|o@1|4|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@24 >}+0#e000e06&| +0#0000000&@73 @75 @@ -9,7 +9,7 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@20 |}+0#e000e06&| +0#0000000&@73 @75 -|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 +|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |s|u|b|s|h|e|l@1| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@21 |)+0#af5f00255&| +0#0000000&@73 @75 diff --git a/runtime/syntax/testdir/dumps/sh_mksh_07.dump b/runtime/syntax/testdir/dumps/sh_mksh_07.dump index 7589946109..6c7c45c6c1 100644 --- a/runtime/syntax/testdir/dumps/sh_mksh_07.dump +++ b/runtime/syntax/testdir/dumps/sh_mksh_07.dump @@ -3,7 +3,7 @@ |#+0#0000e05&| |V|a|l|u|e| |s|u|b|s|t|i|t|u|t|i|o|n|s| |o|f| |t|h|e| |f|o|r|m| |$|{|||c|o|m@1|a|n|d|}| |a|r|e| |o|n|l|y| +0#0000000&@20 |#+0#0000e05&| |s|u|p@1|o|r|t|e|d| |b|y| |m|k|s|h|,| |n|o|t| |k|s|h|9|3|.| +0#0000000&@43 |i+0#af5f00255&|f| |!| +0#0000000&|c+0#af5f00255&|o|m@1|a|n|d| +0#0000000&|e+0#af5f00255&|v|a|l| +0#0000000&|'+0#af5f00255&|(+0#e000002&@1|.|s|h|.|v|e|r|s|i|o|n| |>|=| |2|0@1|7|0|7|0|3|)@1|'+0#af5f00255&| +0#0000000&|2+0#e000002&|>+0#af5f00255&|/+0#0000000&|d|e|v|/|n|u|l@1|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&@9 -@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 +@8>v+0#00e0e07&|a|l|s|u|b|f|u|n|c|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@52 @16|R+0#e000e06&|E|P|L|Y|=+0#af5f00255&|$+0#e000e06&|1| +0#0000000&@50 @8|}+0#e000e06&| +0#0000000&@65 @8|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|{|||v|a|l|s|u|b|f|u|n|c| |t|e|n|}| +0#0000000&@43 diff --git a/runtime/syntax/testdir/dumps/sh_mksh_10.dump b/runtime/syntax/testdir/dumps/sh_mksh_10.dump index 665c13ad92..f037f91d2b 100644 --- a/runtime/syntax/testdir/dumps/sh_mksh_10.dump +++ b/runtime/syntax/testdir/dumps/sh_mksh_10.dump @@ -1,5 +1,5 @@ | +0&#ffffff0@74 -|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 +|.+0#00e0e07&|s|h|.|t|i|l|d|e|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@57 @8|t+0#af5f00255&|r|u|e| +0#0000000&@62 |}+0#e000e06&| +0#0000000&@73 @75 @@ -7,7 +7,7 @@ @8|f+0#af5f00255&|a|l|s|e| +0#0000000&@61 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|f+0#00e0e07&|o@1|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@58 +|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|f+0#00e0e07&|o@1|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@58 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |B|a|s|h|-|s|t|y|l|e| |f|u|n|c|t|i|o|n| |(|m|k|s|h|-|o|n|l|y|)| +0#0000000&@30 |}+0#e000e06&| +0#0000000&@73 @75 @@ -15,6 +15,6 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |f|u|n|c|t|i|o|n| +0#0000000&@37 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|3|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 +|f+0#00e0e07&|o@1|3|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 @57|1|5|4|,|1| @8|7|9|%| diff --git a/runtime/syntax/testdir/dumps/sh_mksh_11.dump b/runtime/syntax/testdir/dumps/sh_mksh_11.dump index 4730aa2763..8ce924e8b9 100644 --- a/runtime/syntax/testdir/dumps/sh_mksh_11.dump +++ b/runtime/syntax/testdir/dumps/sh_mksh_11.dump @@ -1,7 +1,7 @@ | +0&#ffffff0@7|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X| |f|u|n|c|t|i|o|n| +0#0000000&@47 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#00e0e07&|o@1|4|.|g|e|t|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 +|f+0#00e0e07&|o@1|4|.|g|e|t|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@62 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@24 >}+0#e000e06&| +0#0000000&@73 @75 @@ -9,7 +9,7 @@ @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |K|o|r|n|S|h|e|l@1|-|s|t|y|l|e| |k|s|h|9|3| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@20 |}+0#e000e06&| +0#0000000&@73 @75 -|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 +|_+0#00e0e07&|f|o@1|6|.|u|n|s|e|t|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@59 @8|:+0#0000e05&| +0#0000000&@1|#+0#0000e05&| |P|O|S|I|X|-|s|t|y|l|e| |s|u|b|s|h|e|l@1| |d|i|s|c|i|p|l|i|n|e| |f|u|n|c|t|i|o|n| +0#0000000&@21 |)+0#af5f00255&| +0#0000000&@73 @75 diff --git a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_00.dump b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_00.dump index 1af1bd6b4e..73b9046cf8 100644 --- a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_00.dump +++ b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_00.dump @@ -3,18 +3,18 @@ @75 |#+0#0000e05&| |V|a|l|i|d| |f|u|n|c|t|i|o|n| |n|a|m|e|s| +0#0000000&@52 @75 -|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|7+0#00e0e07&|f|o|@|o|.|f|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@54 +|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|7+0#00e0e07&|f|o|@|o|.|f|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@54 @8|e+0#af5f00255&|c|h|o| +0#e000002&|"+0#af5f00255&|G+0#e000002&|i|b@1|e|r|i|s|h| |n|o|t|-|K|o|r|n|S|h|e|l@1| |f|u|n|c|t|i|o|n| |(|t|h|e| |e|n|d|i|n|g| |'|(|)|'| |i|s| |a| |b|a|s|h|i|s |m| |m|k|s|h| |a|l@1|o|w|s|)|"+0#af5f00255&| +0#0000000&@59 |}+0#e000e06&| +0#0000000&@73 -|!+0#00e0e07&|:|@|-|+|.|8|v|f|o|%|o|,|_|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@56 +|!+0#00e0e07&|:|@|-|+|.|8|v|f|o|%|o|,|_|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@56 @8|e+0#af5f00255&|c|h|o| +0#e000002&|'+0#af5f00255&|G+0#e000002&|i|b@1|e|r|i|s|h| |P|O|S|I|X| |f|u|n|c|t|i|o|n|'+0#af5f00255&| +0#0000000&@35 |}+0#e000e06&| +0#0000000&@73 @75 -|,+0#00e0e07&|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 +|,+0#00e0e07&|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|a+0#00e0e07&|%|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@58 +|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|a+0#00e0e07&|%|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@58 @8|f|o@1| @63 |i|s|_|k|o|r|n|s|h|e|l@1|:| |1|,| |i|s|_|m|k|s|h|:| |1|,| @28|1|,|1| @10|T|o|p| diff --git a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_01.dump b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_01.dump index beae9b47d5..205bbcc961 100644 --- a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_01.dump +++ b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_01.dump @@ -1,20 +1,20 @@ -|,+0#00e0e07#ffffff0|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 +|,+0#00e0e07#ffffff0|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|a+0#00e0e07&|%|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@58 +|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|a+0#00e0e07&|%|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@58 @8>f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|%+0#00e0e07&|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 +|%+0#00e0e07&|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|.+0#00e0e07&|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 +|.+0#00e0e07&|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|-+0#00e0e07&|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 +|-+0#00e0e07&|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @57|1|8|,|2|-|9| @7|1@1|%| diff --git a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_02.dump b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_02.dump index 2aeb5366cb..232bc86758 100644 --- a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_02.dump +++ b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_02.dump @@ -1,20 +1,20 @@ |}+0#e000e06#ffffff0| +0#0000000&@73 @75 -|_+0#00e0e07&|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 +|_+0#00e0e07&|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@69 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 > @74 -|++0#00e0e07&|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 +|++0#00e0e07&|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|.+0#00e0e07&|b|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 +|.+0#00e0e07&|b|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|!+0#00e0e07&|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 +|!+0#00e0e07&|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|@+0#00e0e07&|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 +|@+0#00e0e07&|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 @57|3|6|,|0|-|1| @7|2|8|%| diff --git a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_03.dump b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_03.dump index 8225b5869e..3f61dfaeec 100644 --- a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_03.dump +++ b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_03.dump @@ -1,20 +1,20 @@ -|@+0#00e0e07#ffffff0|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 +|@+0#00e0e07#ffffff0|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|!+0#00e0e07&@1|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@67 +|!+0#00e0e07&@1|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@67 @8>f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|!+0#00e0e07&|a|!|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 +|!+0#00e0e07&|a|!|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|@+0#00e0e07&|a|@@1|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@65 +|@+0#00e0e07&|a|@@1|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@65 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @75 -|++0#00e0e07&|a|+|a|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 +|++0#00e0e07&|a|+|a|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@66 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 @57|5|4|,|2|-|9| @7|4@1|%| diff --git a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_04.dump b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_04.dump index 0e1dd04131..c04f2881fe 100644 --- a/runtime/syntax/testdir/dumps/sh_mksh_gibberish_04.dump +++ b/runtime/syntax/testdir/dumps/sh_mksh_gibberish_04.dump @@ -1,6 +1,6 @@ |}+0#e000e06#ffffff0| +0#0000000&@73 @75 -|a+0#00e0e07&|:|(|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 +|a+0#00e0e07&|:|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@68 @8|f|o@1| @63 |}+0#e000e06&| +0#0000000&@73 > @74 diff --git a/src/testdir/dumps/Test_matchparen_sh_case_1.dump b/src/testdir/dumps/Test_matchparen_sh_case_1.dump index 8494e0e3ab..7eccca7536 100644 --- a/src/testdir/dumps/Test_matchparen_sh_case_1.dump +++ b/src/testdir/dumps/Test_matchparen_sh_case_1.dump @@ -1,5 +1,5 @@ |#+0#0000e05#ffffff0|!|/|b|i|n|/|s|h| +0#0000000&@65 -|S+0#00e0e07&|U|S|U|W|U|_|P|R|I|N|T|(|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@58 +|S+0#00e0e07&|U|S|U|W|U|_|P|R|I|N|T|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@58 @2|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|{|L|E|V|E|L|}|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&@54 @4|"+0#af5f00255&|$+0#e000e06&|S|U|S|U|W|U|_|S|H|_|N|O|T|I|C|E|"+0#af5f00255&>)| +0#0000000&@50 @4|$+0#e000e06&|{|S|U|S|U|W|U|_|S|}| +0#0000000&|&+0#af5f00255&@1| +0#0000000&|r+0#af5f00255&|e|t|u|r|n| +0#0000000&|1+0#e000002&| +0#0000000&@47 diff --git a/src/testdir/dumps/Test_matchparen_sh_case_2.dump b/src/testdir/dumps/Test_matchparen_sh_case_2.dump index ae1adfb74b..8e87b98445 100644 --- a/src/testdir/dumps/Test_matchparen_sh_case_2.dump +++ b/src/testdir/dumps/Test_matchparen_sh_case_2.dump @@ -1,5 +1,5 @@ |#+0#0000e05#ffffff0|!|/|b|i|n|/|s|h| +0#0000000&@65 -|S+0#00e0e07&|U|S|U|W|U|_|P|R|I|N|T|(|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@58 +|S+0#00e0e07&|U|S|U|W|U|_|P|R|I|N|T|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@58 @2|c+0#af5f00255&|a|s|e| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|{|L|E|V|E|L|}|"+0#af5f00255&| +0#0000000&|i+0#af5f00255&|n| +0#0000000&@54 @4|"+0#af5f00255&|$+0#e000e06&|S|U|S|U|W|U|_|S|H|_|N|O|T|I|C|E|"+0#af5f00255&|)| +0#0000000&|f|o@1|b|a|r> @43 @4|$+0#e000e06&|{|S|U|S|U|W|U|_|S|}| +0#0000000&|&+0#af5f00255&@1| +0#0000000&|r+0#af5f00255&|e|t|u|r|n| +0#0000000&|1+0#e000002&| +0#0000000&@47 From 591db923bae1657e788f6bb0e6d4527448c72041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20B=C3=ACnh=20An?= Date: Sat, 16 May 2026 08:02:13 +0000 Subject: [PATCH 237/545] runtime(zip,tar): Add support for compressed .cbz and .cbt files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *.cbz and *.cbt files are just zip and tar files for comic books. https://en.wikipedia.org/wiki/Comic_book_archive closes: #20206 Signed-off-by: Phạm Bình An Signed-off-by: Christian Brabandt --- runtime/doc/pi_zip.txt | 6 +++--- runtime/plugin/tarPlugin.vim | 1 + runtime/plugin/zipPlugin.vim | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/runtime/doc/pi_zip.txt b/runtime/doc/pi_zip.txt index e9294b4059..81275b24ec 100644 --- a/runtime/doc/pi_zip.txt +++ b/runtime/doc/pi_zip.txt @@ -1,4 +1,4 @@ -*pi_zip.txt* For Vim version 9.2. Last change: 2026 Apr 05 +*pi_zip.txt* For Vim version 9.2. Last change: 2026 May 16 +====================+ | Zip File Interface | @@ -114,9 +114,9 @@ Copyright: Copyright (C) 2005-2015 Charles E Campbell *zip-copyright* should be treated as zip files. Alternatively, one may change *g:zipPlugin_ext* in one's .vimrc. - Currently (as of October 2025) it holds: > + Currently (as of May 2026) it holds: > - let g:zipPlugin_ext='*.aar,*.apk,*.celzip,*.crtx,*.docm,*.docx, + let g:zipPlugin_ext='*.aar,*.apk,*.cbz,*.celzip,*.crtx,*.docm,*.docx, \ *.dotm,*.dotx,*.ear,*.epub,*.gcsx,*.glox,*.gqsx,*.ja,*.jar,*.kmz, \ *.odb,*.odc,*.odf,*.odg,*.odi,*.odm,*.odp,*.ods,*.odt,*.otc,*.otf, \ *.otg,*.oth,*.oti,*.otp,*.ots,*.ott,*.oxt,*.pkpass,*.potm,*.potx, diff --git a/runtime/plugin/tarPlugin.vim b/runtime/plugin/tarPlugin.vim index e55a367854..d65db84267 100644 --- a/runtime/plugin/tarPlugin.vim +++ b/runtime/plugin/tarPlugin.vim @@ -35,6 +35,7 @@ augroup tar au FileWriteCmd tarfile::*/* call tar#Write(expand("")) endif + au BufReadCmd *.cbt call tar#Browse(expand("")) au BufReadCmd *.lrp call tar#Browse(expand("")) au BufReadCmd *.tar call tar#Browse(expand("")) au BufReadCmd *.tar.bz2 call tar#Browse(expand("")) diff --git a/runtime/plugin/zipPlugin.vim b/runtime/plugin/zipPlugin.vim index a9ab12158b..02d5e978da 100644 --- a/runtime/plugin/zipPlugin.vim +++ b/runtime/plugin/zipPlugin.vim @@ -6,6 +6,7 @@ " Last Change: " 2025 Apr 02 by Vim Project: add *.whl to list of zip extensions (#17038) " 2025 Oct 06 by MultisampledNight: add *.pkpass to list of zip extensions (#18501) +" 2026 May 16 by Vim Project: add .cbz extension (#20206) " License: Vim License (see vim's :help license) " Copyright: Copyright (C) 2005-2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, @@ -31,7 +32,7 @@ set cpo&vim " --------------------------------------------------------------------- " Options: {{{1 if !exists("g:zipPlugin_ext") - let g:zipPlugin_ext='*.aar,*.apk,*.celzip,*.crtx,*.docm,*.docx,*.dotm,*.dotx,*.ear,*.epub,*.gcsx,*.glox,*.gqsx,*.ja,*.jar,*.kmz,*.odb,*.odc,*.odf,*.odg,*.odi,*.odm,*.odp,*.ods,*.odt,*.otc,*.otf,*.otg,*.oth,*.oti,*.otp,*.ots,*.ott,*.oxt,*.pkpass,*.potm,*.potx,*.ppam,*.ppsm,*.ppsx,*.pptm,*.pptx,*.sldx,*.thmx,*.vdw,*.war,*.whl,*.wsz,*.xap,*.xlam,*.xlsb,*.xlsm,*.xlsx,*.xltm,*.xltx,*.xpi,*.zip' + let g:zipPlugin_ext='*.aar,*.apk,*.cbz,*.celzip,*.crtx,*.docm,*.docx,*.dotm,*.dotx,*.ear,*.epub,*.gcsx,*.glox,*.gqsx,*.ja,*.jar,*.kmz,*.odb,*.odc,*.odf,*.odg,*.odi,*.odm,*.odp,*.ods,*.odt,*.otc,*.otf,*.otg,*.oth,*.oti,*.otp,*.ots,*.ott,*.oxt,*.pkpass,*.potm,*.potx,*.ppam,*.ppsm,*.ppsx,*.pptm,*.pptx,*.sldx,*.thmx,*.vdw,*.war,*.whl,*.wsz,*.xap,*.xlam,*.xlsb,*.xlsm,*.xlsx,*.xltm,*.xltx,*.xpi,*.zip' endif " --------------------------------------------------------------------- From 88b00d1c57c8713ac5e7f2828c07e3c7da94ac79 Mon Sep 17 00:00:00 2001 From: glepnir Date: Sat, 16 May 2026 08:36:39 +0000 Subject: [PATCH 238/545] patch 9.2.0490: matchfuzzy() can crash on long multi-word patterns Problem: matchfuzzy() can crash on long multi-word patterns. Solution: Clamp pat_chars to maxMatches and stop before calling match_positions() when the buffer is full (glepnir). closes: #20209 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- src/fuzzy.c | 14 +++++++++++++- src/testdir/test_matchfuzzy.vim | 9 +++++++++ src/version.c | 2 ++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/fuzzy.c b/src/fuzzy.c index bb140fcace..2847a2071c 100644 --- a/src/fuzzy.c +++ b/src/fuzzy.c @@ -85,6 +85,7 @@ fuzzy_match( int complete = FALSE; int score = 0; int numMatches = 0; + int pat_chars = 0; score_t fzy_score; *outScore = 0; @@ -118,6 +119,17 @@ fuzzy_match( complete = TRUE; *p = NUL; } + // match_positions() always writes pat_chars entries, + // so bail if they won't fit. + pat_chars = MB_CHARLEN(pat); + if (pat_chars > maxMatches) + pat_chars = maxMatches; + if (numMatches > maxMatches - pat_chars) + { + numMatches = 0; + *outScore = FUZZY_SCORE_NONE; + break; + } score = FUZZY_SCORE_NONE; if (has_match(pat, str)) @@ -143,7 +155,7 @@ fuzzy_match( else *outScore += score; - numMatches += MB_CHARLEN(pat); + numMatches += pat_chars; if (complete || numMatches >= maxMatches) break; diff --git a/src/testdir/test_matchfuzzy.vim b/src/testdir/test_matchfuzzy.vim index eb4c8c6567..dfeb074d7f 100644 --- a/src/testdir/test_matchfuzzy.vim +++ b/src/testdir/test_matchfuzzy.vim @@ -322,4 +322,13 @@ func Test_matchfuzzy_initialized() call StopVimInTerminal(buf) endfunc +func Test_matchfuzzy_long_multiword_no_overflow() + let word = repeat('a', 100) + let pat_ok = repeat(word . ' ', 9) . word + call assert_equal([word], matchfuzzy([word], pat_ok)) + + let pat_overflow = repeat(word . ' ', 14) . word + call assert_equal([[], [], []], matchfuzzypos([word], pat_overflow)) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index fb89edd04e..0a4a84e29f 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 490, /**/ 489, /**/ From d8c4774273eff95eb07a6723f92f388108efb487 Mon Sep 17 00:00:00 2001 From: Zoltan Arpadffy Date: Sat, 16 May 2026 08:42:47 +0000 Subject: [PATCH 239/545] patch 9.2.0491: VMS: various build issues Problem: VMS: various build issues Solution: Fix issues for VMS (Zoltan Arpadffy) closes: #20131 Signed-off-by: Zoltan Arpadffy Signed-off-by: Christian Brabandt --- src/Make_vms.mms | 48 ++++++++++++++++++++++++----------------------- src/clipboard.c | 2 +- src/fuzzy.c | 2 +- src/os_unix.c | 4 ++++ src/os_vms.c | 7 ++++++- src/os_vms_conf.h | 19 +++++++++++++------ src/version.c | 2 ++ 7 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/Make_vms.mms b/src/Make_vms.mms index a238a8ab9e..e019897fac 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -2,7 +2,7 @@ # Makefile for Vim on OpenVMS # # Maintainer: Zoltan Arpadffy -# Last change: 2025-07-04 Steven M. Schweda +# Last change: 2026-05-04 # # This script has been tested on VMS 6.2 to 9.2 on VAX, ALPHA, IA64 and X86_64 # with MMS and MMK @@ -49,6 +49,10 @@ MODEL = HUGE # If you have XPM installed you might want to build Motif version with toolbar # XPM = YES +# Large-file support. Unavailable on VAX and very old Alpha. +# To disable, define NOLARGE. +# NOLARGE = YES + # Comment out if you want the compiler version with :ver command. # NOTE: This part can make some complications if you're using some # predefined symbols/flags for your compiler. If does, just leave behind @@ -108,23 +112,23 @@ ALPHA_X_ALPHA = 1 IA64_X_IA64 = 1 VAX_X_VAX = 1 X86_64_X_X86_64 = 1 -.IFDEF ARCH # ARCH +.IFDEF ARCH # ARCH ARCH_NAME = $(ARCH) -.ELSE # ARCH +.ELSE # ARCH ARCH_NAME = $(MMS$ARCH_NAME) -.ENDIF # ARCH -.IFDEF $(ARCH_NAME)_X_ALPHA # $(ARCH_NAME)_X_ALPHA +.ENDIF # ARCH +.IFDEF $(ARCH_NAME)_X_ALPHA # $(ARCH_NAME)_X_ALPHA __ALPHA__ = 1 -.ENDIF # $(ARCH_NAME)_X_ALPHA -.IFDEF $(ARCH_NAME)_X_IA64 # $(ARCH_NAME)_X_IA64 +.ENDIF # $(ARCH_NAME)_X_ALPHA +.IFDEF $(ARCH_NAME)_X_IA64 # $(ARCH_NAME)_X_IA64 __IA64__ = 1 -.ENDIF # $(ARCH_NAME)_X_IA64 -.IFDEF $(ARCH_NAME)_X_VAX # $(ARCH_NAME)_X_VAX +.ENDIF # $(ARCH_NAME)_X_IA64 +.IFDEF $(ARCH_NAME)_X_VAX # $(ARCH_NAME)_X_VAX __VAX__ = 1 -.ENDIF # $(ARCH_NAME)_X_VAX -.IFDEF $(ARCH_NAME)_X_X86_64 # $(ARCH_NAME)_X_X86_64 +.ENDIF # $(ARCH_NAME)_X_VAX +.IFDEF $(ARCH_NAME)_X_X86_64 # $(ARCH_NAME)_X_X86_64 __X86_64__ = 1 -.ENDIF # $(ARCH_NAME)_X_X86_64 +.ENDIF # $(ARCH_NAME)_X_X86_64 .ELSE # MMS$ARCH_NAME .IFDEF __MMK__ # __MMK__ .IFDEF ARCH # ARCH @@ -234,8 +238,7 @@ PREFIX = /prefix=all/name=(upper,short) /repository=[.$(DEST)] # This makes Alpha consistent. FLOAT = /float = ieee_float /ieee_mode = denorm_results -# Large-file support. Unavailable on VAX and very old Alpha. To -# disable, define NOLARGE. +# Large-file support. Unavailable on VAX and very old Alpha. .IFDEF NOLARGE .ELSE LARGE_DEF = , "_LARGEFILE" @@ -811,15 +814,14 @@ $(TARGET) : $(OBJ) .c.obj : # Override /optimize for selected modules on VAX. .IFDEF __VAX__ # __VAX__ - @ mod = f$parse( "$@", , , "NAME", "SYNTAX_ONLY") - @ mod = "+"+ f$edit( mod, "LOWERCASE")+ "+" - @ optim_qual = "" - @ if (f$locate( mod, "+$(VAX_NOOPTIM_LIST)+") .lt. - + -@ mod = f$parse( "$@", , , "NAME", "SYNTAX_ONLY") + -@ mod = "+"+ f$edit( mod, "LOWERCASE")+ "+" + -@ optim_qual = "" + -@ if (f$locate( mod, "+$(VAX_NOOPTIM_LIST)+") .lt. - f$length( "+$(VAX_NOOPTIM_LIST)+")) then optim_qual = "/nooptim" - @ if (f$locate( mod, "+$(VAX_NOOPTIM_LIST)+") .lt. - + -@ if (f$locate( mod, "+$(VAX_NOOPTIM_LIST)+") .lt. - f$length( "+$(VAX_NOOPTIM_LIST)+")) then - - @ write sys$output - - " *** NOTE: USING SPECIAL /NOOPTIMIZE RULE. ***" + write sys$output "*** NOTE: USING SPECIAL /NOOPTIMIZE RULE. ***" $(CC_DEF) $(ALL_CFLAGS) 'optim_qual' $< /object = $@ .ELSE # __VAX__ $(CC_DEF) $(ALL_CFLAGS) $< /object = $@ @@ -1468,8 +1470,8 @@ lua_env : [.$(DEST)]gui_gtk_x11.obj : gui_gtk_x11.c vim.h [.$(DEST)]config.h feature.h os_unix.h \ ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \ gui.h beval.h option.h ex_cmds.h proto.h \ - errors.h globals.h gui_gtk_f.h [-.runtime]vim32x32_png.h \ - [-.runtime]vim16x16_png.h [-.runtime]vim48x48_png.h version.h + errors.h globals.h gui_gtk_f.h [-.runtime]vim32x32.xpm \ + [-.runtime]vim16x16.xpm [-.runtime]vim48x48.xpm version.h [.$(DEST)]gui_x11.obj : gui_x11.c vim.h [.$(DEST)]config.h feature.h os_unix.h \ ascii.h keymap.h termdefs.h macros.h structs.h regexp.h \ gui.h beval.h option.h ex_cmds.h proto.h \ diff --git a/src/clipboard.c b/src/clipboard.c index 745a8c47a2..902f9d6032 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -3611,7 +3611,7 @@ clip_provider_paste(char_u *reg, char_u *provider) } *curval++ = NULL; - if (*reg_type != NUL && (STRLEN(reg_type) <= 0 + if (*reg_type != NUL && (STRLEN(reg_type) == 0 || get_yank_type(®_type, &yank_type, &block_len) == FAIL)) { emsg(e_invalid_argument); diff --git a/src/fuzzy.c b/src/fuzzy.c index 2847a2071c..b6d595a700 100644 --- a/src/fuzzy.c +++ b/src/fuzzy.c @@ -31,7 +31,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ - +#define USING_FLOAT_STUFF #include "vim.h" #if defined(FEAT_EVAL) || defined(FEAT_PROTO) diff --git a/src/os_unix.c b/src/os_unix.c index 12285b8e48..5cfc422b7a 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5950,7 +5950,11 @@ mch_get_cmd_output_direct( } BLOCK_SIGNALS(&curset); +# ifdef VMS + pid = 0; // VMS does not have fork +# else pid = fork(); +# endif if (pid == -1) { UNBLOCK_SIGNALS(&curset); diff --git a/src/os_vms.c b/src/os_vms.c index bb93544f09..17fa067743 100644 --- a/src/os_vms.c +++ b/src/os_vms.c @@ -858,7 +858,12 @@ RealWaitForChar( * appropriate time conversion function accordingly. */ #if __IEEE_FLOAT -# define LIB_CVTX_TO_INTERNAL_TIME lib$cvts_to_internal_time // IEEE +// allow fallback for older Alphas +# ifdef lib$cvts_to_internal_time +# define LIB_CVTX_TO_INTERNAL_TIME lib$cvts_to_internal_time // IEEE +# else +# define LIB_CVTX_TO_INTERNAL_TIME lib$cvtf_to_internal_time +# endif #else # define LIB_CVTX_TO_INTERNAL_TIME lib$cvtf_to_internal_time // VAX #endif // __IEEE_FLOAT CVTS diff --git a/src/os_vms_conf.h b/src/os_vms_conf.h index 37be6ddfc6..ddc83b3dc5 100644 --- a/src/os_vms_conf.h +++ b/src/os_vms_conf.h @@ -58,9 +58,6 @@ // Define to `int' if doesn't define. // #undef uid_t -// Define to `unsigned int' or other type that is 32 bit. -#define UINT32_T unsigned int - // Define to `int' if doesn't define. // #undef gid_t @@ -169,7 +166,6 @@ # define ULONG_LONG_MAX (4294967295U) #else // ALPHA, IA64, X86_64 -# define HAVE_FSEEKO /* Use off_t. */ # define HAVE_GETTIMEOFDAY # define HAVE_USLEEP # define HAVE_STRCASECMP @@ -186,7 +182,11 @@ # define HAVE_ISNAN # endif -# define HAVE_XOS_R_H +# if defined(X86_64) +# define HAVE_FSEEKO +# define HAVE_STDINT_H +# define HAVE_XOS_R_H +# endif #endif /* VAX [else] */ @@ -208,7 +208,7 @@ # define HAVE_LOCALE_H # define BROKEN_LOCALE # undef DYNAMIC_ICONV -# define HAVE_STRFTIME +# define HAVE_STRFTIME #endif #if defined(USE_ICONV) @@ -231,3 +231,10 @@ # define USE_FONTSET # undef X_LOCALE #endif + +// Define needed types from stdint - older VMS do not have stdint.h +#ifndef HAVE_STDINT_H + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; +#endif diff --git a/src/version.c b/src/version.c index 0a4a84e29f..7b05c7c451 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 491, /**/ 490, /**/ From 8ae45e420218f5ed913c6b99cbccce99245ea97c Mon Sep 17 00:00:00 2001 From: "K.Takata" Date: Sat, 16 May 2026 15:51:03 +0000 Subject: [PATCH 240/545] NSIS: Don't install 32-bit dll on ARM64 closes: #20234 Signed-off-by: K.Takata Signed-off-by: Christian Brabandt --- nsis/gvim.nsi | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/nsis/gvim.nsi b/nsis/gvim.nsi index 9843ff080d..3d48008863 100644 --- a/nsis/gvim.nsi +++ b/nsis/gvim.nsi @@ -90,7 +90,7 @@ Unicode true ; !include defaults to UTF-8 after Unicode True since 3.0 Alpha 2 # ----------- No configurable settings below this line ----------- ########################################################## -# Installer Attributes, Including headers, Plugins and etc. +# Installer Attributes, Including headers, Plugins and etc. CRCCheck force @@ -638,21 +638,23 @@ SectionGroupEnd !undef LIBRARY_X64 ${EndIf} - # Install DLLs for 32-bit gvimext.dll into the GvimExt32 directory. - SetOutPath $0\GvimExt32 - ClearErrors - !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED \ - "${GETTEXT}\gettext32\libintl-8.dll" \ - "$0\GvimExt32\libintl-8.dll" "$0\GvimExt32" - !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED \ - "${GETTEXT}\gettext32\libiconv-2.dll" \ - "$0\GvimExt32\libiconv-2.dll" "$0\GvimExt32" - # Install libgcc_s_sjlj-1.dll only if it is needed. - !if ${INCLUDE_LIBGCC} - !if /FileExists "${GETTEXT}\gettext32\libgcc_s_sjlj-1.dll" - !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED \ - "${GETTEXT}\gettext32\libgcc_s_sjlj-1.dll" \ - "$0\GvimExt32\libgcc_s_sjlj-1.dll" "$0\GvimExt32" + !if !${ARM64} + # Install DLLs for 32-bit gvimext.dll into the GvimExt32 directory. + SetOutPath $0\GvimExt32 + ClearErrors + !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED \ + "${GETTEXT}\gettext32\libintl-8.dll" \ + "$0\GvimExt32\libintl-8.dll" "$0\GvimExt32" + !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED \ + "${GETTEXT}\gettext32\libiconv-2.dll" \ + "$0\GvimExt32\libiconv-2.dll" "$0\GvimExt32" + # Install libgcc_s_sjlj-1.dll only if it is needed. + !if ${INCLUDE_LIBGCC} + !if /FileExists "${GETTEXT}\gettext32\libgcc_s_sjlj-1.dll" + !insertmacro InstallLib DLL NOTSHARED REBOOT_NOTPROTECTED \ + "${GETTEXT}\gettext32\libgcc_s_sjlj-1.dll" \ + "$0\GvimExt32\libgcc_s_sjlj-1.dll" "$0\GvimExt32" + !endif !endif !endif ${EndIf} From 403ba303b997b47c79241247e0d2b5fc698e3dd0 Mon Sep 17 00:00:00 2001 From: orbisai0security Date: Sun, 17 May 2026 08:19:14 +0000 Subject: [PATCH 241/545] ccfilter: uses unbounded strcat()/strcpy() Problem: ccfilter.c copies compiler output into fixed-size buffers with strcat() and strcpy(), so very long diagnostics can overflow. Solution: replace with snprintf() bounded by LINELENGTH. Automated security fix generated by Orbis Security AI closes: #20233 Signed-off-by: orbisai0security Signed-off-by: Christian Brabandt --- runtime/tools/ccfilter.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/runtime/tools/ccfilter.c b/runtime/tools/ccfilter.c index ae1443e203..269e4ee662 100644 --- a/runtime/tools/ccfilter.c +++ b/runtime/tools/ccfilter.c @@ -249,14 +249,15 @@ int main( int argc, char *argv[] ) stay = (echogets(Line2, echo) != NULL); while ( stay && (Line2[0] == '|') ) - { for (p=&Line2[2]; (*p) && (isspace((unsigned char)*p)); p++); - strcat( Reason, ": " ); - strcat( Reason, p ); + { size_t n; + for (p=&Line2[2]; (*p) && (isspace((unsigned char)*p)); p++); + n = strlen(Reason); + snprintf( Reason + n, LINELENGTH - n, ": %s", p ); Line2[0] = 0; stay = (echogets(Line2, echo) != NULL); } prefetch = 1; - strcpy( Line, Line2 ); + snprintf( Line, LINELENGTH, "%s", Line2 ); break; case COMPILER_IRIX: Col = 1; @@ -291,8 +292,8 @@ int main( int argc, char *argv[] ) prefetch = 0; } else - { strcat( Line, "\n" ); - strcat( Line, Line2 ); + { size_t n = strlen(Line); + snprintf( Line + n, LINELENGTH - n, "\n%s", Line2 ); } } } From 8b25d90b087fdc5cd2bc120a4aff414d080de53c Mon Sep 17 00:00:00 2001 From: tecis <67809811+tecis@users.noreply.github.com> Date: Sun, 17 May 2026 08:32:15 +0000 Subject: [PATCH 242/545] runtime(django): Resolve FIXME of comparrison operators + localization tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Summary: Add highlight of comparison operators resolving FIXME left by maintainer. How it works: By creating a the variable ‘djangoOperator’ with the regex and defining to only highlight when enclosed within ‘djangoTag’ and ‘djangoVarBlock’ the highlight works as expected. Note: Note even though the maintainer had left the note “FIXME ==, !=, <, >, <=, and >= should be djangoStatements” the results do work as I think he intended even though the variable ‘djangoOperator’ had to be created to achieve the result. By doing it this way the highlight process does not get confused depending on the spacing of the comparison operator. Example: {{ x>=10 }} and {{ x >= 10 }} work as expected. Add tags related to localization. Documentation source: - https://docs.djangoproject.com/en/5.2/topics/i18n/formatting/#controlling-localization-in-templates closes: #20225 Signed-off-by: tecis <67809811+tecis@users.noreply.github.com> Signed-off-by: Christian Brabandt --- runtime/syntax/django.vim | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/runtime/syntax/django.vim b/runtime/syntax/django.vim index 5b677063c1..0d0c11b510 100644 --- a/runtime/syntax/django.vim +++ b/runtime/syntax/django.vim @@ -4,6 +4,7 @@ " Last Change: 2021 Nov 29 " 2026 Feb 12 by Vim Project add partial support #19386 " 2026 Apr 17 by Vim Project Update to Django 5.2 version #19994 +" 2026 May 17 by Vim Project Update comparison operators #20225 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -18,8 +19,6 @@ syn match djangoError "%}\|}}\|#}" " Django template built-in tags and parameters " 'comment' doesn't appear here because it gets special treatment syn keyword djangoStatement contained autoescape csrf_token empty -" FIXME ==, !=, <, >, <=, and >= should be djangoStatements: -" syn keyword djangoStatement contained == != < > <= >= syn keyword djangoStatement contained and as block endblock by cycle debug else elif syn keyword djangoStatement contained extends filter endfilter firstof for syn keyword djangoStatement contained endfor if endif ifchanged endifchanged @@ -33,7 +32,9 @@ syn keyword djangoStatement contained get_current_language noop get_available_la syn keyword djangoStatement contained get_current_language_bidi get_language_info plural syn keyword djangoStatement contained translate blocktranslate endblocktranslate syn keyword djangoStatement contained partialdef endpartialdef partial -syn keyword djangoStatement contained querystring lorem verbatim +syn keyword djangoStatement contained querystring lorem verbatim localize endlocalize +syn keyword djangoStatement contained localtime endlocaltime timezone endtimezone +syn keyword djangoStatement contained get_current_timezone " Django templete built-in filters syn keyword djangoFilter contained add addslashes capfirst center cut date @@ -49,6 +50,7 @@ syn keyword djangoFilter contained time timesince timeuntil title truncatechars syn keyword djangoFilter contained truncatewords truncatewords_html unordered_list upper urlencode syn keyword djangoFilter contained urlize urlizetrunc wordcount wordwrap yesno syn keyword djangoFilter contained force_escape iriencode json_script truncatechars_html +syn keyword djangoFilter contained localize unlocalize localtime utc timezone " Keywords to highlight within comments syn keyword djangoTodo contained TODO FIXME XXX @@ -68,6 +70,9 @@ syn region djangoVarBlock start="{{" end="}}" contains=djangoFilter,djangoArgume syn region djangoComment start="{%\s*comment\(\s\+.\{-}\)\?%}" end="{%\s*endcomment\s*%}" contains=djangoTodo syn region djangoComBlock start="{#" end="#}" contains=djangoTodo +" Match comparison operators within Django statements. +syn match djangoOperator "==\|!=\|<=\|>=\|<\|>" contained containedin=djangoTagBlock + " Define the default highlighting. " Only when an item doesn't have highlighting yet @@ -82,6 +87,6 @@ hi def link djangoError Error hi def link djangoComment Comment hi def link djangoComBlock Comment hi def link djangoTodo Todo - +hi def link djangoOperator Operator let b:current_syntax = "django" From f03155aa2ae2139a82e2b4f9c6ec3501d5e48d53 Mon Sep 17 00:00:00 2001 From: tecis <67809811+tecis@users.noreply.github.com> Date: Sun, 17 May 2026 08:44:39 +0000 Subject: [PATCH 243/545] runtime(htmldjango): Add syntax highlighting of comparison operators The presence `djangoOperators` in the file `syntax/django.vim` and having the highlight function with a `match` statement leads to a highlight spill-over with other elements defined in `syntax/html.vim`. To avoid the highlight spill-over declare a region called `djangoTagBlockNaive` to limit `djangoOperator` to only be matched within. related: #20225 closes: #20232 Signed-off-by: tecis <67809811+tecis@users.noreply.github.com> Signed-off-by: Christian Brabandt --- runtime/syntax/htmldjango.vim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runtime/syntax/htmldjango.vim b/runtime/syntax/htmldjango.vim index 07b1c375e8..bffb11aa64 100644 --- a/runtime/syntax/htmldjango.vim +++ b/runtime/syntax/htmldjango.vim @@ -2,6 +2,7 @@ " Language: Django HTML template " Maintainer: Dave Hodder " Last Change: 2014 Jul 13 +" 2026 May 17 by Vim Project Add highlighting for comparison operators #20232 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -23,4 +24,8 @@ syn region djangoVarBlock start="{{" end="}}" contains=djangoFilter,djangoArgume syn region djangoComment start="{%\s*comment\(\s\+.\{-}\)\?%}" end="{%\s*endcomment\s*%}" contains=djangoTodo containedin=ALLBUT,@djangoBlocks syn region djangoComBlock start="{#" end="#}" contains=djangoTodo containedin=ALLBUT,@djangoBlocks +" Use djangoTagBlockNaive to limit the djangoOperator matched characters to avoid spill-over with HTML, JS and CSS. +syn region djangoTagBlockNaive start="{%" end="%}" contains=djangoTagBlock,djangoVarBlock,djangoComment,djangoComBlock +syn match djangoOperator "==\|!=\|<=\|>=\|<\|>" contained containedin=CONTAINS,@djangoTagBlockNaive + let b:current_syntax = "htmldjango" From ab24858cf5dfb229969714328c637c1118510e89 Mon Sep 17 00:00:00 2001 From: "Philip H." <47042125+pheiduck@users.noreply.github.com> Date: Sun, 17 May 2026 08:48:14 +0000 Subject: [PATCH 244/545] CI: Update clang to v22 closes: #20228 Signed-off-by: Philip H. <47042125+pheiduck@users.noreply.github.com> Signed-off-by: Christian Brabandt --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fc0a42f6ab..6a3e619279 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,7 @@ jobs: env: CC: ${{ matrix.compiler }} GCC_VER: 14 - CLANG_VER: 21 + CLANG_VER: 22 TEST: test SRCDIR: ./src LEAK_CFLAGS: -DEXITFREE From 3db4c3a20bd5e19320291dce11053e9e85994ebf Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 17 May 2026 09:27:04 +0000 Subject: [PATCH 245/545] patch 9.2.0492: popup: decoration wrongly drawn with clipping on border Problem: popup: clipwindow popups with border and padding could still spill into the surrounding chrome of the host window Solution: Consume the border first, then the padding, per edge; spill any leftover clip into the opposite edge's decoration; derive the bottom padding row from total_height; skip the scrollbar branch for clipwindow popups (Yasuhiro Matsumoto). closes: #20227 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/popupwin.c | 126 ++++++++++++++++++++++++++++++++++++++++--------- src/version.c | 2 + 2 files changed, 107 insertions(+), 21 deletions(-) diff --git a/src/popupwin.c b/src/popupwin.c index 772148f61d..f122d7a0de 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -1356,15 +1356,15 @@ popup_get_clipwin(win_T *wp) // clip_*_content : how many *content* rows/cols are clipped at each edge // (border/padding is consumed first; the rest comes off // w_height/w_width). >= 0. -// eff_*_extra : 0 when that edge is clipped (border+padding gone), -// otherwise the original *_extra. // eff_border[], // eff_padding[] : per-edge border/padding sizes (indexed [top,right,bot,left] -// matching wp->w_popup_border / wp->w_popup_padding). At a -// clipped edge they collapse to 0; elsewhere they keep the -// original size. Drawing code can replace +// matching wp->w_popup_border / wp->w_popup_padding). The +// clip consumes the border first, then the padding, so when +// only the border is clipped the padding still survives. +// Drawing code can replace // `wp->w_popup_border[N] > 0 && wp->w_popup_*clip == 0` // with a single `cl.eff_border[N] > 0` test. +// eff_*_extra : eff_border + eff_padding at that edge (visible decoration). // eff_height : drawn extent = eff_top_extra + visible content + eff_bot_extra. // eff_width : drawn extent = eff_left_extra + visible content + eff_right_extra // (does NOT include w_leftcol or scrollbar; see callers). @@ -1414,21 +1414,95 @@ popup_compute_clip(win_T *wp, popup_clip_T *cl) if (cl->clip_right_content < 0) cl->clip_right_content = 0; - cl->eff_top_extra = wp->w_popup_topoff > 0 ? 0 : cl->top_extra; - cl->eff_bot_extra = wp->w_popup_bottomoff > 0 ? 0 : cl->bot_extra; - cl->eff_left_extra = wp->w_popup_leftclip > 0 ? 0 : cl->left_extra; - cl->eff_right_extra = wp->w_popup_rightclip > 0 ? 0 : cl->right_extra; + // Border is consumed before padding: when only the border row/column is + // clipped, the adjacent padding row/column is still visible. Horizontal + // edges keep the previous all-or-nothing behaviour for now; the drawing + // code there still uses original w_popup_border / w_popup_padding offsets. + { + int clip = wp->w_popup_topoff; + int b = wp->w_popup_border[0]; + int p = wp->w_popup_padding[0]; + int rem; - cl->eff_border[0] = wp->w_popup_topoff > 0 ? 0 : wp->w_popup_border[0]; + if (clip >= b) + { + cl->eff_border[0] = 0; + rem = clip - b; + cl->eff_padding[0] = (rem >= p) ? 0 : p - rem; + } + else + { + cl->eff_border[0] = b; + cl->eff_padding[0] = p; + } + } + { + int clip = wp->w_popup_bottomoff; + int b = wp->w_popup_border[2]; + int p = wp->w_popup_padding[2]; + int rem; + + if (clip >= b) + { + cl->eff_border[2] = 0; + rem = clip - b; + cl->eff_padding[2] = (rem >= p) ? 0 : p - rem; + } + else + { + cl->eff_border[2] = b; + cl->eff_padding[2] = p; + } + } cl->eff_border[1] = wp->w_popup_rightclip > 0 ? 0 : wp->w_popup_border[1]; - cl->eff_border[2] = wp->w_popup_bottomoff > 0 ? 0 : wp->w_popup_border[2]; cl->eff_border[3] = wp->w_popup_leftclip > 0 ? 0 : wp->w_popup_border[3]; - - cl->eff_padding[0] = wp->w_popup_topoff > 0 ? 0 : wp->w_popup_padding[0]; cl->eff_padding[1] = wp->w_popup_rightclip > 0 ? 0 : wp->w_popup_padding[1]; - cl->eff_padding[2] = wp->w_popup_bottomoff > 0 ? 0 : wp->w_popup_padding[2]; cl->eff_padding[3] = wp->w_popup_leftclip > 0 ? 0 : wp->w_popup_padding[3]; + // When a clip on one edge runs past the content rows, the excess must + // eat into the OPPOSITE edge's decorations. Otherwise the surviving + // padding/border can land outside the host (e.g. a popup whose body is + // wholly below the host still drew its top padding onto the status row). + { + int excess = wp->w_popup_bottomoff - cl->bot_extra - wp->w_height; + if (excess > 0) + { + if (excess >= cl->eff_padding[0]) + { + excess -= cl->eff_padding[0]; + cl->eff_padding[0] = 0; + if (excess >= cl->eff_border[0]) + cl->eff_border[0] = 0; + else + cl->eff_border[0] -= excess; + } + else + cl->eff_padding[0] -= excess; + } + } + { + int excess = wp->w_popup_topoff - cl->top_extra - wp->w_height; + if (excess > 0) + { + if (excess >= cl->eff_padding[2]) + { + excess -= cl->eff_padding[2]; + cl->eff_padding[2] = 0; + if (excess >= cl->eff_border[2]) + cl->eff_border[2] = 0; + else + cl->eff_border[2] -= excess; + } + else + cl->eff_padding[2] -= excess; + } + } + + cl->eff_top_extra = cl->eff_border[0] + cl->eff_padding[0]; + cl->eff_bot_extra = cl->eff_border[2] + cl->eff_padding[2]; + cl->eff_left_extra = wp->w_popup_leftclip > 0 ? 0 : cl->left_extra; + cl->eff_right_extra = wp->w_popup_rightclip > 0 ? 0 : cl->right_extra; + h = wp->w_height - cl->clip_top_content - cl->clip_bot_content; if (h < 0) h = 0; @@ -2172,10 +2246,14 @@ popup_adjust_position(win_T *wp) } if (adjust_height_for_top_aligned && wp->w_want_scrollbar + && !(wp->w_popup_flags & POPF_CLIPWINDOW) && wp->w_winrow + wp->w_height + extra_height > Rows) { // Bottom of the popup goes below the last line, reduce the height and - // add a scrollbar. + // add a scrollbar. For "clipwindow" popups the host-window clip + // already truncates the popup to fit inside the host, so we must not + // also force a scrollbar here -- that would widen the popup by one + // column the moment its decoration crossed the screen edge. wp->w_height = Rows - wp->w_winrow - extra_height; #ifdef FEAT_TERMINAL if (wp->w_buffer->b_term == NULL || term_is_finished(wp->w_buffer)) @@ -6267,7 +6345,7 @@ update_popups(void (*win_update)(win_T *wp)) } if (top_padding > 0) { - row = wp->w_winrow + wp->w_popup_border[0]; + row = wp->w_winrow + cl.eff_border[0]; if (title_len > 0 && row == wp->w_winrow) { // top padding and no border; do not draw over the title @@ -6432,14 +6510,20 @@ update_popups(void (*win_update)(win_T *wp)) if (cl.eff_padding[2] > 0) { - // bottom padding - row = wp->w_winrow + wp->w_popup_border[0] - + wp->w_popup_padding[0] + wp->w_height; + // bottom padding -- sits right after the visible content rows. + // Derive the row from total_height so it always lands inside the + // popup's drawn extent, including the corner case where the top + // clip consumes more rows than the content itself (so the visible + // content height is zero). A formula based on + // w_height - clip_top_content - clip_bot_content can go negative + // there and would draw the padding above w_winrow. + row = wp->w_winrow + total_height + - cl.eff_padding[2] - cl.eff_border[2]; if (screen_opacity_popup != NULL && saved_screen.lines != NULL) - fill_opacity_padding(row, row + wp->w_popup_padding[2], + fill_opacity_padding(row, row + cl.eff_padding[2], padcol, padendcol, &saved_screen); else - screen_fill(row, row + wp->w_popup_padding[2], + screen_fill(row, row + cl.eff_padding[2], padcol, padendcol, ' ', ' ', popup_attr); } diff --git a/src/version.c b/src/version.c index 7b05c7c451..501e951ab2 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 492, /**/ 491, /**/ From abecad5af0231230763931a67323b4ab863caf01 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 17 May 2026 17:42:27 +0000 Subject: [PATCH 246/545] patch 9.2.0493: popup: missing Popup, PopupBorder and PopupTitle hi groups Problem: popup: missing Popup, PopupBorder and PopupTitle highlight groups Solution: add Popup, PopupBorder and PopupTitle highlight groups and fall back to Pmenu related highlighting groups (Yasuhiro Matsumoto). fixes: #20110 closes: #20208 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 8 ++++++-- runtime/doc/popup.txt | 15 ++++++++++++--- runtime/doc/syntax.txt | 14 +++++++++++++- runtime/doc/tags | 3 +++ runtime/doc/version9.txt | 8 +++++++- runtime/syntax/vim.vim | 6 +++--- src/highlight.c | 3 +++ src/optiondefs.h | 2 +- src/popupwin.c | 21 +++++++++++++++++---- src/screen.c | 2 +- src/testdir/test_highlight.vim | 4 +++- src/version.c | 2 ++ src/vim.h | 4 ++++ 13 files changed, 75 insertions(+), 17 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 99e4e70a3a..1c1100a981 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 May 10 +*options.txt* For Vim version 9.2. Last change: 2026 May 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -4774,7 +4774,8 @@ A jump table for the options with a short description can be found at |Q_op|. [:PmenuKind,]:PmenuKindSel, {:PmenuExtra,}:PmenuExtraSel, x:PmenuSbar,X:PmenuThumb,j:PmenuBorder, - H:PmenuShadow,*:TabLine, + H:PmenuShadow,p:Popup,J:PopupBorder, + Q:PopupTitle,*:TabLine, #:TabLineSel,_:TabLineFill,!:CursorColumn, .:CursorLine,o:ColorColumn,q:QuickFixLine, z:StatusLineTerm,Z:StatusLineTermNC, @@ -4845,6 +4846,9 @@ A jump table for the options with a short description can be found at |Q_op|. |hl-PmenuMatchSel| < popup menu matched text in selected line |hl-PmenuBorder| j popup menu border characters |hl-PmenuShadow| H popup menu shadow + |hl-Popup| p popup window body + |hl-PopupBorder| J popup window border characters + |hl-PopupTitle| Q popup window title |hl-PreInsert| I text inserted when "preinsert" is in 'completeopt' |hl-Normal| ( Window color (supersedes 'wincolor' option) diff --git a/runtime/doc/popup.txt b/runtime/doc/popup.txt index 2afa93a756..2e91244778 100644 --- a/runtime/doc/popup.txt +++ b/runtime/doc/popup.txt @@ -1,4 +1,4 @@ -*popup.txt* For Vim version 9.2. Last change: 2026 May 10 +*popup.txt* For Vim version 9.2. Last change: 2026 May 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -44,8 +44,11 @@ A popup window can be used for such things as: The text in the popup window can be colored with |text-properties|. It is also possible to use syntax highlighting. -The default color used is "Pmenu". If you prefer something else use the -"highlight" argument or the 'wincolor' option, e.g.: > +The default colors are taken from |hl-Popup| (body), |hl-PopupBorder| +(border) and |hl-PopupTitle| (title), which all link to |hl-Pmenu| by +default for backward compatibility. Override them to give general popup +windows a different look than the popup completion menu, or use the +"highlight" argument or the 'wincolor' option for a per-popup override: > hi MyPopupColor ctermbg=lightblue guibg=lightblue call setwinvar(winid, '&wincolor', 'MyPopupColor') @@ -760,6 +763,10 @@ The second argument of |popup_create()| is a dictionary with options: border one line of padding is added to put the title on. You might want to add one or more spaces at the start and end as padding. + The title uses |hl-PopupTitle| by default; if + "borderhighlight" is set the top border highlight is + used instead, and if "highlight"/'wincolor' is set + that is used. wrap TRUE to make the lines wrap (default TRUE). drag TRUE to allow the popup to be dragged with the mouse by grabbing at the border. Has no effect if the @@ -808,6 +815,8 @@ The second argument of |popup_create()| is a dictionary with options: the highlight for the top/right/bottom/left border. Example: ['TopColor', 'RightColor', 'BottomColor, 'LeftColor'] + When not given and "highlight"/'wincolor' is also not + set, |hl-PopupBorder| is used. borderchars List with characters, defining the character to use for the top/right/bottom/left border. Optionally followed by the character to use for the diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index c74f95e808..428e8a5522 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 May 15 +*syntax.txt* For Vim version 9.2. Last change: 2026 May 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6077,6 +6077,18 @@ PmenuShadow Popup menu: Used for shadow. ComplMatchIns Matched text of the currently inserted completion. *hl-PreInsert* PreInsert Text inserted when "preinsert" is in 'completeopt'. + *hl-Popup* +Popup Popup window body, used when neither the popup's 'wincolor' + nor explicit "highlight" argument is set. Linked to |hl-Pmenu| + by default. + *hl-PopupBorder* +PopupBorder Popup window border characters, used when "borderhighlight" is + not set and the popup's 'wincolor' is also not set. + Linked to |hl-Pmenu| by default. + *hl-PopupTitle* +PopupTitle Popup window title, used when "borderhighlight" is not set and + the popup's 'wincolor' is also not set. Linked to + |hl-Pmenu| by default. *hl-PopupSelected* PopupSelected Popup window created with |popup_menu()|. Linked to |hl-PmenuSel| by default. diff --git a/runtime/doc/tags b/runtime/doc/tags index d5a8a1b30c..b94169f0f1 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -8464,8 +8464,11 @@ hl-PmenuSbar syntax.txt /*hl-PmenuSbar* hl-PmenuSel syntax.txt /*hl-PmenuSel* hl-PmenuShadow syntax.txt /*hl-PmenuShadow* hl-PmenuThumb syntax.txt /*hl-PmenuThumb* +hl-Popup syntax.txt /*hl-Popup* +hl-PopupBorder syntax.txt /*hl-PopupBorder* hl-PopupNotification syntax.txt /*hl-PopupNotification* hl-PopupSelected syntax.txt /*hl-PopupSelected* +hl-PopupTitle syntax.txt /*hl-PopupTitle* hl-PreInsert syntax.txt /*hl-PreInsert* hl-Question syntax.txt /*hl-Question* hl-QuickFixLine syntax.txt /*hl-QuickFixLine* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 5121fe90c6..5b4bb19906 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 May 11 +*version9.txt* For Vim version 9.2. Last change: 2026 May 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -41880,6 +41880,9 @@ Highlighting: ~ |hl-PmenuMatchSel| Popup menu: highlighting of matched text in selected line |hl-PmenuShadow| Popup menu: highlighting of the popup shadow +|hl-Popup| Popup window: body, default link |hl-Pmenu| +|hl-PopupBorder| Popup window: border characters, default link |hl-Pmenu| +|hl-PopupTitle| Popup window: title, default link |hl-Pmenu| |hl-PreInsert| highlighting for completion preinserted text |hl-TabPanel| |tabpanel|: not active tab page label |hl-TabPanelFill| |tabpanel|: filler space @@ -52690,6 +52693,9 @@ Options: ~ Highlighting: ~ |hl-VertSplitNC| Column separator of non-current window. +|hl-Popup| Popup window body. +|hl-PopupBorder| Popup window border. +|hl-PopupTitle| Popup window title. ============================================================================== PATCHES *patches-9.3* *bug-fixes-9.3* diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 1cf3b9940e..f6b137f521 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -2,7 +2,7 @@ " Language: Vim script " Maintainer: Hirohito Higashi " Doug Kearns -" Last Change: 2026 May 10 +" Last Change: 2026 May 17 " Former Maintainer: Charles E. Campbell " DO NOT CHANGE DIRECTLY. @@ -146,8 +146,8 @@ syn keyword vimGroup contained Added Bold BoldItalic Boolean Changed Character C " Default highlighting groups {{{2 " GEN_SYN_VIM: vimHLGroup, START_STR='syn keyword vimHLGroup contained', END_STR='' -syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit VertSplitNC VisualNOS DiffText DiffTextAdd PmenuSbar TabLineSel TabLineFill TabPanel TabPanelSel TabPanelFill Cursor lCursor TitleBar TitleBarNC QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel PmenuBorder PopupSelected MessageWindow PopupNotification PreInsert Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb PmenuShadow Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC -syn keyword vimHLGroup contained ToolbarLine ToolbarButton TitleBar TitleBarNC Menu Tooltip Scrollbar CursorIM ComplMatchIns LineNrAbove LineNrBelow MsgArea Terminal User1 User2 User3 User4 User5 User6 User7 User8 User9 +syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit VertSplitNC VisualNOS DiffText DiffTextAdd PmenuSbar TabLineSel TabLineFill TabPanel TabPanelSel TabPanelFill Cursor lCursor TitleBar TitleBarNC QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel PmenuBorder PopupSelected Popup PopupBorder PopupTitle MessageWindow PopupNotification PreInsert Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb PmenuShadow Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen +syn keyword vimHLGroup contained StatusLineTerm StatusLineTermNC ToolbarLine ToolbarButton TitleBar TitleBarNC Menu Tooltip Scrollbar CursorIM ComplMatchIns LineNrAbove LineNrBelow MsgArea Terminal User1 User2 User3 User4 User5 User6 User7 User8 User9 syn match vimHLGroup contained "\" syn case match diff --git a/src/highlight.c b/src/highlight.c index b21e489e61..3b1886be0e 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -312,6 +312,9 @@ static char *(highlight_init_both[]) = { "default link PmenuExtraSel PmenuSel", "default link PmenuBorder Pmenu", "default link PopupSelected PmenuSel", + "default link Popup Pmenu", + "default link PopupBorder Pmenu", + "default link PopupTitle Pmenu", "default link MessageWindow WarningMsg", "default link PopupNotification WarningMsg", "default link PreInsert Added", diff --git a/src/optiondefs.h b/src/optiondefs.h index afcaf96319..af556569e3 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -313,7 +313,7 @@ struct vimoption # define ISP_LATIN1 (char_u *)"@,161-255" #endif -#define HIGHLIGHT_INIT "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,|:VertSplitNC,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,E:DiffTextAdd,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,j:PmenuBorder,H:PmenuShadow,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm,Z:StatusLineTermNC,g:MsgArea,h:ComplMatchIns,%:TabPanel,^:TabPanelSel,&:TabPanelFill,I:PreInsert" +#define HIGHLIGHT_INIT "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,|:VertSplitNC,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,E:DiffTextAdd,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,j:PmenuBorder,H:PmenuShadow,p:Popup,J:PopupBorder,Q:PopupTitle,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm,Z:StatusLineTermNC,g:MsgArea,h:ComplMatchIns,%:TabPanel,^:TabPanelSel,&:TabPanelFill,I:PreInsert" // Default python version for pyx* commands #if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3) diff --git a/src/popupwin.c b/src/popupwin.c index f122d7a0de..624e20c613 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -6253,9 +6253,13 @@ update_popups(void (*win_update)(win_T *wp)) for (i = 0; i < 4; ++i) { - border_attr[i] = popup_attr; if (wp->w_border_highlight[i] != NULL) border_attr[i] = syn_name2attr(wp->w_border_highlight[i]); + else if (wp->w_hlfwin_id != 0 + || (wp->w_popup_flags & POPF_INFO)) + border_attr[i] = popup_attr; + else + border_attr[i] = HL_ATTR(HLF_POPB); // Apply blend to border attributes for popup with opacitys if ((wp->w_popup_flags & POPF_OPACITY) && wp->w_popup_blend > 0) @@ -6272,6 +6276,16 @@ update_popups(void (*win_update)(win_T *wp)) title_wincol = wp->w_wincol + 1; if (wp->w_popup_title != NULL) { + int title_attr; + + if (wp->w_popup_border[0] > 0 && wp->w_border_highlight[0] != NULL) + title_attr = border_attr[0]; + else if (wp->w_hlfwin_id != 0 + || (wp->w_popup_flags & POPF_INFO)) + title_attr = popup_attr; + else + title_attr = HL_ATTR(HLF_POPT); + title_len = vim_strsize(wp->w_popup_title); // truncate the title if too long @@ -6285,8 +6299,7 @@ update_popups(void (*win_update)(win_T *wp)) trunc_string(wp->w_popup_title, title_text, total_width - 2, title_byte_len + 1); screen_puts(title_text, wp->w_winrow, title_wincol, - wp->w_popup_border[0] > 0 - ? border_attr[0] : popup_attr); + title_attr); vim_free(title_text); } @@ -6294,7 +6307,7 @@ update_popups(void (*win_update)(win_T *wp)) } else screen_puts(wp->w_popup_title, wp->w_winrow, title_wincol, - wp->w_popup_border[0] > 0 ? border_attr[0] : popup_attr); + title_attr); } wincol = wp->w_wincol - wp->w_popup_leftoff + wp->w_popup_leftclip; diff --git a/src/screen.c b/src/screen.c index 9c76b8763b..8c1b8da0a7 100644 --- a/src/screen.c +++ b/src/screen.c @@ -134,7 +134,7 @@ get_win_attr(win_T *wp) if (wp->w_popup_flags & POPF_INFO) win_attr = HL_ATTR(HLF_PSI); // PmenuSel else - win_attr = HL_ATTR(HLF_PNI); // Pmenu + win_attr = HL_ATTR(HLF_POP); // Popup } #endif diff --git a/src/testdir/test_highlight.vim b/src/testdir/test_highlight.vim index 7ce2e9a2e8..36aee04f9d 100644 --- a/src/testdir/test_highlight.vim +++ b/src/testdir/test_highlight.vim @@ -821,6 +821,8 @@ endfunc " Test for 'highlight' option func Test_highlight_opt() let save_hl = &highlight + " "K" is intentionally an unused 'highlight' flag character; if you add a + " new HLF_ entry, pick a different letter or update this test. call assert_fails('set highlight=K:b', 'E474:') set highlight=f\ r call assert_equal('f r', &highlight) @@ -1622,7 +1624,7 @@ func Test_winhighlight_popupwin() hi B ctermbg=blue ctermfg=white redraw! # Remove intro message - win_execute(g:id, "set filetype=c whl=Pmenu:A,cType:B") + win_execute(g:id, "set filetype=c whl=Popup:A,PopupBorder:A,cType:B") END call writefile(lines, 'Xtest_winhighlight_popupwin', 'D') diff --git a/src/version.c b/src/version.c index 501e951ab2..82e24f7d12 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 493, /**/ 492, /**/ diff --git a/src/vim.h b/src/vim.h index 25c81f2f8b..683aff2b75 100644 --- a/src/vim.h +++ b/src/vim.h @@ -1568,6 +1568,9 @@ typedef enum , HLF_PST // popup menu scrollbar thumb , HLF_PMB // popup menu border , HLF_PMS // popup menu shadow + , HLF_POP // popup window body + , HLF_POPB // popup window border + , HLF_POPT // popup window title , HLF_TP // tabpage line , HLF_TPS // tabpage line selected , HLF_TPF // tabpage line filler @@ -1593,6 +1596,7 @@ typedef enum 'w', 'W', 'f', 'F', 'A', 'C', 'D', 'T', 'E', '-', '>', \ 'B', 'P', 'R', 'L', \ '+', '=', 'k', '<','[', ']', '{', '}', 'x', 'X', 'j', 'H', \ + 'p', 'J', 'Q', \ '*', '#', '_', '!', '.', 'o', 'q', \ 'z', 'Z', 'g', \ '%', '^', '&', 'I', '('} From f0e874a129a702457a383017b576eef1553f95d8 Mon Sep 17 00:00:00 2001 From: Maxim Kim Date: Sun, 17 May 2026 17:58:15 +0000 Subject: [PATCH 247/545] patch 9.2.0494: User commands cannot handle single args with spaces Problem: User commands cannot handle single args with spaces Solution: Add the -nargs=_ attribute (Maxim Kim) -nargs=_ allow user commands to have a single argument with spaces. For example given the following Test command and TestComplete function: ``` vim9script def TestComplete(A: string, _: string, _: number): list var all = ["qqqq", "aaaa", "qq aa"] return all->matchfuzzy(A) enddef command! -nargs=_ -complete=customlist,TestComplete Test echo ``` `:Test q a` should successfully complete `qq aa` fixes: #20102 closes: #20189 Signed-off-by: Maxim Kim Signed-off-by: Christian Brabandt --- runtime/doc/map.txt | 25 +++++++++-- runtime/doc/version9.txt | 2 + src/ex_cmds.h | 4 +- src/testdir/test_usercommands.vim | 71 ++++++++++++++++++++++++++++++- src/usercmd.c | 26 ++++++----- src/version.c | 2 + 6 files changed, 115 insertions(+), 15 deletions(-) diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index a79c0388cb..5d9bed83b0 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1,4 +1,4 @@ -*map.txt* For Vim version 9.2. Last change: 2026 May 02 +*map.txt* For Vim version 9.2. Last change: 2026 May 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1593,7 +1593,10 @@ reported if any are supplied). However, it is possible to specify that the command can take arguments, using the -nargs attribute. Valid cases are: -nargs=0 No arguments are allowed (the default) - -nargs=1 Exactly one argument is required, it includes spaces + -nargs=1 Exactly one argument is required, it includes spaces; + completion treats white spaces as argument separation + -nargs=_ Exactly one argument is required, it includes spaces; + completion treats white spaces as part of the argument -nargs=* Any number of arguments are allowed (0, 1, or many), separated by white space -nargs=? 0 or 1 arguments are allowed @@ -1601,7 +1604,23 @@ command can take arguments, using the -nargs attribute. Valid cases are: Arguments are considered to be separated by (unescaped) spaces or tabs in this context, except when there is one argument, then the white space is part of -the argument. +the argument. The difference between the "-nargs=1" and "-nargs=_": > + + func MyComplete(ArgLead, CmdLine, CursorPos) + return ["one value", "two values", "three values"] + \->matchfuzzy(a:ArgLead) + endfunc + :command -nargs=1 -complete=customlist,MyComplete MyCmd1 echo + :command -nargs=_ -complete=customlist,MyComplete MyCmd2 echo + +Completing ":MyCmd1 two va" will complete with: > + + :MyCmd1 two one value + +Completing ":MyCmd2 two va" will complete with: > + + :MyCmd2 two values + Note that arguments are used as text, not as expressions. Specifically, "s:var" will use the script-local variable in the script where the command was diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 5b4bb19906..a8ea821f4c 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52632,6 +52632,8 @@ Other ~ - |C-indenting| detects comments better. - The |package-hlyank| can now optionally highlight the last put region as well. +- New argument handling for user commands |:command-nargs| using the "-nars=_" + attribute to handle completion of single arguments with spaces as expected. Platform specific ~ ----------------- diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 1621569732..1a3ff0985a 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -31,7 +31,8 @@ #define EX_BANG 0x002 // allow a ! after the command name #define EX_EXTRA 0x004 // allow extra args after command name #define EX_XFILE 0x008 // expand wildcards in extra part -#define EX_NOSPC 0x010 // no spaces allowed in the extra part +#define EX_NOSPC 0x010 // extra part is a single argument (no split on + // whitespace) #define EX_DFLALL 0x020 // default file range is 1,$ #define EX_WHOLEFOLD 0x040 // extend range to include whole fold also // when less than two numbers given @@ -60,6 +61,7 @@ #define EX_EXPR_ARG 0x8000000 // argument is an expression #define EX_WHOLE 0x10000000 // command name cannot be shortened in Vim9 #define EX_EXPORT 0x20000000 // command can be used after :export +#define EX_ARGSPACE 0x40000000 // completion: keep spaces in arg lead #define EX_FILES (EX_XFILE | EX_EXTRA) // multiple extra files allowed #define EX_FILE1 (EX_FILES | EX_NOSPC) // 1 file, defaults to current file diff --git a/src/testdir/test_usercommands.vim b/src/testdir/test_usercommands.vim index 5afd8f1278..6ad39787e3 100644 --- a/src/testdir/test_usercommands.vim +++ b/src/testdir/test_usercommands.vim @@ -339,6 +339,9 @@ func Test_CmdErrors() com! -nargs=1 DoCmd : call assert_fails('DoCmd', 'E471:') + com! -nargs=_ DoCmd : + call assert_fails('DoCmd', 'E471:') + com! -nargs=+ DoCmd : call assert_fails('DoCmd', 'E471:') @@ -360,6 +363,14 @@ func CustomCompleteList(A, L, P) return [ "Monday", "Tuesday", "Wednesday", {}, test_null_string()] endfunc +func CustomCompleteListWithSpaces(A, L, P) + return [ "Monday Here", "Tuesday There", "Wednesday OK", {}, test_null_string()] +endfunc + +func CustomCompleteListFuzzy(A, L, P) + return [ "Monday Here", "Tuesday There", "Wednesday OK", {}, test_null_string()]->matchfuzzy(a:A) +endfunc + func Test_CmdCompletion() call feedkeys(":com -\\\"\", 'tx') call assert_equal('"com -addr bang bar buffer complete count keepscript nargs range register', @:) @@ -368,7 +379,7 @@ func Test_CmdCompletion() call assert_equal('"com -nargs=0 -addr bang bar buffer complete count keepscript nargs range register', @:) call feedkeys(":com -nargs=\\\"\", 'tx') - call assert_equal('"com -nargs=* + 0 1 ?', @:) + call assert_equal('"com -nargs=* + 0 1 ? _', @:) call feedkeys(":com -addr=\\\"\", 'tx') call assert_equal('"com -addr=arguments buffers lines loaded_buffers other quickfix tabs windows', @:) @@ -426,15 +437,27 @@ func Test_CmdCompletion() call feedkeys(":DoCmd \\\"\", 'tx') call assert_equal('"DoCmd mswin xterm', @:) + com! -nargs=_ -complete=behave DoCmd : + call feedkeys(":DoCmd \\\"\", 'tx') + call assert_equal('"DoCmd mswin xterm', @:) + com! -nargs=1 -complete=retab DoCmd : call feedkeys(":DoCmd \\\"\", 'tx') call assert_equal('"DoCmd -indentonly', @:) + com! -nargs=_ -complete=retab DoCmd : + call feedkeys(":DoCmd \\\"\", 'tx') + call assert_equal('"DoCmd -indentonly', @:) + " Test for file name completion com! -nargs=1 -complete=file DoCmd : call feedkeys(":DoCmd READM\\\"\", 'tx') call assert_equal('"DoCmd README.txt', @:) + com! -nargs=_ -complete=file DoCmd : + call feedkeys(":DoCmd READM\\\"\", 'tx') + call assert_equal('"DoCmd README.txt', @:) + " Test for buffer name completion com! -nargs=1 -complete=buffer DoCmd : let bnum = bufadd('BufForUserCmd') @@ -445,6 +468,15 @@ func Test_CmdCompletion() call feedkeys(":DoCmd BufFor\\\"\", 'tx') call assert_equal('"DoCmd BufFor', @:) + com! -nargs=_ -complete=buffer DoCmd : + let bnum = bufadd('BufForUserCmd') + call setbufvar(bnum, '&buflisted', 1) + call feedkeys(":DoCmd BufFor\\\"\", 'tx') + call assert_equal('"DoCmd BufForUserCmd', @:) + bwipe BufForUserCmd + call feedkeys(":DoCmd BufFor\\\"\", 'tx') + call assert_equal('"DoCmd BufFor', @:) + com! -nargs=* -complete=custom,CustomComplete DoCmd : call feedkeys(":DoCmd \\\"\", 'tx') call assert_equal('"DoCmd January February Mars', @:) @@ -453,6 +485,14 @@ func Test_CmdCompletion() call feedkeys(":DoCmd \\\"\", 'tx') call assert_equal('"DoCmd Monday Tuesday Wednesday', @:) + com! -nargs=_ -complete=customlist,CustomCompleteListWithSpaces DoCmd : + call feedkeys(":DoCmd \\\"\", 'tx') + call assert_equal('"DoCmd Monday Here Tuesday There Wednesday OK', @:) + + com! -nargs=_ -complete=customlist,CustomCompleteListFuzzy DoCmd : + call feedkeys(":DoCmd mo he\\\"\", 'tx') + call assert_equal('"DoCmd Monday Here', @:) + com! -nargs=+ -complete=custom,CustomCompleteList DoCmd : call assert_fails("call feedkeys(':DoCmd \', 'tx')", 'E730:') @@ -555,6 +595,27 @@ func Test_addr_all() delcommand DoSomething endfunc +func Test_nargs_underscore_fargs() + " -nargs=_ must behave like -nargs=1 for /: + " the whole argument is one token, whitespace is part of it. + let g:res = [] + com! -nargs=1 DoCmd1 call add(g:res, []) + com! -nargs=_ DoCmdU call add(g:res, []) + DoCmd1 a b c + DoCmdU a b c + call assert_equal([['a b c'], ['a b c']], g:res) + + let g:res = [] + com! -nargs=_ DoCmdQ call add(g:res, ) + DoCmdQ a b c + call assert_equal(['a b c'], g:res) + + delcom DoCmd1 + delcom DoCmdU + delcom DoCmdQ + unlet g:res +endfunc + func Test_command_list() command! DoCmd : call assert_equal("\n Name Args Address Complete Definition" @@ -614,6 +675,10 @@ func Test_command_list() call assert_equal("\n Name Args Address Complete Definition" \ .. "\n DoCmd 1 arglist :", \ execute('command DoCmd')) + command! -nargs=_ -complete=arglist DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd _ arglist :", + \ execute('command DoCmd')) command! -nargs=* -complete=augroup DoCmd : call assert_equal("\n Name Args Address Complete Definition" \ .. "\n DoCmd * augroup :", @@ -636,6 +701,10 @@ func Test_command_list() call assert_equal("\n Name Args Address Complete Definition" \ .. "\n DoCmd 1 :", \ execute('command DoCmd')) + command! -nargs=_ DoCmd : + call assert_equal("\n Name Args Address Complete Definition" + \ .. "\n DoCmd _ :", + \ execute('command DoCmd')) command! -nargs=* DoCmd : call assert_equal("\n Name Args Address Complete Definition" \ .. "\n DoCmd * :", diff --git a/src/usercmd.c b/src/usercmd.c index cef1d18b79..2d47569653 100644 --- a/src/usercmd.c +++ b/src/usercmd.c @@ -344,15 +344,18 @@ set_context_in_user_cmdarg( return set_context_in_map_cmd(xp, (char_u *)"map", arg, forceit, FALSE, FALSE, CMD_map); // Find start of last argument. - p = arg; - while (*p) + if (!(argt & EX_ARGSPACE)) { - if (*p == ' ') - // argument starts after a space - arg = p + 1; - else if (*p == '\\' && *(p + 1) != NUL) - ++p; // skip over escaped character - MB_PTR_ADV(p); + p = arg; + while (*p) + { + if (*p == ' ') + // argument starts after a space + arg = p + 1; + else if (*p == '\\' && *(p + 1) != NUL) + ++p; // skip over escaped character + MB_PTR_ADV(p); + } } xp->xp_pattern = arg; xp->xp_context = context; @@ -451,7 +454,7 @@ get_user_cmd_flags(expand_T *xp UNUSED, int idx) char_u * get_user_cmd_nargs(expand_T *xp UNUSED, int idx) { - static char *user_cmd_nargs[] = {"0", "1", "*", "?", "+"}; + static char *user_cmd_nargs[] = {"0", "1", "_", "*", "?", "+"}; if (idx < 0 || idx >= (int)ARRAY_LENGTH(user_cmd_nargs)) return NULL; @@ -640,13 +643,14 @@ uc_list(char_u *name, size_t name_len) len = 0; // Arguments - switch ((int)(a & (EX_EXTRA|EX_NOSPC|EX_NEEDARG))) + switch ((int)(a & (EX_EXTRA|EX_NOSPC|EX_NEEDARG|EX_ARGSPACE))) { case 0: IObuff[len++] = '0'; break; case (EX_EXTRA): IObuff[len++] = '*'; break; case (EX_EXTRA|EX_NOSPC): IObuff[len++] = '?'; break; case (EX_EXTRA|EX_NEEDARG): IObuff[len++] = '+'; break; case (EX_EXTRA|EX_NOSPC|EX_NEEDARG): IObuff[len++] = '1'; break; + case (EX_EXTRA|EX_NOSPC|EX_NEEDARG|EX_ARGSPACE): IObuff[len++] = '_'; break; } do @@ -975,6 +979,8 @@ uc_scan_attr( *argt |= (EX_EXTRA | EX_NOSPC); else if (*val == '+') *argt |= (EX_EXTRA | EX_NEEDARG); + else if (*val == '_') + *argt |= (EX_EXTRA | EX_NOSPC | EX_NEEDARG | EX_ARGSPACE); else goto wrong_nargs; } diff --git a/src/version.c b/src/version.c index 82e24f7d12..f6e20cfa07 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 494, /**/ 493, /**/ From 23c77d8ec8c8e8f35b2301648c9c1727ebcefedf Mon Sep 17 00:00:00 2001 From: Aliaksei Budavei <0x000c70@gmail.com> Date: Sun, 17 May 2026 18:05:13 +0000 Subject: [PATCH 248/545] runtime(sh): Do not conflate empty array and function declarations in Bash Although the "=" character is permitted in function names, a construct that parses as a variable assignment is preferred to it parsing as a function declaration. See the updated test file "sh_functions_bash.sh" for details. fixes: #20183 closes: #20205 Co-authored-by: Doug Kearns Signed-off-by: Aliaksei Budavei <0x000c70@gmail.com> Signed-off-by: Christian Brabandt --- runtime/syntax/sh.vim | 38 ++++++---------- .../testdir/dumps/sh_functions_bash_01.dump | 2 +- .../testdir/dumps/sh_functions_bash_02.dump | 4 +- .../testdir/dumps/sh_functions_bash_03.dump | 30 ++++++------- .../testdir/dumps/sh_functions_bash_04.dump | 20 +++++++++ .../testdir/dumps/sh_functions_bash_05.dump | 20 +++++++++ .../syntax/testdir/input/sh_functions_bash.sh | 43 ++++++++++++++++++- 7 files changed, 112 insertions(+), 45 deletions(-) create mode 100644 runtime/syntax/testdir/dumps/sh_functions_bash_04.dump create mode 100644 runtime/syntax/testdir/dumps/sh_functions_bash_05.dump diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim index 41570cce4f..044f2a9e3e 100644 --- a/runtime/syntax/sh.vim +++ b/runtime/syntax/sh.vim @@ -3,30 +3,7 @@ " Maintainer: This runtime file is looking for a new maintainer. " Previous Maintainers: Charles E. Campbell " Lennart Schultz -" Last Change: 2024 Mar 04 by Vim Project {{{1 -" 2024 Nov 03 by Aliaksei Budavei <0x000c70 AT gmail DOT com> improved bracket expressions, #15941 -" 2025 Jan 06 add $PS0 to bashSpecialVariables #16394 -" 2025 Jan 18 add bash coproc, remove duplicate syn keywords #16467 -" 2025 Mar 21 update shell capability detection #16939 -" 2025 Apr 03 command substitution opening paren at EOL #17026 -" 2025 Apr 10 improve shell detection #17084 -" 2025 Apr 29 match escaped chars in test operands #17221 -" 2025 May 06 improve single-quote string matching in parameter expansions -" 2025 May 06 match KornShell compound arrays -" 2025 May 10 improve wildcard character class lists -" 2025 May 21 improve supported KornShell features -" 2025 Jun 16 change how sh_fold_enabled is reset #17557 -" 2025 Jul 18 properly delete :commands #17785 -" 2025 Aug 23 bash: add support for ${ cmd;} and ${|cmd;} #18084 -" 2025 Sep 23 simplify ksh logic, update sh statements #18355 -" 2026 Jan 15 highlight command switches that contain a digit -" 2026 Feb 11 improve support for KornShell function names and variables -" 2026 Feb 15 improve comment handling #19414 -" 2026 Mar 23 improve matching of function definitions #19638 -" 2026 Apr 02 improve matching of function definitions #19849 -" 2026 Apr 19 improve detection of special variables #20016 -" 2026 May 14 don't highlight parens as part of the function name #20219 -" }}} +" Last Change: 2026 May 17 by Vim Project " Version: 208 " Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH " For options and settings, please use: :help ft-sh-syntax @@ -281,7 +258,7 @@ syn cluster shDerefList contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial syn cluster shDerefVarList contains=shDerefOffset,shDerefOp,shDerefVarArray,shDerefOpError syn cluster shEchoList contains=shArithmetic,shBracketExpr,shCommandSub,shCommandSubBQ,shDerefVarArray,shSubshare,shValsub,shDeref,shDerefSimple,shEscape,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote syn cluster shExprList1 contains=shBracketExpr,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq -syn cluster shExprList2 contains=@shExprList1,@shCaseList,shTest +syn cluster shExprList2 contains=@shExprList1,@shCaseList,shTest,shFunctionNameError syn cluster shFunctionCmds contains=shFor,shCaseEsac,shIf,shRepeat,shDblBrace,shDblParen if exists("b:is_ksh88") || exists("b:is_mksh") " Offer "shFunctionCmds" as is. @@ -307,7 +284,7 @@ if exists("b:is_kornshell") || exists("b:is_bash") endif syn cluster shPPSLeftList contains=shAlias,shArithmetic,shBracketExpr,shCmdParenRegion,shCommandSub,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable syn cluster shPPSRightList contains=shDeref,shDerefSimple,shEscape,shPosnParm -syn cluster shSubShList contains=shBracketExpr,@shCommandSubList,shCommandSubBQ,shSubshare,shValsub,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq,shOperator +syn cluster shSubShList contains=shBracketExpr,@shCommandSubList,shCommandSubBQ,shSubshare,shValsub,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq,shOperator,shFunctionNameError syn cluster shTestList contains=shArithmetic,shBracketExpr,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shSpecialDQ,shExDoubleQuote,shExpr,shExSingleQuote,shNumber,shOperator,shSingleQuote,shTest,shTestOpr syn cluster shNoZSList contains=shSpecialNoZS syn cluster shForList contains=shTestOpr,shNumber,shDerefSimple,shDeref,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shArithmetic @@ -676,6 +653,12 @@ if exists("b:is_bash") syn match shFunctionTwo "\%#=1\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\ze\s*\%(()\ze\)\=\_s*{" contained skipwhite skipnl nextgroup=shFunctionExpr contains=shFunctionParens syn match shFunctionThree "\%#=1^\s*\zs\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\s*()\ze\_s*((\@!" skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens syn match shFunctionFour "\%#=1\%(\%(\<\k\+\|[^()<>|&$;\t ]\+\)\+\)\@>\ze\s*\%(\%(()\ze\)\=\)\@>\_s*((\@!" contained skipwhite skipnl nextgroup=shFunctionSubSh contains=shFunctionParens + " Claim empty array assignments. + syn match shArrayEmptyDecl "\%#=1\ze\%(\<\h\w*=\)\@>()" transparent nextgroup=shVariable + + if !exists("g:sh_no_error") + syn match shFunctionNameError "\%#=1\%(\%(\<\h\w*\)\@>=\)\%(\%(\w\+\)\@>=\=\)\+()" skipwhite skipnl nextgroup=shExpr,shSubSh + endif elseif exists("b:is_ksh88") " AT&T ksh88 syn match shFunctionCmdOne "^\s*\zs\h\w*\s*()\ze\_s*\%(\%(for\|case\|select\|if\|while\|until\)\>\|\[\[\s\|((\)" skipwhite skipnl nextgroup=@shFunctionCmds contains=shFunctionParens @@ -970,6 +953,9 @@ if !exists("skip_sh_syntax_inits") hi def link shInError Error hi def link shParenError Error hi def link shTestError Error + if exists("b:is_bash") + hi def link shFunctionNameError Error + endif if exists("b:is_kornshell") || exists("b:is_posix") hi def link shDTestError Error endif diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_01.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_01.dump index de0d7d6f12..aba361e65e 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_bash_01.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_bash_01.dump @@ -17,4 +17,4 @@ | +0#0000e05#a8a8a8255@1|f+0#af5f00255#ffffff0|o|r| +0#0000000&|x| |i+0#af5f00255&|n| +0#0000000&|1+0#e000002&| +0#0000000&|2+0#e000002&| +0#0000000&@60 |-+0#0000e05#a8a8a8255| |d+0#af5f00255#ffffff0|o| +0#0000000&@70 ||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|:| @67 -@57|1|9|,|1| @9|3|6|%| +@57|1|9|,|1| @9|1|6|%| diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_02.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_02.dump index 8016beea4f..8901e58ac3 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_bash_02.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_bash_02.dump @@ -16,5 +16,5 @@ |3+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|}+0#e000e06&| +0#0000000&@63 |2+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|@|α|!+0#af5f00255&| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@56 |2+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|)+0#af5f00255&| +0#0000000&@67 -||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|e+0#af5f00255&|v|a|l| +0#0000000&|!+0#af5f00255&|?+0#0000000&|\+0#e000e06&|#| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@54 -@57|3|7|,|1| @9|8|6|%| +||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|e+0#af5f00255&|v|a|l| +0#0000000&|!+0#af5f00255&|?+0#0000000&|\+0#e000e06&|#| +0#0000000&|"+0#af5f00255&|\+0#e000e06&|"|$|1|\|"|"+0#af5f00255&| +0#0000000&@50 +@57|3|7|,|1| @9|4|0|%| diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_03.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_03.dump index a90642e1dc..98fcc942e8 100644 --- a/runtime/syntax/testdir/dumps/sh_functions_bash_03.dump +++ b/runtime/syntax/testdir/dumps/sh_functions_bash_03.dump @@ -1,20 +1,20 @@ -||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|e+0#af5f00255&|v|a|l| +0#0000000&|!+0#af5f00255&|?+0#0000000&|\+0#e000e06&|#| +0#0000000&|"+0#af5f00255&|$+0#e000e06&|1|"+0#af5f00255&| +0#0000000&@54 +||+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|e+0#af5f00255&|v|a|l| +0#0000000&|!+0#af5f00255&|?+0#0000000&|\+0#e000e06&|#| +0#0000000&|"+0#af5f00255&|\+0#e000e06&|"|$|1|\|"|"+0#af5f00255&| +0#0000000&@50 ||+0#0000e05#a8a8a8255| |f+0#af5f00255#ffffff0|i| +0#0000000&@70 | +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 | +0#0000e05#a8a8a8255@1|n+0#00e0e07#ffffff0|a|m|e|s|p|a|c|e| |(+0#e000e06&|)| +0#0000000&@60 |-+0#0000e05#a8a8a8255| |{+0#e000e06#ffffff0| +0#0000000&|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|#|;+0#af5f00255&| +0#0000000&@62 ||+0#0000e05#a8a8a8255| >}+0#e000e06#ffffff0|;+0#0000000&| |n|a|m|e|s|p|a|c|e| |$+0#e000e06&|@| +0#0000000&@57 -|~+0#4040ff13&| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -| +0#0000000&@56|5@1|,|1| @9|B|o|t| +| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 +| +0#0000e05#a8a8a8255@1|#+0&#ffffff0| |W|h|e|t|h|e|r| |"|=|"| |b|e|l|o|n|g|s| |t|o| |a| |n|a|m|e| |o|r| |d|e|l|i|m|i|t|s| |a| |n|a|m|e| |d|e|p|e|n|d|s| |o|n| |w|h|e|t|h|e|r| +0#0000000&@3 +| +0#0000e05#a8a8a8255@1|#+0&#ffffff0| |t|h|e| |r|e|s|e|r|v|e|d| |w|o|r|d| |"|f|u|n|c|t|i|o|n|"| |i|s| |p|r|e|s|e|n|t|,| |i|f| |s|o|,| |t|h|e|n| |"|=|"| |i|s| |p|a|r|t| |o|f| +0#0000000&@3 +| +0#0000e05#a8a8a8255@1|#+0&#ffffff0| |t|h|e| |f|u|n|c|t|i|o|n| |n|a|m|e|;| |e|l|s|e|,| |"|=|"| |d|e|l|i|m|i|t|s| |t|h|e| |n|a|m|e| |o|f| |a| |v|a|r|i|a|b|l|e| |w|h|e|n| |t|h|i|s| +0#0000000& +| +0#0000e05#a8a8a8255@1|#+0&#ffffff0| |n|a|m|e| |i|s| |g|i|v|e|n| |i|n| |a|l|p|h|a|n|u|m|e|r|i|c| |c|h|a|r|a|c|t|e|r|s| |a|n|d| |"|_|"|s| |b|e|f|o|r|e| |t|h|e| |l|e|f|t|m|o|s|t| +0#0000000&@1 +| +0#0000e05#a8a8a8255@1|#+0&#ffffff0| |"|=|"|;| |o|t|h|e|r|w|i|s|e|,| |"|=|"| |i|s| |p|a|r|t| |o|f| |t|h|e| |f|u|n|c|t|i|o|n| |n|a|m|e| |w|h|e|n| |t|h|i|s| |n|a|m|e| |h|a|s| +0#0000000&@3 +| +0#0000e05#a8a8a8255@1|#+0&#ffffff0| |o|n|e| |o|r| |m|o|r|e| |s|u|p@1|o|r|t|e|d| |N|O|N|-|a|l|p|h|a|n|u|m|e|r|i|c| |(|o|r| |"|_|"|)| |c|h|a|r|a|c|t|e|r|s| |b|e|f|o|r|e| |"|=|"|.| +0#0000000& +| +0#0000e05#a8a8a8255@1|x+0#00e0e07#ffffff0|s|=+0#0000000&|(+0#e000e06&|)| +0#0000000&@67 +| +0#0000e05#a8a8a8255@1|(+0#e000e06#ffffff0| +0#0000000&@71 +| +0#0000e05#a8a8a8255@1| +0#e000e06#ffffff0@3|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|(@1| |1+0#e000002&| +0#e000e06&|+| |$|{|#|x|s|[|*+0#0000000&|]+0#e000e06&|}| |)@1| +0#0000000&@43 +| +0#0000e05#a8a8a8255@1| +0#e000e06#ffffff0@3|x+0#00e0e07&|s|=+0#0000000&|(+0#e000e06&|)| +0#0000000&@63 +| +0#0000e05#a8a8a8255@1| +0#e000e06#ffffff0@3|{| +0#0000000&@67 +| +0#0000e05#a8a8a8255@1| +0#e000e06#ffffff0@7|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|(@1| |2+0#e000002&| +0#e000e06&|+| |$|{|#|x|s|[|*+0#0000000&|]+0#e000e06&|}| |)@1| +0#0000000&@39 +@57|5@1|,|1| @9|6|3|%| diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_04.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_04.dump new file mode 100644 index 0000000000..d1be88c7d3 --- /dev/null +++ b/runtime/syntax/testdir/dumps/sh_functions_bash_04.dump @@ -0,0 +1,20 @@ +| +0#0000e05#a8a8a8255@1| +0#e000e06#ffffff0@7|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|(@1| |2+0#e000002&| +0#e000e06&|+| |$|{|#|x|s|[|*+0#0000000&|]+0#e000e06&|}| |)@1| +0#0000000&@39 +| +0#0000e05#a8a8a8255@1| +0#e000e06#ffffff0@7|x+0#00e0e07&|s|=+0#0000000&|(+0#e000e06&|)| +0#0000000&@59 +| +0#0000e05#a8a8a8255@1| +0#e000e06#ffffff0@7|i+0#af5f00255&|f| |:+0#e000e06&|;+0#af5f00255&| +0#e000e06&|t+0#af5f00255&|h|e|n| +0#e000e06&|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|(@1| |3+0#e000002&| +0#e000e06&|+| |$|{|#|x|s|[|*+0#0000000&|]+0#e000e06&|}| |)@1|;+0#af5f00255&| +0#e000e06&|f+0#af5f00255&|i| +0#0000000&@24 +| +0#0000e05#a8a8a8255@1| +0#e000e06#ffffff0@3|}| +0#0000000&@67 +| +0#0000e05#a8a8a8255@1|)+0#e000e06#ffffff0| +0#0000000&@71 +| +0#0000e05#a8a8a8255@1> +0#0000000#ffffff0@72 +|-+0#0000e05#a8a8a8255| |i+0#00e0e07#ffffff0|δ|=|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@65 +|-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|=+0#00e0e07&|i|d|=|(+0#e000e06&|)| +0#0000000&|{+0#e000e06&| +0#0000000&@60 +|2+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|=+0#00e0e07&@2|(+0#e000e06&|)| +0#0000000&@59 +|2+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|i+0#af5f00255&|f| |:+0#0000000&|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|*|;+0#af5f00255&| +0#0000000&|f+0#af5f00255&|i|;| +0#0000000&|=+0#af5f00255&@2| +0#0000000&|$+0#e000e06&|*| +0#0000000&@34 +|2+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|}+0#e000e06&|;+0#af5f00255&| +0#0000000&|=+0#af5f00255&|i|d|=| +0#0000000&|$+0#e000e06&|*| +0#0000000&@58 +||+0#0000e05#a8a8a8255| |)+0#af5f00255#ffffff0|;+0#0000000&| |i+0#af5f00255&|d|=| +0#0000000&|i|δ|=+0#af5f00255&| +0#0000000&|i|δ|=+0#af5f00255&| +0#0000000&|i|δ|=+0#af5f00255&| +0#0000000&@54 +| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 +|-+0#0000e05#a8a8a8255| |f+0#af5f00255#ffffff0|u|n|c|t|i|o|n| +0#0000000&|f+0#00e0e07&|=|(+0#e000e06&|)| +0#0000000&|(+0#af5f00255&| +0#0000000&@57 +|-+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|f+0#00e0e07&|=|f| +0#0000000&|{+0#e000e06&| +0#0000000&@54 +|2+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|f+0#af5f00255&|u|n|c|t|i|o|n| +0#0000000&|f+0#00e0e07&|=|f|=| +0#0000000&@51 +|2+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@7|i+0#af5f00255&|f| |:+0#0000000&|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&|e+0#af5f00255&|c|h|o| +0#e000002&|$+0#e000e06&|*|;+0#af5f00255&| +0#0000000&|f+0#af5f00255&|i|;| +0#0000000&|f|\+0#e000e06&|=|f+0#0000000&|\+0#e000e06&|=| +0#0000000&|$+0#e000e06&|*| +0#0000000&@31 +|2+0#0000e05#a8a8a8255| | +0#0000000#ffffff0@3|}+0#e000e06&|;+0#af5f00255&| +0#0000000&|f|\+0#e000e06&|=|f+0#0000000&| |$+0#e000e06&|*| +0#0000000&@58 +||+0#0000e05#a8a8a8255| |)+0#af5f00255#ffffff0|;+0#0000000&| |f+0#00e0e07&|=+0#0000000&| |f|\|=+0#af5f00255&| +0#0000000&|f+0#00e0e07&|=+0#0000000&| |f+0#00e0e07&|=+0#0000000&| @57 +@57|7|3|,|0|-|1| @7|8|7|%| diff --git a/runtime/syntax/testdir/dumps/sh_functions_bash_05.dump b/runtime/syntax/testdir/dumps/sh_functions_bash_05.dump new file mode 100644 index 0000000000..9b805c1891 --- /dev/null +++ b/runtime/syntax/testdir/dumps/sh_functions_bash_05.dump @@ -0,0 +1,20 @@ +||+0#0000e05#a8a8a8255| |)+0#af5f00255#ffffff0|;+0#0000000&| |f+0#00e0e07&|=+0#0000000&| |f|\|=+0#af5f00255&| +0#0000000&|f+0#00e0e07&|=+0#0000000&| |f+0#00e0e07&|=+0#0000000&| @57 +| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@72 +| +0#0000e05#a8a8a8255@1|#+0&#ffffff0| |P|a|r|e|n|s| |a|r|e| |n|o|t| |e|s|c|a|p|e|d|,| |h|e|n|c|e| |t|h|i|s| |i|s| |i|n|v|a|l|i|d| |v|a|r|i|a|b|l|e| |a|s@1|i|g|n|m|e|n|t|.| +0#0000000&@4 +| +0#0000e05#a8a8a8255@1|f+0#ffffff16#ff404010|=|f|(|)| +0#0000000#ffffff0@67 +| +0#0000e05#a8a8a8255@1|{+0#e000e06#ffffff0| +0#0000000&@71 +| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@3>f+0#ffffff16#ff404010|=|f|=|(|)| +0#0000000#ffffff0@62 +| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@3|(+0#af5f00255&| +0#0000000&@67 +| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@7|f+0#ffffff16#ff404010|=|f|=|f|(|)| +0#0000000#ffffff0@57 +| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@7|i+0#af5f00255&|f| |:+0#0000000&|;+0#af5f00255&| +0#0000000&|t+0#af5f00255&|h|e|n| +0#0000000&|:|;+0#af5f00255&| +0#0000000&|f+0#af5f00255&|i| +0#0000000&@48 +| +0#0000e05#a8a8a8255@1| +0#0000000#ffffff0@3|)+0#af5f00255&| +0#0000000&@67 +| +0#0000e05#a8a8a8255@1|}+0#e000e06#ffffff0| +0#0000000&@71 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|9|1|,|5| @9|B|o|t| diff --git a/runtime/syntax/testdir/input/sh_functions_bash.sh b/runtime/syntax/testdir/input/sh_functions_bash.sh index 161f14f978..714d2a74b3 100644 --- a/runtime/syntax/testdir/input/sh_functions_bash.sh +++ b/runtime/syntax/testdir/input/sh_functions_bash.sh @@ -47,9 +47,50 @@ if :; then } @α! "$1" ) - eval !?\# "$1" + eval !?\# "\"$1\"" fi namespace () { echo $#; }; namespace $@ + +# Whether "=" belongs to a name or delimits a name depends on whether +# the reserved word "function" is present, if so, then "=" is part of +# the function name; else, "=" delimits the name of a variable when this +# name is given in alphanumeric characters and "_"s before the leftmost +# "="; otherwise, "=" is part of the function name when this name has +# one or more supported NON-alphanumeric (or "_") characters before "=". +xs=() +( + echo $(( 1 + ${#xs[*]} )) + xs=() + { + echo $(( 2 + ${#xs[*]} )) + xs=() + if :; then echo $(( 3 + ${#xs[*]} )); fi + } +) + +iδ=() ( + =id=() { + ===() + if :; then echo $*; fi; === $* + }; =id= $* +); id= iδ= iδ= iδ= + +function f=() ( + function f=f { + function f=f= + if :; then echo $*; fi; f\=f\= $* + }; f\=f $* +); f= f\= f= f= + +# Parens are not escaped, hence this is invalid variable assignment. +f=f() +{ + f=f=() + ( + f=f=f() + if :; then :; fi + ) +} From 2a01e5967103a06dd6c83bfca73995557a171a4b Mon Sep 17 00:00:00 2001 From: mathmil <82173590+mathmil@users.noreply.github.com> Date: Sun, 17 May 2026 18:08:46 +0000 Subject: [PATCH 249/545] runtime(just): add 'suffixesadd' to ftplugin closes: #20197 Signed-off-by: mathmil <82173590+mathmil@users.noreply.github.com> Signed-off-by: Peter Benjamin Signed-off-by: Christian Brabandt --- runtime/ftplugin/just.vim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime/ftplugin/just.vim b/runtime/ftplugin/just.vim index 6f2acddf96..7cf188a774 100644 --- a/runtime/ftplugin/just.vim +++ b/runtime/ftplugin/just.vim @@ -2,6 +2,7 @@ " Language: Justfile " Maintainer: Peter Benjamin <@pbnj> " Last Change: 2025 Jan 19 +" 2026 May 17 by Vim Project: add 'suffixesadd' #20197 " Credits: The original author, Noah Bogart " Only do this when not done yet for this buffer @@ -13,5 +14,6 @@ let b:did_ftplugin = 1 setlocal iskeyword+=- setlocal comments=n:# setlocal commentstring=#\ %s +setlocal suffixesadd=.just -let b:undo_ftplugin = "setlocal iskeyword< comments< commentstring<" +let b:undo_ftplugin = "setlocal iskeyword< comments< commentstring< suffixesadd<" From f08ab2f4d7d2947c8dd6c179ae08ee6146a2694b Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 17 May 2026 18:53:48 +0000 Subject: [PATCH 250/545] patch 9.2.0495: [security]: runtime(netrw): code injection via NetrwBookHistSave() Problem: [security]: runtime(netrw): code injection via NetrwBookHistSave() Solution: Properly quote the directory name using string() function (Srinivas Piskala Ganesh Babu) Github Security Advisory: https://github.com/vim/vim/security/advisories/GHSA-crm5-rh6j-2c7c Signed-off-by: Christian Brabandt --- .../pack/dist/opt/netrw/autoload/netrw.vim | 4 ++-- src/testdir/test_plugin_netrw.vim | 20 +++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index 176cdcb87f..1fdbeeb8f7 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -1,7 +1,7 @@ " Creator: Charles E Campbell " Previous Maintainer: Luca Saccarola " Maintainer: This runtime file is looking for a new maintainer. -" Last Change: 2026 May 14 +" Last Change: 2026 May 17 " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright @@ -2935,7 +2935,7 @@ function s:NetrwBookHistSave() while ( first || cnt != g:netrw_dirhistcnt ) let lastline= lastline + 1 if exists("g:netrw_dirhist_{cnt}") - call setline(lastline,'let g:netrw_dirhist_'.cnt."='".g:netrw_dirhist_{cnt}."'") + call setline(lastline,'let g:netrw_dirhist_'.cnt.'='.string(g:netrw_dirhist_{cnt})) endif let first = 0 let cnt = ( cnt - 1 ) % g:netrw_dirhistmax diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim index 4256dc9d2a..7cd2aa89ed 100644 --- a/src/testdir/test_plugin_netrw.vim +++ b/src/testdir/test_plugin_netrw.vim @@ -760,4 +760,24 @@ function Test_netrw_NetrwMaps_CR_dirname() unlet! g:netrw_pwn bw! endfunction + +func Test_netrw_injection() + let g:netrw_home = getcwd() + let savefile = g:netrw_home . '/.netrwhist' + let g:netrw_dirhistmax = 10 + let g:netrw_dirhistcnt = 1 + let g:netrw_dirhist_1 = "x'|let g:injected = 1|let y='z" + call delete(savefile) + try + call netrw#Call('NetrwBookHistSave') + call assert_true(filereadable(savefile), savefile . ' must be written') + unlet g:netrw_dirhist_1 + execute 'source ' . fnameescape(savefile) + call assert_false(exists("g:injected"), 'injected statement must not execute') + call assert_equal("x'|let g:injected = 1|let y='z", g:netrw_dirhist_1, 'dirname must round-trip') + finally + call delete(savefile) + unlet! g:netrw_home g:netrw_dirhistmax g:netrw_dirhistcnt g:netrw_dirhist_1 g:injected + endtry +endfunc " vim:ts=8 sts=2 sw=2 et diff --git a/src/version.c b/src/version.c index f6e20cfa07..0a9e819063 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 495, /**/ 494, /**/ From a65a52d684bc58535ad28a4ae824d22e76399934 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 17 May 2026 19:39:24 +0000 Subject: [PATCH 251/545] patch 9.2.0496: [security]: Code Injection in cucumber filetype plugin Problem: [security]: Code Injection in cucumber filetype plugin (Christopher Lusk) Solution: Use rubys Regexp.new() with the untrusted pattern Github Security Advisory: https://github.com/vim/vim/security/advisories/GHSA-4473-94jm-w5x9 Signed-off-by: Christian Brabandt --- runtime/ftplugin/cucumber.vim | 5 ++++- src/testdir/test_filetype.vim | 30 ++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/runtime/ftplugin/cucumber.vim b/runtime/ftplugin/cucumber.vim index f4848d1c60..9723898d15 100644 --- a/runtime/ftplugin/cucumber.vim +++ b/runtime/ftplugin/cucumber.vim @@ -2,6 +2,8 @@ " Language: Cucumber " Maintainer: Tim Pope " Last Change: 2016 Aug 29 +" 2026 May 26 by Vim Project: prevent Code Injection +" https://github.com/vim/vim/security/advisories/GHSA-4473-94jm-w5x9 " Only do this when not done yet for this buffer if (exists("b:did_ftplugin")) @@ -96,7 +98,8 @@ function! s:stepmatch(receiver,target) catch endtry if has("ruby") && pattern !~ '\\\@ s:steps -> s:stepmatch on every discovered step, + " including the malicious one. Suppress preview and error messages. + silent! normal [d + call assert_false(filereadable(marker), 'Ruby injection executed') + bwipe! + filetype plugin off +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 0a9e819063..2e76cbe480 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 496, /**/ 495, /**/ From 4a99175e3949cef3ebe6a9ee6a82b963cfdf5995 Mon Sep 17 00:00:00 2001 From: Miguel Barro Date: Sun, 17 May 2026 20:00:41 +0000 Subject: [PATCH 252/545] patch 9.2.0497: Cannot jump to remote tags Problem: Cannot jump to remote tags (after v9.2.0405) Solution: Add the 'tagsecure' option (Miguel Barro) closes: #20162 Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Signed-off-by: Miguel Barro Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 16 ++++++++++++++++ runtime/doc/quickref.txt | 3 ++- runtime/doc/tags | 4 ++++ runtime/doc/tagsrch.txt | 5 +++-- runtime/doc/version9.txt | 1 + runtime/optwin.vim | 4 +++- runtime/syntax/vim.vim | 18 +++++++++--------- src/option.h | 1 + src/optiondefs.h | 3 +++ src/po/vim.pot | 5 ++++- src/tag.c | 8 ++++---- src/testdir/test_options.vim | 4 ++-- src/testdir/test_tagjump.vim | 13 +++++++++++++ src/version.c | 2 ++ 14 files changed, 67 insertions(+), 20 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 1c1100a981..2ba59824f6 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -9223,6 +9223,22 @@ A jump table for the options with a short description can be found at |Q_op|. file names from the list. This avoids problems when a future version uses another default. + *'tagsecure'* *'tsc'* *'notagsecure'* *'notsc'* +'tagsecure' 'tsc' boolean (default on) + global + When on, Vim refuses to follow tag entries whose file field looks like + a URL ("scheme://..."), aborting the jump with error |E1576|. This + prevents tag files from causing Vim to open URLs through |netrw|, which + would trigger a network request and expose netrw's URL-handling code to + attacker-controlled input or lead to environment exfiltration. + + Tag files might be distributed alongside source code (e.g. via Git + repositories) and may therefore be untrustworthy. Only disable + this option if you fully control the tag files Vim will read. + + This option cannot be set from a |modeline| or in the |sandbox|, for + security reasons. + *'tagstack'* *'tgst'* *'notagstack'* *'notgst'* 'tagstack' 'tgst' boolean (default on) global diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt index acc5dbdd04..96bfdb7e13 100644 --- a/runtime/doc/quickref.txt +++ b/runtime/doc/quickref.txt @@ -1,4 +1,4 @@ -*quickref.txt* For Vim version 9.2. Last change: 2026 Apr 21 +*quickref.txt* For Vim version 9.2. Last change: 2026 May 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -960,6 +960,7 @@ Short explanation of each option: *option-list* 'taglength' 'tl' number of significant characters for a tag 'tagrelative' 'tr' file names in tag file are relative 'tags' 'tag' list of file names used by the tag command +'tagsecure' 'tsc' do not open remote files using tag commands 'tagstack' 'tgst' push tags onto the tag stack 'tcldll' name of the Tcl dynamic library 'term' name of the terminal diff --git a/runtime/doc/tags b/runtime/doc/tags index b94169f0f1..eb7e219293 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -785,6 +785,7 @@ $quote eval.txt /*$quote* 'nota' options.txt /*'nota'* 'notagbsearch' options.txt /*'notagbsearch'* 'notagrelative' options.txt /*'notagrelative'* +'notagsecure' options.txt /*'notagsecure'* 'notagstack' options.txt /*'notagstack'* 'notbi' options.txt /*'notbi'* 'notbidi' options.txt /*'notbidi'* @@ -803,6 +804,7 @@ $quote eval.txt /*$quote* 'noto' options.txt /*'noto'* 'notop' options.txt /*'notop'* 'notr' options.txt /*'notr'* +'notsc' options.txt /*'notsc'* 'nottimeout' options.txt /*'nottimeout'* 'nottybuiltin' options.txt /*'nottybuiltin'* 'nottyfast' options.txt /*'nottyfast'* @@ -1229,6 +1231,7 @@ $quote eval.txt /*$quote* 'taglength' options.txt /*'taglength'* 'tagrelative' options.txt /*'tagrelative'* 'tags' options.txt /*'tags'* +'tagsecure' options.txt /*'tagsecure'* 'tagstack' options.txt /*'tagstack'* 'tal' options.txt /*'tal'* 'tb' options.txt /*'tb'* @@ -1279,6 +1282,7 @@ $quote eval.txt /*$quote* 'tr' options.txt /*'tr'* 'trz' options.txt /*'trz'* 'ts' options.txt /*'ts'* +'tsc' options.txt /*'tsc'* 'tsl' options.txt /*'tsl'* 'tsr' options.txt /*'tsr'* 'tsrfu' options.txt /*'tsrfu'* diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index cad4caf1a9..d278383863 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -1,4 +1,4 @@ -*tagsrch.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*tagsrch.txt* For Vim version 9.2. Last change: 2026 May 17 VIM REFERENCE MANUAL by Bram Moolenaar @@ -581,7 +581,8 @@ ctags). doubtful). It cannot contain a . *E1576* Using a remote file via network protocol (e.g. using - http://remote/file.txt) is not allowed. + http://remote/file.txt) is not allowed unless 'tagsecure' + is unset. {tagaddress} The Ex command that positions the cursor on the tag. It can be any Ex command, although restrictions apply (see |tag-security|). Posix only allows line numbers and search diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index a8ea821f4c..846d2dfa23 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -52688,6 +52688,7 @@ Options: ~ configure the height. 't_BS' Begin synchronized update. 't_ES' End synchronized update. +'tagsecure' Do not open remote files using tag commands 'termresize' Method for handling terminal resize events. 'termsync' Enable support for terminal DEC 2026 sync mode. 'winhighlight' Window-local highlight group mappings. diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 1ecd602d3b..c926d738f5 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: The Vim Project -" Last Change: 2026 Apr 21 +" Last Change: 2026 May 17 " Former Maintainer: Bram Moolenaar " If there already is an option window, jump to that one. @@ -315,6 +315,8 @@ call append("$", "\t" .. s:global_or_local) call OptionG("tc", &tc) call AddOption("tagrelative", gettext("file names in a tags file are relative to the tags file")) call BinOptionG("tr", &tr) +call AddOption("tagsecure", gettext("a :tag command cannot access remote files")) +call BinOptionG("tsc", &tsc) call AddOption("tagstack", gettext("a :tag command will use the tagstack")) call BinOptionG("tgst", &tgst) call AddOption("showfulltag", gettext("when completing tags in Insert mode show more info")) diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index f6b137f521..4c2c137a04 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -70,23 +70,23 @@ syn keyword vimOption contained efm errorformat ek esckeys ei eventignore eiw ev syn keyword vimOption contained hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine mfd maxfuncdepth skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines mlst modelinestrict ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader pmbcs printmbcharset skipwhite nextgroup=vimSetEqual,vimSetMod syn keyword vimOption contained pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sop scrolloffpad sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote sr shiftround sw shiftwidth skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack tcldll term tbidi termbidi tenc termencoding skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu wim wildmode skipwhite nextgroup=vimSetEqual,vimSetMod -syn keyword vimOption contained wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tsc tagsecure tgst tagstack tcldll term tbidi termbidi skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained tenc termencoding tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore wic wildignorecase wmnu wildmenu skipwhite nextgroup=vimSetEqual,vimSetMod +syn keyword vimOption contained wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes skipwhite nextgroup=vimSetEqual,vimSetMod " vimOptions: These are the turn-off setting variants {{{2 " GEN_SYN_VIM: vimOption turn-off, START_STR='syn keyword vimOption contained', END_STR='' syn keyword vimOption contained noari noallowrevins noarab noarabic noarshape noarabicshape noacd noautochdir noac noautocomplete noai noautoindent noar noautoread noasd noautoshelldir noaw noautowrite noawa noautowriteall nobk nobackup nobeval noballooneval nobevalterm noballoonevalterm nobin nobinary nobomb nobri nobreakindent nobl nobuflisted nocdh nocdhome nocin nocindent nocp nocompatible nocf noconfirm noci nocopyindent nocsre nocscoperelative nocst nocscopetag nocsverb nocscopeverbose nocrb nocursorbind nocuc nocursorcolumn nocul nocursorline nodeco nodelcombine nodiff nodg nodigraph noed noedcompatible noemo noemoji noeof noendoffile noeol noendofline noea noequalalways noeb noerrorbells noek noesckeys noet noexpandtab noex noexrc nofic nofileignorecase syn keyword vimOption contained nofixeol nofixendofline nofen nofoldenable nofs nofsync nogd nogdefault noguipty nohid nohidden nohk nohkmap nohkp nohkmapp nohls nohlsearch noicon noic noignorecase noimc noimcmdline noimd noimdisable nois noincsearch noinf noinfercase noim noinsertmode nojs nojoinspaces nolnr nolangnoremap nolrm nolangremap nolz nolazyredraw nolbr nolinebreak nolisp nolist nolpl noloadplugins nomagic noml nomodeline nomle nomodelineexpr nomlst nomodelinestrict noma nomodifiable nomod nomodified nomore nomousef nomousefocus nomh nomousehide nomousemev nomousemoveevent nonu nonumber noodev noopendevice nopaste nopi nopreserveindent nopvw nopreviewwindow noprompt noro noreadonly nornu norelativenumber noremap nors norestorescreen nori norevins norl norightleft -syn keyword vimOption contained noru noruler noscb noscrollbind noscf noscrollfocus nosecure nossl noshellslash nostmp noshelltemp nosr noshiftround nosn noshortname nosc noshowcmd nosft noshowfulltag nosm noshowmatch nosmd noshowmode noscs nosmartcase nosi nosmartindent nosta nosmarttab nosms nosmoothscroll nospell nosb nosplitbelow nospr nosplitright nosol nostartofline noswf noswapfile notbs notagbsearch notr notagrelative notgst notagstack notbidi notermbidi notgc notermguicolors notsy notermsync noterse nota notextauto notx notextmode notop notildeop noto notimeout notitle nottimeout notbi nottybuiltin notf nottyfast noudf noundofile novb novisualbell nowarn nowiv noweirdinvert nowic nowildignorecase nowmnu nowildmenu nowfb nowinfixbuf nowfh nowinfixheight +syn keyword vimOption contained noru noruler noscb noscrollbind noscf noscrollfocus nosecure nossl noshellslash nostmp noshelltemp nosr noshiftround nosn noshortname nosc noshowcmd nosft noshowfulltag nosm noshowmatch nosmd noshowmode noscs nosmartcase nosi nosmartindent nosta nosmarttab nosms nosmoothscroll nospell nosb nosplitbelow nospr nosplitright nosol nostartofline noswf noswapfile notbs notagbsearch notr notagrelative notsc notagsecure notgst notagstack notbidi notermbidi notgc notermguicolors notsy notermsync noterse nota notextauto notx notextmode notop notildeop noto notimeout notitle nottimeout notbi nottybuiltin notf nottyfast noudf noundofile novb novisualbell nowarn nowiv noweirdinvert nowic nowildignorecase nowmnu nowildmenu nowfb nowinfixbuf nowfh nowinfixheight syn keyword vimOption contained nowfw nowinfixwidth nowrap nows nowrapscan nowrite nowa nowriteany nowb nowritebackup noxtermcodes " vimOptions: These are the invertible variants {{{2 " GEN_SYN_VIM: vimOption invertible, START_STR='syn keyword vimOption contained', END_STR='' syn keyword vimOption contained invari invallowrevins invarab invarabic invarshape invarabicshape invacd invautochdir invac invautocomplete invai invautoindent invar invautoread invasd invautoshelldir invaw invautowrite invawa invautowriteall invbk invbackup invbeval invballooneval invbevalterm invballoonevalterm invbin invbinary invbomb invbri invbreakindent invbl invbuflisted invcdh invcdhome invcin invcindent invcp invcompatible invcf invconfirm invci invcopyindent invcsre invcscoperelative invcst invcscopetag invcsverb invcscopeverbose invcrb invcursorbind invcuc invcursorcolumn invcul invcursorline invdeco invdelcombine invdiff invdg invdigraph inved invedcompatible invemo invemoji inveof invendoffile inveol invendofline invea invequalalways inveb inverrorbells syn keyword vimOption contained invek invesckeys invet invexpandtab invex invexrc invfic invfileignorecase invfixeol invfixendofline invfen invfoldenable invfs invfsync invgd invgdefault invguipty invhid invhidden invhk invhkmap invhkp invhkmapp invhls invhlsearch invicon invic invignorecase invimc invimcmdline invimd invimdisable invis invincsearch invinf invinfercase invim invinsertmode invjs invjoinspaces invlnr invlangnoremap invlrm invlangremap invlz invlazyredraw invlbr invlinebreak invlisp invlist invlpl invloadplugins invmagic invml invmodeline invmle invmodelineexpr invmlst invmodelinestrict invma invmodifiable invmod invmodified invmore invmousef invmousefocus invmh invmousehide invmousemev invmousemoveevent invnu invnumber invodev invopendevice invpaste -syn keyword vimOption contained invpi invpreserveindent invpvw invpreviewwindow invprompt invro invreadonly invrnu invrelativenumber invremap invrs invrestorescreen invri invrevins invrl invrightleft invru invruler invscb invscrollbind invscf invscrollfocus invsecure invssl invshellslash invstmp invshelltemp invsr invshiftround invsn invshortname invsc invshowcmd invsft invshowfulltag invsm invshowmatch invsmd invshowmode invscs invsmartcase invsi invsmartindent invsta invsmarttab invsms invsmoothscroll invspell invsb invsplitbelow invspr invsplitright invsol invstartofline invswf invswapfile invtbs invtagbsearch invtr invtagrelative invtgst invtagstack invtbidi invtermbidi invtgc invtermguicolors invtsy invtermsync invterse invta invtextauto invtx invtextmode -syn keyword vimOption contained invtop invtildeop invto invtimeout invtitle invttimeout invtbi invttybuiltin invtf invttyfast invudf invundofile invvb invvisualbell invwarn invwiv invweirdinvert invwic invwildignorecase invwmnu invwildmenu invwfb invwinfixbuf invwfh invwinfixheight invwfw invwinfixwidth invwrap invws invwrapscan invwrite invwa invwriteany invwb invwritebackup invxtermcodes +syn keyword vimOption contained invpi invpreserveindent invpvw invpreviewwindow invprompt invro invreadonly invrnu invrelativenumber invremap invrs invrestorescreen invri invrevins invrl invrightleft invru invruler invscb invscrollbind invscf invscrollfocus invsecure invssl invshellslash invstmp invshelltemp invsr invshiftround invsn invshortname invsc invshowcmd invsft invshowfulltag invsm invshowmatch invsmd invshowmode invscs invsmartcase invsi invsmartindent invsta invsmarttab invsms invsmoothscroll invspell invsb invsplitbelow invspr invsplitright invsol invstartofline invswf invswapfile invtbs invtagbsearch invtr invtagrelative invtsc invtagsecure invtgst invtagstack invtbidi invtermbidi invtgc invtermguicolors invtsy invtermsync invterse invta invtextauto +syn keyword vimOption contained invtx invtextmode invtop invtildeop invto invtimeout invtitle invttimeout invtbi invttybuiltin invtf invttyfast invudf invundofile invvb invvisualbell invwarn invwiv invweirdinvert invwic invwildignorecase invwmnu invwildmenu invwfb invwinfixbuf invwfh invwinfixheight invwfw invwinfixwidth invwrap invws invwrapscan invwrite invwa invwriteany invwb invwritebackup invxtermcodes " termcap codes (which can also be set) {{{2 " GEN_SYN_VIM: vimOption term output code, START_STR='syn keyword vimOption contained', END_STR='skipwhite nextgroup=vimSetEqual,vimSetMod' syn keyword vimOption contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo t_BS t_ES skipwhite nextgroup=vimSetEqual,vimSetMod @@ -109,9 +109,9 @@ syn keyword vimOptionVarName contained efm errorformat ek esckeys ei eventignore syn keyword vimOptionVarName contained hi history hk hkmap hkp hkmapp hls hlsearch icon iconstring ic ignorecase imaf imactivatefunc imak imactivatekey imc imcmdline imd imdisable imi iminsert ims imsearch imsf imstatusfunc imst imstyle inc include inex includeexpr is incsearch inde indentexpr indk indentkeys inf infercase im insertmode isf isfname isi isident isk iskeyword isp isprint js joinspaces jop jumpoptions key kmp keymap km keymodel kpc keyprotocol kp keywordprg lmap langmap lm langmenu lnr langnoremap lrm langremap ls laststatus lz lazyredraw lhi lhistory lbr linebreak lines lsp linespace lisp lop lispoptions lw lispwords list lcs listchars lpl loadplugins luadll magic mef makeef menc makeencoding mp makeprg mps matchpairs mat matchtime mco maxcombine syn keyword vimOptionVarName contained mfd maxfuncdepth mmd maxmapdepth mm maxmem mmp maxmempattern mmt maxmemtot msc maxsearchcount mis menuitems mopt messagesopt msm mkspellmem ml modeline mle modelineexpr mls modelines mlst modelinestrict ma modifiable mod modified more mouse mousef mousefocus mh mousehide mousem mousemodel mousemev mousemoveevent mouses mouseshape mouset mousetime mzq mzquantum mzschemedll mzschemegcdll nf nrformats nu number nuw numberwidth ofu omnifunc odev opendevice opfunc operatorfunc ost osctimeoutlen pp packpath para paragraphs paste pt pastetoggle pex patchexpr pm patchmode pa path perldll pi preserveindent pvh previewheight pvp previewpopup pvw previewwindow pdev printdevice penc printencoding pexpr printexpr pfn printfont pheader printheader syn keyword vimOptionVarName contained pmbcs printmbcharset pmbfn printmbfont popt printoptions prompt pb pumborder ph pumheight pmw pummaxwidth pumopt pw pumwidth pythondll pythonhome pythonthreedll pythonthreehome pyx pyxversion qftf quickfixtextfunc qe quoteescape ro readonly rdt redrawtime re regexpengine rnu relativenumber remap rop renderoptions report rs restorescreen ri revins rl rightleft rlc rightleftcmd rubydll ru ruler ruf rulerformat rtp runtimepath scr scroll scb scrollbind scf scrollfocus sj scrolljump so scrolloff sop scrolloffpad sbo scrollopt sect sections secure sel selection slm selectmode ssop sessionoptions sh shell shcf shellcmdflag sp shellpipe shq shellquote srr shellredir ssl shellslash stmp shelltemp st shelltype sxe shellxescape sxq shellxquote -syn keyword vimOptionVarName contained sr shiftround sw shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tgst tagstack -syn keyword vimOptionVarName contained tcldll term tbidi termbidi tenc termencoding tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm wig wildignore -syn keyword vimOptionVarName contained wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes +syn keyword vimOptionVarName contained sr shiftround sw shiftwidth shm shortmess sn shortname sbr showbreak sc showcmd sloc showcmdloc sft showfulltag sm showmatch smd showmode stal showtabline stpl showtabpanel ss sidescroll siso sidescrolloff scl signcolumn scs smartcase si smartindent sta smarttab sms smoothscroll sts softtabstop spell spc spellcapcheck spf spellfile spl spelllang spo spelloptions sps spellsuggest sb splitbelow spk splitkeep spr splitright sol startofline stl statusline stlo statuslineopt su suffixes sua suffixesadd swf swapfile sws swapsync swb switchbuf smc synmaxcol syn syntax tcl tabclose tal tabline tpm tabpagemax tpl tabpanel tplo tabpanelopt ts tabstop tbs tagbsearch tc tagcase tfu tagfunc tl taglength tr tagrelative tag tags tsc tagsecure +syn keyword vimOptionVarName contained tgst tagstack tcldll term tbidi termbidi tenc termencoding tgc termguicolors trz termresize tsy termsync twk termwinkey twsl termwinscroll tws termwinsize twt termwintype terse ta textauto tx textmode tw textwidth tsr thesaurus tsrfu thesaurusfunc top tildeop to timeout tm timeoutlen title titlelen titleold titlestring tb toolbar tbis toolbariconsize ttimeout ttm ttimeoutlen tbi ttybuiltin tf ttyfast ttym ttymouse tsl ttyscroll tty ttytype udir undodir udf undofile ul undolevels ur undoreload uc updatecount ut updatetime vsts varsofttabstop vts vartabstop vbs verbose vfile verbosefile vdir viewdir vop viewoptions vi viminfo vif viminfofile ve virtualedit vb visualbell warn wiv weirdinvert ww whichwrap wc wildchar wcm wildcharm +syn keyword vimOptionVarName contained wig wildignore wic wildignorecase wmnu wildmenu wim wildmode wop wildoptions wak winaltkeys wcr wincolor wi window wfb winfixbuf wfh winfixheight wfw winfixwidth wh winheight whl winhighlight wmh winminheight wmw winminwidth winptydll wiw winwidth wse wlseat wtm wltimeoutlen wrap wm wrapmargin ws wrapscan write wa writeany wb writebackup wd writedelay xtermcodes " GEN_SYN_VIM: vimOption term output code variable, START_STR='syn keyword vimOptionVarName contained', END_STR='' syn keyword vimOptionVarName contained t_AB t_AF t_AU t_AL t_al t_bc t_BE t_BD t_cd t_ce t_Ce t_CF t_cl t_cm t_Co t_CS t_Cs t_cs t_CV t_da t_db t_DL t_dl t_ds t_Ds t_EC t_EI t_fs t_fd t_fe t_GP t_IE t_IS t_ke t_ks t_le t_mb t_md t_me t_mr t_ms t_nd t_op t_RF t_RB t_RC t_RI t_Ri t_RK t_RS t_RT t_RV t_Sb t_SC t_se t_Sf t_SH t_SI t_Si t_so t_SR t_sr t_ST t_Te t_te t_TE t_ti t_TI t_Ts t_ts t_u7 t_ue t_us t_Us t_ut t_vb t_ve t_vi t_VS t_vs t_WP t_WS t_XM t_xn t_xs t_ZH t_ZR t_8f t_8b t_8u t_xo t_BS t_ES syn keyword vimOptionVarName contained t_F1 t_F2 t_F3 t_F4 t_F5 t_F6 t_F7 t_F8 t_F9 t_k1 t_K1 t_k2 t_k3 t_K3 t_k4 t_K4 t_k5 t_K5 t_k6 t_K6 t_k7 t_K7 t_k8 t_K8 t_k9 t_K9 t_KA t_kb t_kB t_KB t_KC t_kd t_kD t_KD t_KE t_KF t_KG t_kh t_KH t_kI t_KI t_KJ t_KK t_kl t_KL t_kN t_kP t_kr t_ku diff --git a/src/option.h b/src/option.h index a4359a2590..c3df71fcaf 100644 --- a/src/option.h +++ b/src/option.h @@ -1030,6 +1030,7 @@ EXTERN unsigned tc_flags; // flags from 'tagcase' EXTERN long p_tl; // 'taglength' EXTERN int p_tr; // 'tagrelative' EXTERN char_u *p_tags; // 'tags' +EXTERN int p_tagsecure; // 'tagsecure' EXTERN int p_tgst; // 'tagstack' #if defined(DYNAMIC_TCL) EXTERN char_u *p_tcldll; // 'tcldll' diff --git a/src/optiondefs.h b/src/optiondefs.h index af556569e3..f238209a75 100644 --- a/src/optiondefs.h +++ b/src/optiondefs.h @@ -2638,6 +2638,9 @@ static struct vimoption options[] = (char_u *)"./tags,tags", #endif (char_u *)0L} SCTX_INIT}, + {"tagsecure", "tsc", P_BOOL|P_VI_DEF|P_SECURE, + (char_u *)&p_tagsecure, PV_NONE, NULL, NULL, + {(char_u *)TRUE, (char_u *)TRUE} SCTX_INIT}, {"tagstack", "tgst", P_BOOL|P_VI_DEF, (char_u *)&p_tgst, PV_NONE, NULL, NULL, {(char_u *)TRUE, (char_u *)0L} SCTX_INIT}, diff --git a/src/po/vim.pot b/src/po/vim.pot index 3c07464955..f1369d68ae 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-05-15 16:34+0000\n" +"POT-Creation-Date: 2026-05-17 19:50+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -9349,6 +9349,9 @@ msgstr "" msgid "file names in a tags file are relative to the tags file" msgstr "" +msgid "a :tag command cannot access remote files" +msgstr "" + msgid "a :tag command will use the tagstack" msgstr "" diff --git a/src/tag.c b/src/tag.c index 6861a44cf5..24d33bc11d 100644 --- a/src/tag.c +++ b/src/tag.c @@ -4135,10 +4135,10 @@ expand_tag_fname(char_u *fname, char_u *tag_fname, int expand) char_u *expanded_fname = NULL; expand_T xpc; - // Refuse to follow URLs from tag files. Tag entries are expected - // to reference local source files; a URL would otherwise be passed - // to netrw and trigger a network request. - if (path_with_url(fname)) + // Refuse to follow URLs from tag files unless 'tagsecure' is false. + // Tag entries are expected to reference local source files; a URL would + // otherwise be passed to netrw and trigger a network request. + if (p_tagsecure && path_with_url(fname)) { emsg(_(e_tag_file_entry_must_not_be_url)); return NULL; diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim index 340ca36419..d9ca39634a 100644 --- a/src/testdir/test_options.vim +++ b/src/testdir/test_options.vim @@ -318,7 +318,7 @@ func Test_set_completion() " Expand abbreviation of options. call feedkeys(":set ts\\\"\", 'tx') - call assert_equal('"set tabstop termsync thesaurus thesaurusfunc ttyscroll', @:) + call assert_equal('"set tabstop tagsecure termsync thesaurus thesaurusfunc ttyscroll', @:) " Expand current value call feedkeys(":set suffixes=\\\"\", 'tx') @@ -1696,7 +1696,7 @@ endfunc " Test for changing options in a sandbox func Test_opt_sandbox() - for opt in ['backupdir', 'cdpath', 'exrc', 'findfunc'] + for opt in ['backupdir', 'cdpath', 'exrc', 'findfunc', 'tagsecure'] call assert_fails('sandbox set ' .. opt .. '?', 'E48:') call assert_fails('sandbox let &' .. opt .. ' = 1', 'E48:') endfor diff --git a/src/testdir/test_tagjump.vim b/src/testdir/test_tagjump.vim index d0e18002bb..7df75cff5c 100644 --- a/src/testdir/test_tagjump.vim +++ b/src/testdir/test_tagjump.vim @@ -1719,10 +1719,23 @@ func Test_tagjump_refuse_url() call writefile([ \ "XTagURL\thttp://127.0.0.1:1/$XTAG_SECRET/file.c\t/^int main" \ ], 'Xtags', 'D') + let save_tagsecure = &tagsecure let save_tags = &tags set tags=Xtags + " E1576: Tag file entry must not be a URL + set tagsecure call assert_fails('tag XTagURL', 'E1576:') + set tsc + call assert_fails('tag XTagURL', 'E1576:') + + " E429: File does not exist + set notagsecure + call assert_fails('tag XTagURL', 'E429:') + set notsc + call assert_fails('tag XTagURL', 'E429:') + + let &tagsecure = save_tagsecure let &tags = save_tags endfunc diff --git a/src/version.c b/src/version.c index 2e76cbe480..2eaaa45624 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 497, /**/ 496, /**/ From c920d9344368a1506be0bbff116ff9d4cd96476f Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Sun, 17 May 2026 20:42:20 +0000 Subject: [PATCH 253/545] patch 9.2.0498: potential heap buffer overflow in if_xcmdsrv.c Problem: potential heap buffer overflow in if_xcmdsrv.c server_parse_message() (Michael Bommarito) Solution: Add strlen() call (Foxe Chen) fixes: #20235 closes: #20236 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/if_xcmdsrv.c | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/if_xcmdsrv.c b/src/if_xcmdsrv.c index 070e0db099..43e1e34070 100644 --- a/src/if_xcmdsrv.c +++ b/src/if_xcmdsrv.c @@ -1333,7 +1333,7 @@ server_parse_message( // Initialize the result property. ga_init2(&reply, 1, 100); - (void)ga_grow(&reply, 50 + STRLEN(p_enc)); + (void)ga_grow(&reply, 50 + STRLEN(p_enc) + STRLEN(serial)); sprintf(reply.ga_data, "%cr%c-E %s%c-s %s%c-r ", 0, 0, p_enc, 0, serial, 0); reply.ga_len = 14 + STRLEN(p_enc) + STRLEN(serial); diff --git a/src/version.c b/src/version.c index 2eaaa45624..f348ffc9ef 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 498, /**/ 497, /**/ From 4397227119e3ebaa879552dcbcb18338d18f4850 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 18 May 2026 18:59:03 +0000 Subject: [PATCH 254/545] patch 9.2.0499: modeline: allow to disable modelines with modelinestrict Problem: Cannot disable modeline processing when loading a file (Mao-Yining, after v9.2.0350) Solution: Allow to disable modeline processing even when 'modelienstrict' is in effect. fixes: #20103 closes: #20229 Signed-off-by: Christian Brabandt Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 7 ++++- src/option.c | 20 ++++++++++---- src/testdir/test_modeline.vim | 52 +++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 4 files changed, 75 insertions(+), 6 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 2ba59824f6..b7d74e9f8c 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 May 17 +*options.txt* For Vim version 9.2. Last change: 2026 May 18 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6239,6 +6239,11 @@ A jump table for the options with a short description can be found at |Q_op|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. + As an exception, `set nomodeline` is honored from within a modeline + even when 'modelinestrict' is on. Other forms (`set modeline=0`, + `set modeline!`, `set invmodeline`) are still silently ignored. + This lets a file disable further modeline processing for itself. + The behaviour of 'modeline', 'modelinestrict' and 'modelineexpr' is as follows: diff --git a/src/option.c b/src/option.c index 31f84c98d1..d15cc3f3c5 100644 --- a/src/option.c +++ b/src/option.c @@ -1589,7 +1589,12 @@ is_modeline_whitelisted(char *name) * if it can be changed. */ static int -validate_opt_idx(int opt_idx, int opt_flags, long_u flags, char **errmsg) +validate_opt_idx( + int opt_idx, + int opt_flags, + long_u flags, + char **errmsg, + set_prefix_T prefix) { // Skip all options that are not window-local (used when showing // an already loaded buffer in a window). @@ -1617,9 +1622,14 @@ validate_opt_idx(int opt_idx, int opt_flags, long_u flags, char **errmsg) } // When 'modelinestrict' is on, only whitelisted options may be // set from a modeline. Silently skip others. - if (p_mlstr && opt_idx >= 0 - && !is_modeline_whitelisted(options[opt_idx].fullname)) - return FAIL; + if (p_mlstr && opt_idx >= 0) + { + // special case: allow disabling modeline + if (options[opt_idx].indir == PV_ML && prefix == PREFIX_NO) + return OK; + else if (!is_modeline_whitelisted(options[opt_idx].fullname)) + return FAIL; + } #ifdef FEAT_DIFF // In diff mode some options are overruled. This avoids that // 'foldmethod' becomes "marker" instead of "diff" and that @@ -2864,7 +2874,7 @@ do_set_option( } // Make sure the option value can be changed. - if (validate_opt_idx(opt_idx, opt_flags, flags, &errmsg) == FAIL) + if (validate_opt_idx(opt_idx, opt_flags, flags, &errmsg, prefix) == FAIL) goto skip; int cp_val = p_cp; diff --git a/src/testdir/test_modeline.vim b/src/testdir/test_modeline.vim index cb489ee15f..c436cbc270 100644 --- a/src/testdir/test_modeline.vim +++ b/src/testdir/test_modeline.vim @@ -666,4 +666,56 @@ func Test_modeline_strict_cannot_be_set_from_modeline() let &modeline = modeline endfunc +func Test_modeline_nomodeline_with_modelinestrict() + let modeline = &modeline + let modelinestrict = &modelinestrict + let modelines = &modelines + set modelinestrict modeline modelines=5 + let ml_before = &g:modeline + + call writefile(['# vim: set nomodeline:', 'line2'], 'Xnoml', 'D') + split Xnoml + call assert_equal(0, &l:modeline, 'b_p_ml should be off') + call assert_equal(ml_before, &g:modeline, 'global p_ml must not change') + bwipe! + + " A fresh buffer must still inherit the unchanged global default + new + call assert_equal(ml_before, &l:modeline, + \ 'new buffer should inherit unchanged global') + bwipe! + + let &modeline = modeline + let &modelinestrict = modelinestrict + let &modelines = modelines +endfunc + +func Test_modeline_nomodeline_skips_trailing_modelines() + let modeline = &modeline + let modelinestrict = &modelinestrict + let ts_save = &ts + set modeline modelinestrict ts=8 + + " Line 1 disables modelines; the trailing modeline must therefore + " never execute even though 'tabstop' is whitelisted. + call writefile([ + \ '# vim: set nomodeline :', + \ 'middle line 1', + \ 'middle line 2', + \ 'middle line 3', + \ '# vim: set ts=99 :', + \ ], 'Xmodeline_disable_top', 'D') + split Xmodeline_disable_top + + call assert_equal(0, &l:modeline, + \ 'top modeline must have disabled b_p_ml') + call assert_equal(8, &ts, + \ 'trailing modeline must not have run after nomodeline') + + bwipe! + let &modeline = modeline + let &modelinestrict = modelinestrict + let &ts = ts_save +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index f348ffc9ef..4ce6175650 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 499, /**/ 498, /**/ From e543abbf8e764d701575428461126129476e93c1 Mon Sep 17 00:00:00 2001 From: Aliaksei Budavei <0x000c70@gmail.com> Date: Mon, 18 May 2026 19:07:08 +0000 Subject: [PATCH 255/545] tests(commondumps): Use character counts when marking columns Continue using "strwidth" when calculating the position of a column of interest, and start using "setcharpos" when marking a line of interest so that paired-up marks remain aligned columnwise across all three parts generated by "term_dumpdiff", especially when multibyte characters are written in the line before the marked column. closes: #20237 Signed-off-by: Aliaksei Budavei <0x000c70@gmail.com> Signed-off-by: Christian Brabandt --- src/testdir/commondumps.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/testdir/commondumps.vim b/src/testdir/commondumps.vim index 27d59bcdd1..40206041bd 100644 --- a/src/testdir/commondumps.vim +++ b/src/testdir/commondumps.vim @@ -10,7 +10,7 @@ enddef def TryChangingLastJumpMark(marks: dict>) const pos: list = get(marks, line('.'), []) if !empty(pos) - setpos("'`", pos) + setcharpos("'`", pos) endif enddef @@ -56,7 +56,7 @@ def FoldAndMarkDumpDiffParts(letters: list) var marks: dict> = {} for idx in range(parts[1]->len()) if !empty(letters) - setpos(("'" .. remove(letters, 0)), parts[1][idx]) + setcharpos(("'" .. remove(letters, 0)), parts[1][idx]) endif # Point "bs" to "cs", "cs" to "as", "as" to "cs". marks[parts[1][idx][1]] = parts[2][idx] From 5c496beb188989f702d94b7a1f1d024a192388a0 Mon Sep 17 00:00:00 2001 From: nagbu Date: Mon, 18 May 2026 20:36:07 +0000 Subject: [PATCH 256/545] runtime(typeset): Correct whitespace error in :lcd command closes: #20242 closes: #20244 Signed-off-by: nagbu Signed-off-by: Christian Brabandt --- runtime/autoload/typeset.vim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/autoload/typeset.vim b/runtime/autoload/typeset.vim index 3280cb4efd..11974f1721 100644 --- a/runtime/autoload/typeset.vim +++ b/runtime/autoload/typeset.vim @@ -5,6 +5,7 @@ vim9script # Latest Revision: 2026 Feb 19 # Last Change: # 2026 Mar 30 by Vim project: Use fnameescape for the ProcessOutput command +# 2026 May 19 by Vim project: missing space in :lcd command #20244 # Constants and helpers {{{ const SLASH = !exists("+shellslash") || &shellslash ? '/' : '\' @@ -62,7 +63,7 @@ def ProcessOutput(qfid: number, wd: string, efm: string, ch: channel, msg: strin endif # Make sure the working directory is correct - silent execute "lcd" .. fnameescape(wd) + silent execute "lcd" fnameescape(wd) setqflist([], 'a', {'id': qfid, 'lines': [msg], 'efm': efm}) silent lcd - enddef From 6daa721dc002437a91323f05d04ac2165e854d7b Mon Sep 17 00:00:00 2001 From: tecis <67809811+tecis@users.noreply.github.com> Date: Mon, 18 May 2026 20:40:37 +0000 Subject: [PATCH 257/545] runtime(doc): INSTALL: "libwayland-dev" is required on Ubuntu for Wayland clipboard support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In GNU/Linux distros without X11 and only depending on the Wayland Composer as a display server I could not find official documentation on how to compile VIM with clipbloard support. > This will become more relevant as common distros ship with Wayland only environments. (sources & articles bellow) Sources. - [Gnome Blog: X11 Session Removal FAQ.](https://blogs.gnome.org/alatiera/2025/06/23/x11-session-removal-faq/) - [KDE Blog: Going all-in on a Wayland future 2025-11-26.](https://blogs.kde.org/2025/11/26/going-all-in-on-a-wayland-future/) - [Gnome Blog: An update on the X11 GNOME Session Removal 2025-06-08.](https://blogs.gnome.org/alatiera/2025/06/08/the-x11-session-removal/) Articles. - [It’s Foss: No More Xorg! Fedora 43 Will Be Wayland-only 2025-05-21](https://itsfoss.com/news/fedora-43-wayland-only/) - [Desde Linux: The depreciation of X11 in Fedora continues and in Fedora 41 the Gnome session in X11 will disappear.](https://blog.desdelinux.net/en/The-depreciation-of-x11-in-fedora-continues-and-in-fedora-41-the-gnome-session-in-x11-will-disappear/) - [How-To-Geek: The writing is on the wall for X11 on Linux, here's what is replacing it 2025-12-29.](https://www.howtogeek.com/x11-is-going-away-on-linux-heres-how-to-prepare/). - [Dev.To: GNOME 50 Removes X11: What It Means for Linux Users 2026-03-16.](https://dev.to/the_nazar/gnome-50-removes-x11-what-it-means-for-linux-users-1ike) - [LinuxSecurity.com: GNOME 50: Wayland-Only Brings Enhanced Security and Isolation](https://linuxsecurity.com/news/desktop-security/gnome-50-wayland-linux-security) - [XDA Developers: KDE is finally killing X11, and I'm not as sad as I thought I'd be 2026-05-14](https://www.xda-developers.com/kde-dropping-x11-support/). closes: #20245 Signed-off-by: tecis <67809811+tecis@users.noreply.github.com> Signed-off-by: Christian Brabandt --- src/INSTALL | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/INSTALL b/src/INSTALL index ab760cd13a..083780cc82 100644 --- a/src/INSTALL +++ b/src/INSTALL @@ -61,6 +61,10 @@ To build Vim on Ubuntu from scratch on a clean system using git: % sudo apt install libxt-dev % make reconfig + Add Wayland clipboard support: + % sudo apt install libwayland-dev + % make reconfig + Add GUI support: % sudo apt install libgtk-3-dev % make reconfig From 354ab1a69efbe5f84cd7225bc3945db3f2cec5c4 Mon Sep 17 00:00:00 2001 From: truffle Date: Mon, 18 May 2026 20:46:24 +0000 Subject: [PATCH 258/545] patch 9.2.0500: filetype: some html files wrongly recognized as htmlangular Problem: filetype: some html files are wrongly recognized as htmlangular Solution: Use the \< atom to anchor ng-template and ng-content to start of word (truffle) Prevent false-positive htmlangular detection on words containing 'ng-template' or 'ng-content' as a substring (e.g. 'song-template', 'sing-content'). Anchor both branches with \< to require a word start, matching the \ Signed-off-by: Christian Brabandt --- runtime/autoload/dist/ft.vim | 4 ++-- src/testdir/test_filetype.vim | 21 +++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 3085f3575c..c2f3e2afd0 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -3,7 +3,7 @@ vim9script # Vim functions for file type detection # # Maintainer: The Vim Project -# Last Change: 2026 May 16 +# Last Change: 2026 May 18 # Former Maintainer: Bram Moolenaar # These functions are moved here from runtime/filetype.vim to make startup @@ -547,7 +547,7 @@ export def FThtml() while n < 40 && n <= line("$") # Check for Angular - if getline(n) =~ '@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content' + if getline(n) =~ '@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|\{{ foo }}'] + call writefile(content, 'Xfile.html', 'D') + split Xfile.html + call assert_equal('htmlangular', &filetype) + bwipe! + + " HTML Angular ng-content element + let content = ['

'] + call writefile(content, 'Xfile.html', 'D') + split Xfile.html + call assert_equal('htmlangular', &filetype) + bwipe! + + " Word containing 'ng-template' as a suffix must not trigger htmlangular + let content = ['
', '

Not Angular

', '
'] + call writefile(content, 'Xfile.html', 'D') + split Xfile.html + call assert_equal('html', &filetype) + bwipe! + " Django Template let content = ['{% if foobar %}', \ '
    ', diff --git a/src/version.c b/src/version.c index 4ce6175650..bb0b3d4f53 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 500, /**/ 499, /**/ From 9b1d657bca5838935a4b595b849683e24545f8f3 Mon Sep 17 00:00:00 2001 From: "K.Takata" Date: Mon, 18 May 2026 21:19:27 +0000 Subject: [PATCH 259/545] CI: Stop using AppVeyor We started the Windows CI with AppVeyor (v7.4.872), but nowadays, GitHub Actions supports Windows, and the performance is better than AppVeyor. Especially, AppVeyor doesn't allow running multiple jobs in parallel. Before 9.0.0529, we used VC 2010 on AppVeyor to ensure that C99 syntax was not used. Now we use some C99 syntax. Also, MSVC's C99 support level remains almost the same since VS 2015. So, using VS 2015 isn't very useful. closes: #20251 Signed-off-by: K.Takata Signed-off-by: Christian Brabandt --- .appveyor.yml | 52 ------------------ .github/labeler.yml | 1 - Filelist | 3 - README.md | 1 - ci/appveyor.bat | 131 -------------------------------------------- 5 files changed, 188 deletions(-) delete mode 100644 .appveyor.yml delete mode 100644 ci/appveyor.bat diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 35da109210..0000000000 --- a/.appveyor.yml +++ /dev/null @@ -1,52 +0,0 @@ -version: "{build}" - -image: Visual Studio 2015 - -skip_tags: true - -environment: - matrix: - - FEATURE: HUGE - - # Alternate environments, not used right now. 2022 is a lot slower. - # - # - job_name: VS-2015 - # appveyor_build_worker_image: Visual Studio 2015 - # FEATURE: HUGE - - # - job_name: VS-2017 - # appveyor_build_worker_image: Visual Studio 2017 - # FEATURE: HUGE - - # - job_name: VS-2019 - # appveyor_build_worker_image: Visual Studio 2019 - # FEATURE: HUGE - - # - job_name: VS-2022 - # appveyor_build_worker_image: Visual Studio 2022 - # FEATURE: HUGE - -# disabled -# - FEATURE: TINY -# - FEATURE: NORMAL - -matrix: - fast_finish: true - -before_build: - - call ver - - ci\appveyor.bat install - -build_script: - - ci\appveyor.bat build - -test_script: - - ci\appveyor.bat test - -artifacts: - - path: src/vim.exe - name: vim - - path: src/gvim.exe - name: gvim - -# vim: sw=2 sts=2 et ts=8 sr diff --git a/.github/labeler.yml b/.github/labeler.yml index 720765d383..157f791e70 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -8,7 +8,6 @@ CI: - '.github/dependabot.yml' - '.github/labeler.yml' - '.github/workflows/*' - - '.appveyor.yml' - '.codecov.yml' documentation: diff --git a/Filelist b/Filelist index 13e0f85f64..ceb06739b9 100644 --- a/Filelist +++ b/Filelist @@ -18,11 +18,9 @@ SRC_ALL = \ .github/dependabot.yml \ .gitignore \ .hgignore \ - .appveyor.yml \ .clang-format \ .codecov.yml \ .editorconfig \ - ci/appveyor.bat \ ci/config.mk*.sed \ ci/gen_testset.py \ ci/if_ver*.vim \ @@ -1195,7 +1193,6 @@ LANG_DOS = \ # be excluded from distribution tarballs and the like. # This excludes them from the CI check for unlisted files. IGNORE = \ - .appveyor.yml \ .github/FUNDING.yml \ .github/labeler.yml \ .github/workflows/label.yml \ diff --git a/README.md b/README.md index 949fcd7b4f..3e57f0e8c6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # [![Vim The editor](https://github.com/vim/vim/raw/master/runtime/vimlogo.gif)](https://www.vim.org) [![Github Build status](https://github.com/vim/vim/workflows/GitHub%20CI/badge.svg)](https://github.com/vim/vim/actions?query=workflow%3A%22GitHub+CI%22) -[![Appveyor Build status](https://ci.appveyor.com/api/projects/status/o2qht2kjm02sgghk?svg=true)](https://ci.appveyor.com/project/chrisbra/vim) [![Cirrus Build Status](https://api.cirrus-ci.com/github/vim/vim.svg)](https://cirrus-ci.com/github/vim/vim) [![Coverage Status](https://codecov.io/gh/vim/vim/coverage.svg?branch=master)](https://codecov.io/gh/vim/vim?branch=master) [![Coverity Scan](https://scan.coverity.com/projects/241/badge.svg)](https://scan.coverity.com/projects/vim) diff --git a/ci/appveyor.bat b/ci/appveyor.bat deleted file mode 100644 index 17d95ff691..0000000000 --- a/ci/appveyor.bat +++ /dev/null @@ -1,131 +0,0 @@ -@echo off -:: Batch file for building/testing Vim on AppVeyor -set target=%1 -set "GETTEXT_PATH=c:\gettext64\bin" - -setlocal ENABLEDELAYEDEXPANSION -cd %APPVEYOR_BUILD_FOLDER% - -:: Python3 -set "PYTHON3_VER=311" -set "PYTHON3_RELEASE=3.11.1" -set "PYTHON3_URL=https://www.python.org/ftp/python/%PYTHON3_RELEASE%/python-%PYTHON3_RELEASE%-amd64.exe" -set "PYTHON3_DIR=C:\python%PYTHON3_VER%-x64" - -:: Gettext-tools, iconv and libraries -set "GETTEXT64_URL=https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.21-v1.16/gettext0.21-iconv1.16-shared-64.zip" -set "GETTEXT64_DIR=c:\gettext64" - -set "VSWHERE=%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" - -if exist "%VSWHERE%" ( - for /f "usebackq delims=" %%i in ( - `"%VSWHERE%" -products * -latest -property installationPath` - ) do ( - set "VCVARSALL=%%i\VC\Auxiliary\Build\vcvarsall.bat" - ) -) - -if not exist "%VCVARSALL%" ( - set "VCVARSALL=%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" -) -call "%VCVARSALL%" x64 - -goto %target% -echo Unknown build target. -exit 1 - -:: ---------------------------------------------------------------------------- -:install -@echo on -if not exist downloads mkdir downloads - -:: Python 3 -if not exist %PYTHON3_DIR% ( - call :downloadfile %PYTHON3_URL% downloads\python3.exe - cmd /c start /wait downloads\python3.exe /quiet TargetDir=%PYTHON3_DIR% ^ - Include_pip=0 Include_tcltk=0 Include_test=0 Include_tools=0 ^ - AssociateFiles=0 Shortcuts=0 Include_doc=0 Include_launcher=0 ^ - InstallLauncherAllUsers=0 -) -:: GETTEXT -if not exist %GETTEXT64_DIR% ( - mkdir %GETTEXT64_DIR% - call :downloadfile %GETTEXT64_URL% downloads\gettext64.zip - cmd /c powershell.exe -NoLogo -NoProfile -Command ^ - Add-Type -AssemblyName 'System.IO.Compression.FileSystem'; ^ - [System.IO.Compression.ZipFile]::ExtractToDirectory^('downloads\gettext64.zip', ^ - '%GETTEXT64_DIR%'^) - copy /y %GETTEXT64_DIR%\bin\libintl-8.dll C:\projects\vim\src\ || exit 1 - copy /y %GETTEXT64_DIR%\bin\libiconv-2.dll C:\projects\vim\src\ || exit 1 -) - -@echo off -goto :eof - -:: ---------------------------------------------------------------------------- -:build - -cd src - -echo "Building MSVC 64bit console Version" -nmake -f Make_mvc.mak CPU=AMD64 ^ - OLE=no GUI=no IME=yes ICONV=yes DEBUG=no ^ - FEATURES=%FEATURE% CI_CFLAGS=/we4267 -if not exist vim.exe ( - echo Build failure. - exit 1 -) - -:: build MSVC huge version with python and channel support -:: GUI needs to be last, so that testing works -echo "Building MSVC 64bit GUI Version" -if "%FEATURE%" == "HUGE" ( - nmake -f Make_mvc.mak CPU=AMD64 ^ - OLE=no GUI=yes IME=yes ICONV=yes DEBUG=no POSTSCRIPT=yes ^ - PYTHON_VER=27 DYNAMIC_PYTHON=yes PYTHON=C:\Python27-x64 ^ - PYTHON3_VER=%PYTHON3_VER% DYNAMIC_PYTHON3=yes PYTHON3=%PYTHON3_DIR% ^ - FEATURES=%FEATURE% CI_CFLAGS=/we4267 -) ELSE ( - nmake -f Make_mvc.mak CPU=AMD64 ^ - OLE=no GUI=yes IME=yes ICONV=yes DEBUG=no ^ - FEATURES=%FEATURE% CI_CFLAGS=/we4267 -) -if not exist gvim.exe ( - echo Build failure. - exit 1 -) -.\gvim -u NONE -c "redir @a | ver |0put a | wq" ver_msvc.txt || exit 1 - -echo "version output MSVC console" -.\vim --version || exit 1 -echo "version output MSVC GUI" -type ver_msvc.txt || exit 1 - -goto :eof - -:: ---------------------------------------------------------------------------- -:test -@echo on -cd src/testdir -:: Testing with MSVC gvim -path %PYTHON3_DIR%;%GETTEXT_PATH%;%PATH% -nmake -f Make_mvc.mak VIMPROG=..\gvim -nmake -f Make_mvc.mak clean -:: Testing with MSVC console version -nmake -f Make_mvc.mak VIMPROG=..\vim - -@echo off -goto :eof - -:: ---------------------------------------------------------------------------- -:downloadfile -:: call :downloadfile -if not exist %2 ( - curl -f -L %1 -o %2 -) -if ERRORLEVEL 1 ( - rem Retry once. - curl -f -L %1 -o %2 || exit 1 -) -@goto :eof From d004cc4f893e5999b5a0a28d04c57bd932c55d5c Mon Sep 17 00:00:00 2001 From: tecis <67809811+tecis@users.noreply.github.com> Date: Mon, 18 May 2026 21:46:24 +0000 Subject: [PATCH 260/545] runtime(htmldjango): Remove unnecessary code. I submitted the PR #20232 to resolve an undesired behavior in with the highlighter inheriting from "django.vim" and "html.vim". After further testing I noticed the re-declaration of `djangoOperators` in "htmldjango" is not necessary, and my conclusions where a mistake from a not-clean test environment. This PR reverses the effect of the commit #f03155a. related: #20232 closes: #20248 Signed-off-by: tecis <67809811+tecis@users.noreply.github.com> Signed-off-by: Christian Brabandt --- runtime/syntax/htmldjango.vim | 5 ----- 1 file changed, 5 deletions(-) diff --git a/runtime/syntax/htmldjango.vim b/runtime/syntax/htmldjango.vim index bffb11aa64..07b1c375e8 100644 --- a/runtime/syntax/htmldjango.vim +++ b/runtime/syntax/htmldjango.vim @@ -2,7 +2,6 @@ " Language: Django HTML template " Maintainer: Dave Hodder " Last Change: 2014 Jul 13 -" 2026 May 17 by Vim Project Add highlighting for comparison operators #20232 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -24,8 +23,4 @@ syn region djangoVarBlock start="{{" end="}}" contains=djangoFilter,djangoArgume syn region djangoComment start="{%\s*comment\(\s\+.\{-}\)\?%}" end="{%\s*endcomment\s*%}" contains=djangoTodo containedin=ALLBUT,@djangoBlocks syn region djangoComBlock start="{#" end="#}" contains=djangoTodo containedin=ALLBUT,@djangoBlocks -" Use djangoTagBlockNaive to limit the djangoOperator matched characters to avoid spill-over with HTML, JS and CSS. -syn region djangoTagBlockNaive start="{%" end="%}" contains=djangoTagBlock,djangoVarBlock,djangoComment,djangoComBlock -syn match djangoOperator "==\|!=\|<=\|>=\|<\|>" contained containedin=CONTAINS,@djangoTagBlockNaive - let b:current_syntax = "htmldjango" From da5ebe71cbf56a89796000fa1aa92f6a76acf4c7 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Tue, 19 May 2026 18:13:27 +0000 Subject: [PATCH 261/545] patch 9.2.0501: GTK4: there is no GTK4 UI available Problem: GTK4: there is no GTK4 UI available Solution: Implement GTK4 UI (Yasuhiro Matsumoto). To enable, use the --enable-gui=gtk4 configure switch. Configure currently favors GTK3 over GTK4 if no explicit --enable-gui switch has been given and both libraries are present closes: #19815 Co-authored-by: Claude Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- .github/actions/build_vim_on_linux/action.yml | 1 + Filelist | 4 + src/INSTALL | 6 +- src/Makefile | 29 +- src/auto/configure | 231 +- src/beval.h | 2 +- src/clipboard.c | 5 +- src/config.h.in | 3 + src/configure.ac | 72 +- src/evalfunc.c | 7 + src/feature.h | 16 +- src/getchar.c | 2 +- src/gui.c | 6 + src/gui.h | 22 +- src/gui_beval.c | 18 +- src/gui_gtk4.c | 4638 +++++++++++++++++ src/gui_gtk4_f.c | 324 ++ src/gui_gtk4_f.h | 59 + src/gui_xim.c | 102 +- src/hardcopy.c | 10 + src/po/vim.pot | 13 +- src/proto.h | 8 +- src/proto/gui_gtk4.pro | 110 + src/version.c | 6 +- src/vim.h | 2 +- 25 files changed, 5655 insertions(+), 41 deletions(-) create mode 100644 src/gui_gtk4.c create mode 100644 src/gui_gtk4_f.c create mode 100644 src/gui_gtk4_f.h create mode 100644 src/proto/gui_gtk4.pro diff --git a/.github/actions/build_vim_on_linux/action.yml b/.github/actions/build_vim_on_linux/action.yml index 9a2105164b..c6486cd34f 100644 --- a/.github/actions/build_vim_on_linux/action.yml +++ b/.github/actions/build_vim_on_linux/action.yml @@ -88,6 +88,7 @@ runs: sudo cp ci/pinned-pkgs /etc/apt/preferences.d/pinned-pkgs echo '::endgroup::' + # TODO: switch to GTK4 GUI - name: Install packages shell: bash run: | diff --git a/Filelist b/Filelist index ceb06739b9..dfc828a759 100644 --- a/Filelist +++ b/Filelist @@ -498,6 +498,9 @@ SRC_UNIX = \ src/gui_gtk_f.c \ src/gui_gtk_f.h \ src/gui_gtk_x11.c \ + src/gui_gtk4.c \ + src/gui_gtk4_f.c \ + src/gui_gtk4_f.h \ src/gui_gtk_res.xml \ src/gui_motif.c \ src/gui_xmdlg.c \ @@ -527,6 +530,7 @@ SRC_UNIX = \ src/proto/gui_gtk.pro \ src/proto/gui_gtk_x11.pro \ src/proto/gui_gtk_gresources.pro \ + src/proto/gui_gtk4.pro \ src/proto/gui_motif.pro \ src/proto/gui_xmdlg.pro \ src/proto/gui_x11.pro \ diff --git a/src/INSTALL b/src/INSTALL index 083780cc82..96f32607fc 100644 --- a/src/INSTALL +++ b/src/INSTALL @@ -65,7 +65,11 @@ To build Vim on Ubuntu from scratch on a clean system using git: % sudo apt install libwayland-dev % make reconfig - Add GUI support: + Add GUI (GTK3) support: + % sudo apt install libgtk-3-dev + % make reconfig + + Add GUI (GTK4) support: % sudo apt install libgtk-3-dev % make reconfig diff --git a/src/Makefile b/src/Makefile index 97200a6b07..e08b6cccfd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1232,6 +1232,23 @@ GTK_MAN_TARGETS = yes GTK_TESTTARGET = gui GTK_BUNDLE = +### GTK4 GUI +GTK4_SRC = gui.c gui_gtk4.c gui_gtk4_f.c \ + $(GRESOURCE_SRC) +GTK4_OBJ = objects/gui.o objects/gui_gtk4.o \ + objects/gui_gtk4_f.o \ + $(GRESOURCE_OBJ) +GTK4_DEFS = -DFEAT_GUI_GTK $(NARROW_PROTO) +GTK4_IPATH = $(GUI_INC_LOC) +GTK4_LIBS_DIR = $(GUI_LIB_LOC) +GTK4_LIBS1 = +GTK4_LIBS2 = $(GTK_LIBNAME) +GTK4_INSTALL = install_normal install_gui_extra +GTK4_TARGETS = installglinks +GTK4_MAN_TARGETS = yes +GTK4_TESTTARGET = gui +GTK4_BUNDLE = + ### Motif GUI MOTIF_SRC = gui.c gui_motif.c gui_x11.c gui_beval.c \ gui_xmdlg.c gui_xmebw.c @@ -1289,8 +1306,8 @@ HAIKUGUI_TESTTARGET = gui HAIKUGUI_BUNDLE = # All GUI files -ALL_GUI_SRC = gui.c gui_gtk.c gui_gtk_f.c gui_motif.c gui_xmdlg.c gui_xmebw.c gui_gtk_x11.c gui_x11.c gui_haiku.cc -ALL_GUI_PRO = proto/gui.pro proto/gui_gtk.pro proto/gui_motif.pro proto/gui_xmdlg.pro proto/gui_gtk_x11.pro proto/gui_x11.pro proto/gui_w32.pro proto/gui_photon.pro +ALL_GUI_SRC = gui.c gui_gtk.c gui_gtk_f.c gui_gtk4.c gui_gtk4_f.c gui_motif.c gui_xmdlg.c gui_xmebw.c gui_gtk_x11.c gui_x11.c gui_haiku.cc +ALL_GUI_PRO = proto/gui.pro proto/gui_gtk.pro proto/gui_gtk4.pro proto/gui_motif.pro proto/gui_xmdlg.pro proto/gui_gtk_x11.pro proto/gui_x11.pro proto/gui_w32.pro proto/gui_photon.pro # }}} @@ -3364,6 +3381,13 @@ objects/gui_gtk_gresources.o: auto/gui_gtk_gresources.c objects/gui_gtk_x11.o: gui_gtk_x11.c $(CCC) -o $@ gui_gtk_x11.c +objects/gui_gtk4.o: gui_gtk4.c + $(CCC) -o $@ gui_gtk4.c + +objects/gui_gtk4_f.o: gui_gtk4_f.c + $(CCC) -o $@ gui_gtk4_f.c + + objects/gui_haiku.o: gui_haiku.cc $(CCC) -o $@ gui_haiku.cc @@ -4797,6 +4821,7 @@ proto/winclip.pro: winclip.c proto/window.pro: window.c proto/gui.pro: gui.c proto/gui_gtk.pro: gui_gtk.c +proto/gui_gtk4.pro: gui_gtk4.c proto/gui_motif.pro: gui_motif.c proto/gui_xmdlg.pro: gui_xmdlg.c proto/gui_gtk_x11.pro: gui_gtk_x11.c diff --git a/src/auto/configure b/src/auto/configure index 1705525156..ff4d5e361c 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -862,6 +862,7 @@ with_x enable_gui enable_gtk2_check enable_gnome_check +enable_gtk4_check enable_gtk3_check enable_motif_check enable_gtktest @@ -1538,9 +1539,10 @@ Optional Features: --disable-farsi Deprecated. --enable-xim Include XIM input support. --enable-fontset Include X fontset output support. - --enable-gui=OPTS X11 GUI. default=auto OPTS=auto/no/gtk2/gnome2/gtk3/motif/haiku/photon/carbon + --enable-gui=OPTS X11 GUI. default=auto OPTS=auto/no/gtk2/gnome2/gtk3/gtk4/motif/haiku/photon/carbon --enable-gtk2-check If auto-select GUI, check for GTK+ 2 default=yes --enable-gnome-check If GTK GUI, check for GNOME default=no + --enable-gtk4-check If auto-select GUI, check for GTK 4 default=yes --enable-gtk3-check If auto-select GUI, check for GTK+ 3 default=yes --enable-motif-check If auto-select GUI, check for Motif default=yes --disable-gtktest Do not try to compile and run a test GTK program @@ -10486,6 +10488,7 @@ enable_gui_canon=`echo "_$enable_gui" | \ SKIP_GTK2=YES SKIP_GTK3=YES +SKIP_GTK4=YES SKIP_GNOME=YES SKIP_MOTIF=YES SKIP_PHOTON=YES @@ -10516,7 +10519,7 @@ printf "%s\n" "no GUI support" >&6; } SKIP_PHOTON=YES ;; yes|""|auto) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: automatic GUI support" >&5 printf "%s\n" "automatic GUI support" >&6; } - gui_auto=yes ;; + gui_auto=yes ;; photon) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Photon GUI support" >&5 printf "%s\n" "Photon GUI support" >&6; } ;; *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Sorry, $enable_gui GUI is not supported" >&5 @@ -10530,7 +10533,7 @@ elif test "x$MACOS_X" = "xyes" -a "x$with_x" = "xno" ; then printf "%s\n" "no GUI support" >&6; } ;; yes|"") { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes - automatic GUI support" >&5 printf "%s\n" "yes - automatic GUI support" >&6; } - gui_auto=yes ;; + gui_auto=yes ;; auto) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: auto - disable GUI support for Mac OS" >&5 printf "%s\n" "auto - disable GUI support for Mac OS" >&6; } ;; *) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Sorry, $enable_gui GUI is not supported" >&5 @@ -10546,6 +10549,7 @@ printf "%s\n" "yes/auto - automatic GUI support" >&6; } gui_auto=yes SKIP_GTK2= SKIP_GTK3= + SKIP_GTK4= SKIP_GNOME= SKIP_MOTIF=;; gtk2) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: GTK+ 2.x GUI support" >&5 @@ -10558,6 +10562,9 @@ printf "%s\n" "GNOME 2.x GUI support" >&6; } gtk3) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: GTK+ 3.x GUI support" >&5 printf "%s\n" "GTK+ 3.x GUI support" >&6; } SKIP_GTK3=;; + gtk4) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: GTK 4.x GUI support" >&5 +printf "%s\n" "GTK 4.x GUI support" >&6; } + SKIP_GTK4=;; motif) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Motif GUI support" >&5 printf "%s\n" "Motif GUI support" >&6; } SKIP_MOTIF=;; @@ -10607,6 +10614,25 @@ printf "%s\n" "$enable_gnome_check" >&6; } fi fi +if test "x$SKIP_GTK4" != "xYES" -a "$enable_gui_canon" != "gtk4"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether or not to look for GTK 4" >&5 +printf %s "checking whether or not to look for GTK 4... " >&6; } + # Check whether --enable-gtk4-check was given. +if test ${enable_gtk4_check+y} +then : + enableval=$enable_gtk4_check; +else case e in #( + e) enable_gtk4_check="yes" ;; +esac +fi + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $enable_gtk4_check" >&5 +printf "%s\n" "$enable_gtk4_check" >&6; } + if test "x$enable_gtk4_check" = "xno"; then + SKIP_GTK4=YES + fi +fi + if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether or not to look for GTK+ 3" >&5 printf %s "checking whether or not to look for GTK+ 3... " >&6; } @@ -10681,6 +10707,8 @@ printf "%s\n" "gtk test disabled" >&6; } gtk_pkg_name="gtk+-2.0" ;; #( 3.*) : gtk_pkg_name="gtk+-3.0" ;; #( + 4.*) : + gtk_pkg_name="gtk4" ;; #( *) : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} @@ -10697,7 +10725,7 @@ then : printf "%s\n" "found" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5 printf %s "checking for GTK - version >= $min_gtk_version... " >&6; } - GTK_CPPFLAGS=`$PKG_CONFIG --cflags-only-I $gtk_pkg_name` + GTK_CPPFLAGS=`$PKG_CONFIG --cflags-only-I $gtk_pkg_name` GTK_CFLAGS=`$PKG_CONFIG --cflags-only-other $gtk_pkg_name` GTK_LIBDIR=`$PKG_CONFIG --libs-only-L $gtk_pkg_name` GTK_LIBS=`$PKG_CONFIG --libs $gtk_pkg_name` @@ -10821,6 +10849,7 @@ fi if test -n "$GTK_CPPFLAGS"; then SKIP_GTK2=YES + SKIP_GTK4=YES SKIP_GNOME=YES SKIP_MOTIF=YES GUITYPE=GTK @@ -10831,6 +10860,196 @@ fi fi fi +if test -z "$SKIP_GTK4"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --disable-gtktest argument" >&5 +printf %s "checking --disable-gtktest argument... " >&6; } + # Check whether --enable-gtktest was given. +if test ${enable_gtktest+y} +then : + enableval=$enable_gtktest; +else case e in #( + e) enable_gtktest=yes ;; +esac +fi + + if test "x$enable_gtktest" = "xyes" ; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: gtk test enabled" >&5 +printf "%s\n" "gtk test enabled" >&6; } + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: gtk test disabled" >&5 +printf "%s\n" "gtk test disabled" >&6; } + fi + + if test "x$PKG_CONFIG" != "xno"; then + + min_gtk_version="4.0.0" + + if test "$PKG_CONFIG" != "no"; then + case $min_gtk_version in #( + 2.*) : + gtk_pkg_name="gtk+-2.0" ;; #( + 3.*) : + gtk_pkg_name="gtk+-3.0" ;; #( + 4.*) : + gtk_pkg_name="gtk4" ;; #( + *) : + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} +as_fn_error $? "The configure script does not know which pkg-config name to use for GTK $min_gtk_version\" +See 'config.log' for more details" "$LINENO" 5; } ;; +esac + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pkg-config $gtk_pkg_name" >&5 +printf %s "checking for pkg-config $gtk_pkg_name... " >&6; } + if "$PKG_CONFIG" --exists "$gtk_pkg_name" +then : + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: found" >&5 +printf "%s\n" "found" >&6; } + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5 +printf %s "checking for GTK - version >= $min_gtk_version... " >&6; } + GTK_CPPFLAGS=`$PKG_CONFIG --cflags-only-I $gtk_pkg_name` + GTK_CFLAGS=`$PKG_CONFIG --cflags-only-other $gtk_pkg_name` + GTK_LIBDIR=`$PKG_CONFIG --libs-only-L $gtk_pkg_name` + GTK_LIBS=`$PKG_CONFIG --libs $gtk_pkg_name` + gtk_major_version=`$PKG_CONFIG --modversion $gtk_pkg_name | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\1/'` + gtk_minor_version=`$PKG_CONFIG --modversion $gtk_pkg_name | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\2/'` + gtk_micro_version=`$PKG_CONFIG --modversion $gtk_pkg_name | \ + sed 's/\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\)/\3/'` + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&5 +printf "%s\n" "yes; found version $gtk_major_version.$gtk_minor_version.$gtk_micro_version" >&6; } + +else case e in #( + e) + GTK_CPPFLAGS="" + GTK_CFLAGS="" + GTK_LIBDIR="" + GTK_LIBS="" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no; consider installing your distro GTK -dev package" >&5 +printf "%s\n" "no; consider installing your distro GTK -dev package" >&6; } + if test "$fail_if_missing" = "yes" -a "$gui_auto" != "yes"; then + as_fn_error $? "pkg-config could not find $gtk_pkg_name" "$LINENO" 5 + fi + ;; +esac +fi + fi + + gtktest_success="yes" + if test "$enable_gtktest" = "yes"; then + { + ac_save_CPPFLAGS="$CPPFLAGS" + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CPPFLAGS="$CPPFLAGS $GTK_CPPFLAGS" + CFLAGS="$CFLAGS $GTK_CFLAGS" + LIBS="$LIBS $GTK_LIBS" + + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking ability to compile GTK test program" >&5 +printf %s "checking ability to compile GTK test program... " >&6; } + if test "$cross_compiling" = yes +then : + echo $ac_n "cross compiling; assumed OK... $ac_c" +else case e in #( + e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +#include +#include +#if STDC_HEADERS +# include +# include +#endif + +int +main () +{ + int ex_major = $gtk_major_version; + int ex_minor = $gtk_minor_version; + int ex_micro = $gtk_micro_version; + + #if $gtk_major_version == 2 + guint ob_major = gtk_major_version; + guint ob_minor = gtk_minor_version; + guint ob_micro = gtk_micro_version; + #else + guint ob_major = gtk_get_major_version(); + guint ob_minor = gtk_get_minor_version(); + guint ob_micro = gtk_get_micro_version(); + #endif + + if ((ob_major > ex_major) || + ((ob_major == ex_major) + && (ob_minor > ex_minor)) || + ((ob_major == ex_major) + && (ob_minor == ex_minor) + && (ob_micro >= ex_micro))) + return 0; + else + return 1; +} + +_ACEOF +if ac_fn_c_try_run "$LINENO" +then : + gtktest_success="yes"; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +else case e in #( + e) gtktest_success="no"; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } ;; +esac +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext ;; +esac +fi + + CPPFLAGS="$ac_save_CPPFLAGS" + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + } + fi + + if test "$gtktest_success" = "yes"; then + GUI_LIB_LOC="$GTK_LIBDIR" + GTK_LIBNAME="$GTK_LIBS" + GUI_INC_LOC="$GTK_CPPFLAGS" + else + GTK_CPPFLAGS="" + GTK_CFLAGS="" + GTK_LIBDIR="" + GTK_LIBS="" + if test "$fail_if_missing" = "yes" -a "$gui_auto" != "yes"; then + as_fn_error $? "Failed to compile GTK test program." "$LINENO" 5 + fi + fi + + + + + + if test -n "$GTK_CPPFLAGS"; then + SKIP_GTK3=YES + SKIP_GTK2=YES + SKIP_GNOME=YES + SKIP_MOTIF=YES + GUITYPE=GTK4 + + printf "%s\n" "#define USE_GTK4 1" >>confdefs.h + + X_LIBS= + X_PRE_LIBS= + X_EXTRA_LIBS= + X_LIB= + if test "$enable_xim" = "auto"; then + enable_xim="yes" + fi + fi + fi +fi + if test -z "$SKIP_GTK2"; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --disable-gtktest argument" >&5 printf %s "checking --disable-gtktest argument... " >&6; } @@ -10861,6 +11080,8 @@ printf "%s\n" "gtk test disabled" >&6; } gtk_pkg_name="gtk+-2.0" ;; #( 3.*) : gtk_pkg_name="gtk+-3.0" ;; #( + 4.*) : + gtk_pkg_name="gtk4" ;; #( *) : { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5 printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;} @@ -10877,7 +11098,7 @@ then : printf "%s\n" "found" >&6; } { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for GTK - version >= $min_gtk_version" >&5 printf %s "checking for GTK - version >= $min_gtk_version... " >&6; } - GTK_CPPFLAGS=`$PKG_CONFIG --cflags-only-I $gtk_pkg_name` + GTK_CPPFLAGS=`$PKG_CONFIG --cflags-only-I $gtk_pkg_name` GTK_CFLAGS=`$PKG_CONFIG --cflags-only-other $gtk_pkg_name` GTK_LIBDIR=`$PKG_CONFIG --libs-only-L $gtk_pkg_name` GTK_LIBS=`$PKG_CONFIG --libs $gtk_pkg_name` diff --git a/src/beval.h b/src/beval.h index 694bec4dc5..4225e926d2 100644 --- a/src/beval.h +++ b/src/beval.h @@ -11,7 +11,7 @@ #define BEVAL__H #ifdef FEAT_GUI_GTK -# ifdef USE_GTK3 +# if defined(USE_GTK3) || defined(USE_GTK4) # include # else # include diff --git a/src/clipboard.c b/src/clipboard.c index 902f9d6032..6f847edc1c 100644 --- a/src/clipboard.c +++ b/src/clipboard.c @@ -1891,7 +1891,8 @@ clip_x11_set_selection(Clipboard_T *cbd UNUSED) # endif -# if defined(FEAT_XCLIPBOARD) || defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK) +# if (defined(FEAT_XCLIPBOARD) || defined(FEAT_GUI_X11) || defined(FEAT_GUI_GTK)) \ + && !defined(USE_GTK4) /* * Get the contents of the X CUT_BUFFER0 and put it in "cbd". */ @@ -3298,7 +3299,7 @@ did_set_clipboard(optset_T *args UNUSED) vim_regfree(clip_exclude_prog); clip_exclude_prog = new_exclude_prog; # endif -# ifdef FEAT_GUI_GTK +# if defined(FEAT_GUI_GTK) && !defined(USE_GTK4) if (gui.in_use) { gui_gtk_set_selection_targets((GdkAtom)GDK_SELECTION_PRIMARY); diff --git a/src/config.h.in b/src/config.h.in index 699fde7ae0..e4273f5b7c 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -496,6 +496,9 @@ /* Define if GTK+ GUI is to be linked against GTK+ 3 */ #undef USE_GTK3 +/* Define if GTK GUI is to be linked against GTK 4 */ +#undef USE_GTK4 + /* Define if we have isinf() */ #undef HAVE_ISINF diff --git a/src/configure.ac b/src/configure.ac index 5f79bb6a0e..a808530d24 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -2613,7 +2613,7 @@ test "x$with_x" = xno -a "x$HAIKU" != "xyes" -a "x$MACOS_X" != "xyes" -a "x$QNX" AC_MSG_CHECKING(--enable-gui argument) AC_ARG_ENABLE(gui, - [ --enable-gui[=OPTS] X11 GUI. [default=auto] [OPTS=auto/no/gtk2/gnome2/gtk3/motif/haiku/photon/carbon]], , enable_gui="auto") + [ --enable-gui[=OPTS] X11 GUI. [default=auto] [OPTS=auto/no/gtk2/gnome2/gtk3/gtk4/motif/haiku/photon/carbon]], , enable_gui="auto") dnl Canonicalize the --enable-gui= argument so that it can be easily compared. dnl Do not use character classes for portability with old tools. @@ -2623,6 +2623,7 @@ enable_gui_canon=`echo "_$enable_gui" | \ dnl Skip everything by default. SKIP_GTK2=YES SKIP_GTK3=YES +SKIP_GTK4=YES SKIP_GNOME=YES SKIP_MOTIF=YES SKIP_PHOTON=YES @@ -2646,7 +2647,7 @@ elif test "x$QNX" = "xyes" -a "x$with_x" = "xno" ; then no) AC_MSG_RESULT(no GUI support) SKIP_PHOTON=YES ;; yes|""|auto) AC_MSG_RESULT(automatic GUI support) - gui_auto=yes ;; + gui_auto=yes ;; photon) AC_MSG_RESULT(Photon GUI support) ;; *) AC_MSG_RESULT([Sorry, $enable_gui GUI is not supported]) SKIP_PHOTON=YES ;; @@ -2656,7 +2657,7 @@ elif test "x$MACOS_X" = "xyes" -a "x$with_x" = "xno" ; then case "$enable_gui_canon" in no) AC_MSG_RESULT(no GUI support) ;; yes|"") AC_MSG_RESULT(yes - automatic GUI support) - gui_auto=yes ;; + gui_auto=yes ;; auto) AC_MSG_RESULT(auto - disable GUI support for Mac OS) ;; *) AC_MSG_RESULT([Sorry, $enable_gui GUI is not supported]) ;; esac @@ -2668,6 +2669,7 @@ else gui_auto=yes SKIP_GTK2= SKIP_GTK3= + SKIP_GTK4= SKIP_GNOME= SKIP_MOTIF=;; gtk2) AC_MSG_RESULT(GTK+ 2.x GUI support) @@ -2677,6 +2679,8 @@ else SKIP_GTK2=;; gtk3) AC_MSG_RESULT(GTK+ 3.x GUI support) SKIP_GTK3=;; + gtk4) AC_MSG_RESULT(GTK 4.x GUI support) + SKIP_GTK4=;; motif) AC_MSG_RESULT(Motif GUI support) SKIP_MOTIF=;; *) AC_MSG_RESULT([Sorry, $enable_gui GUI is not supported]) ;; @@ -2708,6 +2712,17 @@ if test "x$SKIP_GNOME" != "xYES" -a "$enable_gui_canon" != "gnome2"; then fi fi +if test "x$SKIP_GTK4" != "xYES" -a "$enable_gui_canon" != "gtk4"; then + AC_MSG_CHECKING(whether or not to look for GTK 4) + AC_ARG_ENABLE(gtk4-check, + [ --enable-gtk4-check If auto-select GUI, check for GTK 4 [default=yes]], + , enable_gtk4_check="yes") + AC_MSG_RESULT($enable_gtk4_check) + if test "x$enable_gtk4_check" = "xno"; then + SKIP_GTK4=YES + fi +fi + if test "x$SKIP_GTK3" != "xYES" -a "$enable_gui_canon" != "gtk3"; then AC_MSG_CHECKING(whether or not to look for GTK+ 3) AC_ARG_ENABLE(gtk3-check, @@ -2747,6 +2762,7 @@ AC_DEFUN(AM_PATH_GTK, AS_CASE([$min_gtk_version], [2.*], [gtk_pkg_name="gtk+-2.0"], [3.*], [gtk_pkg_name="gtk+-3.0"], + [4.*], [gtk_pkg_name="gtk4"], [AC_MSG_FAILURE([The configure script does not know which pkg-config name to use for GTK $min_gtk_version"])]) AC_MSG_CHECKING([for pkg-config $gtk_pkg_name]) @@ -2754,9 +2770,9 @@ AC_DEFUN(AM_PATH_GTK, [ AC_MSG_RESULT(found) AC_MSG_CHECKING([for GTK - version >= $min_gtk_version]) - dnl We should be using PKG_CHECK_MODULES() instead of this hack. - dnl But I guess the dependency on pkgconfig.m4 is not wanted or - dnl something like that. +dnl We should be using PKG_CHECK_MODULES() instead of this hack. +dnl But I guess the dependency on pkgconfig.m4 is not wanted or +dnl something like that. GTK_CPPFLAGS=`$PKG_CONFIG --cflags-only-I $gtk_pkg_name` GTK_CFLAGS=`$PKG_CONFIG --cflags-only-other $gtk_pkg_name` GTK_LIBDIR=`$PKG_CONFIG --libs-only-L $gtk_pkg_name` @@ -2935,7 +2951,7 @@ AC_DEFUN([GNOME_INIT],[ ]) dnl --------------------------------------------------------------------------- -dnl Check for GTK3. If it succeeds, skip the check for GTK2. +dnl Check for GTK3. If it succeeds, skip the check for GTK2/GTK4 dnl --------------------------------------------------------------------------- if test -z "$SKIP_GTK3"; then AC_MSG_CHECKING(--disable-gtktest argument) @@ -2954,6 +2970,7 @@ if test -z "$SKIP_GTK3"; then GUI_INC_LOC="$GTK_CPPFLAGS"]) if test -n "$GTK_CPPFLAGS"; then SKIP_GTK2=YES + SKIP_GTK4=YES SKIP_GNOME=YES SKIP_MOTIF=YES GUITYPE=GTK @@ -2963,6 +2980,47 @@ if test -z "$SKIP_GTK3"; then fi fi +dnl --------------------------------------------------------------------------- +dnl Check for GTK4. If it succeeds, skip the check for GTK3/GTK2. +dnl --------------------------------------------------------------------------- +if test -z "$SKIP_GTK4"; then + AC_MSG_CHECKING(--disable-gtktest argument) + AC_ARG_ENABLE(gtktest, [ --disable-gtktest Do not try to compile and run a test GTK program], + , enable_gtktest=yes) + if test "x$enable_gtktest" = "xyes" ; then + AC_MSG_RESULT(gtk test enabled) + else + AC_MSG_RESULT(gtk test disabled) + fi + + if test "x$PKG_CONFIG" != "xno"; then + AM_PATH_GTK(4.0.0, + [GUI_LIB_LOC="$GTK_LIBDIR" + GTK_LIBNAME="$GTK_LIBS" + GUI_INC_LOC="$GTK_CPPFLAGS"]) + if test -n "$GTK_CPPFLAGS"; then + SKIP_GTK3=YES + SKIP_GTK2=YES + SKIP_GNOME=YES + SKIP_MOTIF=YES + GUITYPE=GTK4 + AC_SUBST(GTK_LIBNAME) + AC_DEFINE(USE_GTK4) + dnl GTK4 does not use any X11 APIs directly. + dnl GTK4 itself links against X11 for its backend, so the + dnl dynamic linker resolves X11 symbols via GTK4's dependency. + X_LIBS= + X_PRE_LIBS= + X_EXTRA_LIBS= + X_LIB= + dnl automatically enable XIM for GTK4 + if test "$enable_xim" = "auto"; then + enable_xim="yes" + fi + fi + fi +fi + dnl --------------------------------------------------------------------------- dnl Check for GTK2. If it fails, then continue on for Motif as before... dnl --------------------------------------------------------------------------- diff --git a/src/evalfunc.c b/src/evalfunc.c index ed03592635..01c19298b9 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -7133,6 +7133,13 @@ f_has(typval_T *argvars, typval_T *rettv) 1 #else 0 +#endif + }, + {"gui_gtk4", +#if defined(FEAT_GUI_GTK) && defined(USE_GTK4) + 1 +#else + 0 #endif }, {"gui_gnome", diff --git a/src/feature.h b/src/feature.h index 3f3c67a176..115db703d2 100644 --- a/src/feature.h +++ b/src/feature.h @@ -296,6 +296,14 @@ # define FEAT_POSTSCRIPT #endif +/* + * +gtk_print Native GTK print dialog for :hardcopy (GTK4). + * Uses GtkPrintOperation + Pango/Cairo instead of PostScript. + */ +#if defined(FEAT_PRINTER) && defined(FEAT_GUI_GTK) && defined(USE_GTK4) +# define FEAT_GUI_GTK_PRINT +#endif + /* * +diff Displaying diffs in a nice way. * Can be enabled in autoconf already. @@ -505,7 +513,8 @@ /* * GUI dark theme variant */ -#if (defined(FEAT_GUI_GTK) && defined(USE_GTK3)) || defined(FEAT_GUI_MSWIN) +#if (defined(FEAT_GUI_GTK) && (defined(USE_GTK3) || defined(USE_GTK4))) \ + || defined(FEAT_GUI_MSWIN) # define FEAT_GUI_DARKTHEME #endif @@ -805,7 +814,7 @@ * +X11 Unix only. Include code for xterm title saving and X * clipboard. Only works if HAVE_X11 is also defined. */ -#if defined(FEAT_NORMAL) || defined(FEAT_GUI_MOTIF) +#if (defined(FEAT_NORMAL) || defined(FEAT_GUI_MOTIF)) && !defined(USE_GTK4) # define WANT_X11 #endif @@ -910,7 +919,8 @@ #if defined(FEAT_NORMAL) \ && (defined(UNIX) || defined(VMS)) \ - && defined(WANT_X11) && defined(HAVE_X11) + && defined(WANT_X11) && defined(HAVE_X11) \ + && !defined(USE_GTK4) # define FEAT_XCLIPBOARD # ifndef FEAT_CLIPBOARD # define FEAT_CLIPBOARD diff --git a/src/getchar.c b/src/getchar.c index 2e3079a1e4..b995698128 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -2018,7 +2018,7 @@ vgetc(void) continue; } #endif -#if defined(FEAT_GUI) && defined(FEAT_GUI_GTK) && defined(FEAT_MENU) +#if defined(FEAT_GUI) && defined(FEAT_GUI_GTK) && !defined(USE_GTK4) && defined(FEAT_MENU) // GTK: normally selects the menu, but it's passed until // here to allow mapping it. Intercept and invoke the GTK // behavior if it's not mapped. diff --git a/src/gui.c b/src/gui.c index bf277e2b93..1448c86bdb 100644 --- a/src/gui.c +++ b/src/gui.c @@ -1709,6 +1709,12 @@ gui_set_shellsize( #if defined(MSWIN) || defined(FEAT_GUI_GTK) // If not setting to a user specified size and maximized, calculate the // number of characters that fit in the maximized window. + // FIXME: gui_mch_newfont() is called here even when the font hasn't + // changed at all. For example, ":set guioptions=k" triggers this path + // via gui_init_which_components() -> gui_set_shellsize(FALSE, ...). + // The intent is to keep the window size and recalculate Rows/Columns, + // which has nothing to do with fonts. This should be a separate + // function with a more descriptive name. if (!mustset && (vim_strchr(p_go, GO_KEEPWINSIZE) != NULL || gui_mch_maximized())) { diff --git a/src/gui.h b/src/gui.h index 4639b3a129..d1e5d2d5c0 100644 --- a/src/gui.h +++ b/src/gui.h @@ -15,7 +15,17 @@ # ifdef VMS # include "gui_gtk_vms.h" # endif -# include +# ifdef USE_GTK4 +// Types used in proto files but not available without X11 headers +typedef void *Widget; +typedef void *XtAppContext; +typedef void Display; +typedef unsigned long Window; +typedef unsigned long Atom; +typedef GdkEvent GdkEventKey; // GTK4: GdkEventKey merged into GdkEvent +# else +# include +# endif # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wstrict-prototypes" # include @@ -344,7 +354,7 @@ typedef struct Gui #endif #ifdef FEAT_GUI_GTK -# ifndef USE_GTK3 +# if !defined(USE_GTK3) && !defined(USE_GTK4) int visibility; // Is shell partially/fully obscured? # endif GdkCursor *blank_pointer; // Blank pointer @@ -365,7 +375,7 @@ typedef struct Gui GtkWidget *menubar_h; // menubar handle GtkWidget *toolbar_h; // toolbar handle # endif -# ifdef USE_GTK3 +# if defined(USE_GTK3) || defined(USE_GTK4) GdkRGBA *fgcolor; // GDK-styled foreground color GdkRGBA *bgcolor; // GDK-styled background color GdkRGBA *spcolor; // GDK-styled special color @@ -374,7 +384,7 @@ typedef struct Gui GdkColor *bgcolor; // GDK-styled background color GdkColor *spcolor; // GDK-styled special color # endif -# ifdef USE_GTK3 +# if defined(USE_GTK3) || defined(USE_GTK4) cairo_surface_t *surface; // drawarea surface # else GdkGC *text_gc; // cached GC for normal text @@ -386,7 +396,9 @@ typedef struct Gui GtkWidget *tabline; // tab pages line handle # endif +# ifndef USE_GTK4 GtkAccelGroup *accel_group; +# endif GtkWidget *filedlg; // file selection dialog char_u *browse_fname; // file name from filedlg @@ -553,7 +565,7 @@ typedef enum * For Solaris Studio, that is not the case. An explicit type cast is needed * to suppress warnings on that particular conversion. */ -# if defined(__SUNPRO_C) && defined(USE_GTK3) +# if defined(__SUNPRO_C) && (defined(USE_GTK3) || defined(USE_GTK4)) # define FUNC2GENERIC(func) (void *)(func) # else # define FUNC2GENERIC(func) G_CALLBACK(func) diff --git a/src/gui_beval.c b/src/gui_beval.c index 1d41089fec..c928d25685 100644 --- a/src/gui_beval.c +++ b/src/gui_beval.c @@ -16,7 +16,9 @@ #if !defined(FEAT_GUI_MSWIN) # ifdef FEAT_GUI_GTK -# if GTK_CHECK_VERSION(3,0,0) +# ifdef USE_GTK4 +# include +# elif GTK_CHECK_VERSION(3,0,0) # include # else # include @@ -390,7 +392,11 @@ pointer_event(BalloonEval *beval, int x, int y, unsigned state) beval->x = x; beval->y = y; +# ifdef USE_GTK4 + if (state & (int)GDK_ALT_MASK) +# else if (state & (int)GDK_MOD1_MASK) +# endif { /* * Alt is pressed -- enter super-evaluate-mode, @@ -416,14 +422,24 @@ key_event(BalloonEval *beval, unsigned keyval, int is_keypress) { switch (keyval) { +# ifdef USE_GTK4 + case GDK_KEY_Shift_L: + case GDK_KEY_Shift_R: +# else case GDK_Shift_L: case GDK_Shift_R: +# endif beval->showState = ShS_UPDATE_PENDING; (*beval->msgCB)(beval, (is_keypress) ? (int)GDK_SHIFT_MASK : 0); break; +# ifdef USE_GTK4 + case GDK_KEY_Control_L: + case GDK_KEY_Control_R: +# else case GDK_Control_L: case GDK_Control_R: +# endif beval->showState = ShS_UPDATE_PENDING; (*beval->msgCB)(beval, (is_keypress) ? (int)GDK_CONTROL_MASK : 0); diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c new file mode 100644 index 0000000000..24ad6ddce2 --- /dev/null +++ b/src/gui_gtk4.c @@ -0,0 +1,4638 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + * + * GTK4 GUI implementation: main window, events, drawing, menus, + * scrollbars, dialogs, and toolbar. This is a clean implementation for + * GTK4, separate from gui_gtk_x11.c which handles GTK2/GTK3. + * + * GTK4 differences from GTK3: + * - No GdkWindow (use GdkSurface for top-level only) + * - No GtkContainer (use gtk_widget_set_parent/gtk_box_append) + * - Events via GtkEventController, not signal+mask + * - Drawing via GtkSnapshot or gtk_drawing_area_set_draw_func + * - No gtk_dialog_run (async dialogs) + * - No GdkAtom (string-based content types) + * - No GtkSocket/GtkPlug + * - gtk_window_new() takes no arguments + */ + +#include "vim.h" + +#ifdef FEAT_GUI_GTK + +#include +#include +#include +#include "gui_gtk4_f.h" + +/* + * Geometry string parser, replacing XParseGeometry to remove X11 dependency. + * Format: [WIDTHxHEIGHT][{+-}XOFF{+-}YOFF] + */ +#define NoValue 0x0000 +#define XValue 0x0001 +#define YValue 0x0002 +#define WidthValue 0x0004 +#define HeightValue 0x0008 +#define XNegative 0x0010 +#define YNegative 0x0020 + + static int +vim_parse_geometry(const char *str, int *x, int *y, + unsigned int *width, unsigned int *height) +{ + int mask = NoValue; + char *end; + long val; + + if (str == NULL || *str == NUL) + return mask; + + // Parse width + if (*str != '+' && *str != '-') + { + val = strtol(str, &end, 10); + if (end != str) + { + *width = (unsigned int)val; + mask |= WidthValue; + str = end; + } + } + + // Parse 'x' or 'X' separator and height + if (*str == 'x' || *str == 'X') + { + str++; + val = strtol(str, &end, 10); + if (end != str) + { + *height = (unsigned int)val; + mask |= HeightValue; + str = end; + } + } + + // Parse x offset + if (*str == '+' || *str == '-') + { + int negative = (*str == '-'); + str++; + val = strtol(str, &end, 10); + if (end != str) + { + *x = negative ? -(int)val : (int)val; + mask |= XValue; + if (negative) + mask |= XNegative; + str = end; + } + } + + // Parse y offset + if (*str == '+' || *str == '-') + { + int negative = (*str == '-'); + str++; + val = strtol(str, &end, 10); + if (end != str) + { + *y = negative ? -(int)val : (int)val; + mask |= YValue; + if (negative) + mask |= YNegative; + } + } + + return mask; +} + +#ifdef FEAT_SOCKETSERVER +# include + +// Used to track the source for the listening socket +static guint socket_server_source_id = 0; +#endif + +#if defined(FEAT_MOUSESHAPE) +// Last set mouse pointer shape +static int last_shape = 0; +#endif + +#define DEFAULT_FONT "Monospace 10" + +// Menu action group for GMenu-based menus +static GSimpleActionGroup *menu_action_group = NULL; + +// Cursor blinking state +static enum { + BLINK_NONE, + BLINK_OFF, + BLINK_ON +} blink_state = BLINK_NONE; + +// GTK4 main loop compatibility +static int gtk4_main_loop_level = 0; +static int gtk4_main_loop_quit = FALSE; + +#ifdef USE_GRESOURCE +# include "auto/gui_gtk_gresources.h" +#endif + +typedef gboolean timeout_cb_type; + +/* + * Table of special key mappings. + */ +static struct special_key +{ + guint key_sym; + char_u code0; + char_u code1; +} +const special_keys[] = +{ + {GDK_KEY_Up, 'k', 'u'}, + {GDK_KEY_Down, 'k', 'd'}, + {GDK_KEY_Left, 'k', 'l'}, + {GDK_KEY_Right, 'k', 'r'}, + {GDK_KEY_F1, 'k', '1'}, + {GDK_KEY_F2, 'k', '2'}, + {GDK_KEY_F3, 'k', '3'}, + {GDK_KEY_F4, 'k', '4'}, + {GDK_KEY_F5, 'k', '5'}, + {GDK_KEY_F6, 'k', '6'}, + {GDK_KEY_F7, 'k', '7'}, + {GDK_KEY_F8, 'k', '8'}, + {GDK_KEY_F9, 'k', '9'}, + {GDK_KEY_F10, 'k', ';'}, + {GDK_KEY_F11, 'F', '1'}, + {GDK_KEY_F12, 'F', '2'}, + {GDK_KEY_Help, '%', '1'}, + {GDK_KEY_Undo, '&', '8'}, + {GDK_KEY_BackSpace, 'k', 'b'}, + {GDK_KEY_Insert, 'k', 'I'}, + {GDK_KEY_Delete, 'k', 'D'}, + {GDK_KEY_Home, 'k', 'h'}, + {GDK_KEY_End, '@', '7'}, + {GDK_KEY_Prior, 'k', 'P'}, + {GDK_KEY_Next, 'k', 'N'}, + {GDK_KEY_Print, '%', '9'}, + {GDK_KEY_KP_Left, 'k', 'l'}, + {GDK_KEY_KP_Right, 'k', 'r'}, + {GDK_KEY_KP_Up, 'k', 'u'}, + {GDK_KEY_KP_Down, 'k', 'd'}, + {GDK_KEY_KP_Insert, KS_EXTRA, (char_u)KE_KINS}, + {GDK_KEY_KP_Delete, KS_EXTRA, (char_u)KE_KDEL}, + {GDK_KEY_KP_Home, 'K', '1'}, + {GDK_KEY_KP_End, 'K', '4'}, + {GDK_KEY_KP_Prior, 'K', '3'}, + {GDK_KEY_KP_Next, 'K', '5'}, + {GDK_KEY_KP_Add, 'K', '6'}, + {GDK_KEY_KP_Subtract, 'K', '7'}, + {GDK_KEY_KP_Divide, 'K', '8'}, + {GDK_KEY_KP_Multiply, 'K', '9'}, + {GDK_KEY_KP_Enter, 'K', 'A'}, + {GDK_KEY_KP_Decimal, 'K', 'B'}, + {GDK_KEY_KP_0, 'K', 'C'}, + {GDK_KEY_KP_1, 'K', 'D'}, + {GDK_KEY_KP_2, 'K', 'E'}, + {GDK_KEY_KP_3, 'K', 'F'}, + {GDK_KEY_KP_4, 'K', 'G'}, + {GDK_KEY_KP_5, 'K', 'H'}, + {GDK_KEY_KP_6, 'K', 'I'}, + {GDK_KEY_KP_7, 'K', 'J'}, + {GDK_KEY_KP_8, 'K', 'K'}, + {GDK_KEY_KP_9, 'K', 'L'}, + {0, 0, 0} +}; + + static int +keyval_to_string(unsigned int keyval, char_u *string) +{ + int len; + guint32 uc; + + uc = gdk_keyval_to_unicode(keyval); + if (uc != 0) + { + len = utf_char2bytes((int)uc, string); + } + else + { + len = 1; + switch (keyval) + { + case GDK_KEY_Tab: case GDK_KEY_KP_Tab: case GDK_KEY_ISO_Left_Tab: + string[0] = TAB; + break; + case GDK_KEY_Linefeed: + string[0] = NL; + break; + case GDK_KEY_Return: case GDK_KEY_ISO_Enter: case GDK_KEY_3270_Enter: + string[0] = CAR; + break; + case GDK_KEY_Escape: + string[0] = ESC; + break; + default: + len = 0; + break; + } + } + string[len] = NUL; + return len; +} + + static int +modifiers_gdk2vim(guint state) +{ + int modifiers = 0; + + if (state & GDK_SHIFT_MASK) + modifiers |= MOD_MASK_SHIFT; + if (state & GDK_CONTROL_MASK) + modifiers |= MOD_MASK_CTRL; + if (state & GDK_ALT_MASK) + modifiers |= MOD_MASK_ALT; + if (state & GDK_META_MASK) + modifiers |= MOD_MASK_META; + if (state & GDK_SUPER_MASK) + modifiers |= MOD_MASK_CMD; + + return modifiers; +} + +static GtkWidget *vbox; // the main vertical box + +// Forward declarations for event callbacks +static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer data); +static gboolean key_press_event(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, gpointer data); +static void key_release_event(GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, gpointer data); +static void button_press_event(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data); +static void button_release_event(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data); +static void motion_notify_event(GtkEventControllerMotion *controller, double x, double y, gpointer data); +static void enter_notify_event(GtkEventControllerMotion *controller, double x, double y, gpointer data); +static void leave_notify_event(GtkEventControllerMotion *controller, gpointer data); +static gboolean scroll_event(GtkEventControllerScroll *controller, double dx, double dy, gpointer data); +static void focus_in_event(GtkEventControllerFocus *controller, gpointer data); +static void focus_out_event(GtkEventControllerFocus *controller, gpointer data); +#ifdef FEAT_DND +static gboolean drop_cb(GtkDropTarget *target, const GValue *value, double x, double y, gpointer data); +#endif +static void mainwin_destroy_cb(GObject *object, gpointer data); +static gboolean delete_event_cb(GtkWindow *window, gpointer data); +static void drawarea_realize_cb(GtkWidget *widget, gpointer data); +static void drawarea_unrealize_cb(GtkWidget *widget, gpointer data); +static void drawarea_resize_cb(GtkDrawingArea *area, int width, int height, gpointer data); + +/* + * Parse the GUI related command-line arguments. Any arguments used are + * deleted from argv, and *argc is decremented accordingly. This is called + * when vim is started, whether or not the GUI has been started. + */ + void +gui_mch_prepare(int *argc, char **argv) +{ + // Don't call gtk_init() here. It will be called in + // gui_mch_init_check() after the fork. Calling it before fork + // breaks the display connection in the child process, causing gvim + // to fail to start without --nofork. +} + +/* + * Free all GUI related resources. + */ + void +gui_mch_free_all(void) +{ +} + + static guint +timeout_add(int time, timeout_cb_type (*callback)(gpointer), int *flagp) +{ + return g_timeout_add((guint)time, (GSourceFunc)callback, flagp); +} + + static void +timeout_remove(guint timer) +{ + g_source_remove(timer); +} + +static long_u blink_waittime = 700; +static long_u blink_ontime = 400; +static long_u blink_offtime = 250; +static guint blink_timer = 0; + + static timeout_cb_type +blink_cb(gpointer data UNUSED) +{ + if (blink_state == BLINK_ON) + { + gui_undraw_cursor(); + blink_state = BLINK_OFF; + blink_timer = timeout_add(blink_offtime, blink_cb, NULL); + } + else + { + gui_update_cursor(TRUE, FALSE); + blink_state = BLINK_ON; + blink_timer = timeout_add(blink_ontime, blink_cb, NULL); + } + return FALSE; +} + + int +gui_mch_is_blinking(void) +{ + return blink_state != BLINK_NONE; +} + + int +gui_mch_is_blink_off(void) +{ + return blink_state == BLINK_OFF; +} + + void +gui_mch_set_blinking(long waittime, long on, long off) +{ + blink_waittime = waittime; + blink_ontime = on; + blink_offtime = off; +} + + void +gui_mch_stop_blink(int may_call_gui_update_cursor) +{ + if (blink_timer) + { + timeout_remove(blink_timer); + blink_timer = 0; + } + if (blink_state == BLINK_OFF && may_call_gui_update_cursor) + gui_update_cursor(TRUE, FALSE); + blink_state = BLINK_NONE; +} + + void +gui_mch_start_blink(void) +{ + if (blink_timer) + { + timeout_remove(blink_timer); + blink_timer = 0; + } + if (blink_waittime && blink_ontime && blink_offtime && gui.in_focus) + { + blink_timer = timeout_add(blink_waittime, blink_cb, NULL); + blink_state = BLINK_ON; + gui_update_cursor(TRUE, FALSE); + } +} + + int +gui_mch_early_init_check(int give_message UNUSED) +{ + return OK; +} + + int +gui_mch_init_check(void) +{ + // This defaults to argv[0], but we want it to match the name of the + // shipped gvim.desktop so that Vim's windows can be associated with this + // file. Also sets WM_CLASS on X11. + g_set_prgname("gvim"); + + // Suppress noisy EGL warnings when GL is not available. Only set + // this when actually starting the GUI, so non-GUI invocations are + // not affected. + if (g_getenv("EGL_LOG_LEVEL") == NULL) + setenv("EGL_LOG_LEVEL", "fatal", 0); + + // Call gtk_init() here after fork(). Calling it before fork() breaks + // the display connection in the child process. + gtk_init(); + return OK; +} + +/* + * Initialise the GUI. Create all the windows, set up all the callbacks etc. + * Returns OK for success, FAIL when the GUI can't be started. + */ + int +gui_mch_init(void) +{ + // Allocate GdkRGBA color structs. + gui.fgcolor = g_new(GdkRGBA, 1); + gui.bgcolor = g_new(GdkRGBA, 1); + gui.spcolor = g_new(GdkRGBA, 1); + + gui.def_norm_pixel = 0x00000000; // black + gui.def_back_pixel = 0x00ffffff; // white + gui.norm_pixel = gui.def_norm_pixel; + gui.back_pixel = gui.def_back_pixel; + + gui.scrollbar_width = SB_DEFAULT_WIDTH; + gui.scrollbar_height = SB_DEFAULT_WIDTH; + + // Create the main window. + gui.mainwin = gtk_window_new(); + gtk_widget_set_name(gui.mainwin, "vim-main-window"); + + // Create the PangoContext used for drawing all text. + gui.text_context = gtk_widget_create_pango_context(gui.mainwin); + pango_context_set_base_dir(gui.text_context, PANGO_DIRECTION_LTR); + + g_signal_connect(G_OBJECT(gui.mainwin), "close-request", + G_CALLBACK(delete_event_cb), NULL); + + // A vertical box holds the menubar, toolbar and main text window. + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_box_set_homogeneous(GTK_BOX(vbox), FALSE); + gtk_window_set_child(GTK_WINDOW(gui.mainwin), vbox); + +#ifdef FEAT_MENU + { + GMenu *gmenu = g_menu_new(); + gui.menubar = gtk_popover_menu_bar_new_from_model( + G_MENU_MODEL(gmenu)); + g_object_set_data_full(G_OBJECT(gui.menubar), "vim-gmenu", + gmenu, g_object_unref); + gtk_widget_set_name(gui.menubar, "vim-menubar"); + gtk_widget_set_visible(gui.menubar, FALSE); + gtk_box_append(GTK_BOX(vbox), gui.menubar); + } +#endif + +#ifdef FEAT_TOOLBAR + gui.toolbar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_name(gui.toolbar, "vim-toolbar"); + gtk_widget_set_visible(gui.toolbar, FALSE); + gtk_box_append(GTK_BOX(vbox), gui.toolbar); +#endif + +#ifdef FEAT_GUI_TABLINE + gui.tabline = gtk_notebook_new(); + gtk_notebook_set_show_border(GTK_NOTEBOOK(gui.tabline), FALSE); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), FALSE); + gtk_notebook_set_scrollable(GTK_NOTEBOOK(gui.tabline), TRUE); + gtk_widget_set_visible(gui.tabline, FALSE); + gtk_box_append(GTK_BOX(vbox), gui.tabline); +#endif + + // The form widget manages absolute positioning of scrollbars. + gui.formwin = gui_gtk_form_new(); + gtk_widget_set_name(gui.formwin, "vim-gtk-form"); + // formwin is overlaid on top of drawarea for scrollbar positioning. + // Disable input targeting so mouse events pass through to drawarea. + gtk_widget_set_can_target(gui.formwin, FALSE); + + // The drawing area for the editor content. + // Placed in an overlay so it fills the formwin, with scrollbars on top. + gui.drawarea = gtk_drawing_area_new(); + gui.surface = NULL; + gtk_widget_set_focusable(gui.drawarea, TRUE); + gtk_widget_set_vexpand(gui.drawarea, TRUE); + gtk_widget_set_hexpand(gui.drawarea, TRUE); + + { + // Use GtkOverlay: drawarea as the main child, formwin as overlay + GtkWidget *overlay = gtk_overlay_new(); + gtk_overlay_set_child(GTK_OVERLAY(overlay), gui.drawarea); + gtk_overlay_add_overlay(GTK_OVERLAY(overlay), gui.formwin); + gtk_widget_set_vexpand(overlay, TRUE); + gtk_widget_set_hexpand(overlay, TRUE); + gtk_box_append(GTK_BOX(vbox), overlay); + } + + // Set up drawing. + gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(gui.drawarea), + (GtkDrawingAreaDrawFunc)draw_event, NULL, NULL); + + g_signal_connect(G_OBJECT(gui.drawarea), "realize", + G_CALLBACK(drawarea_realize_cb), NULL); + g_signal_connect(G_OBJECT(gui.drawarea), "unrealize", + G_CALLBACK(drawarea_unrealize_cb), NULL); + g_signal_connect(G_OBJECT(gui.drawarea), "resize", + G_CALLBACK(drawarea_resize_cb), NULL); + + // Set up event controllers. + { + GtkEventController *key_ctrl = gtk_event_controller_key_new(); + g_signal_connect(key_ctrl, "key-pressed", + G_CALLBACK(key_press_event), NULL); + g_signal_connect(key_ctrl, "key-released", + G_CALLBACK(key_release_event), NULL); + gtk_widget_add_controller(gui.mainwin, key_ctrl); + } + + { + GtkGesture *click = gtk_gesture_click_new(); + gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(click), 0); + g_signal_connect(click, "pressed", + G_CALLBACK(button_press_event), NULL); + g_signal_connect(click, "released", + G_CALLBACK(button_release_event), NULL); + gtk_widget_add_controller(gui.drawarea, GTK_EVENT_CONTROLLER(click)); + } + + { + GtkEventController *motion = gtk_event_controller_motion_new(); + g_signal_connect(motion, "motion", + G_CALLBACK(motion_notify_event), NULL); + g_signal_connect(motion, "enter", + G_CALLBACK(enter_notify_event), NULL); + g_signal_connect(motion, "leave", + G_CALLBACK(leave_notify_event), NULL); + gtk_widget_add_controller(gui.drawarea, motion); + } + + { + GtkEventController *scroll = gtk_event_controller_scroll_new( + GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES); + g_signal_connect(scroll, "scroll", + G_CALLBACK(scroll_event), NULL); + gtk_widget_add_controller(gui.drawarea, scroll); + } + + { + GtkEventController *focus = gtk_event_controller_focus_new(); + g_signal_connect(focus, "enter", + G_CALLBACK(focus_in_event), NULL); + g_signal_connect(focus, "leave", + G_CALLBACK(focus_out_event), NULL); + gtk_widget_add_controller(gui.drawarea, focus); + } + +#ifdef FEAT_DND + // Set up drag-and-drop target for files and text. + { + GtkDropTarget *drop = gtk_drop_target_new(G_TYPE_INVALID, GDK_ACTION_COPY); + GType types[] = { GDK_TYPE_FILE_LIST, G_TYPE_STRING }; + gtk_drop_target_set_gtypes(drop, types, 2); + g_signal_connect(drop, "drop", + G_CALLBACK(drop_cb), NULL); + gtk_widget_add_controller(gui.drawarea, GTK_EVENT_CONTROLLER(drop)); + } +#endif + + gui.border_offset = gui.border_width; + + // Create a blank (invisible) cursor for hiding the mouse pointer. + gui.blank_pointer = gdk_cursor_new_from_name("none", NULL); + + return OK; +} + +/* + * Called when the foreground or background color has been changed. + */ + static void +surface_fill_bg(void) +{ + if (gui.surface != NULL) + { + cairo_t *cr = cairo_create(gui.surface); + cairo_set_source_rgba(cr, + gui.bgcolor->red, gui.bgcolor->green, + gui.bgcolor->blue, gui.bgcolor->alpha); + cairo_paint(cr); + cairo_destroy(cr); + } +} + + void +gui_mch_new_colors(void) +{ + surface_fill_bg(); + if (gui.drawarea != NULL && gtk_widget_get_realized(gui.drawarea)) + gtk_widget_queue_draw(gui.drawarea); +} + +/* + * Open the GUI window which was created by a call to gui_mch_init(). + */ + int +gui_mch_open(void) +{ + guicolor_T fg_pixel = INVALCOLOR; + guicolor_T bg_pixel = INVALCOLOR; + guint pixel_width; + guint pixel_height; + + if (gui.geom != NULL) + { + int mask; + unsigned int w, h; + int x = 0; + int y = 0; + + mask = vim_parse_geometry((char *)gui.geom, &x, &y, &w, &h); + + if (mask & WidthValue) + Columns = w; + if (mask & HeightValue) + { + if (p_window > (long)h - 1 || !option_was_set((char_u *)"window")) + p_window = h - 1; + Rows = h; + } + limit_screen_size(); + + VIM_CLEAR(gui.geom); + } + + // Use 80x24 as the default GUI size, unless geometry was specified. + if (Columns > 80 && gui.geom == NULL) + Columns = 80; + if (Rows > 24 && gui.geom == NULL) + Rows = 24; + pixel_width = (guint)(gui_get_base_width() + Columns * gui.char_width); + pixel_height = (guint)(gui_get_base_height() + Rows * gui.char_height); + gtk_window_set_default_size(GTK_WINDOW(gui.mainwin), + pixel_width, pixel_height); + + if (foreground_argument != NULL) + fg_pixel = gui_get_color((char_u *)foreground_argument); + if (fg_pixel == INVALCOLOR) + fg_pixel = gui_get_color((char_u *)"Black"); + + if (background_argument != NULL) + bg_pixel = gui_get_color((char_u *)background_argument); + if (bg_pixel == INVALCOLOR) + bg_pixel = gui_get_color((char_u *)"White"); + + if (found_reverse_arg) + { + gui.def_norm_pixel = bg_pixel; + gui.def_back_pixel = fg_pixel; + } + else + { + gui.def_norm_pixel = fg_pixel; + gui.def_back_pixel = bg_pixel; + } + + set_normal_colors(); + gui_check_colors(); + highlight_gui_started(); + + g_signal_connect(G_OBJECT(gui.mainwin), "destroy", + G_CALLBACK(mainwin_destroy_cb), NULL); + // Resize is handled by GtkForm's size_allocate callback. + + gtk_widget_set_visible(gui.mainwin, TRUE); + + // Make sure the drawing area gets keyboard focus. + gtk_widget_grab_focus(gui.drawarea); + gui_focus_change(TRUE); + + return OK; +} + + void +gui_mch_exit(int rc UNUSED) +{ + if (gui.mainwin != NULL) + gtk_window_destroy(GTK_WINDOW(gui.mainwin)); +} + + int +gui_mch_get_winpos(int *x, int *y) +{ + // GTK4 does not provide a window position API. + *x = 0; + *y = 0; + return FAIL; +} + + void +gui_mch_set_winpos(int x UNUSED, int y UNUSED) +{ + // GTK4/Wayland: window positioning not available +} + + int +gui_mch_maximized(void) +{ + return gtk_window_is_maximized(GTK_WINDOW(gui.mainwin)); +} + + void +gui_mch_unmaximize(void) +{ + if (gui.mainwin != NULL) + gtk_window_unmaximize(GTK_WINDOW(gui.mainwin)); +} + +/* + * Called when the font changed while the window is maximized or GO_KEEPWINSIZE + * is set. Recalculate Rows and Columns based on the current window size. + * + * NOTE: gui_set_shellsize() calls this when GO_KEEPWINSIZE ('k') is in + * 'guioptions', even when the font hasn't actually changed (e.g. just setting + * "guioptions=k" triggers it via gui_init_which_components()). This is + * arguably a design problem in the common code, but we must not call + * gui_set_shellsize() back from here or it will cause infinite recursion and + * crash. Use gui_resize_shell() to recalculate Rows/Columns from the current + * window size instead. + */ + void +gui_mch_newfont(void) +{ + int w, h; + + w = gtk_widget_get_width(gui.formwin); + h = gtk_widget_get_height(gui.formwin); + w -= get_menu_tool_width(); + h -= get_menu_tool_height(); + gui_resize_shell(w, h); +} + + void +gui_mch_settitle(char_u *title, char_u *icon UNUSED) +{ + if (title != NULL && gui.mainwin != NULL) + gtk_window_set_title(GTK_WINDOW(gui.mainwin), (const char *)title); +} + +static int in_set_shellsize = FALSE; + + void +gui_mch_set_shellsize(int width, int height, + int min_width UNUSED, int min_height UNUSED, + int base_width UNUSED, int base_height UNUSED, + int direction UNUSED) +{ + // Only set window size if it hasn't been shown yet (initial sizing). + // After that, the window size is controlled by the user/WM and + // Vim adapts to it via form_size_allocate -> gui_resize_shell. + if (!gtk_widget_get_realized(gui.mainwin)) + { + width += get_menu_tool_width(); + height += get_menu_tool_height(); + gtk_window_set_default_size(GTK_WINDOW(gui.mainwin), width, height); + } +} + + void +gui_mch_get_screen_dimensions(int *screen_w, int *screen_h) +{ + GdkDisplay *display = gtk_widget_get_display(gui.mainwin); + GdkSurface *surface = gtk_native_get_surface(GTK_NATIVE(gui.mainwin)); + + if (surface != NULL) + { + GdkMonitor *monitor = gdk_display_get_monitor_at_surface(display, + surface); + if (monitor != NULL) + { + GdkRectangle geom; + gdk_monitor_get_geometry(monitor, &geom); + *screen_w = geom.width; + *screen_h = geom.height; + return; + } + } + + *screen_w = 800; + *screen_h = 600; +} + +#ifdef FEAT_MENU + void +gui_mch_enable_menu(int showit) +{ + if (gui.menubar != NULL) + gtk_widget_set_visible(gui.menubar, showit); +} +#endif + +#ifdef FEAT_TOOLBAR + void +gui_mch_show_toolbar(int showit) +{ + if (gui.toolbar != NULL) + gtk_widget_set_visible(gui.toolbar, showit); +} +#endif + + void +gui_mch_set_dark_theme(int dark) +{ + // GTK4: use GtkSettings + GtkSettings *settings = gtk_settings_get_default(); + if (settings != NULL) + g_object_set(settings, "gtk-application-prefer-dark-theme", + (gboolean)dark, NULL); +} + +/* + * ============================================================ + * Font handling + * ============================================================ + */ + + int +gui_mch_adjust_charheight(void) +{ + PangoFontMetrics *metrics; + int ascent; + int descent; + + metrics = pango_context_get_metrics(gui.text_context, gui.norm_font, + pango_context_get_language(gui.text_context)); + ascent = pango_font_metrics_get_ascent(metrics); + descent = pango_font_metrics_get_descent(metrics); + pango_font_metrics_unref(metrics); + + gui.char_height = (ascent + descent + (PANGO_SCALE * 15) / 16) + / PANGO_SCALE + p_linespace; + gui.char_ascent = PANGO_PIXELS(ascent + p_linespace * PANGO_SCALE / 2); + gui.char_ascent = MAX(gui.char_ascent, 0); + gui.char_height = MAX(gui.char_height, gui.char_ascent + 1); + + return OK; +} + +typedef struct { + PangoFontDescription *result; + gboolean done; +} FontDialogData; + + static void +font_dialog_finish_cb(GObject *source, GAsyncResult *res, gpointer data) +{ + FontDialogData *fdd = (FontDialogData *)data; + fdd->result = gtk_font_dialog_choose_font_finish( + GTK_FONT_DIALOG(source), res, NULL); + fdd->done = TRUE; +} + + static gboolean +font_filter(gpointer item, gpointer data UNUSED) +{ + if (PANGO_IS_FONT_FAMILY(item)) + return pango_font_family_is_monospace(PANGO_FONT_FAMILY(item)); + if (PANGO_IS_FONT_FACE(item)) + { + PangoFontFamily *family = pango_font_face_get_family( + PANGO_FONT_FACE(item)); + if (family != NULL) + return pango_font_family_is_monospace(family); + } + return TRUE; +} + + char_u * +gui_mch_font_dialog(char_u *oldval) +{ + GtkFontDialog *dlg; + PangoFontDescription *initial = NULL; + char_u *fontname = NULL; + FontDialogData fdd; + + dlg = gtk_font_dialog_new(); + gtk_font_dialog_set_modal(dlg, TRUE); + gtk_font_dialog_set_filter(dlg, + GTK_FILTER(gtk_custom_filter_new( + (GtkCustomFilterFunc)font_filter, NULL, NULL))); + + if (oldval != NULL && oldval[0] != NUL) + { + char_u *oldname; + + if (output_conv.vc_type != CONV_NONE) + oldname = string_convert(&output_conv, oldval, NULL); + else + oldname = oldval; + + if (STRLEN(oldname) > 0 && !vim_isdigit(oldname[STRLEN(oldname) - 1])) + { + char_u *p = vim_strnsave(oldname, STRLEN(oldname) + 3); + if (p != NULL) + { + STRCPY(p + STRLEN(p), " 10"); + if (oldname != oldval) + vim_free(oldname); + oldname = p; + } + } + + initial = pango_font_description_from_string((const char *)oldname); + if (oldname != oldval) + vim_free(oldname); + } + else + initial = pango_font_description_from_string(DEFAULT_FONT); + + fdd.result = NULL; + fdd.done = FALSE; + + gtk_font_dialog_choose_font(dlg, GTK_WINDOW(gui.mainwin), + initial, NULL, font_dialog_finish_cb, &fdd); + + while (!fdd.done) + g_main_context_iteration(NULL, TRUE); + + if (fdd.result != NULL) + { + char *name = pango_font_description_to_string(fdd.result); + if (name != NULL) + { + char_u *p; + + p = vim_strsave_escaped((char_u *)name, (char_u *)","); + g_free(name); + if (p != NULL && input_conv.vc_type != CONV_NONE) + { + fontname = string_convert(&input_conv, p, NULL); + vim_free(p); + } + else + fontname = p; + } + pango_font_description_free(fdd.result); + } + + if (initial != NULL) + pango_font_description_free(initial); + g_object_unref(dlg); + + return fontname; +} + +/* + * Build a table of glyphs for ASCII characters 32..126. + * This avoids the overhead of itemize+shape for the common case. + */ + static void +ascii_glyph_table_init(void) +{ + char_u ascii_chars[2 * 128]; + PangoAttrList *attr_list; + GList *item_list; + int i; + + if (gui.ascii_glyphs != NULL) + pango_glyph_string_free(gui.ascii_glyphs); + if (gui.ascii_font != NULL) + g_object_unref(gui.ascii_font); + + gui.ascii_glyphs = NULL; + gui.ascii_font = NULL; + + for (i = 0; i < 128; ++i) + { + if (i >= 32 && i < 127) + ascii_chars[2 * i] = i; + else + ascii_chars[2 * i] = '?'; + ascii_chars[2 * i + 1] = ' '; + } + + attr_list = pango_attr_list_new(); + item_list = pango_itemize(gui.text_context, (const char *)ascii_chars, + 0, sizeof(ascii_chars), attr_list, NULL); + + if (item_list != NULL && item_list->next == NULL) + { + PangoItem *item; + int width; + + item = (PangoItem *)item_list->data; + width = gui.char_width * PANGO_SCALE; + + gui.ascii_font = item->analysis.font; + g_object_ref(gui.ascii_font); + + gui.ascii_glyphs = pango_glyph_string_new(); + + pango_shape((const char *)ascii_chars, sizeof(ascii_chars), + &item->analysis, gui.ascii_glyphs); + + if (gui.ascii_glyphs->num_glyphs == (int)sizeof(ascii_chars)) + { + for (i = 0; i < gui.ascii_glyphs->num_glyphs; ++i) + { + PangoGlyphGeometry *geom; + + geom = &gui.ascii_glyphs->glyphs[i].geometry; + geom->x_offset += MAX(0, width - geom->width) / 2; + geom->width = width; + } + } + else + { + pango_glyph_string_free(gui.ascii_glyphs); + gui.ascii_glyphs = NULL; + g_object_unref(gui.ascii_font); + gui.ascii_font = NULL; + } + } + + g_list_foreach(item_list, (GFunc)(void *)&pango_item_free, NULL); + g_list_free(item_list); + pango_attr_list_unref(attr_list); +} + + static void +get_styled_font_variants(void) +{ + PangoFontDescription *bold_font_desc; + PangoFont *plain_font; + PangoFont *bold_font; + + gui.font_can_bold = FALSE; + + plain_font = pango_context_load_font(gui.text_context, gui.norm_font); + if (plain_font == NULL) + return; + + bold_font_desc = pango_font_description_copy_static(gui.norm_font); + pango_font_description_set_weight(bold_font_desc, PANGO_WEIGHT_BOLD); + + bold_font = pango_context_load_font(gui.text_context, bold_font_desc); + if (bold_font != NULL) + { + gui.font_can_bold = (bold_font != plain_font); + g_object_unref(bold_font); + } + + pango_font_description_free(bold_font_desc); + g_object_unref(plain_font); +} + + int +gui_mch_init_font(char_u *font_name, int fontset UNUSED) +{ + PangoFontDescription *font_desc; + PangoLayout *layout; + int width; + + if (font_name == NULL) + font_name = (char_u *)DEFAULT_FONT; + + font_desc = gui_mch_get_font(font_name, FALSE); + if (font_desc == NULL) + return FAIL; + + gui_mch_free_font(gui.norm_font); + gui.norm_font = font_desc; + + pango_context_set_font_description(gui.text_context, font_desc); + + layout = pango_layout_new(gui.text_context); + pango_layout_set_text(layout, "MW", 2); + pango_layout_get_size(layout, &width, NULL); + g_object_unref(layout); + + gui.char_width = (width / 2 + PANGO_SCALE - 1) / PANGO_SCALE; + if (gui.char_width <= 0) + gui.char_width = 8; + + gui_mch_adjust_charheight(); + + hl_set_font_name(font_name); + + get_styled_font_variants(); + ascii_glyph_table_init(); + + return OK; +} + + GuiFont +gui_mch_get_font(char_u *name, int report_error) +{ + PangoFontDescription *font; + + if (name == NULL) + return NULL; + + font = pango_font_description_from_string((const char *)name); + if (font == NULL) + { + if (report_error) + semsg(_(e_unknown_font_str), name); + return NULL; + } + + // Ensure a size is set + if (pango_font_description_get_size(font) <= 0) + pango_font_description_set_size(font, 10 * PANGO_SCALE); + + return font; +} + + char_u * +gui_mch_get_fontname(GuiFont font, char_u *name UNUSED) +{ + if (font != NOFONT) + { + char *desc = pango_font_description_to_string(font); + char_u *ret = vim_strsave((char_u *)desc); + g_free(desc); + return ret; + } + return NULL; +} + + void +gui_mch_free_font(GuiFont font) +{ + if (font != NOFONT) + pango_font_description_free(font); +} + + void +gui_mch_expand_font( + optexpand_T *args, + void *param, + int (*add_match)(char_u *val)) +{ + PangoFontFamily **font_families = NULL; + int n_families = 0; + int wide = *(int *)param; + + if (args->oe_include_orig_val && *args->oe_opt_value == NUL && !wide) + { + // If guifont is empty, suggest the default so the user can modify it. + if (add_match((char_u *)DEFAULT_FONT) != OK) + return; + } + + pango_context_list_families( + gui.text_context, + &font_families, + &n_families); + + for (int i = 0; i < n_families; i++) + { + if (!wide && !pango_font_family_is_monospace(font_families[i])) + continue; + + const char *fam_name = pango_font_family_get_name(font_families[i]); + if (input_conv.vc_type != CONV_NONE) + { + char_u *buf = string_convert(&input_conv, + (char_u *)fam_name, NULL); + if (buf != NULL) + { + if (add_match(buf) != OK) + { + vim_free(buf); + break; + } + vim_free(buf); + } + else + break; + } + else + { + if (add_match((char_u *)fam_name) != OK) + break; + } + } + + g_free(font_families); +} + +/* + * ============================================================ + * Color handling + * ============================================================ + */ + + guicolor_T +gui_mch_get_color(char_u *name) +{ + if (!gui.in_use) + return INVALCOLOR; + + if (name != NULL) + return gui_get_color_cmn(name); + + return INVALCOLOR; +} + + guicolor_T +gui_mch_get_rgb_color(int r, int g, int b) +{ + return gui_get_rgb_color_cmn(r, g, b); +} + + static GdkRGBA +color_to_rgba(guicolor_T color) +{ + GdkRGBA rgba; + rgba.red = ((color & 0xff0000) >> 16) / 255.0; + rgba.green = ((color & 0xff00) >> 8) / 255.0; + rgba.blue = (color & 0xff) / 255.0; + rgba.alpha = 1.0; + return rgba; +} + + void +gui_mch_set_fg_color(guicolor_T color) +{ + *gui.fgcolor = color_to_rgba(color); +} + + void +gui_mch_set_bg_color(guicolor_T color) +{ + *gui.bgcolor = color_to_rgba(color); +} + + void +gui_mch_set_sp_color(guicolor_T color) +{ + *gui.spcolor = color_to_rgba(color); +} + + guicolor_T +gui_mch_get_rgb(guicolor_T pixel) +{ + return pixel; +} + +/* + * ============================================================ + * Drawing + * ============================================================ + */ + +static void set_cairo_source_from_pixel(cairo_t *cr, guicolor_T pixel); + + static void +draw_event(GtkDrawingArea *area UNUSED, cairo_t *cr, + int width, int height, gpointer data UNUSED) +{ + // Surface creation/resizing is handled by drawarea_resize_cb. + // Here we only paint the surface to the widget. + + // Fill background with Vim's background color + set_cairo_source_from_pixel(cr, gui.back_pixel); + cairo_rectangle(cr, 0, 0, width, height); + cairo_fill(cr); + + // Paint the Vim surface on top + if (gui.surface != NULL) + { + cairo_set_source_surface(cr, gui.surface, 0, 0); + cairo_paint(cr); + } +} + + static void +set_cairo_source_from_pixel(cairo_t *cr, guicolor_T pixel) +{ + cairo_set_source_rgb(cr, + ((pixel & 0xff0000) >> 16) / 255.0, + ((pixel & 0xff00) >> 8) / 255.0, + (pixel & 0xff) / 255.0); +} + + void +gui_mch_clear_block(int row1, int col1, int row2, int col2) +{ + cairo_t *cr; + + if (gui.surface == NULL) + return; + + cr = cairo_create(gui.surface); + set_cairo_source_from_pixel(cr, gui.back_pixel); + cairo_rectangle(cr, + FILL_X(col1), FILL_Y(row1), + (col2 - col1 + 1) * gui.char_width, + (row2 - row1 + 1) * gui.char_height); + cairo_fill(cr); + cairo_destroy(cr); + + if (gui.drawarea != NULL) + gtk_widget_queue_draw(gui.drawarea); +} + + void +gui_mch_clear_all(void) +{ + cairo_t *cr; + + if (gui.surface == NULL) + return; + + cr = cairo_create(gui.surface); + set_cairo_source_from_pixel(cr, gui.back_pixel); + cairo_paint(cr); + cairo_destroy(cr); + + if (gui.drawarea != NULL) + gtk_widget_queue_draw(gui.drawarea); +} + + static void +surface_copy_rect(int dest_x, int dest_y, + int src_x, int src_y, + int width, int height) +{ + cairo_t *cr; + cairo_surface_t *tmp; + + if (gui.surface == NULL || width <= 0 || height <= 0) + return; + + // Use a temporary surface to avoid overlap issues + tmp = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + cr = cairo_create(tmp); + cairo_set_source_surface(cr, gui.surface, -src_x, -src_y); + cairo_paint(cr); + cairo_destroy(cr); + + cr = cairo_create(gui.surface); + cairo_set_source_surface(cr, tmp, dest_x, dest_y); + cairo_paint(cr); + cairo_destroy(cr); + cairo_surface_destroy(tmp); +} + + void +gui_mch_delete_lines(int row, int num_lines) +{ + int ncols = gui.scroll_region_right - gui.scroll_region_left + 1; + int nrows = gui.scroll_region_bot - row + 1; + int src_nrows = nrows - num_lines; + + surface_copy_rect( + FILL_X(gui.scroll_region_left), FILL_Y(row), + FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines), + gui.char_width * ncols + 1, gui.char_height * src_nrows); + gui_clear_block( + gui.scroll_region_bot - num_lines + 1, gui.scroll_region_left, + gui.scroll_region_bot, gui.scroll_region_right); + + gtk_widget_queue_draw(gui.drawarea); +} + + void +gui_mch_insert_lines(int row, int num_lines) +{ + int ncols = gui.scroll_region_right - gui.scroll_region_left + 1; + int nrows = gui.scroll_region_bot - row + 1; + int src_nrows = nrows - num_lines; + + surface_copy_rect( + FILL_X(gui.scroll_region_left), FILL_Y(row + num_lines), + FILL_X(gui.scroll_region_left), FILL_Y(row), + gui.char_width * ncols + 1, gui.char_height * src_nrows); + gui_clear_block( + row, gui.scroll_region_left, + row + num_lines - 1, gui.scroll_region_right); + + gtk_widget_queue_draw(gui.drawarea); +} + + void +gui_mch_draw_hollow_cursor(guicolor_T color) +{ + cairo_t *cr; + int i = 1; + + if (gui.surface == NULL) + return; + + cr = cairo_create(gui.surface); + gui_mch_set_fg_color(color); + cairo_set_source_rgba(cr, + gui.fgcolor->red, gui.fgcolor->green, + gui.fgcolor->blue, gui.fgcolor->alpha); + if (mb_lefthalve(gui.row, gui.col)) + i = 2; + cairo_set_line_width(cr, 1.0); + cairo_rectangle(cr, + FILL_X(gui.col) + 0.5, FILL_Y(gui.row) + 0.5, + i * gui.char_width - 1, gui.char_height - 1); + cairo_stroke(cr); + cairo_destroy(cr); + + gtk_widget_queue_draw(gui.drawarea); +} + + void +gui_mch_draw_part_cursor(int w, int h, guicolor_T color) +{ + cairo_t *cr; + + if (gui.surface == NULL) + return; + + gui_mch_set_fg_color(color); + cr = cairo_create(gui.surface); + cairo_set_source_rgba(cr, + gui.fgcolor->red, gui.fgcolor->green, + gui.fgcolor->blue, gui.fgcolor->alpha); + cairo_rectangle(cr, +#ifdef FEAT_RIGHTLEFT + CURSOR_BAR_RIGHT ? FILL_X(gui.col + 1) - w : +#endif + FILL_X(gui.col), FILL_Y(gui.row) + gui.char_height - h, + w, h); + cairo_fill(cr); + cairo_destroy(cr); + + gtk_widget_queue_draw(gui.drawarea); +} + + void +gui_mch_flash(int msec) +{ + // Invert the screen, wait, then invert back + if (gui.surface == NULL) + return; + + gui_mch_invert_rectangle(0, 0, (int)Rows - 1, (int)Columns - 1); + gui_mch_flush(); + ui_delay((long)msec, TRUE); + gui_mch_invert_rectangle(0, 0, (int)Rows - 1, (int)Columns - 1); +} + + void +gui_mch_invert_rectangle(int r, int c, int nr, int nc) +{ + cairo_t *cr; + + if (gui.surface == NULL) + return; + + cr = cairo_create(gui.surface); + cairo_set_operator(cr, CAIRO_OPERATOR_DIFFERENCE); + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_rectangle(cr, + FILL_X(c), FILL_Y(r), + (nc + 1) * gui.char_width, (nr + 1) * gui.char_height); + cairo_fill(cr); + cairo_destroy(cr); + + gtk_widget_queue_draw(gui.drawarea); +} + +/* + * ============================================================ + * Event handling + * ============================================================ + */ + + static gboolean +key_press_event(GtkEventControllerKey *controller UNUSED, + guint key_sym, guint keycode UNUSED, + GdkModifierType state, gpointer data UNUSED) +{ + char_u string[32], string2[32]; + int len; + int i; + int modifiers; + int key; + char_u *s, *d; + +#ifdef FEAT_XIM + // Let the input method have a go at the key event. + // If it consumed the event, we're done. + if (xic != NULL) + { + GdkEvent *event = gtk_event_controller_get_current_event( + GTK_EVENT_CONTROLLER(controller)); + if (event != NULL && gtk_im_context_filter_keypress(xic, event)) + return TRUE; + } +#endif + + len = keyval_to_string(key_sym, string2); + + if (len > 1 && input_conv.vc_type != CONV_NONE) + len = convert_input(string2, len, sizeof(string2)); + + s = string2; + d = string; + for (i = 0; i < len; ++i) + { + *d++ = s[i]; + if (d[-1] == CSI && d + 2 < string + sizeof(string)) + { + *d++ = KS_EXTRA; + *d++ = (int)KE_CSI; + } + } + len = d - string; + + // Shift-Tab results in Left_Tab + if (key_sym == GDK_KEY_ISO_Left_Tab) + { + key_sym = GDK_KEY_Tab; + state |= GDK_SHIFT_MASK; + } + + // Check for special keys + if (len == 0 || len == 1) + { + for (i = 0; special_keys[i].key_sym != 0; i++) + { + if (special_keys[i].key_sym == key_sym) + { + string[0] = CSI; + string[1] = special_keys[i].code0; + string[2] = special_keys[i].code1; + len = -3; + break; + } + } + } + + if (len == 0) + return TRUE; + + if (len == -3) + key = TO_SPECIAL(string[1], string[2]); + else + { + string[len] = NUL; + key = mb_ptr2char(string); + } + + modifiers = modifiers_gdk2vim(state); + + key = simplify_key(key, &modifiers); + if (key == CSI) + key = K_CSI; + if (IS_SPECIAL(key)) + { + string[0] = CSI; + string[1] = K_SECOND(key); + string[2] = K_THIRD(key); + len = 3; + } + else + { + key = may_adjust_key_for_ctrl(modifiers, key); + modifiers = may_remove_shift_modifier(modifiers, key); + len = mb_char2bytes(key, string); + } + + if (modifiers != 0) + { + string2[0] = CSI; + string2[1] = KS_MODIFIER; + string2[2] = modifiers; + add_to_input_buf(string2, 3); + } + + { + int int_ch = check_for_interrupt(key, modifiers); + if (int_ch != NUL) + { + trash_input_buf(); + string[0] = int_ch; + len = 1; + } + } + + add_to_input_buf(string, len); + + if (p_mh) + gui_mch_mousehide(TRUE); + + return TRUE; +} + + static void +key_release_event(GtkEventControllerKey *controller UNUSED, + guint keyval UNUSED, guint keycode UNUSED, + GdkModifierType state UNUSED, gpointer data UNUSED) +{ +} + +static int mouse_timed_out = TRUE; +static guint mouse_click_timer = 0; + + static timeout_cb_type +mouse_click_timer_cb(gpointer data) +{ + *(int *)data = TRUE; + return FALSE; +} + + static int +modifiers_gdk2mouse(guint state) +{ + int modifiers = 0; + + if (state & GDK_SHIFT_MASK) + modifiers |= MOUSE_SHIFT; + if (state & GDK_CONTROL_MASK) + modifiers |= MOUSE_CTRL; + if (state & GDK_ALT_MASK) + modifiers |= MOUSE_ALT; + + return modifiers; +} + +// Track which mouse button is currently pressed for drag detection. +// GtkEventControllerMotion's modifier state may not include button masks +// on all backends (e.g. Wayland), so we track it ourselves. +// -1 means no button is pressed (MOUSE_LEFT is 0x00, so can't use 0). +static int mouse_pressed_button = -1; + + static void +button_press_event(GtkGestureClick *gesture, int n_press UNUSED, + double x, double y, gpointer data UNUSED) +{ + int button; + int repeated_click = FALSE; + int_u vim_modifiers; + guint btn; + GdkModifierType state; + GdkEvent *event; + + event = gtk_event_controller_get_current_event( + GTK_EVENT_CONTROLLER(gesture)); + state = gdk_event_get_modifier_state(event); + btn = gdk_button_event_get_button(event); + + if (!mouse_timed_out && mouse_click_timer) + { + timeout_remove(mouse_click_timer); + mouse_click_timer = 0; + repeated_click = TRUE; + } + + mouse_timed_out = FALSE; + mouse_click_timer = timeout_add(p_mouset, mouse_click_timer_cb, + &mouse_timed_out); + + switch (btn) + { + case 1: button = MOUSE_LEFT; break; + case 2: button = MOUSE_MIDDLE; break; + case 3: button = MOUSE_RIGHT; break; + case 8: button = MOUSE_X1; break; + case 9: button = MOUSE_X2; break; + default: return; + } + + mouse_pressed_button = button; + vim_modifiers = modifiers_gdk2mouse(state); + gui_send_mouse_event(button, (int)x, (int)y, repeated_click, vim_modifiers); +} + + static void +button_release_event(GtkGestureClick *gesture, int n_press UNUSED, + double x, double y, gpointer data UNUSED) +{ + int vim_modifiers; + GdkModifierType state; + GdkEvent *event; + + event = gtk_event_controller_get_current_event( + GTK_EVENT_CONTROLLER(gesture)); + state = gdk_event_get_modifier_state(event); + vim_modifiers = modifiers_gdk2mouse(state); + + mouse_pressed_button = -1; + gui_send_mouse_event(MOUSE_RELEASE, (int)x, (int)y, FALSE, vim_modifiers); +} + + static void +motion_notify_event(GtkEventControllerMotion *controller UNUSED, + double x, double y, gpointer data UNUSED) +{ + if (mouse_pressed_button >= 0) + { + GdkModifierType state; + GdkEvent *event; + + event = gtk_event_controller_get_current_event( + GTK_EVENT_CONTROLLER(controller)); + if (event != NULL) + { + state = gdk_event_get_modifier_state(event); + gui_send_mouse_event(MOUSE_DRAG, (int)x, (int)y, + FALSE, modifiers_gdk2mouse(state)); + } + } + + if (p_mh) + gui_mch_mousehide(FALSE); +} + + static void +enter_notify_event(GtkEventControllerMotion *controller UNUSED, + double x UNUSED, double y UNUSED, gpointer data UNUSED) +{ + if (blink_state == BLINK_NONE) + gui_mch_start_blink(); + + // Make sure keyboard input goes to the drawing area. + if (!gtk_widget_has_focus(gui.drawarea)) + gtk_widget_grab_focus(gui.drawarea); +} + + static void +leave_notify_event(GtkEventControllerMotion *controller UNUSED, + gpointer data UNUSED) +{ + if (blink_state != BLINK_NONE) + gui_mch_stop_blink(TRUE); +} + + static gboolean +scroll_event(GtkEventControllerScroll *controller UNUSED, + double dx UNUSED, double dy, gpointer data UNUSED) +{ + int button; + int_u vim_modifiers; + GdkModifierType state; + GdkEvent *event; + + event = gtk_event_controller_get_current_event( + GTK_EVENT_CONTROLLER(controller)); + if (event == NULL) + return FALSE; + state = gdk_event_get_modifier_state(event); + + if (dy < 0) + button = MOUSE_4; // scroll up + else if (dy > 0) + button = MOUSE_5; // scroll down + else if (dx < 0) + button = MOUSE_7; // scroll left + else if (dx > 0) + button = MOUSE_6; // scroll right + else + return FALSE; + + vim_modifiers = modifiers_gdk2mouse(state); + + { + double mx, my; + gdk_event_get_position(event, &mx, &my); + gui_send_mouse_event(button, (int)mx, (int)my, FALSE, vim_modifiers); + } + + return TRUE; +} + + static void +focus_in_event(GtkEventControllerFocus *controller UNUSED, + gpointer data UNUSED) +{ + gui_focus_change(TRUE); + if (blink_state == BLINK_NONE) + gui_mch_start_blink(); +} + + static void +focus_out_event(GtkEventControllerFocus *controller UNUSED, + gpointer data UNUSED) +{ + gui_focus_change(FALSE); + if (blink_state != BLINK_NONE) + gui_mch_stop_blink(TRUE); +} + + static void +drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED) +{ + int w, h; + + // Use formwin size since drawarea may not have its final size yet + if (gui.formwin != NULL) + { + w = gtk_widget_get_width(gui.formwin); + h = gtk_widget_get_height(gui.formwin); + } + else + { + w = gtk_widget_get_width(widget); + h = gtk_widget_get_height(widget); + } + + if (w <= 0) w = 800; + if (h <= 0) h = 600; + + if (gui.surface != NULL) + cairo_surface_destroy(gui.surface); + gui.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + + gui_mch_new_colors(); + +#ifdef FEAT_XIM + xim_init(); +#endif +} + + static void +drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED) +{ + if (gui.surface != NULL) + { + cairo_surface_destroy(gui.surface); + gui.surface = NULL; + } +} + + static void +drawarea_resize_cb(GtkDrawingArea *area UNUSED, int width, int height, + gpointer data UNUSED) +{ + cairo_t *cr; + + if (width <= 0 || height <= 0) + return; + + if (gui.surface != NULL) + { + int sw = cairo_image_surface_get_width(gui.surface); + int sh = cairo_image_surface_get_height(gui.surface); + + if (sw == width && sh == height) + return; + + cairo_surface_destroy(gui.surface); + } + + // Create a fresh surface filled with the background color. + // Do not copy old surface content: gui_resize_shell() will trigger + // a full redraw, and stale content (e.g. intro screen text) would + // otherwise remain as ghost artifacts. + gui.surface = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, width, height); + cr = cairo_create(gui.surface); + set_cairo_source_from_pixel(cr, gui.back_pixel); + cairo_paint(cr); + cairo_destroy(cr); + + // Notify Vim about the new size - this will cause a full redraw + gui_resize_shell(width, height); +} + +#ifdef FEAT_DND +/* + * Drag-and-drop handler for files and text. + */ + static gboolean +drop_cb(GtkDropTarget *target UNUSED, const GValue *value, + double x, double y, gpointer data UNUSED) +{ + if (G_VALUE_HOLDS(value, GDK_TYPE_FILE_LIST)) + { + GSList *files = g_value_get_boxed(value); + int nfiles = g_slist_length(files); + char_u **fnames; + int i; + + if (nfiles <= 0) + return FALSE; + + fnames = ALLOC_MULT(char_u *, nfiles); + if (fnames == NULL) + return FALSE; + + i = 0; + for (GSList *l = files; l != NULL; l = l->next) + { + GFile *file = l->data; + char *path = g_file_get_path(file); + if (path != NULL) + fnames[i++] = vim_strsave((char_u *)path); + g_free(path); + } + nfiles = i; + + if (nfiles > 0) + gui_handle_drop((int)x, (int)y, 0, fnames, nfiles); + else + vim_free(fnames); + + return TRUE; + } + else if (G_VALUE_HOLDS(value, G_TYPE_STRING)) + { + const char *text = g_value_get_string(value); + char_u dropkey[6] = {CSI, KS_MODIFIER, 0, + CSI, KS_EXTRA, (char_u)KE_DROP}; + + if (text == NULL || *text == NUL) + return FALSE; + + dnd_yank_drag_data((char_u *)text, (long)STRLEN(text)); + add_to_input_buf(dropkey + 3, 3); + + return TRUE; + } + + return FALSE; +} +#endif + + static void +mainwin_destroy_cb(GObject *object UNUSED, gpointer data UNUSED) +{ + gui.mainwin = NULL; + gui.drawarea = NULL; + if (!exiting) + gui_shell_closed(); +} + + static gboolean +delete_event_cb(GtkWindow *window UNUSED, gpointer data UNUSED) +{ + gui_shell_closed(); + return TRUE; +} + +/* + * ============================================================ + * Misc functions + * ============================================================ + */ + + static timeout_cb_type +input_timer_cb(gpointer data) +{ + int *timed_out = (int *)data; + + *timed_out = TRUE; + return FALSE; // don't call me again +} + + void +gui_mch_update(void) +{ + int cnt = 0; + + while (g_main_context_pending(NULL) && !vim_is_input_buf_full() + && ++cnt < 100) + g_main_context_iteration(NULL, TRUE); +} + + int +gui_mch_wait_for_chars(long wtime) +{ + int focus; + guint timer; + static int timed_out; + int retval = FAIL; + + timed_out = FALSE; + + if (wtime >= 0) + timer = timeout_add(wtime == 0 ? 1L : wtime, + input_timer_cb, &timed_out); + else + timer = 0; + + focus = gui.in_focus; + + do + { + // Stop or start blinking when focus changes + if (gui.in_focus != focus) + { + if (gui.in_focus) + gui_mch_start_blink(); + else + gui_mch_stop_blink(TRUE); + focus = gui.in_focus; + } + +#ifdef MESSAGE_QUEUE +# ifdef FEAT_TIMERS + did_add_timer = FALSE; +# endif + parse_queued_messages(); +# ifdef FEAT_TIMERS + if (did_add_timer) + goto theend; +# endif +#endif + + if (gui.mainwin == NULL) + goto theend; + + // gtk_main_quit() is a wake-up request; consume it so later + // waits resume. + if (gtk4_main_loop_quit) + { + gtk4_main_loop_quit = FALSE; + goto theend; + } + + if (!input_available()) + { + ++gtk4_main_loop_level; + g_main_context_iteration(NULL, TRUE); + --gtk4_main_loop_level; + } + + if (input_available()) + { + retval = OK; + goto theend; + } + } while (wtime < 0 || !timed_out); + + gui_mch_update(); + +theend: + if (timer != 0 && !timed_out) + timeout_remove(timer); + + return retval; +} + + void +gui_mch_flush(void) +{ + // Ensure the offscreen surface content gets painted to the widget. + if (gui.drawarea != NULL) + gtk_widget_queue_draw(gui.drawarea); + if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin)) + gdk_display_flush(gtk_widget_get_display(gui.mainwin)); +} + + void +gui_mch_beep(void) +{ + GdkDisplay *display; + + if (gui.mainwin != NULL && gtk_widget_get_realized(gui.mainwin)) + { + display = gtk_widget_get_display(gui.mainwin); + if (display != NULL) + gdk_display_beep(display); + } +} + + void * +gui_mch_get_display(void) +{ + if (gui.mainwin != NULL && gtk_widget_get_display(gui.mainwin)) + return gtk_widget_get_display(gui.mainwin); + return NULL; +} + + void +gui_mch_iconify(void) +{ + gtk_window_minimize(GTK_WINDOW(gui.mainwin)); +} + + void +gui_mch_set_foreground(void) +{ + gtk_window_present(GTK_WINDOW(gui.mainwin)); +} + + void +gui_mch_getmouse(int *x, int *y) +{ + *x = 0; + *y = 0; + // GTK4: No reliable way to query pointer position synchronously. +} + + void +gui_mch_setmouse(int x UNUSED, int y UNUSED) +{ + // GTK4/Wayland: cannot warp pointer +} + + void +gui_mch_mousehide(int hide) +{ + if (gui.pointer_hidden == hide) + return; + + gui.pointer_hidden = hide; + if (gui.blank_pointer != NULL) + { + if (hide) + gtk_widget_set_cursor(gui.drawarea, gui.blank_pointer); + else +#ifdef FEAT_MOUSESHAPE + mch_set_mouse_shape(last_shape); +#else + gtk_widget_set_cursor(gui.drawarea, NULL); +#endif + } +} + + int +gui_mch_haskey(char_u *name) +{ + int i; + + for (i = 0; special_keys[i].key_sym != 0; i++) + if (name[0] == special_keys[i].code0 + && name[1] == special_keys[i].code1) + return OK; + return FAIL; +} + + void +gui_mch_forked(void) +{ +} + +/* + * ============================================================ + * Scrollbar + * ============================================================ + */ + + void +gui_mch_enable_scrollbar(scrollbar_T *sb, int flag) +{ + if (sb->id != NULL) + gtk_widget_set_visible(sb->id, flag); +} + +/* + * ============================================================ + * Menu stubs + * ============================================================ + */ + + void +gui_mch_menu_grey(vimmenu_T *menu, int grey) +{ + if (menu->id == NULL || menu_action_group == NULL) + return; + + // For toolbar items, use gtk_widget_set_sensitive + if (menu->parent != NULL && menu_is_toolbar(menu->parent->name)) + { + if (menu->id != (GtkWidget *)1) + gtk_widget_set_sensitive(menu->id, !grey); + return; + } + + // For menu items, enable/disable the GSimpleAction + if (menu->label != NULL) + { + GAction *action = g_action_map_lookup_action( + G_ACTION_MAP(menu_action_group), + (const char *)menu->label); + if (action != NULL) + g_simple_action_set_enabled(G_SIMPLE_ACTION(action), !grey); + } +} + + void +gui_mch_menu_hidden(vimmenu_T *menu UNUSED, int hidden UNUSED) +{ + // No-op: menu system not yet implemented for GTK4. +} + + void +gui_mch_draw_menubar(void) +{ + // No-op: menu system not yet implemented for GTK4. +} + +/* + * ============================================================ + * Tabline + * ============================================================ + */ + +#ifdef FEAT_GUI_TABLINE + void +gui_mch_show_tabline(int showit) +{ + if (gui.tabline != NULL) + gtk_widget_set_visible(gui.tabline, showit); +} + + int +gui_mch_showing_tabline(void) +{ + return gui.tabline != NULL && gtk_widget_get_visible(gui.tabline); +} + +static int ignore_tabline_evt = FALSE; + + void +gui_mch_update_tabline(void) +{ + GtkWidget *page; + GtkWidget *event_box; + GtkWidget *label; + tabpage_T *tp; + int nr = 0; + int tab_num; + int curtabidx = 0; + char_u *labeltext; + + if (gui.tabline == NULL) + return; + + ignore_tabline_evt = TRUE; + + for (tp = first_tabpage; tp != NULL; tp = tp->tp_next, ++nr) + { + if (tp == curtab) + curtabidx = nr; + + tab_num = nr + 1; + + page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr); + if (page == NULL) + { + page = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_set_visible(page, TRUE); + event_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_set_visible(event_box, TRUE); + label = gtk_label_new("-Empty-"); + gtk_box_append(GTK_BOX(event_box), label); + gtk_widget_set_visible(label, TRUE); + gtk_notebook_insert_page(GTK_NOTEBOOK(gui.tabline), + page, event_box, nr++); + gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(gui.tabline), + page, TRUE); + } + + event_box = gtk_notebook_get_tab_label(GTK_NOTEBOOK(gui.tabline), page); + g_object_set_data(G_OBJECT(event_box), "tab_num", + GINT_TO_POINTER(tab_num)); + label = gtk_widget_get_first_child(event_box); + get_tabline_label(tp, FALSE); + labeltext = CONVERT_TO_UTF8(NameBuff); + if (label != NULL && GTK_IS_LABEL(label)) + gtk_label_set_text(GTK_LABEL(label), (const char *)labeltext); + CONVERT_TO_UTF8_FREE(labeltext); + + get_tabline_label(tp, TRUE); + labeltext = CONVERT_TO_UTF8(NameBuff); + gtk_widget_set_tooltip_text(event_box, (const gchar *)labeltext); + CONVERT_TO_UTF8_FREE(labeltext); + } + + while (gtk_notebook_get_nth_page(GTK_NOTEBOOK(gui.tabline), nr) != NULL) + gtk_notebook_remove_page(GTK_NOTEBOOK(gui.tabline), nr); + + if (gtk_notebook_get_current_page(GTK_NOTEBOOK(gui.tabline)) != curtabidx) + gtk_notebook_set_current_page(GTK_NOTEBOOK(gui.tabline), curtabidx); + + ignore_tabline_evt = FALSE; +} + + void +gui_mch_set_curtab(int nr) +{ + if (gui.tabline != NULL) + gtk_notebook_set_current_page(GTK_NOTEBOOK(gui.tabline), nr - 1); +} +#endif + +/* + * ============================================================ + * Sign support + * ============================================================ + */ + +#if defined(FEAT_SIGN_ICONS) +# define SIGN_WIDTH (2 * gui.char_width) +# define SIGN_HEIGHT (gui.char_height) + + void +gui_mch_drawsign(int row, int col, int typenr) +{ + GdkPixbuf *sign; + cairo_t *cr; + int width, height; + + sign = (GdkPixbuf *)sign_get_image(typenr); + if (sign == NULL || gui.surface == NULL) + return; + + cr = cairo_create(gui.surface); + + width = gdk_pixbuf_get_width(sign); + height = gdk_pixbuf_get_height(sign); + + // Scale to fit the sign area if needed + if (width != SIGN_WIDTH || height != SIGN_HEIGHT) + { + GdkPixbuf *scaled = gdk_pixbuf_scale_simple(sign, + SIGN_WIDTH, SIGN_HEIGHT, GDK_INTERP_BILINEAR); + if (scaled != NULL) + { + gdk_cairo_set_source_pixbuf(cr, scaled, + FILL_X(col), FILL_Y(row)); + g_object_unref(scaled); + } + else + gdk_cairo_set_source_pixbuf(cr, sign, + FILL_X(col), FILL_Y(row)); + } + else + gdk_cairo_set_source_pixbuf(cr, sign, + FILL_X(col), FILL_Y(row)); + + cairo_paint(cr); + cairo_destroy(cr); + + gtk_widget_queue_draw(gui.drawarea); +} + + void * +gui_mch_register_sign(char_u *signfile) +{ + if (signfile[0] != NUL && signfile[0] != '-' && gui.in_use) + { + GdkPixbuf *sign; + GError *error = NULL; + + sign = gdk_pixbuf_new_from_file((const char *)signfile, &error); + if (error == NULL) + return sign; + + semsg("E255: %s", error->message); + g_error_free(error); + } + return NULL; +} + + void +gui_mch_destroy_sign(void *sign) +{ + if (sign != NULL) + g_object_unref(sign); +} +#endif + +/* + * ============================================================ + * Stubs for functions not yet implemented or not applicable in GTK4 + * ============================================================ + */ + +/* + * Ligature and text drawing support. + * Ported from gui_gtk_x11.c (GTK3) to support 'guiligatures' in GTK4. + */ + +#define INSERT_PANGO_ATTR(Attribute, AttrList, Start, End) \ + G_STMT_START{ \ + PangoAttribute *tmp_attr_; \ + tmp_attr_ = (Attribute); \ + tmp_attr_->start_index = (Start); \ + tmp_attr_->end_index = (End); \ + pango_attr_list_insert((AttrList), tmp_attr_); \ + }G_STMT_END + +/* + * Apply the 'guifontwide' font to double-width characters in the string. + */ + static void +apply_wide_font_attr(char_u *s, int len, PangoAttrList *attr_list) +{ + char_u *start = NULL; + char_u *p; + int uc; + + for (p = s; p < s + len; p += utf_byte2len(*p)) + { + uc = utf_ptr2char(p); + + if (start == NULL) + { + if (uc >= 0x80 && utf_char2cells(uc) == 2) + start = p; + } + else if (uc < 0x80 + || (utf_char2cells(uc) != 2 && !utf_iscomposing(uc))) + { + INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font), + attr_list, start - s, p - s); + start = NULL; + } + } + + if (start != NULL) + INSERT_PANGO_ATTR(pango_attr_font_desc_new(gui.wide_font), + attr_list, start - s, len); +} + +/* + * Count the number of display cells occupied by a glyph cluster. + */ + static int +count_cluster_cells(char_u *s, PangoItem *item, + PangoGlyphString *glyphs, int i, + int *cluster_width, + int *last_glyph_rbearing) +{ + char_u *p; + int next; + int start, end; + int width; + int uc; + int cellcount = 0; + + width = glyphs->glyphs[i].geometry.width; + + for (next = i + 1; next < glyphs->num_glyphs; ++next) + { + if (glyphs->glyphs[next].attr.is_cluster_start) + break; + else if (glyphs->glyphs[next].geometry.width > width) + width = glyphs->glyphs[next].geometry.width; + } + + start = item->offset + glyphs->log_clusters[i]; + end = item->offset + ((next < glyphs->num_glyphs) ? + glyphs->log_clusters[next] : item->length); + + for (p = s + start; p < s + end; p += utf_byte2len(*p)) + { + uc = utf_ptr2char(p); + if (uc < 0x80) + ++cellcount; + else if (!utf_iscomposing(uc)) + cellcount += utf_char2cells(uc); + } + + if (last_glyph_rbearing != NULL + && cellcount > 0 && next == glyphs->num_glyphs) + { + PangoRectangle ink_rect; + + pango_font_get_glyph_extents(item->analysis.font, + glyphs->glyphs[i].glyph, + &ink_rect, NULL); + + if (PANGO_RBEARING(ink_rect) > 0) + *last_glyph_rbearing = PANGO_RBEARING(ink_rect); + } + + if (cellcount > 0) + *cluster_width = width; + + return cellcount; +} + +/* + * Handle combining characters that form a zero-width cluster. + */ + static void +setup_zero_width_cluster(PangoItem *item, PangoGlyphInfo *glyph, + int last_cellcount, int last_cluster_width, + int last_glyph_rbearing) +{ + PangoRectangle ink_rect; + PangoRectangle logical_rect; + int width; + + width = last_cellcount * gui.char_width * PANGO_SCALE; + glyph->geometry.x_offset = -width + MAX(0, width - last_cluster_width) / 2; + glyph->geometry.width = 0; + + pango_font_get_glyph_extents(item->analysis.font, + glyph->glyph, + &ink_rect, &logical_rect); + if (ink_rect.x < 0) + { + glyph->geometry.x_offset += last_glyph_rbearing; + glyph->geometry.y_offset = logical_rect.height + - (gui.char_height - p_linespace) * PANGO_SCALE; + } + else + glyph->geometry.x_offset = -width + MAX(0, width - ink_rect.width) / 2; +} + +/* + * Draw a single glyph string segment: background, foreground, and fake bold. + */ + static void +draw_glyph_string(int row, int col, int num_cells, int flags, + PangoFont *font, PangoGlyphString *glyphs, + cairo_t *cr) +{ + if (!(flags & DRAW_TRANSP)) + { + cairo_set_source_rgba(cr, + gui.bgcolor->red, gui.bgcolor->green, gui.bgcolor->blue, + gui.bgcolor->alpha); + cairo_rectangle(cr, + FILL_X(col), FILL_Y(row), + num_cells * gui.char_width, gui.char_height); + cairo_fill(cr); + } + + cairo_set_source_rgba(cr, + gui.fgcolor->red, gui.fgcolor->green, gui.fgcolor->blue, + gui.fgcolor->alpha); + cairo_move_to(cr, TEXT_X(col), TEXT_Y(row)); + pango_cairo_show_glyph_string(cr, font, glyphs); + + // Redraw with offset of 1 to emulate bold + if ((flags & DRAW_BOLD) && !gui.font_can_bold) + { + cairo_move_to(cr, TEXT_X(col) + 1, TEXT_Y(row)); + pango_cairo_show_glyph_string(cr, font, glyphs); + } +} + +/* + * Draw underline, undercurl, and strikethrough decorations. + */ + static void +draw_under(int flags, int row, int col, int cells, cairo_t *cr) +{ + // Draw underline + if (flags & DRAW_UNDERL) + { + int y = FILL_Y(row + 1) - 1; + cairo_set_source_rgba(cr, + gui.fgcolor->red, gui.fgcolor->green, + gui.fgcolor->blue, gui.fgcolor->alpha); + cairo_set_line_width(cr, 1.0); + cairo_move_to(cr, FILL_X(col), y + 0.5); + cairo_line_to(cr, FILL_X(col + cells), y + 0.5); + cairo_stroke(cr); + } + + // Draw undercurl + if (flags & DRAW_UNDERC) + { + static const int val[8] = {1, 0, 0, 0, 1, 2, 2, 2}; + int y = FILL_Y(row + 1) - 1; + int i, offset; + + cairo_set_line_width(cr, 1.0); + cairo_set_source_rgba(cr, + gui.spcolor->red, gui.spcolor->green, + gui.spcolor->blue, gui.spcolor->alpha); + cairo_move_to(cr, FILL_X(col) + 1, y - 2 + 0.5); + for (i = FILL_X(col) + 1; i < FILL_X(col + cells); ++i) + { + offset = val[i % 8]; + cairo_line_to(cr, i, y - offset + 0.5); + } + cairo_stroke(cr); + } + + // Draw strikethrough + if (flags & DRAW_STRIKE) + { + int y = FILL_Y(row) + gui.char_height / 2; + cairo_set_source_rgba(cr, + gui.fgcolor->red, gui.fgcolor->green, + gui.fgcolor->blue, gui.fgcolor->alpha); + cairo_set_line_width(cr, 1.0); + cairo_move_to(cr, FILL_X(col), y + 0.5); + cairo_line_to(cr, FILL_X(col + cells), y + 0.5); + cairo_stroke(cr); + } +} + +/* + * Draw a string of characters on the screen. + * "force_pango" is set when ligature characters require Pango shaping + * instead of the fast ASCII glyph cache path. + * Returns the number of display cells used. + */ + int +gui_gtk_draw_string_ext( + int row, + int col, + char_u *s, + int len, + int flags, + int force_pango) +{ + GdkRectangle area; + PangoGlyphString *glyphs; + int column_offset = 0; + int i; + cairo_t *cr; + + if (gui.text_context == NULL || gui.surface == NULL) + return len; + + // Restrict all drawing to the current screen line. + area.x = gui.border_offset; + area.y = FILL_Y(row); + area.width = gui.num_cols * gui.char_width; + area.height = gui.char_height; + + cr = cairo_create(gui.surface); + cairo_rectangle(cr, area.x, area.y, area.width, area.height); + cairo_clip(cr); + + glyphs = pango_glyph_string_new(); + + // Fast path for pure ASCII: use cached glyph table. + // Skip this path when force_pango is set (ligatures need shaping). + if (!(flags & DRAW_ITALIC) + && !((flags & DRAW_BOLD) && gui.font_can_bold) + && gui.ascii_glyphs != NULL + && !force_pango) + { + char_u *p; + + for (p = s; p < s + len; ++p) + if (*p & 0x80) + goto not_ascii; + + pango_glyph_string_set_size(glyphs, len); + + for (i = 0; i < len; ++i) + { + glyphs->glyphs[i] = gui.ascii_glyphs->glyphs[2 * s[i]]; + glyphs->log_clusters[i] = i; + } + + draw_glyph_string(row, col, len, flags, gui.ascii_font, glyphs, cr); + + column_offset = len; + } + else +not_ascii: + { + PangoAttrList *attr_list; + GList *item_list; + int cluster_width; + int last_glyph_rbearing; + int cells = 0; + + // Safety check: pango crashes with invalid utf-8. + if (!utf_valid_string(s, s + len)) + { + column_offset = len; + goto skipitall; + } + + cluster_width = PANGO_SCALE * gui.char_width; + last_glyph_rbearing = PANGO_SCALE * gui.char_width; + + attr_list = pango_attr_list_new(); + + // If 'guifontwide' is set then use that for double-width characters. + if (gui.wide_font != NULL) + apply_wide_font_attr(s, len, attr_list); + + if ((flags & DRAW_BOLD) && gui.font_can_bold) + INSERT_PANGO_ATTR(pango_attr_weight_new(PANGO_WEIGHT_BOLD), + attr_list, 0, len); + if (flags & DRAW_ITALIC) + INSERT_PANGO_ATTR(pango_attr_style_new(PANGO_STYLE_ITALIC), + attr_list, 0, len); + + item_list = pango_itemize(gui.text_context, + (const char *)s, 0, len, attr_list, NULL); + + while (item_list != NULL) + { + PangoItem *item; + int item_cells = 0; + + item = (PangoItem *)item_list->data; + item_list = g_list_delete_link(item_list, item_list); + + // Force LTR direction; Vim handles bidi on its own. + item->analysis.level = (item->analysis.level + 1) & (~1U); + + pango_shape_full((const char *)s + item->offset, item->length, + (const char *)s, len, &item->analysis, glyphs); + + // Fixed-width hack: assign a fixed width to each glyph based on + // the number of cells it occupies, handling composing characters + // and cluster boundaries properly. + for (i = 0; i < glyphs->num_glyphs; ++i) + { + PangoGlyphInfo *glyph; + + glyph = &glyphs->glyphs[i]; + + if (glyph->attr.is_cluster_start) + { + int cellcount; + + cellcount = count_cluster_cells( + s, item, glyphs, i, &cluster_width, + (item_list != NULL) ? &last_glyph_rbearing : NULL); + + if (cellcount > 0) + { + int width; + + width = cellcount * gui.char_width * PANGO_SCALE; + glyph->geometry.x_offset += + MAX(0, width - cluster_width) / 2; + glyph->geometry.width = width; + } + else + { + setup_zero_width_cluster(item, glyph, cells, + cluster_width, + last_glyph_rbearing); + } + + item_cells += cellcount; + cells = cellcount; + } + else if (i > 0) + { + int width; + + if (glyph->geometry.x_offset >= 0) + { + glyphs->glyphs[i].geometry.width = + glyphs->glyphs[i - 1].geometry.width; + glyphs->glyphs[i - 1].geometry.width = 0; + } + width = cells * gui.char_width * PANGO_SCALE; + glyph->geometry.x_offset += + MAX(0, width - cluster_width) / 2; + } + else + { + glyph->geometry.width = 0; + } + } + + draw_glyph_string(row, col + column_offset, item_cells, + flags, item->analysis.font, glyphs, cr); + + pango_item_free(item); + + column_offset += item_cells; + } + + pango_attr_list_unref(attr_list); + } + +skipitall: + draw_under(flags, row, col, column_offset, cr); + + pango_glyph_string_free(glyphs); + + cairo_destroy(cr); + + if (gui.drawarea != NULL) + gtk_widget_queue_draw(gui.drawarea); + + return column_offset; +} + +/* + * Draw a string of characters on the screen using the current font and colors. + * Splits the string into ASCII and ligature/UTF-8 segments so that ligature + * characters are sent through Pango for proper shaping, while plain ASCII + * uses the fast cached glyph path. + * Returns the number of display cells used. + */ + int +gui_gtk_draw_string(int row, int col, char_u *s, int len, int flags) +{ + char_u *conv_buf = NULL; + int convlen; + int len_sum; + int byte_sum; + char_u *cs; + int needs_pango; + int should_need_pango = FALSE; + int slen; + int is_ligature; + int is_utf8; + char_u backup_ch; + + if (gui.text_context == NULL || gui.surface == NULL) + return len; + + if (output_conv.vc_type != CONV_NONE) + { + convlen = len; + conv_buf = string_convert(&output_conv, s, &convlen); + if (conv_buf != NULL) + { + s = conv_buf; + len = convlen; + } + } + + /* + * Ligature support: + * Split the string into segments that are either pure ASCII (fast path) + * or ligature/UTF-8 (Pango path). A single ligature character between + * ASCII characters is treated as ASCII since it can't form a ligature + * on its own. + */ + len_sum = 0; + byte_sum = 0; + cs = s; + + // First char decides starting mode. + is_utf8 = (*cs & 0x80); + is_ligature = gui.ligatures_map[*cs] && (len > 1); + if (is_ligature) + is_ligature = gui.ligatures_map[*(cs + 1)]; + if (!is_utf8 && len > 1) + is_utf8 = (*(cs + 1) & 0x80) != 0; + needs_pango = is_utf8 || is_ligature; + + while (cs < s + len) + { + slen = 0; + while (slen < (len - byte_sum)) + { + is_ligature = gui.ligatures_map[*(cs + slen)]; + // Look ahead: single ligature char between ASCII is ASCII. + if (is_ligature && !needs_pango) + { + if ((slen + 1) < (len - byte_sum)) + is_ligature = gui.ligatures_map[*(cs + slen + 1)]; + else + is_ligature = 0; + } + is_utf8 = *(cs + slen) & 0x80; + // ASCII followed by UTF-8 could be combining. + if ((!is_utf8) && ((slen + 1) < (len - byte_sum))) + is_utf8 = (*(cs + slen + 1) & 0x80); + should_need_pango = (is_ligature || is_utf8); + if (needs_pango != should_need_pango) + break; + if (needs_pango) + { + if (is_ligature) + { + slen++; + } + else + { + if ((*(cs + slen) & 0xC0) == 0x80) + { + while ((slen < (len - byte_sum)) + && ((*(cs + slen) & 0xC0) == 0x80)) + slen++; + } + else if ((*(cs + slen) & 0xE0) == 0xC0) + slen++; + else if ((*(cs + slen) & 0xF0) == 0xE0) + slen += 2; + else if ((*(cs + slen) & 0xF8) == 0xF0) + slen += 3; + else + slen++; + } + } + else + { + slen++; + } + } + + if (slen < len) + { + backup_ch = *(cs + slen); + *(cs + slen) = NUL; + } + len_sum += gui_gtk_draw_string_ext(row, col + len_sum, cs, slen, + flags, needs_pango); + if (slen < len) + *(cs + slen) = backup_ch; + cs += slen; + byte_sum += slen; + needs_pango = should_need_pango; + } + vim_free(conv_buf); + return len_sum; +} + + int +gui_get_x11_windis(Window *win UNUSED, Display **dis UNUSED) +{ + // GTK4: not applicable + return FAIL; +} + +#if defined(FEAT_SOCKETSERVER) + +/* + * Callback for new events from the socket server listening socket. + */ + static int +socket_server_poll_in(int fd UNUSED, GIOCondition cond, + void *user_data UNUSED) +{ + if (cond & G_IO_IN) + socket_server_accept_client(); + else if (cond & (G_IO_ERR | G_IO_HUP)) + { + socket_server_uninit(); + return FALSE; + } + + return TRUE; +} + +#endif // FEAT_SOCKETSERVER + +/* + * Initialize socket server for use in the GUI (does not actually initialize + * the socket server, only attaches a source). + */ + void +gui_gtk_init_socket_server(void) +{ +#if defined(FEAT_SOCKETSERVER) + if (socket_server_source_id > 0) + return; + // Register source for file descriptor to global default context + socket_server_source_id = g_unix_fd_add(socket_server_get_fd(), + G_IO_IN | G_IO_ERR | G_IO_HUP, socket_server_poll_in, NULL); +#endif +} + +/* + * Remove the source for the socket server listening socket. + */ + void +gui_gtk_uninit_socket_server(void) +{ +#if defined(FEAT_SOCKETSERVER) + if (socket_server_source_id > 0) + { + g_source_remove(socket_server_source_id); + socket_server_source_id = 0; + } +#endif +} + + void +gui_gtk_set_mnemonics(int enable UNUSED) +{ + // No-op: menu mnemonics depend on menu system, not yet implemented + // for GTK4. +} + + void +gui_make_popup(char_u *path_name UNUSED, int mouse_pos UNUSED) +{ + // No-op: popup menus depend on menu system, not yet implemented + // for GTK4. +} + + int +get_menu_tool_width(void) +{ + return 0; +} + + int +get_menu_tool_height(void) +{ + int height = 0; + +#ifdef FEAT_MENU + if (gui.menubar != NULL && gtk_widget_get_visible(gui.menubar)) + { + GtkRequisition req; + gtk_widget_get_preferred_size(gui.menubar, &req, NULL); + height += req.height; + } +#endif +#ifdef FEAT_TOOLBAR + if (gui.toolbar != NULL && gtk_widget_get_visible(gui.toolbar)) + { + GtkRequisition req; + gtk_widget_get_preferred_size(gui.toolbar, &req, NULL); + height += req.height; + } +#endif + return height; +} + +/* + * Get the GdkClipboard for the given Clipboard_T. + * clip_star (*) uses PRIMARY, clip_plus (+) uses CLIPBOARD. + */ + static GdkClipboard * +gtk4_get_clipboard(Clipboard_T *cbd) +{ + GdkDisplay *display; + + if (gui.mainwin == NULL) + return NULL; + + display = gtk_widget_get_display(gui.mainwin); + if (display == NULL) + return NULL; + + if (cbd == &clip_plus) + return gdk_display_get_clipboard(display); + else + return gdk_display_get_primary_clipboard(display); +} + +typedef struct { + Clipboard_T *cbd; + gboolean done; +} ClipReadData; + +/* + * Callback for gdk_clipboard_read_text_async(). + */ + static void +clip_read_text_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + GdkClipboard *clipboard = GDK_CLIPBOARD(source); + ClipReadData *crd = (ClipReadData *)user_data; + Clipboard_T *cbd = crd->cbd; + char *text; + GError *error = NULL; + + text = gdk_clipboard_read_text_finish(clipboard, result, &error); + if (text != NULL) + { + char_u *tmpbuf = NULL; + char_u *p; + int len; + int motion_type = MAUTO; + + len = (int)STRLEN(text); + + // Convert from UTF-8 to 'encoding' if needed. + if (input_conv.vc_type != CONV_NONE) + { + tmpbuf = string_convert(&input_conv, (char_u *)text, &len); + if (tmpbuf != NULL) + p = tmpbuf; + else + p = (char_u *)text; + } + else + p = (char_u *)text; + + // Chop off any trailing NUL bytes. + while (len > 0 && p[len - 1] == NUL) + --len; + + clip_yank_selection(motion_type, p, (long)len, cbd); + vim_free(tmpbuf); + g_free(text); + } + else + { + if (error != NULL) + g_error_free(error); + } + crd->done = TRUE; +} + +/* + * Request the selection from the clipboard. + */ + void +clip_mch_request_selection(Clipboard_T *cbd) +{ + GdkClipboard *clipboard; + ClipReadData crd; + time_t start; + + clipboard = gtk4_get_clipboard(cbd); + if (clipboard == NULL) + return; + + crd.cbd = cbd; + crd.done = FALSE; + gdk_clipboard_read_text_async(clipboard, NULL, clip_read_text_cb, &crd); + + // Spin until the async callback fires, with a 3-second wall-clock + // timeout as a safety net. + start = time(NULL); + while (!crd.done && time(NULL) < start + 3) + g_main_context_iteration(NULL, TRUE); +} + +/* + * Send the current selection to the clipboard. + */ + void +clip_mch_set_selection(Clipboard_T *cbd) +{ + GdkClipboard *clipboard; + char_u *str = NULL; + long_u len; + int motion_type; + + clipboard = gtk4_get_clipboard(cbd); + if (clipboard == NULL) + return; + + // Get the selection text from the register. + clip_get_selection(cbd); + motion_type = clip_convert_selection(&str, &len, cbd); + if (motion_type < 0 || str == NULL) + return; + + // Convert from 'encoding' to UTF-8 if needed. + if (output_conv.vc_type != CONV_NONE) + { + char_u *conv_str; + int conv_len = (int)len; + + conv_str = string_convert(&output_conv, str, &conv_len); + if (conv_str != NULL) + { + vim_free(str); + str = conv_str; + len = conv_len; + } + } + + // Ensure NUL-terminated string for GTK. + { + char_u *nul_str = alloc(len + 1); + + if (nul_str != NULL) + { + mch_memmove(nul_str, str, len); + nul_str[len] = NUL; + gdk_clipboard_set_text(clipboard, (const char *)nul_str); + vim_free(nul_str); + } + } + + vim_free(str); +} + +/* + * Own the selection. In GTK4, ownership is implicit when content is set + * on the clipboard. Return OK to indicate we can own it. + */ + int +clip_mch_own_selection(Clipboard_T *cbd UNUSED) +{ + return OK; +} + +/* + * Disown the selection. In GTK4, we clear the clipboard content to + * release ownership. + */ + void +clip_mch_lose_selection(Clipboard_T *cbd) +{ + GdkClipboard *clipboard; + + clipboard = gtk4_get_clipboard(cbd); + if (clipboard == NULL) + return; + + // Setting NULL content provider releases ownership. + gdk_clipboard_set_content(clipboard, NULL); +} + +// Balloon eval - use GTK4 tooltip + void +gui_mch_post_balloon(BalloonEval *beval UNUSED, char_u *mesg) +{ + if (mesg != NULL && gui.drawarea != NULL) + { + char_u *text = CONVERT_TO_UTF8(mesg); + gtk_widget_set_tooltip_text(gui.drawarea, (const char *)text); + CONVERT_TO_UTF8_FREE(text); + } + else if (gui.drawarea != NULL) + gtk_widget_set_tooltip_text(gui.drawarea, NULL); +} + + BalloonEval * +gui_mch_create_beval_area(void *target UNUSED, char_u *mesg UNUSED, + void (*mesgCB)(BalloonEval *, int) UNUSED, void *clientData UNUSED) +{ + return NULL; +} + + void +gui_mch_enable_beval_area(BalloonEval *beval UNUSED) +{ +} + + void +gui_mch_disable_beval_area(BalloonEval *beval UNUSED) +{ +} + +// GTK4 does not have gtk_main_level/gtk_main_quit. +// Provide compatibility stubs using a simple flag. + guint +gtk_main_level(void) +{ + return gtk4_main_loop_level; +} + + void +gtk_main_quit(void) +{ + gtk4_main_loop_quit = TRUE; +} + +#if defined(FEAT_MOUSESHAPE) + +// Table of CSS cursor names corresponding to Vim's mouse shape IDs. +// Keep in sync with the mshape_names[] table in misc2.c. +static const char *mshape_css_names[] = +{ + "default", // arrow + "none", // blank + "text", // beam + "ns-resize", // updown + "nwse-resize", // udsizing + "ew-resize", // leftright + "ew-resize", // lrsizing + "progress", // busy + "not-allowed", // no + "crosshair", // crosshair + "pointer", // hand1 + "pointer", // hand2 + "default", // pencil (no CSS analogue) + "help", // question + "default", // right-arrow (no CSS analogue) + "default", // up-arrow (no CSS analogue) + "default" // last entry +}; + + void +mch_set_mouse_shape(int shape) +{ + GdkCursor *c; + const char *css_name = "default"; + + if (gui.drawarea == NULL) + return; + + if (shape == MSHAPE_HIDE || gui.pointer_hidden) + gtk_widget_set_cursor(gui.drawarea, gui.blank_pointer); + else + { + if (shape >= MSHAPE_NUMBERED) + css_name = "default"; + else if (shape < (int)ARRAY_LENGTH(mshape_css_names)) + css_name = mshape_css_names[shape]; + else + return; + + // GTK4: gdk_cursor_new_from_name(name, fallback) + c = gdk_cursor_new_from_name(css_name, NULL); + gtk_widget_set_cursor(gui.drawarea, c); + g_object_unref(G_OBJECT(c)); + } + if (shape != MSHAPE_HIDE) + last_shape = shape; +} + +#else // !FEAT_MOUSESHAPE + + void +mch_set_mouse_shape(int shape UNUSED) +{ +} + +#endif // FEAT_MOUSESHAPE + + + +/* + * Menus, scrollbars, dialogs, toolbar. + * (merged from gui_gtk4.c) + */ + + + +static int last_text_area_w = 0; +static int last_text_area_h = 0; + +/* + * ============================================================ + * Menu functions + * ============================================================ + * TODO: Implement using GMenu + GtkPopoverMenuBar + */ + +/* + * Icon name table for toolbar buttons. + * Must match toolbar_names[] in menu.c. + */ +static const char * const toolbar_icon_names[] = +{ + /* 00 */ "document-new", + /* 01 */ "document-open", + /* 02 */ "document-save", + /* 03 */ "edit-undo", + /* 04 */ "edit-redo", + /* 05 */ "edit-cut", + /* 06 */ "edit-copy", + /* 07 */ "edit-paste", + /* 08 */ "document-print", + /* 09 */ "help-browser", + /* 10 */ "edit-find", + /* 11 */ "document-save", // save all (no standard icon) + /* 12 */ "document-save", // session save + /* 13 */ "document-new", // session new + /* 14 */ "document-open", // session load + /* 15 */ "system-run", + /* 16 */ "edit-find-replace", + /* 17 */ "window-close", + /* 18 */ "window-maximize-symbolic", // maximize + /* 19 */ "window-minimize-symbolic", // minimize + /* 20 */ "window-maximize-symbolic", // split (no standard icon) + /* 21 */ "utilities-terminal", // shell + /* 22 */ "go-previous", + /* 23 */ "go-next", + /* 24 */ "help-browser", // find help + /* 25 */ "edit-find", // convert (no standard icon) + /* 26 */ "go-jump", + /* 27 */ "go-previous", // back (reuse) + /* 28 */ "go-next", // forward (reuse) + /* 29 */ "image-missing", + /* 30 */ "image-missing", +}; + + static void +toolbar_button_clicked_cb(GtkWidget *widget UNUSED, gpointer data) +{ + gui_menu_cb((vimmenu_T *)data); +} + + static GtkWidget * +create_toolbar_icon(vimmenu_T *menu) +{ + char_u buf[MAXPATHL]; + GtkWidget *image = NULL; + + // Try specified icon file first + if (menu->iconfile != NULL) + { + expand_env(menu->iconfile, buf, MAXPATHL); + if (vim_fexists(buf)) + { + GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file_at_scale( + (const char *)buf, 24, 24, TRUE, NULL); + if (pixbuf != NULL) + { + GdkTexture *texture = + gdk_texture_new_for_pixbuf(pixbuf); + image = gtk_image_new_from_paintable( + GDK_PAINTABLE(texture)); + g_object_unref(texture); + g_object_unref(pixbuf); + } + } + } + + // Use themed icon + if (image == NULL) + { + const char *icon_name = "image-missing"; + int n = (int)ARRAY_LENGTH(toolbar_icon_names); + + if (menu->iconidx >= 0 && menu->iconidx < n) + icon_name = toolbar_icon_names[menu->iconidx]; + + image = gtk_image_new_from_icon_name(icon_name); + } + + return image; +} + +/* + * GTK4 Menu system using GMenu + GSimpleActionGroup + GtkPopoverMenuBar. + * + * Each menu/submenu has a GMenu stored in menu->submenu_id (cast to + * GtkWidget* to fit the struct field type). + * Actions are added to a GSimpleActionGroup attached to gui.mainwin. + */ + +static int menu_action_id = 0; + + static void +menu_action_cb(GSimpleAction *action UNUSED, GVariant *parameter UNUSED, + gpointer data) +{ + // Force-close any open popover menus in the menubar. + // GTK4 marks them as not-visible but Vim's custom main loop + // may not process the rendering update, so we flush explicitly. + if (gui.menubar != NULL) + { + GtkWidget *item; + + for (item = gtk_widget_get_first_child(gui.menubar); + item != NULL; + item = gtk_widget_get_next_sibling(item)) + { + GtkWidget *child; + + for (child = gtk_widget_get_first_child(item); + child != NULL; + child = gtk_widget_get_next_sibling(child)) + { + if (GTK_IS_POPOVER(child)) + gtk_popover_popdown(GTK_POPOVER(child)); + } + } + } + + gui_menu_cb((vimmenu_T *)data); + gui_mch_flush(); +} + + static char * +make_action_name(vimmenu_T *menu) +{ + // Create a unique action name from the menu pointer + static char buf[64]; + vim_snprintf(buf, sizeof(buf), "menu%d", menu_action_id++); + return buf; +} + + void +gui_mch_add_menu(vimmenu_T *menu, int idx UNUSED) +{ + GMenu *submenu; + + if (menu->name[0] == ']' || menu_is_popup(menu->name)) + { + // Popup menus - just create a GMenu, don't add to menubar + submenu = g_menu_new(); + menu->submenu_id = (GtkWidget *)(gpointer)submenu; + return; + } + + if (menu->parent != NULL && menu->parent->submenu_id == NULL) + return; + if (!menu_is_menubar(menu->name)) + return; + + // Create a submenu for this menu + submenu = g_menu_new(); + menu->submenu_id = (GtkWidget *)(gpointer)submenu; + + // Add to parent menu or menubar's model + { + GMenu *parent_menu; + char_u *label; + + label = CONVERT_TO_UTF8(menu->dname); + + if (menu->parent != NULL) + parent_menu = (GMenu *)(gpointer)menu->parent->submenu_id; + else + parent_menu = (GMenu *)(gpointer)g_object_get_data( + G_OBJECT(gui.menubar), "vim-gmenu"); + + if (parent_menu != NULL) + g_menu_append_submenu(parent_menu, (const char *)label, + G_MENU_MODEL(submenu)); + + CONVERT_TO_UTF8_FREE(label); + } +} + + void +gui_mch_add_menu_item(vimmenu_T *menu, int idx UNUSED) +{ + vimmenu_T *parent = menu->parent; + +#ifdef FEAT_TOOLBAR + if (parent != NULL && menu_is_toolbar(parent->name)) + { + if (menu_is_separator(menu->name)) + { + GtkWidget *sep = gtk_separator_new(GTK_ORIENTATION_VERTICAL); + gtk_box_append(GTK_BOX(gui.toolbar), sep); + menu->id = sep; + } + else + { + GtkWidget *btn; + GtkWidget *icon; + char_u *tooltip; + + icon = create_toolbar_icon(menu); + btn = gtk_button_new(); + gtk_button_set_child(GTK_BUTTON(btn), icon); + gtk_widget_set_focusable(btn, FALSE); + gtk_widget_add_css_class(btn, "flat"); + + tooltip = CONVERT_TO_UTF8(menu->strings[MENU_INDEX_TIP]); + if (tooltip != NULL && utf_valid_string(tooltip, NULL)) + gtk_widget_set_tooltip_text(btn, (const gchar *)tooltip); + CONVERT_TO_UTF8_FREE(tooltip); + + g_signal_connect(btn, "clicked", + G_CALLBACK(toolbar_button_clicked_cb), menu); + + gtk_box_append(GTK_BOX(gui.toolbar), btn); + menu->id = btn; + } + return; + } +#endif + + // Menu items (non-toolbar) + if (parent == NULL || parent->submenu_id == NULL) + return; + + { + GMenu *parent_menu = (GMenu *)(gpointer)parent->submenu_id; + + if (menu_is_separator(menu->name)) + { + // GMenu doesn't have real separators; use a section + GMenu *section = g_menu_new(); + g_menu_append_section(parent_menu, NULL, G_MENU_MODEL(section)); + g_object_unref(section); + menu->id = NULL; + } + else + { + char *action_name; + char detailed[80]; + char_u *label; + GSimpleAction *action; + + // Create a unique action + action_name = make_action_name(menu); + action = g_simple_action_new(action_name, NULL); + g_signal_connect(action, "activate", + G_CALLBACK(menu_action_cb), menu); + + if (menu_action_group == NULL) + { + menu_action_group = g_simple_action_group_new(); + gtk_widget_insert_action_group(gui.mainwin, "menu", + G_ACTION_GROUP(menu_action_group)); + } + g_action_map_add_action(G_ACTION_MAP(menu_action_group), + G_ACTION(action)); + g_object_unref(action); + + label = CONVERT_TO_UTF8(menu->dname); + vim_snprintf(detailed, sizeof(detailed), "menu.%s", action_name); + g_menu_append(parent_menu, (const char *)label, detailed); + CONVERT_TO_UTF8_FREE(label); + + menu->id = (GtkWidget *)1; // non-NULL marker + // Store action name for later use (grey/enable) + menu->label = (GtkWidget *)vim_strsave( + (char_u *)action_name); + } + } +} + + void +gui_mch_toggle_tearoffs(int enable UNUSED) +{ + // GTK4: tearoff menus don't exist. +} + + void +gui_mch_menu_set_tip(vimmenu_T *menu UNUSED) +{ +} + + void +gui_mch_destroy_menu(vimmenu_T *menu) +{ + // For toolbar buttons, remove from toolbar + if (menu->id != NULL && menu->id != (GtkWidget *)1) + { + GtkWidget *parent_widget = gtk_widget_get_parent(menu->id); + if (parent_widget != NULL) + gtk_box_remove(GTK_BOX(parent_widget), menu->id); + menu->id = NULL; + } + else + menu->id = NULL; + + // Free stored action name + vim_free(menu->label); + menu->label = NULL; + + // GMenu items cannot be individually removed easily. + // The submenu GMenu is unreffed if present. + if (menu->submenu_id != NULL) + { + // Don't unref - GMenu may be referenced by the model + menu->submenu_id = NULL; + } +} + + static void +popupmenu_closed_cb(GtkPopover *popover, gpointer data UNUSED) +{ + gtk_widget_unparent(GTK_WIDGET(popover)); +} + + void +gui_mch_show_popupmenu(vimmenu_T *menu) +{ + GMenu *gmenu; + GtkWidget *popover; + + if (menu == NULL || menu->submenu_id == NULL) + return; + + gmenu = (GMenu *)(gpointer)menu->submenu_id; + popover = gtk_popover_menu_new_from_model(G_MENU_MODEL(gmenu)); + gtk_widget_set_parent(popover, gui.drawarea); + g_signal_connect(popover, "closed", + G_CALLBACK(popupmenu_closed_cb), NULL); + gtk_popover_popup(GTK_POPOVER(popover)); +} + +/* + * ============================================================ + * Scrollbar functions + * ============================================================ + */ + + void +gui_mch_set_scrollbar_thumb(scrollbar_T *sb, long val, long size, long max) +{ + GtkAdjustment *adj; + + if (sb->id == NULL) + return; + if (!GTK_IS_WIDGET(sb->id) || !GTK_IS_RANGE(sb->id)) + return; + + adj = gtk_range_get_adjustment(GTK_RANGE(sb->id)); + gtk_adjustment_set_lower(adj, 0.0); + gtk_adjustment_set_upper(adj, (gdouble)max + 1); + gtk_adjustment_set_value(adj, (gdouble)val); + gtk_adjustment_set_step_increment(adj, 1.0); + gtk_adjustment_set_page_increment(adj, (gdouble)(size > 2 ? size - 2 : 1)); + gtk_adjustment_set_page_size(adj, (gdouble)size); +} + + void +gui_mch_set_scrollbar_pos(scrollbar_T *sb, int x, int y, int w, int h) +{ + if (sb->id != NULL) + { + gtk_widget_set_size_request(sb->id, w, h); + gui_gtk_form_move(GTK_FORM(gui.formwin), sb->id, x, y); + } +} + + int +gui_mch_get_scrollbar_xpadding(void) +{ + int formwin_w = gtk_widget_get_width(gui.formwin); + int sbar_w = 0; + int xpad; + + if (gui.which_scrollbars[SBAR_LEFT]) + sbar_w += gui.scrollbar_width; + if (gui.which_scrollbars[SBAR_RIGHT]) + sbar_w += gui.scrollbar_width; + + xpad = formwin_w - last_text_area_w - sbar_w; + return (xpad < 0) ? 0 : xpad; +} + + int +gui_mch_get_scrollbar_ypadding(void) +{ + int formwin_h = gtk_widget_get_height(gui.formwin); + int ypad; + + ypad = formwin_h - last_text_area_h; + if (gui.which_scrollbars[SBAR_BOTTOM]) + ypad -= gui.scrollbar_height; + + return (ypad < 0) ? 0 : ypad; +} + + static void +adjustment_value_changed(GtkAdjustment *adj, gpointer data UNUSED) +{ + scrollbar_T *sb = (scrollbar_T *)g_object_get_data(G_OBJECT(adj), "vim-sb"); + long value = (long)gtk_adjustment_get_value(adj); + + if (sb != NULL) + gui_drag_scrollbar(sb, value, FALSE); +} + + void +gui_mch_create_scrollbar(scrollbar_T *sb, int orient) +{ + if (orient == SBAR_HORIZ) + sb->id = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, NULL); + else + sb->id = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, NULL); + + if (sb->id != NULL && GTK_IS_RANGE(sb->id)) + { + GtkAdjustment *adj = gtk_range_get_adjustment(GTK_RANGE(sb->id)); + + gtk_widget_set_visible(sb->id, FALSE); + gui_gtk_form_put(GTK_FORM(gui.formwin), sb->id, 0, 0); + if (adj != NULL && G_IS_OBJECT(adj)) + { + g_object_set_data(G_OBJECT(adj), "vim-sb", (gpointer)sb); + g_signal_connect(G_OBJECT(adj), "value-changed", + G_CALLBACK(adjustment_value_changed), NULL); + } + } +} + + void +gui_mch_destroy_scrollbar(scrollbar_T *sb) +{ + if (sb->id != NULL) + { + gui_gtk_form_remove(GTK_FORM(gui.formwin), sb->id); + sb->id = NULL; + } +} + +/* + * ============================================================ + * Text area position + * ============================================================ + */ + + void +gui_mch_set_text_area_pos(int x, int y, int w, int h) +{ + last_text_area_w = w; + last_text_area_h = h; + // Don't use gui_gtk_form_move_resize for drawarea because its + // set_size_request would prevent the window from shrinking. + // Just update position; the actual allocation is handled by + // form_size_allocate which gives drawarea the formwin's full size. + gui_gtk_form_move(GTK_FORM(gui.formwin), gui.drawarea, x, y); + + // Update surface to match new text area size + if (w > 0 && h > 0) + { + if (gui.surface != NULL) + { + int sw = cairo_image_surface_get_width(gui.surface); + int sh = cairo_image_surface_get_height(gui.surface); + if (sw == w && sh == h) + return; + cairo_surface_destroy(gui.surface); + } + gui.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + } +} + +/* + * ============================================================ + * Browse dialogs + * ============================================================ + */ + +/* + * Blocking helper: run a GtkFileDialog and wait for result. + */ +typedef struct { + GFile *result; + gboolean done; +} FileDialogData; + + static void +file_dialog_open_cb(GObject *source, GAsyncResult *res, gpointer data) +{ + FileDialogData *fdd = (FileDialogData *)data; + fdd->result = gtk_file_dialog_open_finish( + GTK_FILE_DIALOG(source), res, NULL); + fdd->done = TRUE; +} + + static void +file_dialog_save_cb(GObject *source, GAsyncResult *res, gpointer data) +{ + FileDialogData *fdd = (FileDialogData *)data; + fdd->result = gtk_file_dialog_save_finish( + GTK_FILE_DIALOG(source), res, NULL); + fdd->done = TRUE; +} + + static void +file_dialog_folder_cb(GObject *source, GAsyncResult *res, gpointer data) +{ + FileDialogData *fdd = (FileDialogData *)data; + fdd->result = gtk_file_dialog_select_folder_finish( + GTK_FILE_DIALOG(source), res, NULL); + fdd->done = TRUE; +} + + char_u * +gui_mch_browse(int saving, + char_u *title, + char_u *dflt, + char_u *ext UNUSED, + char_u *initdir, + char_u *filter UNUSED) +{ + GtkFileDialog *dlg; + FileDialogData fdd; + char_u dirbuf[MAXPATHL]; + char_u *result = NULL; + + title = CONVERT_TO_UTF8(title); + + if (initdir == NULL || *initdir == NUL) + mch_dirname(dirbuf, MAXPATHL); + else if (vim_FullName(initdir, dirbuf, MAXPATHL - 2, FALSE) == FAIL) + dirbuf[0] = NUL; + add_pathsep(dirbuf); + + gui_mch_mousehide(FALSE); + + dlg = gtk_file_dialog_new(); + gtk_file_dialog_set_modal(dlg, TRUE); + if (title != NULL) + gtk_file_dialog_set_title(dlg, (const char *)title); + + { + GFile *dir = g_file_new_for_path((const char *)dirbuf); + gtk_file_dialog_set_initial_folder(dlg, dir); + g_object_unref(dir); + } + + if (saving && dflt != NULL && *dflt != NUL) + gtk_file_dialog_set_initial_name(dlg, (const char *)dflt); + + fdd.result = NULL; + fdd.done = FALSE; + + if (saving) + gtk_file_dialog_save(dlg, GTK_WINDOW(gui.mainwin), NULL, + file_dialog_save_cb, &fdd); + else + gtk_file_dialog_open(dlg, GTK_WINDOW(gui.mainwin), NULL, + file_dialog_open_cb, &fdd); + + while (!fdd.done) + g_main_context_iteration(NULL, TRUE); + + if (fdd.result != NULL) + { + char *path = g_file_get_path(fdd.result); + if (path != NULL) + { + result = vim_strsave((char_u *)path); + g_free(path); + } + g_object_unref(fdd.result); + } + + g_object_unref(dlg); + CONVERT_TO_UTF8_FREE(title); + + return result; +} + + char_u * +gui_mch_browsedir(char_u *title, char_u *initdir) +{ + GtkFileDialog *dlg; + FileDialogData fdd; + char_u *result = NULL; + + title = CONVERT_TO_UTF8(title); + gui_mch_mousehide(FALSE); + + dlg = gtk_file_dialog_new(); + gtk_file_dialog_set_modal(dlg, TRUE); + if (title != NULL) + gtk_file_dialog_set_title(dlg, (const char *)title); + + if (initdir != NULL && *initdir != NUL) + { + GFile *dir = g_file_new_for_path((const char *)initdir); + gtk_file_dialog_set_initial_folder(dlg, dir); + g_object_unref(dir); + } + + fdd.result = NULL; + fdd.done = FALSE; + + gtk_file_dialog_select_folder(dlg, GTK_WINDOW(gui.mainwin), NULL, + file_dialog_folder_cb, &fdd); + + while (!fdd.done) + g_main_context_iteration(NULL, TRUE); + + if (fdd.result != NULL) + { + char *path = g_file_get_path(fdd.result); + if (path != NULL) + { + result = vim_strsave((char_u *)path); + g_free(path); + } + g_object_unref(fdd.result); + } + + g_object_unref(dlg); + CONVERT_TO_UTF8_FREE(title); + + return result; +} + +/* + * ============================================================ + * Message dialog + * ============================================================ + */ + +typedef struct { + int response; + gboolean done; +} AlertDialogData; + + static void +alert_dialog_cb(GObject *source, GAsyncResult *res, gpointer data) +{ + AlertDialogData *add = (AlertDialogData *)data; + add->response = gtk_alert_dialog_choose_finish( + GTK_ALERT_DIALOG(source), res, NULL); + add->done = TRUE; +} + + int +gui_mch_dialog( + int type UNUSED, + char_u *title, + char_u *message, + char_u *buttons, + int dfltbutton, + char_u *textfield UNUSED, + int ex_cmd UNUSED) +{ + GtkAlertDialog *dlg; + AlertDialogData add; + char_u *p; + char_u *buf = NULL; + int butcount = 0; + int i; + const char *btn_labels[64]; + char_u *btn_conv[64]; + + title = CONVERT_TO_UTF8(title); + message = CONVERT_TO_UTF8(message); + + // Parse button labels from the "&Yes\n&No\n&Cancel" format + if (buttons != NULL) + { + buf = vim_strsave(buttons); + if (buf != NULL) + { + p = buf; + while (*p != NUL && butcount < 63) + { + char_u *start = p; + while (*p != NUL && *p != '\n') + ++p; + if (*p == '\n') + *p++ = NUL; + // Skip '&' mnemonic marker + if (*start == '&') + ++start; + btn_conv[butcount] = CONVERT_TO_UTF8(start); + btn_labels[butcount] = (const char *)btn_conv[butcount]; + butcount++; + } + } + } + btn_labels[butcount] = NULL; + + dlg = gtk_alert_dialog_new("%s", message ? (char *)message : ""); + if (title != NULL) + gtk_alert_dialog_set_detail(dlg, (const char *)title); + gtk_alert_dialog_set_buttons(dlg, btn_labels); + gtk_alert_dialog_set_modal(dlg, TRUE); + + if (dfltbutton > 0 && dfltbutton <= butcount) + gtk_alert_dialog_set_default_button(dlg, dfltbutton - 1); + if (butcount > 0) + gtk_alert_dialog_set_cancel_button(dlg, butcount - 1); + + add.response = -1; + add.done = FALSE; + + gtk_alert_dialog_choose(dlg, GTK_WINDOW(gui.mainwin), NULL, + alert_dialog_cb, &add); + + while (!add.done) + g_main_context_iteration(NULL, TRUE); + + g_object_unref(dlg); + + for (i = 0; i < butcount; i++) + CONVERT_TO_UTF8_FREE(btn_conv[i]); + vim_free(buf); + CONVERT_TO_UTF8_FREE(title); + CONVERT_TO_UTF8_FREE(message); + + // GTK returns 0-based index, Vim wants 1-based + return add.response >= 0 ? add.response + 1 : 0; +} + +/* + * ============================================================ + * Find/Replace dialogs + * ============================================================ + */ + +/* + * ============================================================ + * Find/Replace dialog + * ============================================================ + */ + +typedef struct +{ + GtkWidget *dialog; + GtkWidget *what; // Find what entry + GtkWidget *with; // Replace with entry + GtkWidget *wword; // Whole word check + GtkWidget *mcase; // Match case check + GtkWidget *up; // Direction up radio + GtkWidget *down; // Direction down radio +} SharedFindReplace; + +static SharedFindReplace find_widgets = {0}; +static SharedFindReplace repl_widgets = {0}; + + static void +find_replace_cb(GtkWidget *widget UNUSED, gpointer data) +{ + int flags; + char_u *find_text; + char_u *repl_text; + gboolean direction_down; + SharedFindReplace *sfr; + + flags = GPOINTER_TO_INT(data); + + if (flags == FRD_FINDNEXT) + { + repl_text = NULL; + sfr = &find_widgets; + } + else + { + repl_text = (char_u *)gtk_editable_get_text( + GTK_EDITABLE(repl_widgets.with)); + sfr = &repl_widgets; + } + + find_text = (char_u *)gtk_editable_get_text(GTK_EDITABLE(sfr->what)); + direction_down = gtk_check_button_get_active( + GTK_CHECK_BUTTON(sfr->down)); + + if (gtk_check_button_get_active(GTK_CHECK_BUTTON(sfr->wword))) + flags |= FRD_WHOLE_WORD; + if (gtk_check_button_get_active(GTK_CHECK_BUTTON(sfr->mcase))) + flags |= FRD_MATCH_CASE; + + repl_text = CONVERT_FROM_UTF8(repl_text); + find_text = CONVERT_FROM_UTF8(find_text); + gui_do_findrepl(flags, find_text, repl_text, direction_down); + CONVERT_FROM_UTF8_FREE(repl_text); + CONVERT_FROM_UTF8_FREE(find_text); +} + + static void +dialog_destroyed_cb(GtkWidget *widget UNUSED, gpointer data) +{ + *(GtkWidget **)data = NULL; +} + + static void +find_replace_dialog_create(char_u *arg, int do_replace) +{ + SharedFindReplace *frdp; + char_u *entry_text; + int wword = FALSE; + int mcase = !p_ic; + GtkWidget *vbox, *grid, *hbox, *tmp, *btn; + gboolean sensitive; + + frdp = do_replace ? &repl_widgets : &find_widgets; + entry_text = get_find_dialog_text(arg, &wword, &mcase); + + if (entry_text != NULL && output_conv.vc_type != CONV_NONE) + { + char_u *old = entry_text; + entry_text = string_convert(&output_conv, entry_text, NULL); + vim_free(old); + } + + // If the dialog already exists, just raise it. + if (frdp->dialog) + { + if (entry_text != NULL) + { + gtk_editable_set_text(GTK_EDITABLE(frdp->what), + (char *)entry_text); + gtk_check_button_set_active(GTK_CHECK_BUTTON(frdp->wword), + (gboolean)wword); + gtk_check_button_set_active(GTK_CHECK_BUTTON(frdp->mcase), + (gboolean)mcase); + } + gtk_window_present(GTK_WINDOW(frdp->dialog)); + gtk_widget_grab_focus(frdp->what); + vim_free(entry_text); + return; + } + + // Create a new dialog window. + frdp->dialog = gtk_window_new(); + gtk_window_set_transient_for(GTK_WINDOW(frdp->dialog), + GTK_WINDOW(gui.mainwin)); + gtk_window_set_destroy_with_parent(GTK_WINDOW(frdp->dialog), TRUE); + gtk_window_set_title(GTK_WINDOW(frdp->dialog), + do_replace ? _("VIM - Search and Replace...") + : _("VIM - Search...")); + gtk_window_set_resizable(GTK_WINDOW(frdp->dialog), FALSE); + + g_signal_connect(frdp->dialog, "destroy", + G_CALLBACK(dialog_destroyed_cb), &frdp->dialog); + + vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 6); + gtk_widget_set_margin_start(vbox, 12); + gtk_widget_set_margin_end(vbox, 12); + gtk_widget_set_margin_top(vbox, 12); + gtk_widget_set_margin_bottom(vbox, 12); + gtk_window_set_child(GTK_WINDOW(frdp->dialog), vbox); + + // Grid for labels + entries + grid = gtk_grid_new(); + gtk_grid_set_row_spacing(GTK_GRID(grid), 6); + gtk_grid_set_column_spacing(GTK_GRID(grid), 6); + gtk_box_append(GTK_BOX(vbox), grid); + + // "Find what:" label + entry + tmp = gtk_label_new(_("Find what:")); + gtk_label_set_xalign(GTK_LABEL(tmp), 0.0); + gtk_grid_attach(GTK_GRID(grid), tmp, 0, 0, 1, 1); + + frdp->what = gtk_entry_new(); + gtk_widget_set_hexpand(frdp->what, TRUE); + sensitive = (entry_text != NULL && entry_text[0] != NUL); + if (entry_text != NULL) + gtk_editable_set_text(GTK_EDITABLE(frdp->what), (char *)entry_text); + gtk_grid_attach(GTK_GRID(grid), frdp->what, 1, 0, 1, 1); + + if (do_replace) + { + // "Replace with:" label + entry + tmp = gtk_label_new(_("Replace with:")); + gtk_label_set_xalign(GTK_LABEL(tmp), 0.0); + gtk_grid_attach(GTK_GRID(grid), tmp, 0, 1, 1, 1); + + frdp->with = gtk_entry_new(); + gtk_widget_set_hexpand(frdp->with, TRUE); + gtk_grid_attach(GTK_GRID(grid), frdp->with, 1, 1, 1, 1); + } + + // Checkboxes + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); + gtk_box_append(GTK_BOX(vbox), hbox); + + frdp->wword = gtk_check_button_new_with_label(_("Match whole word only")); + gtk_check_button_set_active(GTK_CHECK_BUTTON(frdp->wword), + (gboolean)wword); + gtk_box_append(GTK_BOX(hbox), frdp->wword); + + frdp->mcase = gtk_check_button_new_with_label(_("Match case")); + gtk_check_button_set_active(GTK_CHECK_BUTTON(frdp->mcase), + (gboolean)mcase); + gtk_box_append(GTK_BOX(hbox), frdp->mcase); + + // Direction radio buttons + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); + gtk_box_append(GTK_BOX(vbox), hbox); + + tmp = gtk_label_new(_("Direction:")); + gtk_box_append(GTK_BOX(hbox), tmp); + + frdp->up = gtk_check_button_new_with_label(_("Up")); + gtk_box_append(GTK_BOX(hbox), frdp->up); + + frdp->down = gtk_check_button_new_with_label(_("Down")); + gtk_check_button_set_group(GTK_CHECK_BUTTON(frdp->down), + GTK_CHECK_BUTTON(frdp->up)); + gtk_check_button_set_active(GTK_CHECK_BUTTON(frdp->down), TRUE); + gtk_box_append(GTK_BOX(hbox), frdp->down); + + // Action buttons + hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_set_halign(hbox, GTK_ALIGN_END); + gtk_box_append(GTK_BOX(vbox), hbox); + + btn = gtk_button_new_with_label(_("Find Next")); + gtk_widget_set_sensitive(btn, sensitive); + g_signal_connect(btn, "clicked", G_CALLBACK(find_replace_cb), + GINT_TO_POINTER(do_replace ? FRD_R_FINDNEXT : FRD_FINDNEXT)); + gtk_box_append(GTK_BOX(hbox), btn); + + if (do_replace) + { + btn = gtk_button_new_with_label(_("Replace")); + g_signal_connect(btn, "clicked", G_CALLBACK(find_replace_cb), + GINT_TO_POINTER(FRD_REPLACE)); + gtk_box_append(GTK_BOX(hbox), btn); + + btn = gtk_button_new_with_label(_("Replace All")); + g_signal_connect(btn, "clicked", G_CALLBACK(find_replace_cb), + GINT_TO_POINTER(FRD_REPLACEALL)); + gtk_box_append(GTK_BOX(hbox), btn); + } + + btn = gtk_button_new_with_label(_("Close")); + g_signal_connect_swapped(btn, "clicked", + G_CALLBACK(gtk_window_destroy), frdp->dialog); + gtk_box_append(GTK_BOX(hbox), btn); + + // Connect Enter key in entry to Find Next + g_signal_connect_swapped(frdp->what, "activate", + G_CALLBACK(find_replace_cb), + GINT_TO_POINTER(do_replace ? FRD_R_FINDNEXT : FRD_FINDNEXT)); + + gtk_window_present(GTK_WINDOW(frdp->dialog)); + gtk_widget_grab_focus(frdp->what); + if (do_replace && entry_text != NULL && entry_text[0] != NUL) + gtk_widget_grab_focus(frdp->with); + + vim_free(entry_text); +} + + void +gui_mch_find_dialog(exarg_T *eap) +{ + if (gui.in_use) + find_replace_dialog_create(eap->arg, FALSE); +} + + void +gui_mch_replace_dialog(exarg_T *eap) +{ + if (gui.in_use) + find_replace_dialog_create(eap->arg, TRUE); +} + +/* + * ============================================================ + * Help find (for :helpfind command) + * ============================================================ + */ + + void +ex_helpfind(exarg_T *eap UNUSED) +{ + do_cmdline_cmd((char_u *)"emenu ToolBar.FindHelp"); +} + + +/* + * ============================================================ + * Printing with GtkPrintOperation + * ============================================================ + */ +#ifdef FEAT_GUI_GTK_PRINT + +typedef struct +{ + linenr_T first_line; // first line to print (from range) + linenr_T last_line; // last line to print (from range) + int n_pages; // total number of pages + int lines_per_page; // lines that fit on one page + PangoFontDescription *font_desc; + int do_syntax; // whether to use syntax highlighting + double line_height; // line height in points + double char_width; // character width in points +} print_data_T; + +/* + * "begin-print" signal handler. + * Calculate pagination based on page size and font metrics. + */ + static void +print_begin_cb( + GtkPrintOperation *op, + GtkPrintContext *context, + gpointer user_data) +{ + print_data_T *pd = (print_data_T *)user_data; + PangoLayout *layout; + PangoFontMetrics *metrics; + double page_height; + int total_lines; + + page_height = gtk_print_context_get_height(context); + + // Create a PangoLayout to measure font metrics on the print surface. + layout = gtk_print_context_create_pango_layout(context); + pango_layout_set_font_description(layout, pd->font_desc); + + metrics = pango_context_get_metrics( + pango_layout_get_context(layout), + pd->font_desc, NULL); + + pd->line_height = (double)(pango_font_metrics_get_ascent(metrics) + + pango_font_metrics_get_descent(metrics)) / PANGO_SCALE; + pd->char_width = (double)pango_font_metrics_get_approximate_char_width( + metrics) / PANGO_SCALE; + + pango_font_metrics_unref(metrics); + g_object_unref(layout); + + if (pd->line_height <= 0) + pd->line_height = 12.0; + + pd->lines_per_page = (int)(page_height / pd->line_height); + if (pd->lines_per_page <= 0) + pd->lines_per_page = 1; + + total_lines = (int)(pd->last_line - pd->first_line + 1); + pd->n_pages = (total_lines + pd->lines_per_page - 1) / pd->lines_per_page; + if (pd->n_pages <= 0) + pd->n_pages = 1; + + gtk_print_operation_set_n_pages(op, pd->n_pages); +} + +/* + * "draw-page" signal handler. + * Render one page of buffer text with optional syntax highlighting. + */ + static void +print_draw_page_cb( + GtkPrintOperation *op UNUSED, + GtkPrintContext *context, + int page_nr, + gpointer user_data) +{ + print_data_T *pd = (print_data_T *)user_data; + cairo_t *cr; + linenr_T lnum; + linenr_T first; + linenr_T last; + int page_line; + double y; + + cr = gtk_print_context_get_cairo_context(context); + + first = pd->first_line + (linenr_T)page_nr * pd->lines_per_page; + last = first + pd->lines_per_page - 1; + if (last > pd->last_line) + last = pd->last_line; + + y = 0; + page_line = 0; + + for (lnum = first; lnum <= last; ++lnum, ++page_line) + { + char_u *line; + PangoLayout *layout; + PangoAttrList *attr_list; + + line = ml_get(lnum); + layout = gtk_print_context_create_pango_layout(context); + pango_layout_set_font_description(layout, pd->font_desc); + + attr_list = pango_attr_list_new(); + +# ifdef FEAT_SYN_HL + if (pd->do_syntax && syntax_present(curwin)) + { + colnr_T col; + int prev_syn_id = -1; + int attr_start = 0; + long_u prev_fg = 0; + int prev_bold = FALSE; + int prev_italic = FALSE; + int len = (int)STRLEN(line); + + for (col = 0; col < len; ) + { + int id; + int outputlen; + long_u fg_color; + int is_bold; + int is_italic; + + if (has_mbyte) + { + outputlen = (*mb_ptr2len)(line + col); + if (outputlen < 1) + outputlen = 1; + } + else + outputlen = 1; + + id = syn_get_id(curwin, lnum, col, 1, NULL, FALSE); + if (id > 0) + id = syn_get_final_id(id); + else + id = 0; + // syn_get_id may invalidate the line pointer. + line = ml_get(lnum); + + fg_color = highlight_gui_color_rgb(id, TRUE); + is_bold = (highlight_has_attr(id, HL_BOLD, 'g') != NULL); + is_italic = (highlight_has_attr(id, HL_ITALIC, 'g') != NULL); + + // When attributes change, flush the previous run. + if (id != prev_syn_id && col > 0) + { + if (prev_fg != 0 && prev_fg != (long_u)0xffffffL) + { + PangoAttribute *a = pango_attr_foreground_new( + (guint16)(((prev_fg >> 16) & 0xff) * 257), + (guint16)(((prev_fg >> 8) & 0xff) * 257), + (guint16)((prev_fg & 0xff) * 257)); + a->start_index = attr_start; + a->end_index = col; + pango_attr_list_insert(attr_list, a); + } + if (prev_bold) + { + PangoAttribute *a = pango_attr_weight_new( + PANGO_WEIGHT_BOLD); + a->start_index = attr_start; + a->end_index = col; + pango_attr_list_insert(attr_list, a); + } + if (prev_italic) + { + PangoAttribute *a = pango_attr_style_new( + PANGO_STYLE_ITALIC); + a->start_index = attr_start; + a->end_index = col; + pango_attr_list_insert(attr_list, a); + } + attr_start = col; + } + + prev_syn_id = id; + prev_fg = fg_color; + prev_bold = is_bold; + prev_italic = is_italic; + + col += outputlen; + } + + // Flush the last run. + if (attr_start < len) + { + if (prev_fg != 0 && prev_fg != (long_u)0xffffffL) + { + PangoAttribute *a = pango_attr_foreground_new( + (guint16)(((prev_fg >> 16) & 0xff) * 257), + (guint16)(((prev_fg >> 8) & 0xff) * 257), + (guint16)((prev_fg & 0xff) * 257)); + a->start_index = attr_start; + a->end_index = len; + pango_attr_list_insert(attr_list, a); + } + if (prev_bold) + { + PangoAttribute *a = pango_attr_weight_new( + PANGO_WEIGHT_BOLD); + a->start_index = attr_start; + a->end_index = len; + pango_attr_list_insert(attr_list, a); + } + if (prev_italic) + { + PangoAttribute *a = pango_attr_style_new( + PANGO_STYLE_ITALIC); + a->start_index = attr_start; + a->end_index = len; + pango_attr_list_insert(attr_list, a); + } + } + } +# endif // FEAT_SYN_HL + + pango_layout_set_attributes(layout, attr_list); + + // Expand tabs. Use a tab array matching Vim's tabstop. + { + PangoTabArray *tabs; + int tab_width = (int)(curbuf->b_p_ts * pd->char_width); + + if (tab_width <= 0) + tab_width = (int)(8 * pd->char_width); + tabs = pango_tab_array_new(1, TRUE); + pango_tab_array_set_tab(tabs, 0, PANGO_TAB_LEFT, tab_width); + pango_layout_set_tabs(layout, tabs); + pango_tab_array_free(tabs); + } + + pango_layout_set_text(layout, (const char *)line, -1); + + cairo_move_to(cr, 0, y); + pango_cairo_show_layout(cr, layout); + + pango_attr_list_unref(attr_list); + g_object_unref(layout); + + y += pd->line_height; + } +} + +/* + * Main entry point for GTK4 native printing. + * Called from ex_hardcopy() when running in a GTK4 GUI. + */ + void +gui_gtk4_hardcopy(exarg_T *eap) +{ + GtkPrintOperation *op; + GtkPrintOperationResult res; + print_data_T pd; + char_u *font_name; + + static GtkPrintSettings *settings = NULL; + + CLEAR_FIELD(pd); + pd.first_line = eap->line1; + pd.last_line = eap->line2; + + // Use 'printfont' if set, otherwise fall back to 'guifont'. + font_name = *p_pfn != NUL ? p_pfn : p_guifont; + if (font_name == NULL || *font_name == NUL) + font_name = (char_u *)"Monospace 10"; + + pd.font_desc = pango_font_description_from_string((const char *)font_name); + if (pd.font_desc == NULL) + { + semsg(_(e_unknown_font_str), font_name); + return; + } + + // Ensure the font description has a size (default 10pt if missing). + if (pango_font_description_get_size(pd.font_desc) == 0) + pango_font_description_set_size(pd.font_desc, 10 * PANGO_SCALE); + +# ifdef FEAT_SYN_HL + pd.do_syntax = syntax_present(curwin); +# endif + + op = gtk_print_operation_new(); + + if (settings != NULL) + gtk_print_operation_set_print_settings(op, settings); + + gtk_print_operation_set_job_name(op, + curbuf->b_fname != NULL + ? (const char *)curbuf->b_fname : "Vim"); + gtk_print_operation_set_show_progress(op, TRUE); + gtk_print_operation_set_unit(op, GTK_UNIT_POINTS); + + g_signal_connect(op, "begin-print", G_CALLBACK(print_begin_cb), &pd); + g_signal_connect(op, "draw-page", G_CALLBACK(print_draw_page_cb), &pd); + + res = gtk_print_operation_run(op, + GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, + GTK_WINDOW(gui.mainwin), NULL); + + if (res == GTK_PRINT_OPERATION_RESULT_APPLY) + { + if (settings != NULL) + g_object_unref(settings); + settings = g_object_ref( + gtk_print_operation_get_print_settings(op)); + } + + g_object_unref(op); + pango_font_description_free(pd.font_desc); +} + +#endif // FEAT_GUI_GTK_PRINT + +#endif // FEAT_GUI_GTK diff --git a/src/gui_gtk4_f.c b/src/gui_gtk4_f.c new file mode 100644 index 0000000000..759f1065b9 --- /dev/null +++ b/src/gui_gtk4_f.c @@ -0,0 +1,324 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + * + * GTK4 GtkForm widget - a simple container for absolute child positioning. + * This is a clean rewrite of gui_gtk_f.c for GTK4. + * + * In GTK4, widgets no longer have their own GdkWindows (now GdkSurface), + * GtkContainer is removed, and child positioning uses GskTransform via + * gtk_widget_allocate(). This makes the form widget much simpler. + */ + +#include "vim.h" +#include +#include "gui_gtk4_f.h" + +typedef struct _GtkFormChild GtkFormChild; + +struct _GtkFormChild +{ + GtkWidget *widget; + gint x; + gint y; +}; + +// Forward declarations +static void gui_gtk_form_class_init(GtkFormClass *klass); +static void gui_gtk_form_init(GtkForm *form); +static void form_measure(GtkWidget *widget, GtkOrientation orientation, + int for_size, int *minimum, int *natural, + int *minimum_baseline, int *natural_baseline); +static void form_size_allocate(GtkWidget *widget, int width, int height, + int baseline); +static void form_snapshot(GtkWidget *widget, GtkSnapshot *snapshot); +static void form_dispose(GObject *object); +static void form_position_child(GtkForm *form, GtkFormChild *child, + gboolean force_allocate); + +G_DEFINE_TYPE(GtkForm, gui_gtk_form, GTK_TYPE_WIDGET) + +// Public interface + + GtkWidget * +gui_gtk_form_new(void) +{ + return GTK_WIDGET(g_object_new(GTK_TYPE_FORM, NULL)); +} + + void +gui_gtk_form_put( + GtkForm *form, + GtkWidget *child_widget, + gint x, + gint y) +{ + GtkFormChild *child; + + g_return_if_fail(GTK_IS_FORM(form)); + + child = g_new(GtkFormChild, 1); + if (child == NULL) + return; + + child->widget = child_widget; + child->x = x; + child->y = y; + + gtk_widget_set_size_request(child->widget, -1, -1); + + form->children = g_list_append(form->children, child); + + gtk_widget_set_parent(child_widget, GTK_WIDGET(form)); + form_position_child(form, child, TRUE); +} + + void +gui_gtk_form_move( + GtkForm *form, + GtkWidget *child_widget, + gint x, + gint y) +{ + GList *tmp_list; + + g_return_if_fail(GTK_IS_FORM(form)); + + for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next) + { + GtkFormChild *child = tmp_list->data; + if (child->widget == child_widget) + { + child->x = x; + child->y = y; + form_position_child(form, child, TRUE); + return; + } + } +} + + void +gui_gtk_form_move_resize( + GtkForm *form, + GtkWidget *widget, + gint x, + gint y, + gint w, + gint h) +{ + gtk_widget_set_size_request(widget, w, h); + gui_gtk_form_move(form, widget, x, y); +} + + void +gui_gtk_form_remove(GtkForm *form, GtkWidget *child_widget) +{ + GList *tmp_list; + + g_return_if_fail(GTK_IS_FORM(form)); + + for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next) + { + GtkFormChild *child = tmp_list->data; + if (child->widget == child_widget) + { + form->children = g_list_remove_link(form->children, tmp_list); + g_list_free_1(tmp_list); + gtk_widget_unparent(child_widget); + g_free(child); + return; + } + } +} + + void +gui_gtk_form_freeze(GtkForm *form) +{ + g_return_if_fail(GTK_IS_FORM(form)); + ++form->freeze_count; +} + + void +gui_gtk_form_thaw(GtkForm *form) +{ + g_return_if_fail(GTK_IS_FORM(form)); + + if (!form->freeze_count) + return; + + if (!(--form->freeze_count)) + { + GList *tmp_list; + + for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next) + form_position_child(form, tmp_list->data, FALSE); + gtk_widget_queue_draw(GTK_WIDGET(form)); + } +} + +// GObject/GtkWidget class implementation + + static void +gui_gtk_form_class_init(GtkFormClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); + + gobject_class->dispose = form_dispose; + + widget_class->measure = form_measure; + widget_class->size_allocate = form_size_allocate; + widget_class->snapshot = form_snapshot; +} + + static void +gui_gtk_form_init(GtkForm *form) +{ + form->children = NULL; + form->freeze_count = 0; +} + + static void +form_measure( + GtkWidget *widget UNUSED, + GtkOrientation orientation UNUSED, + int for_size UNUSED, + int *minimum, + int *natural, + int *minimum_baseline, + int *natural_baseline) +{ + *minimum = 1; + *natural = 1; + *minimum_baseline = -1; + *natural_baseline = -1; +} + +static guint form_resize_idle_id = 0; +static int form_last_width = 0; +static int form_last_height = 0; + + static gboolean +form_resize_idle_cb(gpointer data UNUSED) +{ + int w, h; + + form_resize_idle_id = 0; + + // Use drawarea's actual allocation, not formwin's + if (gui.drawarea == NULL) + return FALSE; + w = gtk_widget_get_width(gui.drawarea); + h = gtk_widget_get_height(gui.drawarea); + + if (w > 1 && h > 1) + gui_resize_shell(w, h); + + return FALSE; +} + + static void +form_size_allocate(GtkWidget *widget, int width, int height, + int baseline UNUSED) +{ + GtkForm *form; + GList *tmp_list; + + g_return_if_fail(GTK_IS_FORM(widget)); + + form = GTK_FORM(widget); + + for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next) + form_position_child(form, tmp_list->data, TRUE); + + // Notify Vim about size change via idle callback + if (width != form_last_width || height != form_last_height) + { + form_last_width = width; + form_last_height = height; + if (form_resize_idle_id == 0) + form_resize_idle_id = g_idle_add(form_resize_idle_cb, NULL); + } +} + + static void +form_snapshot(GtkWidget *widget, GtkSnapshot *snapshot) +{ + GtkForm *form; + GList *tmp_list; + + g_return_if_fail(GTK_IS_FORM(widget)); + + form = GTK_FORM(widget); + + for (tmp_list = form->children; tmp_list; tmp_list = tmp_list->next) + { + GtkFormChild *child = tmp_list->data; + if (child->widget != NULL + && GTK_IS_WIDGET(child->widget) + && gtk_widget_get_parent(child->widget) == widget) + gtk_widget_snapshot_child(widget, child->widget, snapshot); + } +} + + static void +form_dispose(GObject *object) +{ + GtkForm *form = GTK_FORM(object); + GList *tmp_list; + + tmp_list = form->children; + while (tmp_list) + { + GtkFormChild *child = tmp_list->data; + tmp_list = tmp_list->next; + + gtk_widget_unparent(child->widget); + g_free(child); + } + g_list_free(form->children); + form->children = NULL; + + G_OBJECT_CLASS(gui_gtk_form_parent_class)->dispose(object); +} + +// Child positioning using GskTransform + + static void +form_position_child( + GtkForm *form UNUSED, + GtkFormChild *child, + gboolean force_allocate) +{ + if (!force_allocate) + return; + + if (child->widget == NULL || !GTK_IS_WIDGET(child->widget)) + return; + + { + GtkRequisition requisition; + GskTransform *transform; + int w, h; + + gtk_widget_get_preferred_size(child->widget, &requisition, NULL); + w = requisition.width; + h = requisition.height; + + // If widget has no size request (e.g. drawarea), use parent size + if (w <= 0) + w = gtk_widget_get_width(GTK_WIDGET(form)); + if (h <= 0) + h = gtk_widget_get_height(GTK_WIDGET(form)); + if (w <= 0) w = 1; + if (h <= 0) h = 1; + + transform = gsk_transform_translate(NULL, + &GRAPHENE_POINT_INIT((float)child->x, (float)child->y)); + gtk_widget_allocate(child->widget, w, h, -1, transform); + } +} diff --git a/src/gui_gtk4_f.h b/src/gui_gtk4_f.h new file mode 100644 index 0000000000..a927061608 --- /dev/null +++ b/src/gui_gtk4_f.h @@ -0,0 +1,59 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * + * GTK4 GtkForm widget - a simple container for absolute positioning. + */ + +#ifndef GUI_GTK4_FORM_H +#define GUI_GTK4_FORM_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define GTK_TYPE_FORM (gui_gtk_form_get_type()) +#define GTK_FORM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GTK_TYPE_FORM, GtkForm)) +#define GTK_FORM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GTK_TYPE_FORM, GtkFormClass)) +#define GTK_IS_FORM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GTK_TYPE_FORM)) + +typedef struct _GtkForm GtkForm; +typedef struct _GtkFormClass GtkFormClass; + +struct _GtkForm +{ + GtkWidget widget; + GList *children; + gint freeze_count; +}; + +struct _GtkFormClass +{ + GtkWidgetClass parent_class; +}; + +GType gui_gtk_form_get_type(void); + +GtkWidget *gui_gtk_form_new(void); + +void gui_gtk_form_put(GtkForm *form, GtkWidget *widget, gint x, gint y); + +void gui_gtk_form_move(GtkForm *form, GtkWidget *widget, gint x, gint y); + +void gui_gtk_form_move_resize(GtkForm *form, GtkWidget *widget, + gint x, gint y, gint w, gint h); + +void gui_gtk_form_remove(GtkForm *form, GtkWidget *widget); + +void gui_gtk_form_freeze(GtkForm *form); +void gui_gtk_form_thaw(GtkForm *form); + +#ifdef __cplusplus +} +#endif +#endif // GUI_GTK4_FORM_H diff --git a/src/gui_xim.c b/src/gui_xim.c index 26b125d919..acc6351936 100644 --- a/src/gui_xim.c +++ b/src/gui_xim.c @@ -18,15 +18,19 @@ #endif #if defined(FEAT_GUI_GTK) && defined(FEAT_XIM) -# if GTK_CHECK_VERSION(3,0,0) +# ifdef USE_GTK4 +# include +# elif GTK_CHECK_VERSION(3,0,0) # include # else # include # endif -# ifdef MSWIN -# include -# else -# include +# if !defined(USE_GTK4) +# ifdef MSWIN +# include +# else +# include +# endif # endif #endif @@ -185,7 +189,11 @@ static int im_preedit_cursor = 0; // cursor offset in characters static int im_preedit_trailing = 0; // number of characters after cursor static unsigned long im_commit_handler_id = 0; +# ifdef USE_GTK4 +static unsigned int im_activatekey_keyval = GDK_KEY_VoidSymbol; +# else static unsigned int im_activatekey_keyval = GDK_VoidSymbol; +# endif static unsigned int im_activatekey_state = 0; static GtkWidget *preedit_window = NULL; @@ -272,6 +280,19 @@ im_preedit_window_set_position(void) if (preedit_window == NULL) return; +# ifdef USE_GTK4 + // GTK4: positioning popup windows is limited. + // Use a simpler approach - just place near the cursor. + x = FILL_X(gui.col); + y = FILL_Y(gui.row) + gui.char_height; + width = 0; + height = 0; + screen_x = 0; + screen_y = 0; + screen_width = 0; + screen_height = 0; + // GTK4 doesn't have gtk_window_move; preedit is shown in-place. +# else gui_gtk_get_screen_geom_of_win(gui.drawarea, 0, 0, &screen_x, &screen_y, &screen_width, &screen_height); gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), &x, &y); @@ -283,6 +304,7 @@ im_preedit_window_set_position(void) if (y + height > screen_y + screen_height) y = screen_y + screen_height - height; gtk_window_move(GTK_WINDOW(preedit_window), x, y); +# endif } static void @@ -305,18 +327,28 @@ im_preedit_window_open(void) if (preedit_window == NULL) { +# ifdef USE_GTK4 + preedit_window = gtk_window_new(); +# else preedit_window = gtk_window_new(GTK_WINDOW_POPUP); +# endif gtk_window_set_transient_for(GTK_WINDOW(preedit_window), GTK_WINDOW(gui.mainwin)); preedit_label = gtk_label_new(""); gtk_widget_set_name(preedit_label, "vim-gui-preedit-area"); +# ifdef USE_GTK4 + gtk_window_set_child(GTK_WINDOW(preedit_window), preedit_label); +# else gtk_container_add(GTK_CONTAINER(preedit_window), preedit_label); +# endif } # if GTK_CHECK_VERSION(3,16,0) { +# ifndef USE_GTK4 GtkStyleContext * const context = gtk_widget_get_style_context(preedit_label); +# endif GtkCssProvider * const provider = gtk_css_provider_new(); gchar *css = NULL; const char * const fontname @@ -329,10 +361,15 @@ im_preedit_window_open(void) { // fontsize was given in points. Convert it into that in pixels // to use with CSS. +# ifdef USE_GTK4 + // GTK4: assume 96 DPI as default + fontsize = 96 * fontsize / 72; +# else GdkScreen * const screen = gdk_window_get_screen(gtk_widget_get_window(gui.mainwin)); const gdouble dpi = gdk_screen_get_resolution(screen); fontsize = dpi * fontsize / 72; +# endif } if (fontsize > 0) fontsize_propval = g_strdup_printf("%dpx", fontsize); @@ -355,9 +392,16 @@ im_preedit_window_open(void) (gui.back_pixel >> 8) & 0xff, gui.back_pixel & 0xff); +# ifdef USE_GTK4 + gtk_css_provider_load_from_string(provider, css); + gtk_style_context_add_provider_for_display( + gdk_display_get_default(), + GTK_STYLE_PROVIDER(provider), G_MAXUINT); +# else gtk_css_provider_load_from_data(provider, css, -1, NULL); gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), G_MAXUINT); +# endif g_free(css); g_free(fontsize_propval); @@ -396,9 +440,13 @@ im_preedit_window_open(void) layout = gtk_label_get_layout(GTK_LABEL(preedit_label)); pango_layout_get_pixel_size(layout, &w, &h); h = MAX(h, gui.char_height); +# ifdef USE_GTK4 + gtk_window_set_default_size(GTK_WINDOW(preedit_window), w, h); + gtk_widget_set_visible(preedit_window, TRUE); +# else gtk_window_resize(GTK_WINDOW(preedit_window), w, h); - gtk_widget_show_all(preedit_window); +# endif im_preedit_window_set_position(); } @@ -411,7 +459,11 @@ im_preedit_window_open(void) im_preedit_window_close(void) { if (preedit_window != NULL) +# ifdef USE_GTK4 + gtk_widget_set_visible(preedit_window, FALSE); +# else gtk_widget_hide(preedit_window); +# endif } static void @@ -874,7 +926,9 @@ xim_init(void) # endif g_return_if_fail(gui.drawarea != NULL); +# ifndef USE_GTK4 g_return_if_fail(gtk_widget_get_window(gui.drawarea) != NULL); +# endif xic = gtk_im_multicontext_new(); g_object_ref(xic); @@ -888,7 +942,11 @@ xim_init(void) g_signal_connect(G_OBJECT(xic), "preedit_end", G_CALLBACK(&im_preedit_end_cb), NULL); +# ifdef USE_GTK4 + gtk_im_context_set_client_widget(xic, gui.drawarea); +# else gtk_im_context_set_client_window(xic, gtk_widget_get_window(gui.drawarea)); +# endif } void @@ -911,6 +969,7 @@ im_shutdown(void) xim_has_preediting = FALSE; } +# ifndef USE_GTK4 /* * Convert the string argument to keyval and state for GdkEventKey. * If str is valid return TRUE, otherwise FALSE. @@ -980,7 +1039,9 @@ im_xim_isvalid_imactivate(void) &im_activatekey_keyval, &im_activatekey_state); } +# endif // !USE_GTK4 +# ifndef USE_GTK4 static void im_synthesize_keypress(unsigned int keyval, unsigned int state) { @@ -1008,6 +1069,7 @@ im_synthesize_keypress(unsigned int keyval, unsigned int state) gdk_event_free((GdkEvent *)event); } +# endif // !USE_GTK4 void xim_reset(void) @@ -1027,6 +1089,11 @@ xim_reset(void) { xim_set_focus(gui.in_focus); +# ifdef USE_GTK4 + im_shutdown(); + xim_init(); + xim_set_focus(gui.in_focus); +# else if (im_activatekey_keyval != GDK_VoidSymbol) { if (im_is_active) @@ -1043,6 +1110,7 @@ xim_reset(void) xim_init(); xim_set_focus(gui.in_focus); } +# endif } } @@ -1051,12 +1119,13 @@ xim_reset(void) xim_has_preediting = FALSE; } +# ifndef USE_GTK4 int xim_queue_key_press_event(GdkEventKey *event, int down) { -# ifdef FEAT_GUI_GTK +# ifdef FEAT_GUI_GTK if (event->state & GDK_SUPER_MASK) return FALSE; -# endif +# endif if (down) { // Workaround GTK2 XIM 'feature' that always converts keypad keys to @@ -1185,6 +1254,23 @@ xim_queue_key_press_event(GdkEventKey *event, int down) return FALSE; } +# else // USE_GTK4 +// GTK4: imactivatekey is not supported because GTK4's GtkIMContext +// does not allow synthesizing key events for IM activation. + int +im_xim_isvalid_imactivate(void) +{ + // Empty string is always valid (means no activation key). + // Any other value is not supported in GTK4. + return p_imak[0] == NUL; +} + + int +xim_queue_key_press_event(GdkEvent *event UNUSED, int down UNUSED) +{ + return FALSE; +} +# endif // !USE_GTK4 int im_get_status(void) diff --git a/src/hardcopy.c b/src/hardcopy.c index e87caf3020..2eb63ea588 100644 --- a/src/hardcopy.c +++ b/src/hardcopy.c @@ -560,6 +560,16 @@ ex_hardcopy(exarg_T *eap) CLEAR_FIELD(settings); settings.has_color = TRUE; +#ifdef FEAT_GUI_GTK_PRINT + // Use the native GTK print dialog only for interactive printing; + // ":hardcopy >file" must fall through to the PostScript writer. + if (gui.in_use && *eap->arg != '>') + { + gui_gtk4_hardcopy(eap); + return; + } +#endif + #ifdef FEAT_POSTSCRIPT if (*eap->arg == '>') { diff --git a/src/po/vim.pot b/src/po/vim.pot index f1369d68ae..2057c23a92 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-05-17 19:50+0000\n" +"POT-Creation-Date: 2026-05-19 18:20+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -921,9 +921,11 @@ msgstr "" msgid "VIM - Search..." msgstr "" +#. "Find what:" label + entry msgid "Find what:" msgstr "" +#. "Replace with:" label + entry msgid "Replace with:" msgstr "" @@ -957,6 +959,12 @@ msgstr "" msgid "_Close" msgstr "" +msgid "Direction:" +msgstr "" + +msgid "Close" +msgstr "" + msgid "Vim: Received \"die\" request from session manager\n" msgstr "" @@ -3454,6 +3462,9 @@ msgstr "" msgid "without GUI." msgstr "" +msgid "with GTK4 GUI." +msgstr "" + msgid "with GTK3 GUI." msgstr "" diff --git a/src/proto.h b/src/proto.h index e46b25624f..ed7a5a054e 100644 --- a/src/proto.h +++ b/src/proto.h @@ -302,8 +302,12 @@ extern char_u *vimpty_getenv(const char_u *string); // in misc2.c # include "gui_w32.pro" # endif # ifdef FEAT_GUI_GTK -# include "gui_gtk.pro" -# include "gui_gtk_x11.pro" +# ifdef USE_GTK4 +# include "gui_gtk4.pro" +# else +# include "gui_gtk.pro" +# include "gui_gtk_x11.pro" +# endif # endif # ifdef FEAT_GUI_MOTIF # include "gui_motif.pro" diff --git a/src/proto/gui_gtk4.pro b/src/proto/gui_gtk4.pro new file mode 100644 index 0000000000..1f13ab6ca6 --- /dev/null +++ b/src/proto/gui_gtk4.pro @@ -0,0 +1,110 @@ +/* gui_gtk4.c */ +void gui_mch_prepare(int *argc, char **argv); +void gui_mch_free_all(void); +int gui_mch_is_blinking(void); +int gui_mch_is_blink_off(void); +void gui_mch_set_blinking(long waittime, long on, long off); +void gui_mch_stop_blink(int may_call_gui_update_cursor); +void gui_mch_start_blink(void); +int gui_mch_early_init_check(int give_message); +int gui_mch_init_check(void); +int gui_mch_init(void); +void gui_mch_new_colors(void); +int gui_mch_open(void); +void gui_mch_exit(int rc); +int gui_mch_get_winpos(int *x, int *y); +void gui_mch_set_winpos(int x, int y); +int gui_mch_maximized(void); +void gui_mch_unmaximize(void); +void gui_mch_newfont(void); +void gui_mch_settitle(char_u *title, char_u *icon); +void gui_mch_set_shellsize(int width, int height, int min_width, int min_height, int base_width, int base_height, int direction); +void gui_mch_get_screen_dimensions(int *screen_w, int *screen_h); +void gui_mch_enable_menu(int showit); +void gui_mch_show_toolbar(int showit); +void gui_mch_set_dark_theme(int dark); +int gui_mch_adjust_charheight(void); +char_u *gui_mch_font_dialog(char_u *oldval); +int gui_mch_init_font(char_u *font_name, int fontset); +GuiFont gui_mch_get_font(char_u *name, int report_error); +char_u *gui_mch_get_fontname(GuiFont font, char_u *name); +void gui_mch_free_font(GuiFont font); +void gui_mch_expand_font(optexpand_T *args, void *param, int (*add_match)(char_u *val)); +guicolor_T gui_mch_get_color(char_u *name); +guicolor_T gui_mch_get_rgb_color(int r, int g, int b); +void gui_mch_set_fg_color(guicolor_T color); +void gui_mch_set_bg_color(guicolor_T color); +void gui_mch_set_sp_color(guicolor_T color); +guicolor_T gui_mch_get_rgb(guicolor_T pixel); +void gui_mch_clear_block(int row1, int col1, int row2, int col2); +void gui_mch_clear_all(void); +void gui_mch_delete_lines(int row, int num_lines); +void gui_mch_insert_lines(int row, int num_lines); +void gui_mch_draw_hollow_cursor(guicolor_T color); +void gui_mch_draw_part_cursor(int w, int h, guicolor_T color); +void gui_mch_flash(int msec); +void gui_mch_invert_rectangle(int r, int c, int nr, int nc); +void gui_mch_update(void); +int gui_mch_wait_for_chars(long wtime); +void gui_mch_flush(void); +void gui_mch_beep(void); +void *gui_mch_get_display(void); +void gui_mch_iconify(void); +void gui_mch_set_foreground(void); +void gui_mch_getmouse(int *x, int *y); +void gui_mch_setmouse(int x, int y); +void gui_mch_mousehide(int hide); +int gui_mch_haskey(char_u *name); +void gui_mch_forked(void); +void gui_mch_enable_scrollbar(scrollbar_T *sb, int flag); +void gui_mch_menu_grey(vimmenu_T *menu, int grey); +void gui_mch_menu_hidden(vimmenu_T *menu, int hidden); +void gui_mch_draw_menubar(void); +void gui_mch_show_tabline(int showit); +int gui_mch_showing_tabline(void); +void gui_mch_update_tabline(void); +void gui_mch_set_curtab(int nr); +void gui_mch_drawsign(int row, int col, int typenr); +void *gui_mch_register_sign(char_u *signfile); +void gui_mch_destroy_sign(void *sign); +int gui_gtk_draw_string_ext(int row, int col, char_u *s, int len, int flags, int force_pango); +int gui_gtk_draw_string(int row, int col, char_u *s, int len, int flags); +int gui_get_x11_windis(Window *win, Display **dis); +void gui_gtk_init_socket_server(void); +void gui_gtk_uninit_socket_server(void); +void gui_gtk_set_mnemonics(int enable); +void gui_make_popup(char_u *path_name, int mouse_pos); +int get_menu_tool_width(void); +int get_menu_tool_height(void); +void clip_mch_request_selection(Clipboard_T *cbd); +void clip_mch_set_selection(Clipboard_T *cbd); +int clip_mch_own_selection(Clipboard_T *cbd); +void clip_mch_lose_selection(Clipboard_T *cbd); +void gui_mch_post_balloon(BalloonEval *beval, char_u *mesg); +BalloonEval *gui_mch_create_beval_area(void *target, char_u *mesg, void (*mesgCB)(BalloonEval *, int), void *clientData); +void gui_mch_enable_beval_area(BalloonEval *beval); +void gui_mch_disable_beval_area(BalloonEval *beval); +guint gtk_main_level(void); +void gtk_main_quit(void); +void mch_set_mouse_shape(int shape); +void gui_mch_add_menu(vimmenu_T *menu, int idx); +void gui_mch_add_menu_item(vimmenu_T *menu, int idx); +void gui_mch_toggle_tearoffs(int enable); +void gui_mch_menu_set_tip(vimmenu_T *menu); +void gui_mch_destroy_menu(vimmenu_T *menu); +void gui_mch_show_popupmenu(vimmenu_T *menu); +void gui_mch_set_scrollbar_thumb(scrollbar_T *sb, long val, long size, long max); +void gui_mch_set_scrollbar_pos(scrollbar_T *sb, int x, int y, int w, int h); +int gui_mch_get_scrollbar_xpadding(void); +int gui_mch_get_scrollbar_ypadding(void); +void gui_mch_create_scrollbar(scrollbar_T *sb, int orient); +void gui_mch_destroy_scrollbar(scrollbar_T *sb); +void gui_mch_set_text_area_pos(int x, int y, int w, int h); +char_u *gui_mch_browse(int saving, char_u *title, char_u *dflt, char_u *ext, char_u *initdir, char_u *filter); +char_u *gui_mch_browsedir(char_u *title, char_u *initdir); +int gui_mch_dialog(int type, char_u *title, char_u *message, char_u *buttons, int dfltbutton, char_u *textfield, int ex_cmd); +void gui_mch_find_dialog(exarg_T *eap); +void gui_mch_replace_dialog(exarg_T *eap); +void ex_helpfind(exarg_T *eap); +void gui_gtk4_hardcopy(exarg_T *eap); +/* vim: set ft=c : */ diff --git a/src/version.c b/src/version.c index bb0b3d4f53..e6bc4886cc 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 501, /**/ 500, /**/ @@ -2077,7 +2079,9 @@ list_version(void) #if !defined(FEAT_GUI) msg_puts(_("without GUI.")); #elif defined(FEAT_GUI_GTK) -# if defined(USE_GTK3) +# if defined(USE_GTK4) + msg_puts(_("with GTK4 GUI.")); +# elif defined(USE_GTK3) msg_puts(_("with GTK3 GUI.")); # elif defined(FEAT_GUI_GNOME) msg_puts(_("with GTK2-GNOME GUI.")); diff --git a/src/vim.h b/src/vim.h index 683aff2b75..6906547c4e 100644 --- a/src/vim.h +++ b/src/vim.h @@ -2365,7 +2365,7 @@ typedef struct Atom sel_atom; // PRIMARY/CLIPBOARD selection ID # endif -# ifdef FEAT_GUI_GTK +# if defined(FEAT_GUI_GTK) && !defined(USE_GTK4) GdkAtom gtk_sel_atom; // PRIMARY/CLIPBOARD selection ID # endif From 7a57f597562ccd6fb8762cfeb7ed1326fd69bc89 Mon Sep 17 00:00:00 2001 From: Aliaksei Budavei <0x000c70@gmail.com> Date: Tue, 19 May 2026 18:30:50 +0000 Subject: [PATCH 262/545] runtime(syntax-tests): Always delete our copy of "src/testdir/vimcmd" Also prune this file whenever: - tests cannot be run on this OS; - tests fail and no QuitPre event is supported for ":cquit". related: #19127 closes: #20247 Signed-off-by: Aliaksei Budavei <0x000c70@gmail.com> Signed-off-by: Christian Brabandt --- runtime/syntax/testdir/runtest.vim | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/runtime/syntax/testdir/runtest.vim b/runtime/syntax/testdir/runtest.vim index f71ccf1638..b3a6612b77 100644 --- a/runtime/syntax/testdir/runtest.vim +++ b/runtime/syntax/testdir/runtest.vim @@ -76,7 +76,14 @@ let s:vimcmdSyntaxFname = fnameescape(syntaxDir .. '/testdir/vimcmd') if filereadable(s:vimcmdSyntaxFname) call delete('vimcmd') call filecopy(s:vimcmdSyntaxFname, 'vimcmd') - exe 'au ExitPre call delete("' .. fnameescape(getcwd() .. '/vimcmd') .. '")' + " Work around uneventful support for ":cquit". + exe printf("%s\n%s\n%s", + \ 'def s:DeleteVimcmdCopy()', + \ 'delete("' .. fnameescape(getcwd() .. '/vimcmd') .. '")', + \ 'enddef') +else + def s:DeleteVimcmdCopy() + enddef endif source util/screendump.vim @@ -87,6 +94,7 @@ exe 'cd ' .. fnameescape(syntaxDir) " MS-Windows the console only has 16 colors and the GUI can't run in a " terminal. if !CanRunVimInTerminal() + call s:DeleteVimcmdCopy() call Fatal('Cannot make screendumps, aborting') endif @@ -240,6 +248,7 @@ def s:TermWaitAndPollRuler(buf: number, in_name_and_out_name: string): list Date: Tue, 19 May 2026 18:46:59 +0000 Subject: [PATCH 263/545] runtime(doc): document new GTK4 GUI in version9.txt Signed-off-by: Christian Brabandt --- runtime/doc/version9.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 846d2dfa23..1e7d1c7135 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 May 17 +*version9.txt* For Vim version 9.2. Last change: 2026 May 19 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52585,6 +52585,11 @@ VERSION 9.3 *version-9.3* *version9.3* *vim-9.3* This section is about improvements made between version 9.2 and 9.3 and is work in progress. +GUI ~ +----- +- Added support for the GTK 4 GUI (via patch 9.2.0501). Enable with + `--enable-gui=gtk4` configure switch at build time. + Popups ~ ------ - Support for transparency, see |popup-opacity|. From 35b767a090cc7b918dcafc9feb1a78cf0938d6bc Mon Sep 17 00:00:00 2001 From: "J. Paulo Seibt" Date: Tue, 19 May 2026 18:51:14 +0000 Subject: [PATCH 264/545] patch 9.2.0502: runtime(netrw): bookmark handling can be improved Problem: To goto or delete a bookmark, one needs to prefix a count for the bookmark number (e.g., "2gb" to open bookmark#2). As the bookmark list gets or deletes entries, the numbers keep changing, requiring listing the bookmarks with qb to discover the desired bookmark number. Typing gb or mB without a count targets g:netrw_bookmarklist[-1]. Solution: If no count is given to gb or mB, list all bookmarks and prompt for a number using inputlist(), similar to tag jump with g]. closes: #20211 Signed-off-by: J. Paulo Seibt Signed-off-by: Christian Brabandt --- runtime/doc/pi_netrw.txt | 19 ++++-- .../pack/dist/opt/netrw/autoload/netrw.vim | 68 +++++++++++++++---- runtime/pack/dist/opt/netrw/doc/netrw.txt | 19 ++++-- src/testdir/test_plugin_netrw.vim | 44 +++++++++++- src/version.c | 2 + 5 files changed, 126 insertions(+), 26 deletions(-) diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 3f744bc8f5..6f20073d5a 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1047,7 +1047,7 @@ QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 C Setting the editing window |netrw-C| d Make a directory |netrw-d| D Attempt to remove the file(s)/directory(ies) |netrw-D| - gb Go to previous bookmarked directory |netrw-gb| + gb Go to bookmark |netrw-gb| gd Force treatment as directory |netrw-gd| gf Force treatment as file |netrw-gf| gh Quick hide/unhide of dot-files |netrw-gh| @@ -1056,11 +1056,12 @@ QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 i Cycle between thin, long, wide, and tree listings |netrw-i| I Toggle the displaying of the banner |netrw-I| mb Bookmark current directory |netrw-mb| + mB Delete bookmark |netrw-mB| mc Copy marked files to marked-file target directory |netrw-mc| md Apply diff to marked files (up to 3) |netrw-md| me Place marked files on arg list and edit them |netrw-me| mf Mark a file |netrw-mf| - mF Unmark files |netrw-mF| + mF Unmark buffer-local files |netrw-mF| mg Apply vimgrep to marked files |netrw-mg| mh Toggle marked file suffices' presence on hiding list |netrw-mh| mm Move marked files to marked-file target directory |netrw-mm| @@ -1365,13 +1366,16 @@ Currently, this only works for local files. Associated setting variables: |g:netrw_chgperm| -CHANGING TO A BOOKMARKED DIRECTORY *netrw-gb* {{{2 +CHANGING TO A BOOKMARKED PATH *netrw-gb* {{{2 -To change directory back to a bookmarked directory, use +To change to a bookmarked path, use - {cnt}gb + [{cnt}]gb Any count may be used to reference any of the bookmarks. +If {cnt} is omitted, it shows a list of the current bookmarks +and prompts for a bookmark number to go to. + Note that |netrw-qb| shows both bookmarks and history; to go to a location stored in the history see |netrw-u| and |netrw-U|. @@ -1434,10 +1438,13 @@ DELETING BOOKMARKS *netrw-mB* {{{2 To delete a bookmark, use > - {cnt}mB + [{cnt}]mB If there are marked files, then mB will remove them from the bookmark list. +If no files are marked and {cnt} is omitted, it shows a list +of the current bookmarks and prompts for a bookmark number +to delete. Alternatively, one may use :NetrwMB! (see |netrw-:NetrwMB|). > diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index 1fdbeeb8f7..89db6e3ca6 100644 --- a/runtime/pack/dist/opt/netrw/autoload/netrw.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -2707,7 +2707,7 @@ endfunction " s:NetrwBookHistHandler: {{{2 " 0: (user: ) bookmark current directory -" 1: (user: ) change to the bookmarked directory +" 1: (user: ) change to the bookmarked path " 2: (user: ) list bookmarks " 3: (browsing) records current directory history " 4: (user: ) go up (previous) directory, using history @@ -2737,11 +2737,33 @@ function s:NetrwBookHistHandler(chg,curdir) endtry elseif a:chg == 1 - " change to the bookmarked directory - if exists("g:netrw_bookmarklist[v:count-1]") - exe "NetrwKeepj e ".fnameescape(g:netrw_bookmarklist[v:count-1]) + " change to bookmarked path + if exists("g:netrw_bookmarklist") && !empty(g:netrw_bookmarklist) + let len_bookmarklist = len(g:netrw_bookmarklist) + let bookmark_num = v:count + + " v:count value is set to zero if no count (prefix) is given to the `gb` map + if bookmark_num == 0 + " list bookmarks and prompt for a bookmark number + let goto_list = [" # | Goto Bookmark:"] + let i = 0 + while i < len_bookmarklist + call add(goto_list, printf("%3d| %s", i + 1, g:netrw_bookmarklist[i])) + let i += 1 + endwhile + let bookmark_num = inputlist(goto_list) + endif + + if bookmark_num > 0 + if bookmark_num <= len_bookmarklist + exe "NetrwKeepj e " . fnameescape(g:netrw_bookmarklist[bookmark_num - 1]) + else + echomsg "Sorry, bookmark#" . bookmark_num . " doesn't exist!" + endif + endif + " Exit silently if user cancels with `q` or empty after inputlist() else - echomsg "Sorry, bookmark#".v:count." doesn't exist!" + echo "Bookmark list is empty." endif elseif a:chg == 2 @@ -2842,16 +2864,38 @@ function s:NetrwBookHistHandler(chg,curdir) endif elseif a:chg == 6 - if exists("s:netrwmarkfilelist_{curbufnr}") + if exists("s:netrwmarkfilelist_{curbufnr}") && !empty(s:netrwmarkfilelist_{curbufnr}) call s:NetrwBookmark(1) echo "removed marked files from bookmarks" + elseif exists("g:netrw_bookmarklist") && !empty(g:netrw_bookmarklist) + let len_bookmarklist = len(g:netrw_bookmarklist) + let bookmark_num = v:count + + " v:count value is set to zero if no count (prefix) is given to the `gb` map + if bookmark_num == 0 + " list bookmarks and prompt for a bookmark number + let goto_list = [" # | Delete Bookmark:"] + let i = 0 + while i < len_bookmarklist + call add(goto_list, printf("%3d| %s", i + 1, g:netrw_bookmarklist[i])) + let i += 1 + endwhile + let bookmark_num = inputlist(goto_list) + endif + + if bookmark_num > 0 + if bookmark_num <= len_bookmarklist + let bookmark_path = g:netrw_bookmarklist[bookmark_num - 1] + call s:MergeBookmarks() + NetrwKeepj call remove(g:netrw_bookmarklist, bookmark_num - 1) + echo "removed " . bookmark_path . " from g:netrw_bookmarklist." + else + echomsg "Sorry, bookmark#" . bookmark_num . " doesn't exist!" + endif + endif + " Exit silently if user cancels with `q` or empty after inputlist() else - " delete the v:count'th bookmark - let iremove = v:count - let dremove = g:netrw_bookmarklist[iremove - 1] - call s:MergeBookmarks() - NetrwKeepj call remove(g:netrw_bookmarklist,iremove-1) - echo "removed ".dremove." from g:netrw_bookmarklist" + echo "Bookmark list is empty." endif try diff --git a/runtime/pack/dist/opt/netrw/doc/netrw.txt b/runtime/pack/dist/opt/netrw/doc/netrw.txt index 168deed345..2c5d6d822e 100644 --- a/runtime/pack/dist/opt/netrw/doc/netrw.txt +++ b/runtime/pack/dist/opt/netrw/doc/netrw.txt @@ -1047,7 +1047,7 @@ QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 C Setting the editing window |netrw-C| d Make a directory |netrw-d| D Attempt to remove the file(s)/directory(ies) |netrw-D| - gb Go to previous bookmarked directory |netrw-gb| + gb Go to bookmark |netrw-gb| gd Force treatment as directory |netrw-gd| gf Force treatment as file |netrw-gf| gh Quick hide/unhide of dot-files |netrw-gh| @@ -1056,11 +1056,12 @@ QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 i Cycle between thin, long, wide, and tree listings |netrw-i| I Toggle the displaying of the banner |netrw-I| mb Bookmark current directory |netrw-mb| + mB Delete bookmark |netrw-mB| mc Copy marked files to marked-file target directory |netrw-mc| md Apply diff to marked files (up to 3) |netrw-md| me Place marked files on arg list and edit them |netrw-me| mf Mark a file |netrw-mf| - mF Unmark files |netrw-mF| + mF Unmark buffer-local files |netrw-mF| mg Apply vimgrep to marked files |netrw-mg| mh Toggle marked file suffices' presence on hiding list |netrw-mh| mm Move marked files to marked-file target directory |netrw-mm| @@ -1365,13 +1366,16 @@ Currently, this only works for local files. Associated setting variables: |g:netrw_chgperm| -CHANGING TO A BOOKMARKED DIRECTORY *netrw-gb* {{{2 +CHANGING TO A BOOKMARKED PATH *netrw-gb* {{{2 -To change directory back to a bookmarked directory, use +To change to a bookmarked path, use - {cnt}gb + [{cnt}]gb Any count may be used to reference any of the bookmarks. +If {cnt} is omitted, it shows a list of the current bookmarks +and prompts for a bookmark number to go to. + Note that |netrw-qb| shows both bookmarks and history; to go to a location stored in the history see |netrw-u| and |netrw-U|. @@ -1434,10 +1438,13 @@ DELETING BOOKMARKS *netrw-mB* {{{2 To delete a bookmark, use > - {cnt}mB + [{cnt}]mB If there are marked files, then mB will remove them from the bookmark list. +If no files are marked and {cnt} is omitted, it shows a list +of the current bookmarks and prompts for a bookmark number +to delete. Alternatively, one may use :NetrwMB! (see |netrw-:NetrwMB|). > diff --git a/src/testdir/test_plugin_netrw.vim b/src/testdir/test_plugin_netrw.vim index 7cd2aa89ed..76fa410921 100644 --- a/src/testdir/test_plugin_netrw.vim +++ b/src/testdir/test_plugin_netrw.vim @@ -330,7 +330,9 @@ func Test_netrw_parse_special_char_user() call assert_equal(result.path, 'test.txt') endfunction -func Test_netrw_empty_buffer_fastpath_wipe() +" Note: Test_netrw_a_empty_buffer_fastpath_wipe() should run before +" any other tests that open a netrw buffer (e.g, :Explore). +func Test_netrw_a_empty_buffer_fastpath_wipe() " SetUp() may have opened some buffers let previous = bufnr('$') let g:netrw_fastbrowse=0 @@ -717,7 +719,44 @@ func Test_netrw_bookmark_marked_file() let g:netrw_keepdir = save_keepdir if save_bookmarklist is v:null - unlet g:netrw_bookmarklist + unlet! g:netrw_bookmarklist + else + let g:netrw_bookmarklist = save_bookmarklist + endif + + bw! +endfunc + +" Typing gb or mB without a count should prompt +" for a bookmark number through an inputlist(). +" Expected dialog output (gb): # | Goto Bookmark: +" 1| /foo/bar/baz +" +" Expected dialog output (mB): # | Delete Bookmark: +" 1| /foo/bar/baz +func Test_netrw_bookmark_goto_delete_prompt() + let save_home = g:netrw_home + let save_bookmarklist = exists('g:netrw_bookmarklist') ? g:netrw_bookmarklist : v:null + + let g:netrw_home = getcwd() + let g:netrw_bookmarklist = ['/foo/bar/baz'] + Explore . + + " Inject 'q' to cancel the inputlist() prompt + call feedkeys('q', 't') + let dialog_out = execute('normal gb') + call assert_match('Goto Bookmark:', dialog_out) + + call feedkeys('q', 't') + let dialog_out = execute('normal mB') + call assert_match('Delete Bookmark:', dialog_out) + + " Tear down + call delete(g:netrw_home . '/.netrwbook') + call delete(g:netrw_home . '/.netrwhist') + let g:netrw_home = save_home + if save_bookmarklist is v:null + unlet! g:netrw_bookmarklist else let g:netrw_bookmarklist = save_bookmarklist endif @@ -780,4 +819,5 @@ func Test_netrw_injection() unlet! g:netrw_home g:netrw_dirhistmax g:netrw_dirhistcnt g:netrw_dirhist_1 g:injected endtry endfunc + " vim:ts=8 sts=2 sw=2 et diff --git a/src/version.c b/src/version.c index e6bc4886cc..2e75367bf4 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 502, /**/ 501, /**/ From 0fa3603d6d9cec774f3060a33c59778c9baf0e41 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 20 May 2026 09:13:52 +0200 Subject: [PATCH 265/545] runtime(doc): update cmdline-history (after v9.1.0895) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Hernán Ibarra Mejia Signed-off-by: Christian Brabandt --- runtime/doc/cmdline.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index bf47c51999..6427507d7e 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -1,4 +1,4 @@ -*cmdline.txt* For Vim version 9.2. Last change: 2026 Apr 20 +*cmdline.txt* For Vim version 9.2. Last change: 2026 May 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -56,7 +56,7 @@ history tables: These are completely separate. Each history can only be accessed when entering the same type of line. Use the 'history' option to set the number of lines that are remembered -(default: 50). +(default: 200). Notes: - When you enter a command-line that is exactly the same as an older one, the old one is removed (to avoid repeated commands moving older commands out of From 71bcc867c95514322abbce560fcb0dec4908951d Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 20 May 2026 17:34:22 +0000 Subject: [PATCH 266/545] patch 9.2.0503: Makefile: Missing dependencies for new GTK4 source files Problem: Makefile: Missing dependencies for new GTK4 source files (Reilly Brogan, after v9.2.0501) Solution: Re-run make depend, clean the result up and include the missing dependencies for the GTK4 source files fixes: #20267 Signed-off-by: Christian Brabandt --- src/Makefile | 10 ++++++++++ src/version.c | 2 ++ 2 files changed, 12 insertions(+) diff --git a/src/Makefile b/src/Makefile index e08b6cccfd..4e00a2eb88 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4447,6 +4447,16 @@ objects/gui_gtk.o: auto/osdef.h gui_gtk.c vim.h protodef.h auto/config.h feature structs.h regexp.h gui.h libvterm/include/vterm.h \ libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \ globals.h errors.h gui_gtk_f.h +objects/gui_gtk4.o: auto/osdef.h gui_gtk4.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + ascii.h keymap.h termdefs.h macros.h option.h beval.h \ + structs.h regexp.h gui.h libvterm/include/vterm.h \ + libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \ + globals.h errors.h gui_gtk4_f.h auto/gui_gtk_gresources.h +objects/gui_gtk4_f.o: auto/osdef.h gui_gtk4_f.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h ascii.h keymap.h termdefs.h macros.h option.h \ + beval.h structs.h regexp.h gui.h \ + libvterm/include/vterm.h libvterm/include/vterm_keycodes.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h errors.h gui_gtk4_f.h objects/gui_gtk_f.o: auto/osdef.h gui_gtk_f.c vim.h protodef.h auto/config.h feature.h \ os_unix.h ascii.h keymap.h termdefs.h macros.h option.h \ beval.h structs.h regexp.h gui.h \ diff --git a/src/version.c b/src/version.c index 2e75367bf4..c989738312 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 503, /**/ 502, /**/ From 129486193c16bb5c0208eff5501f9e665b7c4803 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Wed, 20 May 2026 17:56:05 +0000 Subject: [PATCH 267/545] runtime(getscript,vimball,rust): Use correct shellescape() form for ! ex cmd Problem: shellescape() called without {special} flag for :! ex command Solution: Pass 1 as second argument to shellescape() in :! contexts related: Commit: 3fb5e58fbc63d86a3e65f1a141b0d67af2 (patch 9.2.0479: [security]: runtime(tar): command injection in tar plugin) Signed-off-by: Christian Brabandt --- runtime/autoload/getscript.vim | 37 +++++++++++++++++----------------- runtime/autoload/rust.vim | 3 ++- runtime/autoload/vimball.vim | 17 ++++++++-------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/runtime/autoload/getscript.vim b/runtime/autoload/getscript.vim index 27a5a49535..04dd19a4c5 100644 --- a/runtime/autoload/getscript.vim +++ b/runtime/autoload/getscript.vim @@ -15,6 +15,7 @@ " 2025 Feb 28 by Vim Project: add support for bzip3 (#16755) " 2025 May 11 by Vim Project: check network connectivity (#17249) " 2025 Dec 21 by Vim Project: make the wget check more robust (#18987) +" 2026 May 20 by Vim Project: use correct shellescape() with ! command " }}} " " GetLatestVimScripts: 642 1 :AutoInstall: getscript.vim @@ -433,9 +434,9 @@ fun! s:GetOneScript(...) let itry= 1 while itry <= 3 if has("win32") || has("win16") || has("win95") - new|exe "silent r!".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(tmpfile).' '.shellescape(scriptaddr)|bw! + new|exe "silent r!".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(tmpfile,1).' '.shellescape(scriptaddr,1)|bw! else - exe "silent !".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(tmpfile)." ".shellescape(scriptaddr) + exe "silent !".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(tmpfile,1)." ".shellescape(scriptaddr,1) endif if itry == 1 exe "silent vsplit ".fnameescape(tmpfile) @@ -503,9 +504,9 @@ fun! s:GetOneScript(...) " ----------------------------------------------------------------------------- echomsg ".downloading new <".sname.">" if has("win32") || has("win16") || has("win95") - new|exe "silent r!".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid)|bw! + new|exe "silent r!".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname,1)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid,1)|bw! else - exe "silent !".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid) + exe "silent !".g:GetLatestVimScripts_wget." ".g:GetLatestVimScripts_options." ".shellescape(sname,1)." ".shellescape(g:GetLatestVimScripts_downloadaddr.latestsrcid,1) endif " -------------------------------------------------------------------------- @@ -513,7 +514,7 @@ fun! s:GetOneScript(...) " -------------------------------------------------------------------------- if doautoinstall if filereadable(sname) - exe "silent !".g:GetLatestVimScripts_mv." ".shellescape(sname)." ".shellescape(s:autoinstall) + exe "silent !".g:GetLatestVimScripts_mv." ".shellescape(sname,1)." ".shellescape(s:autoinstall,1) let curdir = fnameescape(substitute(getcwd(),'\','/','ge')) let installdir= curdir."/Installed" if !isdirectory(installdir) @@ -532,33 +533,33 @@ fun! s:GetOneScript(...) " decompress if sname =~ '\.bz2$' - exe "sil !".g:GetLatestVimScripts_bunzip2." ".shellescape(sname) + exe "sil !".g:GetLatestVimScripts_bunzip2." ".shellescape(sname,1) let sname= substitute(sname,'\.bz2$','','') elseif sname =~ '\.bz3$' - exe "sil !".g:GetLatestVimScripts_bunzip3." ".shellescape(sname) + exe "sil !".g:GetLatestVimScripts_bunzip3." ".shellescape(sname,1) let sname= substitute(sname,'\.bz3$','','') elseif sname =~ '\.gz$' - exe "sil !".g:GetLatestVimScripts_gunzip." ".shellescape(sname) + exe "sil !".g:GetLatestVimScripts_gunzip." ".shellescape(sname,1) let sname= substitute(sname,'\.gz$','','') elseif sname =~ '\.xz$' - exe "sil !".g:GetLatestVimScripts_unxz." ".shellescape(sname) + exe "sil !".g:GetLatestVimScripts_unxz." ".shellescape(sname,1) let sname= substitute(sname,'\.xz$','','') else endif " distribute archive(.zip, .tar, .vba, .vmb, ...) contents if sname =~ '\.zip$' - exe "silent !".g:GetLatestVimScripts_unzip." -o ".shellescape(sname) + exe "silent !".g:GetLatestVimScripts_unzip." -o ".shellescape(sname,1) elseif sname =~ '\.tar$' - exe "silent !tar -xvf ".shellescape(sname) + exe "silent !tar -xvf ".shellescape(sname,1) elseif sname =~ '\.tgz$' - exe "silent !tar -zxvf ".shellescape(sname) + exe "silent !tar -zxvf ".shellescape(sname,1) elseif sname =~ '\.taz$' - exe "silent !tar -Zxvf ".shellescape(sname) + exe "silent !tar -Zxvf ".shellescape(sname,1) elseif sname =~ '\.tbz$' - exe "silent !tar -jxvf ".shellescape(sname) + exe "silent !tar -jxvf ".shellescape(sname,1) elseif sname =~ '\.txz$' - exe "silent !tar -Jxvf ".shellescape(sname) + exe "silent !tar -Jxvf ".shellescape(sname,1) elseif sname =~ '\.vba$\|\.vmb$' silent 1split if exists("g:vimball_home") @@ -579,12 +580,12 @@ fun! s:GetOneScript(...) " move plugin to plugin/ or AsNeeded/ directory " --------------------------------------------- if sname =~ '.vim$' - exe "silent !".g:GetLatestVimScripts_mv." ".shellescape(sname)." ".tgtdir + exe "silent !".g:GetLatestVimScripts_mv." ".shellescape(sname,1)." ".tgtdir else - exe "silent !".g:GetLatestVimScripts_mv." ".shellescape(sname)." ".installdir + exe "silent !".g:GetLatestVimScripts_mv." ".shellescape(sname,1)." ".installdir endif if tgtdir != "plugin" - exe "silent !".g:GetLatestVimScripts_mv." ".shellescape("plugin/".pname)." ".tgtdir + exe "silent !".g:GetLatestVimScripts_mv." ".shellescape("plugin/".pname,1)." ".tgtdir endif " helptags step diff --git a/runtime/autoload/rust.vim b/runtime/autoload/rust.vim index 5ccbf4b382..6510f23be7 100644 --- a/runtime/autoload/rust.vim +++ b/runtime/autoload/rust.vim @@ -1,5 +1,6 @@ " Description: Helper functions for Rust commands/mappings " Last Modified: 2023-09-11 +" 2026 May 20 by Vim project: use correct shellescape() with ! command " For bugs, patches and license go to https://github.com/rust-lang/rust.vim function! rust#Load() @@ -125,7 +126,7 @@ function! s:Run(dict, rustc_args, args) echohl None endif if !v:shell_error - exe '!' . shellescape(exepath) . " " . join(map(a:args, 'shellescape(v:val)')) + exe '!' . shellescape(exepath,1) . " " . join(map(a:args, 'shellescape(v:val,1)')) endif endfunction diff --git a/runtime/autoload/vimball.vim b/runtime/autoload/vimball.vim index 3afd8e6c08..352e94d02d 100644 --- a/runtime/autoload/vimball.vim +++ b/runtime/autoload/vimball.vim @@ -1,10 +1,9 @@ " vimball.vim : construct a file containing both paths and files " Maintainer: This runtime file is looking for a new maintainer. " Original Author: Charles E. Campbell -" Date: Apr 16, 2026 +" Date: May 20, 2026 " Version: 37 (with modifications from the Vim Project) " GetLatestVimScripts: 1502 1 :AutoInstall: vimball.vim -" Last Change: " Copyright: (c) 2004-2011 by Charles E. Campbell " The VIM LICENSE applies to Vimball.vim, and Vimball.txt " (see |copyright|) except use "Vimball" instead of "Vim". @@ -427,7 +426,7 @@ fun! vimball#Decompress(fname,...) " decompression: if expand("%") =~ '.*\.gz' && executable("gunzip") " handle *.gz with gunzip - silent exe "!gunzip ".shellescape(a:fname) + silent exe "!gunzip ".shellescape(a:fname,1) if v:shell_error != 0 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) gunzip may have failed with <".a:fname.">") endif @@ -437,7 +436,7 @@ fun! vimball#Decompress(fname,...) elseif expand("%") =~ '.*\.gz' && executable("gzip") " handle *.gz with gzip -d - silent exe "!gzip -d ".shellescape(a:fname) + silent exe "!gzip -d ".shellescape(a:fname,1) if v:shell_error != 0 call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "gzip -d" may have failed with <'.a:fname.">") endif @@ -447,7 +446,7 @@ fun! vimball#Decompress(fname,...) elseif expand("%") =~ '.*\.bz2' && executable("bunzip2") " handle *.bz2 with bunzip2 - silent exe "!bunzip2 ".shellescape(a:fname) + silent exe "!bunzip2 ".shellescape(a:fname,1) if v:shell_error != 0 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) bunzip2 may have failed with <".a:fname.">") endif @@ -457,7 +456,7 @@ fun! vimball#Decompress(fname,...) elseif expand("%") =~ '.*\.bz2' && executable("bzip2") " handle *.bz2 with bzip2 -d - silent exe "!bzip2 -d ".shellescape(a:fname) + silent exe "!bzip2 -d ".shellescape(a:fname,1) if v:shell_error != 0 call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "bzip2 -d" may have failed with <'.a:fname.">") endif @@ -467,7 +466,7 @@ fun! vimball#Decompress(fname,...) elseif expand("%") =~ '.*\.bz3' && executable("bunzip3") " handle *.bz3 with bunzip3 - silent exe "!bunzip3 ".shellescape(a:fname) + silent exe "!bunzip3 ".shellescape(a:fname,1) if v:shell_error != 0 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) bunzip3 may have failed with <".a:fname.">") endif @@ -477,7 +476,7 @@ fun! vimball#Decompress(fname,...) elseif expand("%") =~ '.*\.bz3' && executable("bzip3") " handle *.bz3 with bzip3 -d - silent exe "!bzip3 -d ".shellescape(a:fname) + silent exe "!bzip3 -d ".shellescape(a:fname,1) if v:shell_error != 0 call vimball#ShowMesg(s:WARNING,'(vimball#Decompress) "bzip3 -d" may have failed with <'.a:fname.">") endif @@ -487,7 +486,7 @@ fun! vimball#Decompress(fname,...) elseif expand("%") =~ '.*\.zip' && executable("unzip") " handle *.zip with unzip - silent exe "!unzip ".shellescape(a:fname) + silent exe "!unzip ".shellescape(a:fname,1) if v:shell_error != 0 call vimball#ShowMesg(s:WARNING,"(vimball#Decompress) unzip may have failed with <".a:fname.">") endif From f7a58aee36d7fe17657b6eea6f30806050e0571d Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 20 May 2026 18:01:52 +0000 Subject: [PATCH 268/545] patch 9.2.0504: configure: requires X11 libraries for GTK4 build Problem: configure: requires X11 libraries for GTK4 build (after v9.2.0501) Solution: Allow to build GTK4 even when no X11 libraries are present (Yasuhiro Matsumoto) GTK4 does not use X11 APIs directly; the X11 backend is loaded by GTK4 at runtime. Skip the X11 dependency enforcement when the user explicitly passes --enable-gui=gtk4 so the build can succeed on systems without X11 development headers. closes: #20265 Co-authored-by: Claude Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/auto/configure | 4 ++-- src/configure.ac | 4 ++-- src/version.c | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/auto/configure b/src/auto/configure index ff4d5e361c..69b1a39ef0 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -5193,7 +5193,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \ CPPFLAGS="$CPPFLAGS -DMACOS_X_DARWIN" - if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3; then + if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3 -a "X$enable_gui" != Xgtk4; then with_x=no fi fi @@ -10469,7 +10469,7 @@ if test "x$with_x" = xno -a "x$with_x_arg" = xyes; then as_fn_error $? "could not configure X" "$LINENO" 5 fi -test "x$with_x" = xno -a "x$HAIKU" != "xyes" -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && enable_gui=no +test "x$with_x" = xno -a "x$HAIKU" != "xyes" -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" -a "x$enable_gui" != xgtk4 && enable_gui=no { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-gui argument" >&5 printf %s "checking --enable-gui argument... " >&6; } diff --git a/src/configure.ac b/src/configure.ac index a808530d24..a49c6d26b9 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -330,7 +330,7 @@ if test "$vim_cv_uname_output" = Darwin; then dnl Assume we don't want X11 unless it was specifically asked for dnl (--with-x) or Motif or GTK GUI is used. - if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3; then + if test -z "$with_x" -a "X$enable_gui" != Xmotif -a "X$enable_gui" != Xgtk2 -a "X$enable_gui" != Xgtk3 -a "X$enable_gui" != Xgtk4; then with_x=no fi fi @@ -2609,7 +2609,7 @@ if test "x$with_x" = xno -a "x$with_x_arg" = xyes; then AC_MSG_ERROR([could not configure X]) fi -test "x$with_x" = xno -a "x$HAIKU" != "xyes" -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && enable_gui=no +test "x$with_x" = xno -a "x$HAIKU" != "xyes" -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" -a "x$enable_gui" != xgtk4 && enable_gui=no AC_MSG_CHECKING(--enable-gui argument) AC_ARG_ENABLE(gui, diff --git a/src/version.c b/src/version.c index c989738312..e07f50f04a 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 504, /**/ 503, /**/ From 146f46e26446f75a25457d8b00065f7057352d8e Mon Sep 17 00:00:00 2001 From: Lifepillar Date: Wed, 20 May 2026 18:20:04 +0000 Subject: [PATCH 269/545] runtime(context,typeset): Correct whitespace error in Log()'s 'edit' command Also drop Last Change headers as this commit comes from the plugin's maintainer. related: #20242 related: #20244 closes: #20263 Signed-off-by: Lifepillar Signed-off-by: Christian Brabandt --- runtime/autoload/context.vim | 6 ++---- runtime/autoload/typeset.vim | 5 +---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/runtime/autoload/context.vim b/runtime/autoload/context.vim index 6b013f2a7b..8b65bb9a62 100644 --- a/runtime/autoload/context.vim +++ b/runtime/autoload/context.vim @@ -3,9 +3,7 @@ vim9script # Language: ConTeXt typesetting engine # Maintainer: Nicola Vitacolonna # Former Maintainers: Nikolai Weibull -# Latest Revision: 2026 Feb 03 -# Last Change: -# 2026 Mar 30 by Vim project: Use fnameescape for the Log command +# Latest Revision: 2026 May 20 # Typesetting {{{ import autoload './typeset.vim' @@ -35,7 +33,7 @@ export def Log(bufname: string) var logpath = typeset.LogPath(bufname) if filereadable(logpath) - execute 'edit' .. fnameescape(typeset.LogPath(bufname)) + execute 'edit' fnameescape(typeset.LogPath(bufname)) return endif diff --git a/runtime/autoload/typeset.vim b/runtime/autoload/typeset.vim index 11974f1721..0cb6149f5b 100644 --- a/runtime/autoload/typeset.vim +++ b/runtime/autoload/typeset.vim @@ -2,10 +2,7 @@ vim9script # Language: Generic TeX typesetting engine # Maintainer: Nicola Vitacolonna -# Latest Revision: 2026 Feb 19 -# Last Change: -# 2026 Mar 30 by Vim project: Use fnameescape for the ProcessOutput command -# 2026 May 19 by Vim project: missing space in :lcd command #20244 +# Latest Revision: 2026 May 20 # Constants and helpers {{{ const SLASH = !exists("+shellslash") || &shellslash ? '/' : '\' From aed758986de7e677d63b1f6caa845938a2fabbdc Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 20 May 2026 18:26:14 +0000 Subject: [PATCH 270/545] patch 9.2.0505: GTK4: text looks blurry on HiDPI displays Problem: GTK4: text looks blurry on HiDPI displays (Foxe Chen, after v9.2.0501) Solution: Allocate the cairo surface at physical resolution and set the device scale, recreate it on scale-factor changes (Yasuhiro Matsumoto). The backing cairo image surface was created at logical pixel size, so GTK4 upscaled it when blitting to the physical framebuffer. Allocate the surface at width*scale x height*scale and apply cairo_surface_set_device_scale() so drawing code keeps using logical coordinates while the surface itself has full physical resolution. Also recreate the surface on notify::scale-factor when the window moves between monitors with different scales. fixes: #20252 closes: #20258 Co-authored-by: Claude Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 84 ++++++++++++++++++++++++++++++++++++++++++++------ src/version.c | 2 ++ 2 files changed, 77 insertions(+), 9 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index 24ad6ddce2..083ac9f400 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -290,6 +290,8 @@ static gboolean delete_event_cb(GtkWindow *window, gpointer data); static void drawarea_realize_cb(GtkWidget *widget, gpointer data); static void drawarea_unrealize_cb(GtkWidget *widget, gpointer data); static void drawarea_resize_cb(GtkDrawingArea *area, int width, int height, gpointer data); +static void drawarea_scale_factor_cb(GObject *object, GParamSpec *pspec, gpointer data); +static cairo_surface_t *create_backing_surface(int width, int height); /* * Parse the GUI related command-line arguments. Any arguments used are @@ -523,6 +525,8 @@ gui_mch_init(void) G_CALLBACK(drawarea_unrealize_cb), NULL); g_signal_connect(G_OBJECT(gui.drawarea), "resize", G_CALLBACK(drawarea_resize_cb), NULL); + g_signal_connect(G_OBJECT(gui.drawarea), "notify::scale-factor", + G_CALLBACK(drawarea_scale_factor_cb), NULL); // Set up event controllers. { @@ -1302,6 +1306,34 @@ set_cairo_source_from_pixel(cairo_t *cr, guicolor_T pixel) (pixel & 0xff) / 255.0); } + static int +get_drawarea_scale(void) +{ + int scale = 1; + + if (gui.drawarea != NULL) + scale = gtk_widget_get_scale_factor(gui.drawarea); + if (scale < 1) + scale = 1; + return scale; +} + + static cairo_surface_t * +create_backing_surface(int width, int height) +{ + cairo_surface_t *surf; + int scale; + + if (width <= 0 || height <= 0) + return NULL; + + scale = get_drawarea_scale(); + surf = cairo_image_surface_create( + CAIRO_FORMAT_ARGB32, width * scale, height * scale); + cairo_surface_set_device_scale(surf, (double)scale, (double)scale); + return surf; +} + void gui_mch_clear_block(int row1, int col1, int row2, int col2) { @@ -1352,7 +1384,9 @@ surface_copy_rect(int dest_x, int dest_y, return; // Use a temporary surface to avoid overlap issues - tmp = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height); + tmp = create_backing_surface(width, height); + if (tmp == NULL) + return; cr = cairo_create(tmp); cairo_set_source_surface(cr, gui.surface, -src_x, -src_y); cairo_paint(cr); @@ -1829,7 +1863,7 @@ drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED) if (gui.surface != NULL) cairo_surface_destroy(gui.surface); - gui.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + gui.surface = create_backing_surface(w, h); gui_mch_new_colors(); @@ -1853,14 +1887,15 @@ drawarea_resize_cb(GtkDrawingArea *area UNUSED, int width, int height, gpointer data UNUSED) { cairo_t *cr; + int scale = get_drawarea_scale(); if (width <= 0 || height <= 0) return; if (gui.surface != NULL) { - int sw = cairo_image_surface_get_width(gui.surface); - int sh = cairo_image_surface_get_height(gui.surface); + int sw = cairo_image_surface_get_width(gui.surface) / scale; + int sh = cairo_image_surface_get_height(gui.surface) / scale; if (sw == width && sh == height) return; @@ -1872,8 +1907,7 @@ drawarea_resize_cb(GtkDrawingArea *area UNUSED, int width, int height, // Do not copy old surface content: gui_resize_shell() will trigger // a full redraw, and stale content (e.g. intro screen text) would // otherwise remain as ghost artifacts. - gui.surface = cairo_image_surface_create( - CAIRO_FORMAT_ARGB32, width, height); + gui.surface = create_backing_surface(width, height); cr = cairo_create(gui.surface); set_cairo_source_from_pixel(cr, gui.back_pixel); cairo_paint(cr); @@ -1883,6 +1917,36 @@ drawarea_resize_cb(GtkDrawingArea *area UNUSED, int width, int height, gui_resize_shell(width, height); } + static void +drawarea_scale_factor_cb(GObject *object UNUSED, + GParamSpec *pspec UNUSED, gpointer data UNUSED) +{ + int w, h; + + if (gui.drawarea == NULL) + return; + + w = gtk_widget_get_width(gui.drawarea); + h = gtk_widget_get_height(gui.drawarea); + if (w <= 0 || h <= 0) + return; + + if (gui.surface != NULL) + cairo_surface_destroy(gui.surface); + gui.surface = create_backing_surface(w, h); + + if (gui.surface != NULL) + { + cairo_t *cr = cairo_create(gui.surface); + set_cairo_source_from_pixel(cr, gui.back_pixel); + cairo_paint(cr); + cairo_destroy(cr); + } + gtk_widget_queue_draw(gui.drawarea); + if (gui.in_use) + redraw_all_later(UPD_CLEAR); +} + #ifdef FEAT_DND /* * Drag-and-drop handler for files and text. @@ -3791,15 +3855,17 @@ gui_mch_set_text_area_pos(int x, int y, int w, int h) // Update surface to match new text area size if (w > 0 && h > 0) { + int scale = get_drawarea_scale(); + if (gui.surface != NULL) { - int sw = cairo_image_surface_get_width(gui.surface); - int sh = cairo_image_surface_get_height(gui.surface); + int sw = cairo_image_surface_get_width(gui.surface) / scale; + int sh = cairo_image_surface_get_height(gui.surface) / scale; if (sw == w && sh == h) return; cairo_surface_destroy(gui.surface); } - gui.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + gui.surface = create_backing_surface(w, h); } } diff --git a/src/version.c b/src/version.c index e07f50f04a..f46d48acc3 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 505, /**/ 504, /**/ From aee12156ee3018efcdae87a8c2c327db386f0a5d Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Wed, 20 May 2026 18:36:11 +0000 Subject: [PATCH 271/545] runtime(doc): fix GTK4 package name in src/INSTALL The GTK4 section in src/INSTALL pointed at libgtk-3-dev. Use libgtk-4-dev and note that GTK4 is not the default and requires --enable-gui=gtk4. closes: #20254 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/INSTALL | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/INSTALL b/src/INSTALL index 96f32607fc..054df8ac24 100644 --- a/src/INSTALL +++ b/src/INSTALL @@ -70,7 +70,9 @@ To build Vim on Ubuntu from scratch on a clean system using git: % make reconfig Add GUI (GTK4) support: - % sudo apt install libgtk-3-dev + % sudo apt install libgtk-4-dev + (GTK4 is not the default; configure with + --enable-gui=gtk4 to select it.) % make reconfig Add Python 3 support: From a0931a90eeee6692556f330b5ba1fb413f9ea85d Mon Sep 17 00:00:00 2001 From: John Marriott Date: Wed, 20 May 2026 18:38:55 +0000 Subject: [PATCH 272/545] patch 9.2.0506: home_replace() function can be improved Problem: home_replace() function can be improved Solution: Refactor home_replace() to return the length of the string (John Marriott). In addition: - in function set_b0_fname() move ulen into the block where it is used. - In function findswapname() rework logic around displaying "swap file already exists" dialogue so that literal message text is set once. closes: #20249 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/buffer.c | 5 ++--- src/fileio.c | 8 +++++--- src/filepath.c | 31 ++++++++++++++++------------- src/memline.c | 45 +++++++++++++++++++++++++----------------- src/po/vim.pot | 6 +++--- src/proto/filepath.pro | 2 +- src/version.c | 2 ++ src/viminfo.c | 6 ++++-- 8 files changed, 61 insertions(+), 44 deletions(-) diff --git a/src/buffer.c b/src/buffer.c index 43cba3ad8c..a903ab3753 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -3948,9 +3948,8 @@ fileinfo( name = curbuf->b_fname; else name = curbuf->b_ffname; - home_replace(shorthelp ? curbuf : NULL, name, (char_u *)buffer + bufferlen, - IOSIZE - (int)bufferlen, TRUE); - bufferlen += STRLEN(buffer + bufferlen); + bufferlen += home_replace(shorthelp ? curbuf : NULL, name, + (char_u *)buffer + bufferlen, IOSIZE - (int)bufferlen, TRUE); } bufferlen += vim_snprintf_safelen( diff --git a/src/fileio.c b/src/fileio.c index 1a7ee9b94d..af0360dc8c 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -3200,11 +3200,13 @@ set_rw_fname(char_u *fname, char_u *sfname) void msg_add_fname(buf_T *buf, char_u *fname) { + size_t IObufflen = 0; + if (fname == NULL) fname = (char_u *)"-stdin-"; - home_replace(buf, fname, IObuff + 1, IOSIZE - 4, TRUE); - IObuff[0] = '"'; - STRCAT(IObuff, "\" "); + IObuff[IObufflen++] = '"'; + IObufflen += home_replace(buf, fname, IObuff + IObufflen, IOSIZE - 4, TRUE); + STRCPY(IObuff + IObufflen, "\" "); } /* diff --git a/src/filepath.c b/src/filepath.c index a41f8c27fc..8bdba335c6 100644 --- a/src/filepath.c +++ b/src/filepath.c @@ -442,27 +442,29 @@ modify_fname( if (p != NULL) { + size_t dirnamelen = 0; + if (c == '.') { - size_t namelen; - mch_dirname(dirname, MAXPATHL); if (has_homerelative) { s = vim_strsave(dirname); if (s != NULL) { - home_replace(NULL, s, dirname, MAXPATHL, TRUE); + dirnamelen = home_replace(NULL, s, dirname, MAXPATHL, TRUE); vim_free(s); } } - namelen = STRLEN(dirname); + + if (dirnamelen == 0) + dirnamelen = STRLEN(dirname); // Do not call shorten_fname() here since it removes the prefix // even though the path does not have a prefix. - if (fnamencmp(p, dirname, namelen) == 0) + if (fnamencmp(p, dirname, dirnamelen) == 0) { - p += namelen; + p += dirnamelen; if (vim_ispathsep(*p)) { while (*p && vim_ispathsep(*p)) @@ -480,11 +482,11 @@ modify_fname( } else { - home_replace(NULL, p, dirname, MAXPATHL, TRUE); + dirnamelen = home_replace(NULL, p, dirname, MAXPATHL, TRUE); // Only replace it when it starts with '~' if (*dirname == '~') { - s = vim_strsave(dirname); + s = vim_strnsave(dirname, dirnamelen); if (s != NULL) { *fnamep = s; @@ -2724,7 +2726,7 @@ f_filecopy(typval_T *argvars, typval_T *rettv) * 'src'. * If anything fails (except when out of space) dst equals src. */ - void + size_t home_replace( buf_T *buf, // when not NULL, check for help files char_u *src, // input file name @@ -2737,21 +2739,19 @@ home_replace( size_t len; char_u *homedir_env, *homedir_env_orig; char_u *p; + char_u *dst_start; if (src == NULL) { *dst = NUL; - return; + return 0; } /* * If the file is a help file, remove the path completely. */ if (buf != NULL && buf->b_help) - { - vim_snprintf((char *)dst, dstlen, "%s", gettail(src)); - return; - } + return vim_snprintf_safelen((char *)dst, dstlen, "%s", gettail(src)); /* * We check both the value of the $HOME environment variable and the @@ -2793,6 +2793,7 @@ home_replace( if (!one) src = skipwhite(src); + dst_start = dst; // remember the start while (*src && dstlen > 0) { /* @@ -2842,6 +2843,8 @@ home_replace( if (homedir_env != homedir_env_orig) vim_free(homedir_env); + + return (size_t)(dst - dst_start); } /* diff --git a/src/memline.c b/src/memline.c index ffd3fbd72a..51b09cc1ca 100644 --- a/src/memline.c +++ b/src/memline.c @@ -1021,7 +1021,7 @@ set_b0_fname(ZERO_BL *b0p, buf_T *buf) forward_slash(b0p->b0_fname); # endif #else - size_t flen, ulen; + size_t flen; char_u uname[B0_UNAME_SIZE]; /* @@ -1031,11 +1031,12 @@ set_b0_fname(ZERO_BL *b0p, buf_T *buf) * First replace home dir path with "~/" with home_replace(). * Then insert the user name to get "~user/". */ - home_replace(NULL, buf->b_ffname, b0p->b0_fname, + flen = home_replace(NULL, buf->b_ffname, b0p->b0_fname, B0_FNAME_SIZE_CRYPT, TRUE); if (b0p->b0_fname[0] == '~') { - flen = STRLEN(b0p->b0_fname); + size_t ulen; + // If there is no user name or it is too long, don't use "~/" if (get_user_name(uname, B0_UNAME_SIZE) == FAIL || (ulen = STRLEN(uname)) + flen > B0_FNAME_SIZE_CRYPT - 1) @@ -5343,24 +5344,32 @@ findswapname( if (swap_exists_action != SEA_NONE && choice == SEA_CHOICE_NONE) { - char_u *name; - int dialog_result; - size_t len = STRLEN(_("Swap file \"")); - - name = alloc(STRLEN(fname) - + len - + STRLEN(_("\" already exists!")) + 5); - if (name != NULL) + string_T prefix = {(char_u *)_("Swap file \""), 0}; + string_T suffix = {(char_u *)_("\" already exists!"), 0}; + size_t message_size; + string_T message; + char_u *tofree; + int dialog_result; + + prefix.length = STRLEN(prefix.string); + suffix.length = STRLEN(suffix.string); + message_size = prefix.length + + STRLEN(fname) + + suffix.length + 5; + message.string = tofree = alloc(message_size); + if (message.string != NULL) { - STRCPY(name, _("Swap file \"")); - home_replace(NULL, fname, name + len, 1000, TRUE); - STRCAT(name, _("\" already exists!")); + STRCPY(message.string, prefix.string); + message.length = prefix.length; + message.length += home_replace(NULL, fname, + message.string + message.length, (int)(message_size - message.length), TRUE); + STRCPY(message.string + message.length, suffix.string); } + else + message.string = (char_u *)_("Swap file already exists!"); dialog_result = do_dialog(VIM_WARNING, (char_u *)_("VIM - ATTENTION"), - name == NULL - ? (char_u *)_("Swap file already exists!") - : name, + message.string, # ifdef HAVE_PROCESS_STILL_RUNNING process_still_running ? (char_u *)_("&Open Read-Only\n&Edit anyway\n&Recover\n&Quit\n&Abort") : @@ -5373,7 +5382,7 @@ findswapname( dialog_result++; # endif choice = dialog_result; - vim_free(name); + vim_free(tofree); // pretend screen didn't scroll, need redraw anyway msg_scrolled = 0; diff --git a/src/po/vim.pot b/src/po/vim.pot index 2057c23a92..e5cf8c102d 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-05-19 18:20+0000\n" +"POT-Creation-Date: 2026-05-20 18:41+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -2169,10 +2169,10 @@ msgstr "" msgid "\" already exists!" msgstr "" -msgid "VIM - ATTENTION" +msgid "Swap file already exists!" msgstr "" -msgid "Swap file already exists!" +msgid "VIM - ATTENTION" msgstr "" msgid "" diff --git a/src/proto/filepath.pro b/src/proto/filepath.pro index d00a66775f..ac87623834 100644 --- a/src/proto/filepath.pro +++ b/src/proto/filepath.pro @@ -36,7 +36,7 @@ char_u *do_browse(int flags, char_u *title, char_u *dflt, char_u *ext, char_u *i void f_browse(typval_T *argvars, typval_T *rettv); void f_browsedir(typval_T *argvars, typval_T *rettv); void f_filecopy(typval_T *argvars, typval_T *rettv); -void home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, int one); +size_t home_replace(buf_T *buf, char_u *src, char_u *dst, int dstlen, int one); char_u *home_replace_save(buf_T *buf, char_u *src); int fullpathcmp(char_u *s1, char_u *s2, int checkname, int expandenv); char_u *gettail(char_u *fname); diff --git a/src/version.c b/src/version.c index f46d48acc3..dc31e54a77 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 506, /**/ 505, /**/ diff --git a/src/viminfo.c b/src/viminfo.c index bb84726c8f..95ec55ebb3 100644 --- a/src/viminfo.c +++ b/src/viminfo.c @@ -435,6 +435,8 @@ write_viminfo_bufferlist(FILE *fp) fputs(_("\n# Buffer list:\n"), fp); FOR_ALL_BUFFERS(buf) { + size_t linelen; + if (buf->b_fname == NULL || !buf->b_p_bl || bt_quickfix(buf) @@ -445,8 +447,8 @@ write_viminfo_bufferlist(FILE *fp) if (max_buffers-- == 0) break; putc('%', fp); - home_replace(NULL, buf->b_ffname, line, MAXPATHL, TRUE); - vim_snprintf_add((char *)line, LINE_BUF_LEN, "\t%ld\t%d", + linelen = home_replace(NULL, buf->b_ffname, line, MAXPATHL, TRUE); + vim_snprintf((char *)line + linelen, LINE_BUF_LEN - linelen, "\t%ld\t%d", (long)buf->b_last_cursor.lnum, buf->b_last_cursor.col); viminfo_writestring(fp, line); From 85eb099bf2210a3882005c0bf5d47306902c999e Mon Sep 17 00:00:00 2001 From: Cyril Roelandt Date: Wed, 20 May 2026 21:15:33 +0000 Subject: [PATCH 273/545] runtime(mbsync): Properly handle values for the "Sync" keyword This has been manually tested with my personal mbsync configuration and with the following test file: $ cat test.mbsyncrc Channel Foo # None may not be combined with other operations Sync None Sync None New # First form Sync Pull Sync Push Sync New Sync Old Sync Upgrade Sync ReNew Sync Gone Sync Delete Sync Flags Sync Invalid # Second form Sync PullNew Sync PullOld Sync PullUpgrade Sync PullReNew Sync PullGone Sync PullDelete Sync PullFlags Sync PullFull Sync PullAll Sync PullInvalid Sync PushNew Sync PushOld Sync PushUpgrade Sync PushReNew Sync PushGone Sync PushDelete Sync PushFlags Sync PushFull Sync PushAll Sync PushInvalid Sync NewInvalid # Multiple operations Sync New Upgrade Gone Flags # Mix of the two styles (an example from the mbsync manpage) Sync PullNew PullGone Push # Syntaxically correct, though they will raise a warning in mbsync: Sync PullNew Pull Sync PullNew Gone Push closes: #20243 Signed-off-by: Pierrick Guillaume Signed-off-by: Cyril Roelandt Signed-off-by: Christian Brabandt --- runtime/syntax/mbsync.vim | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/runtime/syntax/mbsync.vim b/runtime/syntax/mbsync.vim index daef41ddd3..eacd46ddbf 100644 --- a/runtime/syntax/mbsync.vim +++ b/runtime/syntax/mbsync.vim @@ -4,6 +4,7 @@ " Last Change: 2025 Apr 13 " 2025 Jun 04 by Vim project: match TLSType configuration variable " 2026 Jan 15 by Vim project: support TLSVersions keyword +" 2026 May 20 by Vim project: handle sync keyword #20243 " " Syntax support for mbsync config file @@ -132,7 +133,28 @@ syn match mbsCConfStPattern '^Patterns\?\s\+\ze.*$' contains=mbsCConfI syn match mbsCConfStMaxSize '^MaxSize\s\+\ze.*$' contains=mbsCConfItemK contained nextgroup=mbsSize transparent syn match mbsCConfStMaxMessages '^MaxMessages\s\+\ze.*$' contains=mbsCConfItemK contained nextgroup=mbsNumber transparent syn match mbsCConfStExpireUnread '^ExpireUnread\s\+\ze.*$' contains=mbsCConfItemK contained nextgroup=mbsBool transparent -syn match mbsCConfSyncOpt 'None\|All\|\%(\s\+\%(Pull\|Push\|New\|ReNew\|Delete\|Flags\)\)\+' display contained +" Properly matching mbsCConfSyncOpt: +" +" None is a special case. From mbsync's man page: +" "None may not be combined with any other operation." +" Once "None" is out of the way, first try to match operations, including: +" - New, Old, Upgrade, Gone, Flags +" - ReNew (deprecated synonym for Upgrade) +" - Delete (deprecated synonym for Gone) +" - All and Full, since they can be combined with other flags +" Then try to match the "second style" (as defined by mbsync's man page), +" which is a concatenation of direction Flags (Pull/Push) and of the +" operations seen above (New, Old, Upgrade/Renew, Gone/Delete, Flags, Full). +" Note that while "PullFull" exists, "PullAll" is not handled by mbsync's +" parser. +" Last but not least, match "\s+%(All of the above, except None) as multiple +" operations may be given to Sync. +syn match mbsCConfSyncOpt /\v( + \None + \|%(Pull|Push|New|Old|Upgrade|ReNew|Gone|Delete|Flags|Full|All + \|%(Pull|Push)%(New|Old|Upgrade|ReNew|Gone|Delete|Flags|Full)) + \%(\s+%(Pull|Push|New|Old|Upgrade|ReNew|Gone|Delete|Flags|Full|All + \|%(Pull|Push)%(New|Old|Upgrade|ReNew|Gone|Delete|Flags|Full)))*)$/ display contained syn match mbsCConfStSync '^Sync\s\+\ze.*$' contains=mbsCConfItemK contained nextgroup=mbsCConfSyncOpt transparent syn keyword mbsCConfManipOpt None Far Near Both contained syn match mbsCConfStCreate '^Create\s\+\ze.*$' contains=mbsCConfItemK contained nextgroup=mbsCConfManipOpt transparent From d0af3bcee1d91514a33fbaf19ccc2aa2ab73ba40 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Thu, 21 May 2026 19:03:50 +0000 Subject: [PATCH 274/545] runtime(doc): fix help tags for removed/reused error codes Problem: Several error codes (E614, E1319, E1321, E1323, E1400, E1401, E1402, E1406) were removed from errors.h in v9.1.0600 but their *Ennn* tags remained in the help files, so :help Ennn jumps to obsolete locations. E1395 was later reused with a new meaning ("Using a null class") in v9.1.1119, but its tag is still placed in the type-alias section. Solution: Remove the stale *Ennn* tags from the help files. Move *E1395* to the vim9.txt null-values section to match its current meaning. Also fix the indentation of *E1411*. Regenerate runtime/doc/tags. closes: #20279 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 4 ++-- runtime/doc/eval.txt | 6 +++--- runtime/doc/tags | 10 +--------- runtime/doc/vim9.txt | 4 ++-- runtime/doc/vim9class.txt | 10 +++++----- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index e9036c18f1..0268d158e7 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 May 04 +*builtin.txt* For Vim version 9.2. Last change: 2026 May 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6259,7 +6259,7 @@ insert({object}, {item} [, {idx}]) *insert()* Return type: |Number| -instanceof({object}, {class}) *instanceof()* *E614* *E616* *E693* +instanceof({object}, {class}) *instanceof()* *E616* *E693* The result is a Number, which is |TRUE| when the {object} argument is a direct or indirect instance of a |Class|, |Interface|, or class |:type| alias specified by {class}. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 2bcad93a39..a451c9f2fe 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 9.2. Last change: 2026 May 15 +*eval.txt* For Vim version 9.2. Last change: 2026 May 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -170,8 +170,8 @@ Note that " " and "0" are also non-empty strings, thus considered to be TRUE. A List, Dictionary or Float is not a Number or String, thus evaluate to FALSE. *E611* *E745* *E728* *E703* *E729* *E730* *E731* *E908* *E910* - *E913* *E974* *E975* *E976* *E1319* *E1320* *E1321* *E1322* - *E1323* *E1324* *E1520* *E1522* + *E913* *E974* *E975* *E976* *E1320* *E1322* *E1324* *E1520* + *E1522* |List|, |Tuple|, |Dictionary|, |Funcref|, |Job|, |Channel|, |Blob|, |Class| and |object| types are not automatically converted. diff --git a/runtime/doc/tags b/runtime/doc/tags index eb7e219293..6cac191f33 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -4562,12 +4562,9 @@ E1315 vim9class.txt /*E1315* E1316 vim9class.txt /*E1316* E1317 vim9class.txt /*E1317* E1318 vim9class.txt /*E1318* -E1319 eval.txt /*E1319* E132 userfunc.txt /*E132* E1320 eval.txt /*E1320* -E1321 eval.txt /*E1321* E1322 eval.txt /*E1322* -E1323 eval.txt /*E1323* E1324 eval.txt /*E1324* E1325 vim9class.txt /*E1325* E1326 vim9class.txt /*E1326* @@ -4644,19 +4641,15 @@ E1391 eval.txt /*E1391* E1392 eval.txt /*E1392* E1393 vim9class.txt /*E1393* E1394 vim9class.txt /*E1394* -E1395 vim9class.txt /*E1395* +E1395 vim9.txt /*E1395* E1396 vim9class.txt /*E1396* E1397 vim9class.txt /*E1397* E1398 vim9class.txt /*E1398* E1399 vim9class.txt /*E1399* E140 message.txt /*E140* -E1400 vim9class.txt /*E1400* -E1401 vim9class.txt /*E1401* -E1402 vim9class.txt /*E1402* E1403 vim9class.txt /*E1403* E1404 vim9class.txt /*E1404* E1405 vim9class.txt /*E1405* -E1406 vim9class.txt /*E1406* E1407 vim9class.txt /*E1407* E1408 vim9class.txt /*E1408* E1409 vim9class.txt /*E1409* @@ -5273,7 +5266,6 @@ E610 editing.txt /*E610* E611 eval.txt /*E611* E612 sign.txt /*E612* E613 print.txt /*E613* -E614 builtin.txt /*E614* E616 builtin.txt /*E616* E617 options.txt /*E617* E618 print.txt /*E618* diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 5d6b71b5c2..84502f19cf 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 9.2. Last change: 2026 May 04 +*vim9.txt* For Vim version 9.2. Last change: 2026 May 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1117,7 +1117,7 @@ should be used. *false* *true* *null* *null_blob* *null_channel* *null_class* *null_dict* *null_function* *null_job* *null_list* *null_object* *null_partial* *null_string* - *E1034* + *E1034* *E1395* In Vim9 script one can use the following predefined values: > true false diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt index 6f936a01c0..7c92c455ca 100644 --- a/runtime/doc/vim9class.txt +++ b/runtime/doc/vim9class.txt @@ -1,4 +1,4 @@ -*vim9class.txt* For Vim version 9.2. Last change: 2026 Mar 07 +*vim9class.txt* For Vim version 9.2. Last change: 2026 May 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -110,14 +110,14 @@ referred to with the "this." prefix. This is different from languages like Java and TypeScript. The naming convention makes the object members easy to spot. Also, when a variable does not have the "this." prefix you know it is not an object variable. - *E1411* + *E1411* From outside the class definition, access an object's methods and variables by using the object name followed by a dot following by the member: > pos.lnum pos.SetCol(10) < - *E1405* *E1406* + *E1405* A class name cannot be used as an expression. A class name cannot be used in the left-hand-side of an assignment. @@ -921,7 +921,7 @@ methods defined in a class: > 7. Type definition *typealias* *Vim9-type* *:type* - *E1393* *E1395* *E1396* *E1397* *E1398* + *E1393* *E1396* *E1397* *E1398* A type definition is giving a name to a type specification. This is also known as a "type alias". The type alias can be used wherever a built-in type can be used. Example: > @@ -942,7 +942,7 @@ types can be aliased. A type alias can be created only at the script level and not inside a function. A type alias can be exported and used across scripts. - *E1400* *E1401* *E1402* *E1403* *E1407* + *E1403* *E1407* A type alias cannot be used as an expression. A type alias cannot be used in the left-hand-side of an assignment. From ff9fd819aeca68b55c013af78edec6da7e269a8f Mon Sep 17 00:00:00 2001 From: Guilherme Puida Moreira Date: Thu, 21 May 2026 19:34:20 +0000 Subject: [PATCH 275/545] runtime(debversions): Add stonking (26.10) as Ubuntu release name closes: #20282 Signed-off-by: Guilherme Puida Moreira Signed-off-by: James McCoy Signed-off-by: Christian Brabandt --- runtime/syntax/shared/debversions.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/syntax/shared/debversions.vim b/runtime/syntax/shared/debversions.vim index 2548ddd350..e6982803ae 100644 --- a/runtime/syntax/shared/debversions.vim +++ b/runtime/syntax/shared/debversions.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Debian version information " Maintainer: Debian Vim Maintainers -" Last Change: 2026 Jan 01 +" Last Change: 2026 May 21 " URL: https://salsa.debian.org/vim-team/vim-debian/blob/main/syntax/shared/debversions.vim let s:cpo = &cpo @@ -12,7 +12,7 @@ let g:debSharedSupportedVersions = [ \ 'oldstable', 'stable', 'testing', 'unstable', 'experimental', 'sid', 'rc-buggy', \ 'bookworm', 'trixie', 'forky', 'duke', \ - \ 'jammy', 'noble', 'questing', 'resolute', + \ 'jammy', 'noble', 'questing', 'resolute', 'stonking', \ 'devel' \ ] " Historic version names, no longer under standard support From 06a2c7105c3d678cd0c04b934d26810af15b7470 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Thu, 21 May 2026 19:45:59 +0000 Subject: [PATCH 276/545] patch 9.2.0507: Vim9 class: public/protected member name clash uses same error Problem: When a public member and a protected member in a Vim9 class have the same name (differing only in the leading '_'), Vim reports E1369 "Duplicate variable", which is also used for plain duplicate definitions. Users cannot tell from the message whether the conflict is the public/protected naming rule or a real duplicate. Solution: Add a dedicated error E1406 "Public and protected member have the same name" and emit it only when the name clash is between a public and a protected member. Keep E1369 for genuine duplicate variable definitions (Hirohito Higashi). fixes: #20240 closes: #20277 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/tags | 1 + runtime/doc/vim9class.txt | 2 +- src/errors.h | 3 ++- src/po/vim.pot | 6 +++++- src/testdir/test_vim9_class.vim | 12 +++++------ src/version.c | 2 ++ src/vim9class.c | 36 ++++++++++++++++++++++----------- 7 files changed, 41 insertions(+), 21 deletions(-) diff --git a/runtime/doc/tags b/runtime/doc/tags index 6cac191f33..7a4c09031b 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -4650,6 +4650,7 @@ E140 message.txt /*E140* E1403 vim9class.txt /*E1403* E1404 vim9class.txt /*E1404* E1405 vim9class.txt /*E1405* +E1406 vim9class.txt /*E1406* E1407 vim9class.txt /*E1407* E1408 vim9class.txt /*E1408* E1409 vim9class.txt /*E1409* diff --git a/runtime/doc/vim9class.txt b/runtime/doc/vim9class.txt index 7c92c455ca..58458b627d 100644 --- a/runtime/doc/vim9class.txt +++ b/runtime/doc/vim9class.txt @@ -635,7 +635,7 @@ once. They can appear in any order, although this order is recommended: > < The "specifies" feature is currently not implemented. - *E1355* *E1369* + *E1355* *E1369* *E1406* Each variable and method name can be used only once. It is not possible to define a method with the same name and different type of arguments. It is not possible to use a public and protected member variable with the same name. An diff --git a/src/errors.h b/src/errors.h index 3ba7eca8f5..4e261c9232 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3564,7 +3564,8 @@ EXTERN char e_abstract_cannot_be_used_in_interface[] INIT(= N_("E1404: Abstract cannot be used in an interface")); EXTERN char e_using_class_as_value_str[] INIT(= N_("E1405: Class \"%s\" cannot be used as a value")); -// E1406 unused +EXTERN char e_public_and_protected_member_have_same_name_str_str[] + INIT(= N_("E1406: Public and protected member have the same name: %s and _%s")); EXTERN char e_using_typealias_as_var_val[] INIT(= N_("E1407: Cannot use a Typealias as a variable or value")); EXTERN char e_final_variable_not_supported_in_interface[] diff --git a/src/po/vim.pot b/src/po/vim.pot index e5cf8c102d..ad805b30b7 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-05-20 18:41+0000\n" +"POT-Creation-Date: 2026-05-21 19:38+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -8488,6 +8488,10 @@ msgstr "" msgid "E1405: Class \"%s\" cannot be used as a value" msgstr "" +#, c-format +msgid "E1406: Public and protected member have the same name: %s and _%s" +msgstr "" + msgid "E1407: Cannot use a Typealias as a variable or value" msgstr "" diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 5db6c65d12..35b87b208a 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -5143,7 +5143,7 @@ def Test_dup_member_variable() var _val = 20 endclass END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 4) + v9.CheckSourceFailure(lines, 'E1406: Public and protected member have the same name: val and _val', 4) # Duplicate public and protected member variable lines =<< trim END @@ -5153,7 +5153,7 @@ def Test_dup_member_variable() public var val = 10 endclass END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 4) + v9.CheckSourceFailure(lines, 'E1406: Public and protected member have the same name: val and _val', 4) # Duplicate class member variable lines =<< trim END @@ -5163,7 +5163,7 @@ def Test_dup_member_variable() static var _s: string = "def" endclass END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4) + v9.CheckSourceFailure(lines, 'E1406: Public and protected member have the same name: s and _s', 4) # Duplicate public and protected class member variable lines =<< trim END @@ -5173,7 +5173,7 @@ def Test_dup_member_variable() static var _s: string = "def" endclass END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _s', 4) + v9.CheckSourceFailure(lines, 'E1406: Public and protected member have the same name: s and _s', 4) # Duplicate class and object member variable lines =<< trim END @@ -5230,7 +5230,7 @@ def Test_dup_member_variable() var _val = 20 endclass END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: _val', 9) + v9.CheckSourceFailure(lines, 'E1406: Public and protected member have the same name: val and _val', 9) # Duplicate object member variable in a derived class lines =<< trim END @@ -5244,7 +5244,7 @@ def Test_dup_member_variable() var val = 20 endclass END - v9.CheckSourceFailure(lines, 'E1369: Duplicate variable: val', 9) + v9.CheckSourceFailure(lines, 'E1406: Public and protected member have the same name: val and _val', 9) # Two member variables with a common prefix lines =<< trim END diff --git a/src/version.c b/src/version.c index dc31e54a77..863043dabd 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 507, /**/ 506, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index 8edd022bd9..bc10ce8b07 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -461,13 +461,14 @@ extends_check_dup_members( { class_T *p_cl = extends_cl; ocmember_T *c_m = members + c_i; - char_u *pstr = (*c_m->ocm_name.string == '_') - ? c_m->ocm_name.string + 1 : c_m->ocm_name.string; + bool c_protected = (*c_m->ocm_name.string == '_'); + char_u *pstr = c_m->ocm_name.string + c_protected; // Check in all the parent classes in the lineage while (p_cl != NULL) { int p_member_count = p_cl->class_obj_member_count; + if (p_member_count == 0) { p_cl = p_cl->class_extends; @@ -478,12 +479,17 @@ extends_check_dup_members( // Compare against all the members in the parent class for (int p_i = 0; p_i < p_member_count; p_i++) { - ocmember_T *p_m = p_members + p_i; - char_u *qstr = (*p_m->ocm_name.string == '_') - ? p_m->ocm_name.string + 1 : p_m->ocm_name.string; + ocmember_T *p_m = p_members + p_i; + bool p_protected = (*p_m->ocm_name.string == '_'); + char_u *qstr = p_m->ocm_name.string + p_protected; if (STRCMP(pstr, qstr) == 0) { - semsg(_(e_duplicate_variable_str), c_m->ocm_name.string); + if (c_protected != p_protected) + semsg(_(e_public_and_protected_member_have_same_name_str_str), + pstr, pstr); + else + semsg(_(e_duplicate_variable_str), + c_m->ocm_name.string); return FALSE; } } @@ -1040,18 +1046,18 @@ is_duplicate_variable( char_u *varname, char_u *varname_end) { - string_T pstr = {varname, (size_t)(varname_end - varname)}; // Note: the .string field may - // point to a string longer - // than the .length field. - // So we need to use STRNCMP() - // to compare it. + // Note: the .string field may point to a string longer than the .length + // field. So we need to use STRNCMP() to compare it. + string_T pstr = {varname, (size_t)(varname_end - varname)}; int dup = FALSE; + bool p_protected = false; // Step over a leading '_'. if (*pstr.string == '_') { pstr.string++; pstr.length--; + p_protected = true; } // loop == 1: class variables, loop == 2: object variables @@ -1062,12 +1068,14 @@ is_duplicate_variable( { ocmember_T *m = ((ocmember_T *)vgap->ga_data) + i; string_T qstr = {m->ocm_name.string, m->ocm_name.length}; + bool q_protected = false; // Step over a leading '_'. if (*qstr.string == '_') { qstr.string++; qstr.length--; + q_protected = true; } if (pstr.length == qstr.length @@ -1076,7 +1084,11 @@ is_duplicate_variable( char_u save_c = *varname_end; *varname_end = NUL; - semsg(_(e_duplicate_variable_str), varname); + if (p_protected != q_protected) + semsg(_(e_public_and_protected_member_have_same_name_str_str), + pstr.string, pstr.string); + else + semsg(_(e_duplicate_variable_str), varname); *varname_end = save_c; dup = TRUE; break; From 1dfaeb4fa358586c3fa111c5d3fea34eb4278bc9 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 21 May 2026 19:52:00 +0000 Subject: [PATCH 277/545] runtime(doc): re-generate vim.man related: #20186 Signed-off-by: Christian Brabandt --- runtime/doc/vim.man | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/doc/vim.man b/runtime/doc/vim.man index ab21a4b825..9a5881b3db 100644 --- a/runtime/doc/vim.man +++ b/runtime/doc/vim.man @@ -396,7 +396,7 @@ OPTIONS --startuptime {file} During startup write timing messages to the file {fname}. - --ttyfail When stdin or stdout is not a terminal (tty) then exit + --ttyfail When stdin or stdout is not a terminal (tty) then exit right away. --version Print version information and exit. From 6845c7a63d573682b82d03d4ad606d27547f9498 Mon Sep 17 00:00:00 2001 From: nyngwang Date: Thu, 21 May 2026 19:54:22 +0000 Subject: [PATCH 278/545] runtime(doc): fix a typo in :map-local closes: #20276 Signed-off-by: nyngwang Signed-off-by: Christian Brabandt --- runtime/doc/map.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 5d9bed83b0..58f27d099e 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1,4 +1,4 @@ -*map.txt* For Vim version 9.2. Last change: 2026 May 17 +*map.txt* For Vim version 9.2. Last change: 2026 May 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -208,7 +208,7 @@ be effective in the current buffer only. Example: > Then you can map ",w" to something else in another buffer: > :map ,w /[#&!] The local buffer mappings are used before the global ones. See below -to make a short local mapping not taking effect when a longer global one +to make a short local mapping taking effect when a longer global one exists. The "" argument can also be used to clear mappings: > :unmap ,w From b54e57ee54f0a4e6eac74b071071a9d85326de3a Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Thu, 21 May 2026 19:57:06 +0000 Subject: [PATCH 279/545] patch 9.2.0508: completion: cannot complete user cmd :K with 'ignorecase' Problem: completion: cannot complete user cmd :K with 'ignorecase' (rendcrx) Solution: Skip the short-circuit when 'ignorecase' is set (Yasuhiro Matsumoto) The set_cmd_index() short-circuit for the :k command treats ":k" as ":k {X}" (mark argument), which makes ":kz" never reach the command-name expansion path. With 'ignorecase' the same prefix on other letters (":gz") completes a user command like :Gz, so the result is inconsistent. Skip the short-circuit when 'ignorecase' is set; default behaviour is preserved so the existing :k tests still pass. fixes: #20241 closes: #20275 Co-authored-by: zeertzjq Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/cmdexpand.c | 9 +++++---- src/testdir/test_cmdline.vim | 21 +++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/cmdexpand.c b/src/cmdexpand.c index 19ae319981..8d9c2d7b7f 100644 --- a/src/cmdexpand.c +++ b/src/cmdexpand.c @@ -1759,11 +1759,12 @@ set_cmd_index(char_u *cmd, exarg_T *eap, expand_T *xp, int *complp) // Isolate the command and search for it in the command table. // Exceptions: // - the 'k' command can directly be followed by any character, but do - // accept "keepmarks", "keepalt" and "keepjumps". As fuzzy matching can - // find matches anywhere in the command name, do this only for command - // expansion based on regular expression and not for fuzzy matching. + // accept "keepmarks", "keepalt" and "keepjumps". Bypass also when + // 'ignorecase' is set so a lowercase ":kz" still completes a user + // command like :Kz (#20241), and for fuzzy matching as that can find + // matches anywhere in the command name. // - the 's' command can be followed directly by 'c', 'g', 'i', 'I' or 'r' - if (!fuzzy && (*cmd == 'k' && cmd[1] != 'e')) + if (!fuzzy && !p_ic && (*cmd == 'k' && cmd[1] != 'e')) { eap->cmdidx = CMD_k; p = cmd + 1; diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 7c1365b6a9..31fb1f8ffc 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -4095,6 +4095,27 @@ func Test_fuzzy_completion_cmd_k() set wildoptions& endfunc +" Issue #20241: with 'ignorecase', a lowercase "k"-prefixed input should +" still complete a user command starting with "K". +func Test_cmdline_complete_user_cmd_k_with_ignorecase() + command! Kz echo "hello" + command! Gz echo "here" + + set noignorecase + call assert_equal([], getcompletion('kz', 'cmdline')) + call assert_equal([], getcompletion('gz', 'cmdline')) + call assert_equal(['Kz'], getcompletion('Kz', 'cmdline')) + call assert_equal(['Gz'], getcompletion('Gz', 'cmdline')) + + set ignorecase + call assert_equal(['Kz'], getcompletion('kz', 'cmdline')) + call assert_equal(['Gz'], getcompletion('gz', 'cmdline')) + + set ignorecase& + delcommand Kz + delcommand Gz +endfunc + " Test for fuzzy completion for user defined custom completion function func Test_fuzzy_completion_custom_func() func Tcompl(a, c, p) diff --git a/src/version.c b/src/version.c index 863043dabd..40b26d7cfe 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 508, /**/ 507, /**/ From daad5ea9058fae5c5c0014a0b49963d12caea9f8 Mon Sep 17 00:00:00 2001 From: Muraoka Taro Date: Thu, 21 May 2026 20:05:14 +0000 Subject: [PATCH 280/545] patch 9.2.0509: term.c: compile error when LOG_TRN is enabled Problem: compile error when LOG_TRN is enabled Solution: Use valid arguments (Muraoka Taro) closes: #20278 Signed-off-by: Muraoka Taro Signed-off-by: Christian Brabandt --- src/term.c | 4 ++-- src/version.c | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/term.c b/src/term.c index 08a070cef6..1e7ea22ae5 100644 --- a/src/term.c +++ b/src/term.c @@ -5999,7 +5999,7 @@ check_for_color_response(char_u *resp, int len) char *new_bg_val = (3 * '6' < *tp_r + *tp_g + *tp_b) ? "light" : "dark"; - LOG_TRN("Received RBG response: %s", tp); + LOG_TRN("Received RBG response: r=%d g=%d b=%d", rval, gval, bval); #ifdef FEAT_TERMRESPONSE rbg_status.tr_progress = STATUS_GOT; # ifdef FEAT_TERMINAL @@ -6021,7 +6021,7 @@ check_for_color_response(char_u *resp, int len) #if defined(FEAT_TERMRESPONSE) && defined(FEAT_TERMINAL) else { - LOG_TRN("Received RFG response: %s", tp); + LOG_TRN("Received RFG response: r=%d g=%d b=%d", rval, gval, bval); rfg_status.tr_progress = STATUS_GOT; fg_r = rval; fg_g = gval; diff --git a/src/version.c b/src/version.c index 40b26d7cfe..7b80b546f3 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 509, /**/ 508, /**/ From 0b14d6de60dba104b3ff91461982072b5bbe95a5 Mon Sep 17 00:00:00 2001 From: Antonio Giovanni Colombo Date: Thu, 21 May 2026 20:13:42 +0000 Subject: [PATCH 281/545] translation(it): Update Italian translation Signed-off-by: Antonio Giovanni Colombo Signed-off-by: Christian Brabandt --- src/po/it.po | 132 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 82 insertions(+), 50 deletions(-) diff --git a/src/po/it.po b/src/po/it.po index 217e85eb37..8da30e86bf 100644 --- a/src/po/it.po +++ b/src/po/it.po @@ -4,18 +4,19 @@ # Vlad Sandrini , 2002 # Luciano Montanaro , 2006 # -# Ogni commento è benvenuto... # Every remark is very welcome... +# Ogni commento è benvenuto... # -# Translation done under Linux and using an Italian keyboard. -# English words left in the text are unmodified at plural. -# Option names are mostly left untouched. +# Traduzione fatta in ambiebte Linux, usnado una tastiera italiana. +# Le parole inglesi presenti nel testo sono sempre al singolare. +# I nomi delle opzioni sono quasi sempre lasciati in inglese. +# I nomi dei modi di Vim sono lasciati in inglese. # msgid "" msgstr "" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-04-02 17:32+0200\n" -"PO-Revision-Date: 2026-04-02 20:20+0100\n" +"POT-Creation-Date: 2026-05-21 07:38+0200\n" +"PO-Revision-Date: 2026-05-21 08:20+0100\n" "Last-Translator: Antonio Colombo \n" "Language-Team: Italian\n" "Language: it\n" @@ -344,7 +345,7 @@ msgid "xchacha20v2: using default algorithm \"%d\" for Key derivation." msgstr "xchacha20v2: uso algoritmo predefinito \"%d\" per ricavare la Chiave." msgid "Entering Debug mode. Type \"cont\" to continue." -msgstr "Entro modalità Debug. Batti \"cont\" per continuare." +msgstr "Entro modo Debug. Batti \"cont\" per continuare." #, c-format msgid "Oldval = \"%s\"" @@ -634,7 +635,7 @@ msgstr "" "W21: Versione richiesta di python 3.x non supportata, ignoro il file: %s" msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." -msgstr "Entro modalità Ex. Batti \"visual\" per tornare a modalità Normale." +msgstr "Entro modo Ex. Batti \"visual\" per tornare a modo Normal." #, c-format msgid "Executing: %s" @@ -959,9 +960,11 @@ msgstr "VIM - Sostituisci..." msgid "VIM - Search..." msgstr "VIM - Cerca..." +#. "Find what:" label + entry msgid "Find what:" msgstr "Trovare:" +#. "Replace with:" label + entry msgid "Replace with:" msgstr "Sostituire con:" @@ -995,6 +998,12 @@ msgstr "Sostituisci Tutto" msgid "_Close" msgstr "_Chiudi" +msgid "Direction:" +msgstr "Direzione:" + +msgid "Close" +msgstr "Chiudi" + msgid "Vim: Received \"die\" request from session manager\n" msgstr "Vim: Ricevuta richiesta \"die\" dal Session Manager\n" @@ -1076,7 +1085,7 @@ msgstr "Font%d: %s" #, c-format msgid "Font%d width is not twice that of font0" -msgstr "La larghezza di font%d non è doppia di quella di font0" +msgstr "La larghezza di font%d non è doppia di quella di Font0" #, c-format msgid "Font0 width: %d" @@ -1648,19 +1657,19 @@ msgid "-f or --nofork\tForeground: Don't fork when starting GUI" msgstr "-f opp. --nofork\tForeground: Non usare 'fork' inizializzando GUI" msgid "-v\t\t\tVi mode (like \"vi\")" -msgstr "-v\t\t\tModalità Vi (come \"vi\")" +msgstr "-v\t\t\tModo Vi (come \"vi\")" msgid "-e\t\t\tEx mode (like \"ex\")" -msgstr "-e\t\t\tModalità Ex (come \"ex\")" +msgstr "-e\t\t\tModo Ex (come \"ex\")" msgid "-E\t\t\tImproved Ex mode" -msgstr "-E\t\t\tModalità Ex migliorata" +msgstr "-E\t\t\tModo Ex migliorata" msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")" -msgstr "-s\t\t\tModalità Silenziosa (batch) (solo per \"ex\")" +msgstr "-s\t\t\tModo Silenzioso (batch) (solo per \"ex\")" msgid "-d\t\t\tDiff mode (like \"vimdiff\")" -msgstr "-d\t\t\tModalità Diff (come \"vimdiff\")" +msgstr "-d\t\t\tModo Diff (come \"vimdiff\")" msgid "-y\t\t\tEasy mode (like \"evim\", modeless)" msgstr "-y\t\t\tModo Facile (come \"evim\", senza modi)" @@ -1669,7 +1678,7 @@ msgid "-R\t\t\tReadonly mode (like \"view\")" msgstr "-R\t\t\tModo Sola-Lettura (come \"view\")" msgid "-Z\t\t\tRestricted mode (like \"rvim\")" -msgstr "-Z\t\t\tModalità Ristretta (come \"rvim\")" +msgstr "-Z\t\t\tModo Restricted (come \"rvim\")" msgid "-m\t\t\tModifications (writing files) not allowed" msgstr "-m\t\t\tModifiche (riscritture file) non consentita" @@ -1678,7 +1687,7 @@ msgid "-M\t\t\tModifications in text not allowed" msgstr "-M\t\t\tModifiche nel testo non consentite" msgid "-b\t\t\tBinary mode" -msgstr "-b\t\t\tModalità Binaria" +msgstr "-b\t\t\tModo Bynary" msgid "-l\t\t\tLisp mode" msgstr "-l\t\t\tModo Lisp" @@ -1693,7 +1702,7 @@ msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]" msgstr "-V[N][fname]\t\tVerbosità [livello N] [log su file fname]" msgid "-D\t\t\tDebugging mode" -msgstr "-D\t\t\tModalità Debug" +msgstr "-D\t\t\tModo Debug" msgid "-n\t\t\tNo swap file, use memory only" msgstr "-n\t\t\tNiente file di swap, usa solo memoria" @@ -1714,10 +1723,10 @@ msgid "-dev \t\tUse for I/O" msgstr "-dev \t\tUsa per I/O" msgid "-A\t\t\tStart in Arabic mode" -msgstr "-A\t\t\tComincia in modalità Araba" +msgstr "-A\t\t\tComincia in modo Arabic" msgid "-H\t\t\tStart in Hebrew mode" -msgstr "-H\t\t\tComincia in modalità Ebraica" +msgstr "-H\t\t\tComincia in modo Hebrew" msgid "-T \tSet terminal type to " msgstr "-T \tImposta tipo terminale a " @@ -1767,7 +1776,7 @@ msgstr "" "-S \tEsegui comandi in file dopo caricamento primo file" msgid "-s \tRead Normal mode commands from file " -msgstr "-s \tLeggi comandi in modalità normale da file " +msgstr "-s \tLeggi comandi in modo Normal da file " msgid "-w \tAppend all typed commands to file " msgstr "-w \tAggiungi tutti i comandi immessi a file " @@ -1987,8 +1996,8 @@ msgid "" "Maybe no changes were made or Vim did not update the swap file." msgstr "" "\n" -"Forse non ci sono state modifiche oppure Vim non ha aggiornato il file di " -" swap." +"Forse non ci sono state modifiche oppure Vim non ha aggiornato il file di " +"swap." msgid " cannot be used with this version of Vim.\n" msgstr " impossibile da usare con questa versione di Vim.\n" @@ -2074,6 +2083,9 @@ msgstr "???BLOCCO MANCANTE" msgid "??? from here until ???END lines may be messed up" msgstr "??? da qui fino a ???END le righe possono essere fuori ordine" +msgid "??? block header corrupted" +msgstr "??? intestazione di blocco non valida" + msgid "??? from here until ???END lines may have been inserted/deleted" msgstr "" "??? da qui fino a ???END righe possono essere state inserite/cancellate" @@ -2209,7 +2221,7 @@ msgid "" " [not usable on this computer]" msgstr "" "\n" -" [not utilizzabile su questo computer]" +" [non utilizzabile su questo computer]" msgid " [cannot be read]" msgstr " [non leggibile]" @@ -2293,12 +2305,12 @@ msgstr "File di swap \"" msgid "\" already exists!" msgstr "\" già esistente!" -msgid "VIM - ATTENTION" -msgstr "VIM - ATTENZIONE" - msgid "Swap file already exists!" msgstr "Il file di swap esiste già!" +msgid "VIM - ATTENTION" +msgstr "VIM - ATTENZIONE" + msgid "" "&Open Read-Only\n" "&Edit anyway\n" @@ -2426,6 +2438,7 @@ msgstr " (Interrotto)" msgid "Beep!" msgstr "Beep!" +#, c-format msgid "Executing directly: \"%s\"" msgstr "Eseguo direttamente: \"%s\"" @@ -2559,7 +2572,7 @@ msgid "Vim exiting with %d\n" msgstr "Vim esce con %d\n" msgid "cannot change console mode ?!\n" -msgstr "non posso modificare modalità console ?!\n" +msgstr "non posso modificare modo Console ?!\n" msgid "mch_get_shellsize: not a console??\n" msgstr "mch_get_shellsize: non una console??\n" @@ -3702,6 +3715,9 @@ msgstr "" msgid "without GUI." msgstr "senza GUI." +msgid "with GTK4 GUI." +msgstr "con GUI GTK4." + msgid "with GTK3 GUI." msgstr "con GUI GTK3." @@ -3811,7 +3827,7 @@ msgid "type :help version9 for version info" msgstr "batti :help version9 per informazioni su versione" msgid "Running in Vi compatible mode" -msgstr "Eseguo in modalità compatibile Vi" +msgstr "Eseguo in modo compatibile Vi" msgid "type :set nocp for Vim defaults" msgstr "batti :set nocp per valori predefiniti Vim" @@ -3823,7 +3839,7 @@ msgid "menu Help->Orphans for information " msgstr "menu Aiuto->Orfani per informazioni " msgid "Running modeless, typed text is inserted" -msgstr "Esecuzione senza modalità: solo inserimento" +msgstr "Esecuzione senza modo: il testo immesso viene inserito" msgid "menu Edit->Global Settings->Toggle Insert Mode " msgstr "menu Modifica->Impost.Globali->Modal.Inser. Sì/No" @@ -5081,7 +5097,7 @@ msgstr "" "E327: Parte del percorso di questo elemento di Menù non è un sotto-Menù" msgid "E328: Menu only exists in another mode" -msgstr "E328: I Menù esistono solo in un'altra modalità" +msgstr "E328: I Menù esistono solo in un altro Modo" #, c-format msgid "E329: No menu \"%s\"" @@ -5106,7 +5122,7 @@ msgstr "E334: Menù non trovato: %s" #, c-format msgid "E335: Menu not defined for %s mode" -msgstr "E335: Menù non definito per la modalità %s" +msgstr "E335: Menù non definito per il modo %s" msgid "E336: Menu path must lead to a sub-menu" msgstr "E336: Il percorso Menù deve condurre ad un sotto-Menù" @@ -5115,7 +5131,7 @@ msgid "E337: Menu not found - check menu names" msgstr "E337: Menù non trovato - controlla nomi Menù" msgid "E338: Sorry, no file browser in console mode" -msgstr "E338: Spiacente, niente esplorazione file in modalità console" +msgstr "E338: Spiacente, niente esplorazione file in modo Console" msgid "E339: Pattern too long" msgstr "E339: Espressione troppo lunga" @@ -5187,7 +5203,7 @@ msgid "E358: 'langmap': Extra characters after semicolon: %s" msgstr "E358: 'langmap': Caratteri in più dopo il ';': %s" msgid "E359: Screen mode setting not supported" -msgstr "E359: Impostazione modalità schermo non supportata" +msgstr "E359: Impostazione modo schermo non supportata" msgid "E360: Cannot execute shell with -f option" msgstr "E360: Non posso eseguire la shell con l'opzione -f" @@ -5525,11 +5541,6 @@ msgstr "E456: Non trovo file risorse PostScript \"%s.ps\"" msgid "E457: Can't read PostScript resource file \"%s\"" msgstr "E457: Non riesco a leggere file risorse PostScript \"%s\"" -msgid "E458: Cannot allocate colormap entry, some colors may be incorrect" -msgstr "" -"E458: Non riesco ad allocare elemento di colormap, alcuni colori possono " -"essere errati" - msgid "E459: Cannot go back to previous directory" msgstr "E459: Non posso tornare alla directory precedente" @@ -5869,7 +5880,7 @@ msgid "E545: Missing colon" msgstr "E545: Manca ':'" msgid "E546: Illegal mode" -msgstr "E546: Modalità non consentita" +msgstr "E546: Modo non consentito" msgid "E547: Illegal mouseshape" msgstr "E547: Forma del mouse non consentita" @@ -6192,6 +6203,9 @@ msgstr "E647: Identificativo buffer non valido in setDot" msgid "E648: Invalid buffer identifier in close" msgstr "E648: Identificativo buffer non valido in close" +msgid "E649: Invalid identifier name in defineAnnoType" +msgstr "E649: Nome identificativo non valido in defineAnnoType" + msgid "E650: Invalid buffer identifier in defineAnnoType" msgstr "E650: Identificativo buffer non valido in defineAnnoType" @@ -6654,7 +6668,7 @@ msgid "E784: Cannot close last tab page" msgstr "E784: Non posso chiudere l'ultima pagina di schede" msgid "E785: complete() can only be used in Insert mode" -msgstr "E785: complete() può essere usata solo in modalità inserimento" +msgstr "E785: complete() può essere usata solo in modo Insert" msgid "E786: Range not allowed" msgstr "E786: Intervallo non consentito" @@ -7121,8 +7135,10 @@ msgstr "E910: Uso di un Job come un Numero" msgid "E911: Using a Job as a Float" msgstr "E911: Uso di un Job come un Numero-a-virgola-mobile" -msgid "E912: Cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" -msgstr "E912: Impossibile usare ch_evalexpr() con un canale grezzo o nl" +msgid "" +"E912: Cannot use ch_evalexpr()/ch_sendexpr() with a raw, nl or blob channel" +msgstr "E912: Impossibile usare ch_evalexpr()/ch_sendexpr con un canale " +"grezzo o nl o blob" msgid "E913: Using a Channel as a Number" msgstr "E913: Uso di un Canale come un Numero" @@ -9208,11 +9224,11 @@ msgstr "E1545: Stack delle Liste quickfix non disponibile" msgid "E1546: Cannot switch to a closing buffer" msgstr "E1546: Non posso passare a un buffer che si sta chiudendo" -msgid "E1547: This version of Vim does support :redrawtabpanel" -msgstr "E1547: Questa versione di Vim supporta :redrawtabpanel" +msgid "E1547: This version of Vim does not support :redrawtabpanel" +msgstr "E1547: Questa versione di Vim non supporta :redrawtabpanel" msgid "E1548: Wayland connection is unavailable" -msgstr "E1548: Connession a Wayland non disponibile" +msgstr "E1548: Connessione a Wayland non disponibile" #, c-format msgid "E1549: Cannot have more than %d diff anchors" @@ -9321,6 +9337,13 @@ msgstr "" msgid "E1575: Cannot create pipes" msgstr "E1575: Non posso create delle pipe" +msgid "E1576: Tag file entry must not be a URL" +msgstr "E1576: Il puntatore nel file di tag non dev'essere un URL" + +#, c-format +msgid "E1577: Invalid format string, only one \"%s\" is allowed" +msgstr "E1577: Stringa di formato non valida, solo un \"%s\" è consentito" + #. type of cmdline window or 0 #. result of cmdline window or 0 #. buffer of cmdline window or NULL @@ -9863,6 +9886,9 @@ msgstr "" "i nomi di file in un file di tag sono espressi a partire dalla\n" "posizione del file di tag" +msgid "a :tag command cannot access remote files" +msgstr "un comando :tag non può accedere a file remoti" + msgid "a :tag command will use the tagstack" msgstr "un comando :tag userà la pila dei tag (tagstack)" @@ -9911,6 +9937,9 @@ msgstr "" "numero di righe dello schermo da mostrare attorno alla riga che contiene\n" "il cursore" +msgid "vertically center cursor even at end of file" +msgstr "centrare il cursore verticalmente anche a fine file" + msgid "long lines wrap" msgstr "mandare a capo righe lunghe" @@ -10516,11 +10545,6 @@ msgstr "" msgid "Wayland seat to use" msgstr "Seggio Wayland da usare" -msgid "" -"Enable wayland focus stealing functionality in order to access the clipboard" -msgstr "" -"Abilita funzionalità cattura focus in Wayland per accedere agli appunti" - msgid "\"startsel\" and/or \"stopsel\"; what special keys can do" msgstr "\"startsel\" e/o \"stopsel\"; cosa fare usando tasti speciali" @@ -10640,6 +10664,9 @@ msgstr "larghezza massima del menù dinamico" msgid "popup border style" msgstr "stile bordo menù dinamico" +msgid "additional options for the popup menu" +msgstr "impostazioni opzionali per il menù dinamico (popup)" + msgid "user defined function for Insert mode completion" msgstr "funzione definita dall'utente per il completamento in modo Insert" @@ -10877,6 +10904,11 @@ msgstr "" "consentire impostazione di opzioni tramite espressioni\n" "da modeline" +msgid "only allow safe options to be set from a modeline" +msgstr "" +"consentire solo impostazione di opzioni non pericolose tramite " +"modeline" + msgid "number of lines to check for modelines" msgstr "numero di righe da controllare per modeline" From 44a1a6a33171ee34dddccf5236c38791fb489dfc Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Thu, 21 May 2026 20:15:59 +0000 Subject: [PATCH 282/545] runtime(doc): Update wrong shellescape() example Signed-off-by: Christian Brabandt --- runtime/doc/various.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index e60a557dfd..761dba1c61 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -1,4 +1,4 @@ -*various.txt* For Vim version 9.2. Last change: 2026 Apr 27 +*various.txt* For Vim version 9.2. Last change: 2026 May 21 VIM REFERENCE MANUAL by Bram Moolenaar @@ -296,7 +296,7 @@ g8 Print the hex values of the bytes used in the name does not contain a single quote: > :!ls '%' < This should always work, but it's more typing: > - :exe "!ls " .. shellescape(expand("%")) + :exe "!ls " .. shellescape(expand("%"),1) < To get a literal "%" or "#" prepend it with a backslash. For example, to list all files starting with "%": > From c7645fcda50f95cc098545b70ce937a986b997d1 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Fri, 22 May 2026 17:44:50 +0000 Subject: [PATCH 283/545] runtime(doc): add a few references to mouse behaviour fixes: #20281 Signed-off-by: Christian Brabandt --- runtime/doc/options.txt | 7 +++++-- runtime/doc/visual.txt | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index b7d74e9f8c..bfab0a87b4 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 May 18 +*options.txt* For Vim version 9.2. Last change: 2026 May 22 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6369,7 +6369,10 @@ A jump table for the options with a short description can be found at |Q_op|. be acted upon, i.e. no cursor move. This implies of course, that right clicking outside a selection will end Visual mode. - Overview of what button does what for each model: + + For a detailed description of 'mousemodel' behaviour see + |mouse-mode-table|. Overview of what button does what for each model: + mouse extend popup(_setpos) ~ left click place cursor place cursor left drag start selection start selection diff --git a/runtime/doc/visual.txt b/runtime/doc/visual.txt index 67f32c4dfb..3f3c687083 100644 --- a/runtime/doc/visual.txt +++ b/runtime/doc/visual.txt @@ -1,4 +1,4 @@ -*visual.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*visual.txt* For Vim version 9.2. Last change: 2026 May 22 VIM REFERENCE MANUAL by Bram Moolenaar @@ -157,6 +157,9 @@ gN Like |gn| but searches backward, like with `N`. You cannot use these, but they might show up in some places. + Starts or extends the Visual area blockwise, see + ||. + If Visual mode is not active and the "v", "V" or CTRL-V is preceded with a count, the size of the previously highlighted area is used for a start. You can then move the end of the highlighted area and give an operator. The type From e3dedac77b2076210e7027b14a5927622434f014 Mon Sep 17 00:00:00 2001 From: glepnir Date: Fri, 22 May 2026 17:59:23 +0000 Subject: [PATCH 284/545] patch 9.2.0510: setline() mapping may trigger autoindent Problem: setline() insert mode mapping may trigger autoindent, corrupting the newly inserted line content (Evgeni Chasnovski) Solution: Only strip autoindent whitespace when the rest of the line is all whitespace (glepnir). fixes: #19363 closes: #20290 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- src/edit.c | 57 +++++++++++++++++++++++---------------- src/testdir/test_edit.vim | 24 +++++++++++++++++ src/version.c | 2 ++ 3 files changed, 60 insertions(+), 23 deletions(-) diff --git a/src/edit.c b/src/edit.c index 1db9c13071..512c0b23c4 100644 --- a/src/edit.c +++ b/src/edit.c @@ -2613,34 +2613,45 @@ stop_insert( { pos_T tpos = curwin->w_cursor; colnr_T prev_col = end_insert_pos->col; + colnr_T strip_col; curwin->w_cursor = *end_insert_pos; check_cursor_col(); // make sure it is not past the line - for (;;) - { - if (gchar_cursor() == NUL && curwin->w_cursor.col > 0) - --curwin->w_cursor.col; - cc = gchar_cursor(); - if (!VIM_ISWHITE(cc)) - break; - if (del_char(TRUE) == FAIL) - break; // should not happen - } - if (curwin->w_cursor.lnum != tpos.lnum) - curwin->w_cursor = tpos; - else if (curwin->w_cursor.col < prev_col) + + // Where the loop would actually start (back up if on NUL). + strip_col = curwin->w_cursor.col; + if (gchar_cursor() == NUL && strip_col > 0) + --strip_col; + + // Don't strip if non-whitespace follows: setline() from a + // mapping or CursorHoldI autocmd may have inserted content. + if (*skipwhite(ml_get_curline() + strip_col) == NUL) { - // reset tpos, could have been invalidated in the loop above - tpos = curwin->w_cursor; - tpos.col++; - if (cc != NUL && gchar_pos(&tpos) == NUL) - ++curwin->w_cursor.col; // put cursor back on the NUL - } + curwin->w_cursor.col = strip_col; + for (;;) + { + cc = gchar_cursor(); + if (!VIM_ISWHITE(cc)) + break; + if (del_char(TRUE) == FAIL) + break; // should not happen + } + if (curwin->w_cursor.lnum != tpos.lnum) + curwin->w_cursor = tpos; + else if (curwin->w_cursor.col < prev_col) + { + // reset tpos, could have been invalidated in the loop above + tpos = curwin->w_cursor; + tpos.col++; + if (cc != NUL && gchar_pos(&tpos) == NUL) + ++curwin->w_cursor.col; // put cursor back on the NUL + } - // may have started Visual mode, adjust the position for - // deleted characters. - if (VIsual_active) - check_visual_pos(); + // may have started Visual mode, adjust the position for + // deleted characters. + if (VIsual_active) + check_visual_pos(); + } } } did_ai = FALSE; diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim index 196933f7d9..6511e178cf 100644 --- a/src/testdir/test_edit.vim +++ b/src/testdir/test_edit.vim @@ -2458,4 +2458,28 @@ func Test_edit_CAR_with_completion() bw! endfunc +func Test_autoindent_no_strip_after_cmd_setline() + new + setlocal autoindent + inoremap call setline('.', 'v v')call cursor(line('.'), 2) + call feedkeys("Go\\", 'tx') + call assert_equal('v v', getline(2)) + bwipe! +endfunc + +func Test_autoindent_no_strip_after_cursorholdi() + CheckFeature timers + new + setlocal autoindent + set updatetime=50 + au CursorHoldI call setline('.', 'v v') + call setline(1, ' x') + call cursor(1, 2) + call timer_start(120, {-> feedkeys("\", 't')}) + call feedkeys("o", 'tx!') + call assert_equal('v v', getline(2)) + set updatetime& + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 7b80b546f3..7b6e33045e 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 510, /**/ 509, /**/ From 1d727b6f74896915a94f7d0e134d64cb0eac8045 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Fri, 22 May 2026 18:09:33 +0000 Subject: [PATCH 285/545] patch 9.2.0511: configure: when GTK4 is used also links in X11 libs Problem: configure: when GTK4 is used also links in X11 libs (Reilly Brogan) Solution: Disable linking against X11 libraries when GTK4 GUI is to be used (Yasuhiro Matsumoto) GTK4 does not use any X11 APIs directly; the X11 backend is loaded by GTK4 at runtime. Force with_x=no when --enable-gui=gtk4 so configure does not probe for libICE/libSM/libX11/libXt/libXdmcp/libXpm, and so packagers do not pull those into build dependencies. Also skip the XSMP X11/SM/SMlib.h header check when X11 is disabled, since USE_XSMP itself requires HAVE_X11. fixes: #20268 closes: #20289 Co-authored-by: Claude Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/auto/configure | 9 ++++++--- src/configure.ac | 17 +++++++++++++---- src/version.c | 2 ++ 3 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/auto/configure b/src/auto/configure index 69b1a39ef0..90110a812b 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -9296,8 +9296,11 @@ CPPFLAGS=$cppflags_save CFLAGS=$cflags_save fi +if test "x$enable_gui" = "xgtk4"; then + with_x=no +fi test -z "$with_x" && with_x=yes -test "${enable_gui-yes}" != no -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && with_x=yes +test "${enable_gui-yes}" != no -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" -a "x$enable_gui" != "xgtk4" && with_x=yes if test "$with_x" = no; then { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: defaulting to: don't HAVE_X11" >&5 printf "%s\n" "defaulting to: don't HAVE_X11" >&6; } @@ -10465,7 +10468,7 @@ printf "%s\n" "#define SIZEOF_WCHAR_T $ac_cv_sizeof_wchar_t" >>confdefs.h fi fi -if test "x$with_x" = xno -a "x$with_x_arg" = xyes; then +if test "x$with_x" = xno -a "x$with_x_arg" = xyes -a "x$enable_gui" != "xgtk4"; then as_fn_error $? "could not configure X" "$LINENO" 5 fi @@ -11921,7 +11924,7 @@ printf "%s\n" "$NARROW_PROTO" >&6; } fi -if test "$enable_xsmp" = "yes"; then +if test "$enable_xsmp" = "yes" -a "x$with_x" != "xno"; then cppflags_save=$CPPFLAGS CPPFLAGS="$CPPFLAGS $X_CFLAGS" ac_fn_c_check_header_compile "$LINENO" "X11/SM/SMlib.h" "ac_cv_header_X11_SM_SMlib_h" "$ac_includes_default" diff --git a/src/configure.ac b/src/configure.ac index a49c6d26b9..6f6590f6da 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -2489,8 +2489,15 @@ CPPFLAGS=$cppflags_save CFLAGS=$cflags_save fi +dnl GTK4 does not use X11 APIs directly, so skip X11 detection entirely. +dnl This avoids pulling libICE/libSM/libX11/libXt into build dependencies +dnl for GTK4-only builds, even when --with-x=yes is passed (e.g. by a +dnl distro's default packaging script). +if test "x$enable_gui" = "xgtk4"; then + with_x=no +fi test -z "$with_x" && with_x=yes -test "${enable_gui-yes}" != no -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" && with_x=yes +test "${enable_gui-yes}" != no -a "x$MACOS_X" != "xyes" -a "x$QNX" != "xyes" -a "x$enable_gui" != "xgtk4" && with_x=yes if test "$with_x" = no; then AC_MSG_RESULT(defaulting to: don't HAVE_X11) else @@ -2605,7 +2612,8 @@ else fi dnl Check if --with-x was given but it doesn't work. -if test "x$with_x" = xno -a "x$with_x_arg" = xyes; then +dnl GTK4 always forces with_x=no above, so don't treat that as a failure. +if test "x$with_x" = xno -a "x$with_x_arg" = xyes -a "x$enable_gui" != "xgtk4"; then AC_MSG_ERROR([could not configure X]) fi @@ -3275,8 +3283,9 @@ EOF fi dnl Look for XSMP support - but don't necessarily restrict it to X11 GUIs -dnl use the X11 include path -if test "$enable_xsmp" = "yes"; then +dnl use the X11 include path. Skip when X11 is not in use; USE_XSMP itself +dnl requires HAVE_X11, so the header check would be wasted otherwise. +if test "$enable_xsmp" = "yes" -a "x$with_x" != "xno"; then cppflags_save=$CPPFLAGS CPPFLAGS="$CPPFLAGS $X_CFLAGS" AC_CHECK_HEADERS(X11/SM/SMlib.h) diff --git a/src/version.c b/src/version.c index 7b6e33045e..7be1f150ba 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 511, /**/ 510, /**/ From e9c793bebcad2b585b3f5d25ca6108bcb48772fe Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Fri, 22 May 2026 18:30:52 +0000 Subject: [PATCH 286/545] patch 9.2.0512: clientserver uses binary protocol Problem: clientserver feature uses binary protocol and is hard to understand Solution: Rewrite the code based on channels and JSON messages (Foxe Chen). closes: #19782 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- Filelist | 2 + runtime/doc/remote.txt | 79 +- runtime/doc/tags | 3 +- runtime/doc/version9.txt | 3 +- runtime/doc/vim.1 | 7 +- runtime/doc/vim.man | 7 +- src/Make_cyg_ming.mak | 1 + src/Make_mvc.mak | 4 + src/Makefile | 11 + src/auto/configure | 32 - src/channel.c | 132 +- src/clientserver.c | 362 ++-- src/configure.ac | 16 - src/errors.h | 12 +- src/feature.h | 22 +- src/gc.c | 3 + src/getchar.c | 3 + src/globals.h | 19 +- src/gui.c | 6 - src/gui_gtk_x11.c | 55 - src/json.c | 2 + src/main.c | 19 +- src/os_unix.c | 1735 +------------------- src/po/vim.pot | 26 +- src/proto.h | 3 + src/proto/channel.pro | 10 +- src/proto/gui_gtk_x11.pro | 2 - src/proto/os_unix.pro | 11 - src/proto/socketserver.pro | 11 + src/socketserver.c | 1404 ++++++++++++++++ src/structs.h | 7 + src/testdir/dumps/Test_clientserver_1.dump | 8 + src/testdir/test_clientserver.vim | 253 ++- src/testdir/test_remote.vim | 20 +- src/testdir/test_usercommands.vim | 1 - src/testdir/test_vim9_builtin.vim | 31 +- src/testdir/util/check.vim | 11 - src/testdir/util/socketserver.vim | 17 - src/ui.c | 18 +- src/version.c | 2 + 40 files changed, 2099 insertions(+), 2271 deletions(-) create mode 100644 src/proto/socketserver.pro create mode 100644 src/socketserver.c create mode 100644 src/testdir/dumps/Test_clientserver_1.dump delete mode 100644 src/testdir/util/socketserver.vim diff --git a/Filelist b/Filelist index dfc828a759..4ea946c841 100644 --- a/Filelist +++ b/Filelist @@ -145,6 +145,7 @@ SRC_ALL = \ src/session.c \ src/sha256.c \ src/sign.c \ + src/socketserver.c \ src/sound.c \ src/spell.c \ src/spell.h \ @@ -344,6 +345,7 @@ SRC_ALL = \ src/proto/session.pro \ src/proto/sha256.pro \ src/proto/sign.pro \ + src/proto/socketserver.pro \ src/proto/sound.pro \ src/proto/spell.pro \ src/proto/spellfile.pro \ diff --git a/runtime/doc/remote.txt b/runtime/doc/remote.txt index 2f77fcaa57..260fc864f6 100644 --- a/runtime/doc/remote.txt +++ b/runtime/doc/remote.txt @@ -79,9 +79,10 @@ The following command line arguments are available: *--clientserver* --clientserver {method} Use the specified method {method} as the backend for clientserver functionality. Can - either be "socket" or "x11". + either be "socket", "x11", or "mswin". {only available when compiled with both |+X11| - and |+socketserver| features} + and |+socketserver| features, or + |+socketserver| on MS-Windows} Examples ~ @@ -220,12 +221,16 @@ When using gvim, the --remote-wait only works properly this way: > < ============================================================================== 4. Socket server specific items *socketserver-clientserver* - *E1563* *E1564* *E1565* *E1566* *E1567* + *E1564* *E1565* *E1566* -The communication between client and server is done using Unix domain sockets. -These sockets are either placed in these directories in the following order of +NOTE: Vim version before patch 9.2.511 use a different socketserver backend +which is incompatible with the new one, which is based on channels and JSON. + +The communication between client and server is done using channels internally. +When using the traditional/generic clientserver naming (only available on +Unix), sockets are placed in these directories in the following order of availability: - 1. "$XDG_RUTIME_DIR/vim" if $XDG_RUNTIME_DIR is set in the environment. + 1. "$XDG_RUNTIME_DIR/vim" if $XDG_RUNTIME_DIR is set in the environment. 2. "$TMPDIR/vim-[uid]", where "[uid]" is the uid of the user. This directory will have the access permissions set to 700 so only the user can read or write from/to it. If $TMPDIR is not set, "/tmp" is used. @@ -237,45 +242,55 @@ absolute or relative path. If the server id starts with either a "/" Otherwise the server id will be the filename of the socket which will be placed in the above common directories. Note that a server id/name can only contain slashes "/" if it is taken as a path, so names such as "abc/dir" will -be invalid. +be invalid. This feature is only available on Unix. + + *socketserver-address* +In addition, the socketserver can also be created as a |channel-address|. To +do this, prefix the address with "channel:" (which will be ignored). This is +available on both Unix and MS-Windows, and is the only available naming for the +socketserver backend on Windows. However, note that |ch_listen()| +restrictions apply, meaning only port numbers can be used for TCP sockets. + +If the name is prefixed with "name:", then the legacy servername behaviour is +used, so "name:VIM" is the same as "VIM". -Socket server functionality is available in both GTK GUI and terminal versions -of Vim. Unless Vim is compiled with |+autoservername| feature, the socket -server will have to started explicitly, just like X11, even in the GUI. +Unless Vim is compiled with |+autoservername| feature, the socket server will +have to started explicitly, similar to X11. This is unless a X11 GUI Vim is +being used. If Vim crashes or does not exit cleanly, the socket server will not remove the socket file and it will be left around. This is generally not a problem, because if a socket name is taken, Vim checks if the socket in its place is dead (not attached to any process), and can replace it instead of finding a -new name. - -To send commands to a Vim socket server from another application, read the -source file src/os_unix.c, there is detailed description of the protocol used. +new name. This is only relevant for Unix. *socketserver-differences* -Most of the functionality is the same as X11, however unlike X11, where the -client does not need to be a server in order to communicate with another -server, the socket server requires the server to be running even as a client. -The exception is |serverlist()| or the |--serverlist| argument, which does not -require the server to be running. +Most of the functionality is the same as the other clientserver backends. -Additionally, the server id or client id will not be a number like X11 or +However, the server id or client id will not be a number like X11 or MS-Windows (shown in hex representation), instead it is the absolute path to -the socket. This can be seen via the |v:servername| variable. +the socket. This can be seen via the |v:servername| variable. If the name is +a channel address, then it will be the address with the "a/" prefix as well. + +Note when using |--remote-wait|, the client id will look like "t/". This naming is specifically for this command, and attempting to use +it as an address will fail. -The |--serverlist| argument will act just like X11, however it only checks the -given common directories above. If a custom path is used for a socket, it -will not be detected, such as a path either not in $XDG_RUNTIME_DIR or -<$TMPDIR or /tmp>/vim of the |--serverlist| Vim process. +If on Unix, the |--serverlist| argument will act just like X11, however it +only checks the given common directories above. If a custom path is used for +a socket, it will not be detected, such as a path either not in +$XDG_RUNTIME_DIR or <$TMPDIR or /tmp>/vim of the |--serverlist| Vim process. +If on MS-Windows, then the |--serverlist| argument will output nothing. -If you have both |+socketserver| and |+X11| compiled, you will need to add -|--clientserver| set to "socket" in combination with |--serverlist| to list -the available servers. You cannot list both types of backends in one command. +The |+socketserver| feature is automatically enabled when on Unix or +MS-Windows, and the |+channel| feature is enabled *socketserver-x11* -If Vim is compiled with both |+X11| and |+socketserver|, then deciding which -backend to use is done at startup time, via the |--clientserver| argument. By -default if it is not specified, then X11 will be used. A Vim instance using a -socket server cannot communicate with one using X11. +If Vim is compiled with |+socketserver| and another clientserver backend, then +deciding which backend to use is done at startup time, via the +|--clientserver| argument. By default if it is not specified, then the native +backend (X11 or MS-Window messages) will be used, if available. A Vim +instance using a socket server cannot communicate with one using another +clientserver backend. vim:tw=78:sw=4:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/tags b/runtime/doc/tags index 7a4c09031b..9c0298aedd 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -4762,11 +4762,9 @@ E156 sign.txt /*E156* E1560 vim9.txt /*E1560* E1561 vim9.txt /*E1561* E1562 options.txt /*E1562* -E1563 remote.txt /*E1563* E1564 remote.txt /*E1564* E1565 remote.txt /*E1565* E1566 remote.txt /*E1566* -E1567 remote.txt /*E1567* E1568 options.txt /*E1568* E1569 builtin.txt /*E1569* E157 sign.txt /*E157* @@ -10377,6 +10375,7 @@ slow-fast-terminal term.txt /*slow-fast-terminal* slow-start starting.txt /*slow-start* slow-terminal term.txt /*slow-terminal* socket-interface channel.txt /*socket-interface* +socketserver-address remote.txt /*socketserver-address* socketserver-clientserver remote.txt /*socketserver-clientserver* socketserver-differences remote.txt /*socketserver-differences* socketserver-name remote.txt /*socketserver-name* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 1e7d1c7135..4f7b9b62da 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 May 19 +*version9.txt* For Vim version 9.2. Last change: 2026 May 22 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52665,6 +52665,7 @@ Changed ~ if needed, see |wayland-primary-selection| for an example. - On Unix, filename completion for single-file Ex commands now treats embedded whitespace as part of the filename, like on other platforms. +- Rewrite the clientserver socketserver backend to use channels and JSON. *added-9.3* diff --git a/runtime/doc/vim.1 b/runtime/doc/vim.1 index cf9a080482..cb1d3fc762 100644 --- a/runtime/doc/vim.1 +++ b/runtime/doc/vim.1 @@ -504,9 +504,10 @@ socketserver backend is being used, if the name starts with "/", "./", or "../", it is taken as either an absolute, relative or relative path to the socket. .TP \-\-clientserver {backend} -Use {backend} as the backend for clientserver functionality, either "socket" or -"x11" respectively. Only available when compiled with both socketserver and X11 -features present +Use {backend} as the backend for clientserver functionality, either "socket", +"x11", or "mswin" respectively. Only available when compiled with both +socketserver and X11 features present, or if compiled with socketserver on +MS-Windows. .TP \-\-socketid {id} GTK GUI only: Use the GtkPlug mechanism to run gVim in another window. diff --git a/runtime/doc/vim.man b/runtime/doc/vim.man index 9a5881b3db..ec4058f913 100644 --- a/runtime/doc/vim.man +++ b/runtime/doc/vim.man @@ -385,9 +385,10 @@ OPTIONS --clientserver {backend} Use {backend} as the backend for clientserver functional‐ - ity, either "socket" or "x11" respectively. Only available - when compiled with both socketserver and X11 features - present + ity, either "socket", "x11", or "mswin" respectively. Only + available when compiled with both socketserver and X11 fea‐ + tures present, or if compiled with socketserver on MS-Win‐ + dows. --socketid {id} GTK GUI only: Use the GtkPlug mechanism to run gVim in an‐ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 7f3d519bf1..24db4ccfff 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -877,6 +877,7 @@ OBJ = \ $(OUTDIR)/session.o \ $(OUTDIR)/sha256.o \ $(OUTDIR)/sign.o \ + $(OUTDIR)/socketserver.o \ $(OUTDIR)/spell.o \ $(OUTDIR)/spellfile.o \ $(OUTDIR)/spellsuggest.o \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 8994143c92..3b9ccb98f8 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -772,6 +772,7 @@ OBJ = \ $(OUTDIR)\session.obj \ $(OUTDIR)\sha256.obj \ $(OUTDIR)\sign.obj \ + $(OUTDIR)\socketserver.obj \ $(OUTDIR)\spell.obj \ $(OUTDIR)\spellfile.obj \ $(OUTDIR)\spellsuggest.obj \ @@ -1767,6 +1768,8 @@ $(OUTDIR)/sha256.obj: $(OUTDIR) sha256.c $(INCL) $(OUTDIR)/sign.obj: $(OUTDIR) sign.c $(INCL) +$(OUTDIR)/socketserver.obj: $(OUTDIR) socketserver.c $(INCL) + $(OUTDIR)/spell.obj: $(OUTDIR) spell.c $(INCL) $(OUTDIR)/spellfile.obj: $(OUTDIR) spellfile.c $(INCL) @@ -2017,6 +2020,7 @@ proto.h: \ proto/session.pro \ proto/sha256.pro \ proto/sign.pro \ + proto/socketserver.pro \ proto/spell.pro \ proto/spellfile.pro \ proto/spellsuggest.pro \ diff --git a/src/Makefile b/src/Makefile index 4e00a2eb88..dd94e74180 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1571,6 +1571,7 @@ BASIC_SRC = \ session.c \ sha256.c \ sign.c \ + socketserver.c \ sound.c \ spell.c \ spellfile.c \ @@ -1749,6 +1750,7 @@ OBJ_COMMON = \ objects/session.o \ objects/sha256.o \ objects/sign.o \ + objects/socketserver.o \ objects/sound.o \ objects/spell.o \ objects/spellfile.o \ @@ -3592,6 +3594,9 @@ objects/sha256.o: sha256.c objects/sign.o: sign.c $(CCC) -o $@ sign.c +objects/socketserver.o: socketserver.c + $(CCC) -o $@ socketserver.c + objects/sound.o: sound.c $(CCC) -o $@ sound.c @@ -4267,6 +4272,11 @@ objects/sign.o: auto/osdef.h sign.c vim.h protodef.h auto/config.h feature.h os_ structs.h regexp.h gui.h libvterm/include/vterm.h \ libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \ globals.h errors.h +objects/socketserver.o: socketserver.c vim.h protodef.h auto/config.h feature.h \ + os_unix.h auto/osdef.h ascii.h keymap.h termdefs.h macros.h option.h \ + beval.h structs.h regexp.h gui.h libvterm/include/vterm.h \ + libvterm/include/vterm_keycodes.h xdiff/xdiff.h xdiff/../vim.h alloc.h \ + ex_cmds.h spell.h proto.h globals.h errors.h objects/sound.o: auto/osdef.h sound.c vim.h protodef.h auto/config.h feature.h os_unix.h \ ascii.h keymap.h termdefs.h macros.h option.h beval.h \ structs.h regexp.h gui.h libvterm/include/vterm.h \ @@ -4793,6 +4803,7 @@ proto/search.pro: search.c proto/session.pro: session.c proto/sha256.pro: sha256.c proto/sign.pro: sign.c +proto/socketserver.pro: socketserver.c proto/sound.pro: sound.c proto/spell.pro: spell.c proto/spellfile.pro: spellfile.c diff --git a/src/auto/configure b/src/auto/configure index 90110a812b..a65e2182fa 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -850,7 +850,6 @@ enable_netbeans enable_channel enable_terminal enable_autoservername -enable_socketserver enable_multibyte enable_rightleft enable_arabic @@ -1532,7 +1531,6 @@ Optional Features: --disable-channel Disable process communication support. --enable-terminal Enable terminal emulation support. --enable-autoservername Automatically define servername at vim startup. - --enable-socketserver Use sockets for clientserver communication. --enable-multibyte Include multibyte editing support. --disable-rightleft Do not include Right-to-Left language support. --disable-arabic Do not include Arabic language support. @@ -9077,36 +9075,6 @@ if test "$enable_autoservername" = "yes"; then fi -{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-socketserver argument" >&5 -printf %s "checking --enable-socketserver argument... " >&6; } -# Check whether --enable-socketserver was given. -if test ${enable_socketserver+y} -then : - enableval=$enable_socketserver; enable_socketserver=$enableval -else case e in #( - e) if test "x$features" = xtiny -then : - enable_socketserver=no_msg - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cannot use socketserver with tiny features" >&5 -printf "%s\n" "cannot use socketserver with tiny features" >&6; } -else case e in #( - e) enable_socketserver=yes ;; -esac -fi ;; -esac -fi - - -if test "$enable_socketserver" = "yes"; then - printf "%s\n" "#define WANT_SOCKETSERVER 1" >>confdefs.h - - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -printf "%s\n" "yes" >&6; } -elif test "$enable_socketserver" = "no"; then - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 -printf "%s\n" "no" >&6; } -fi - { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking --enable-multibyte argument" >&5 printf %s "checking --enable-multibyte argument... " >&6; } # Check whether --enable-multibyte was given. diff --git a/src/channel.c b/src/channel.c index 579087b374..27aa259e2b 100644 --- a/src/channel.c +++ b/src/channel.c @@ -820,7 +820,7 @@ channel_connect( * Returns the channel for success. * Returns NULL for failure. */ - static channel_T * + channel_T * channel_open_unix( const char *path, void (*nb_close_cb)(void)) @@ -1290,6 +1290,46 @@ channel_set_options(channel_T *channel, jobopt_T *opt) channel->ch_part[PART_IN].ch_io = opt->jo_io[PART_IN]; } +/* + * Parse the address for socketserver and store port in "port", or the path in + * "unix_path" if it is a unix domain socket. Returns OK on success and FAIL on + * failure. + */ + int +channel_parse_socketserver_address( + char_u *address, + int *port, + char_u **unix_path, + bool quiet) +{ + char *rest; + long val; + + if (*address == NUL) + goto fail; + + if (!STRNCMP(address, "unix:", 5)) + { + *unix_path = vim_strsave(address + 5); + return OK; + } + + val = strtol((char *)(address), &rest, 10); + if (val < 0 || val >= 65536 || *rest != NUL) + { + if (!quiet) + semsg(_(e_invalid_argument_str), address); + return FAIL; + } + *port = (int)val; + + return OK; +fail: + if (!quiet) + semsg(_(e_invalid_argument_str), address); + return FAIL; +} + /* * Implements ch_open(). */ @@ -1449,7 +1489,7 @@ channel_listen_func(typval_T *argvars) } if (is_unix) - channel = channel_listen_unix((char *)arg, NULL); + channel = channel_listen_unix((char *)arg, NULL, true); else channel = channel_listen(port, NULL); if (channel != NULL) @@ -1571,13 +1611,15 @@ channel_listen( /* * Listen to a Unix domain socket channel. + * If "replace" is true, then unlink the path first beforehand. * Returns the channel for success. * Returns NULL for failure. */ channel_T * channel_listen_unix( char *path, - void (*nb_close_cb)(void)) + void (*nb_close_cb)(void), + bool replace) { int sd = -1; struct sockaddr_un server; @@ -1619,8 +1661,9 @@ channel_listen_unix( return NULL; } - // Unlink the socket in case it already exists - unlink(server.sun_path); + if (replace) + // Unlink the socket in case it already exists + unlink(server.sun_path); // Bind the socket to the path server_len = offsetof(struct sockaddr_un, sun_path) + path_len + 1; @@ -1983,7 +2026,7 @@ channel_buffer_free(buf_T *buf) /* * Write any lines waiting to be written to "channel". */ - static void + void channel_write_input(channel_T *channel) { chanpart_T *in_part = &channel->ch_part[PART_IN]; @@ -2364,7 +2407,7 @@ channel_save(channel_T *channel, ch_part_T part, char_u *buf, int len, * Try to fill the buffer of "reader". * Returns FALSE when nothing was added. */ - static int + int channel_fill(js_read_T *reader) { channel_T *channel = (channel_T *)reader->js_cookie; @@ -2484,11 +2527,12 @@ channel_process_lspdap_http_hdr(js_read_T *reader) /* * Use the read buffer of "channel"/"part" and parse a JSON message that is - * complete. The messages are added to the queue. + * complete. The messages are added to the queue. If "socketserver" is true, + * then ignore the channel mode. * Return TRUE if there is more to read. */ - static int -channel_parse_json(channel_T *channel, ch_part_T part) + int +channel_parse_json(channel_T *channel, ch_part_T part, bool socketserver) { js_read_T reader; typval_T listtv; @@ -2507,8 +2551,8 @@ channel_parse_json(channel_T *channel, ch_part_T part) reader.js_cookie = channel; reader.js_cookie_arg = part; - if (chanpart->ch_mode == CH_MODE_LSP - || chanpart->ch_mode == CH_MODE_DAP) + if (!socketserver && (chanpart->ch_mode == CH_MODE_LSP + || chanpart->ch_mode == CH_MODE_DAP)) status = channel_process_lspdap_http_hdr(&reader); // When a message is incomplete we wait for a short while for more to @@ -2526,14 +2570,16 @@ channel_parse_json(channel_T *channel, ch_part_T part) { // Only accept the response when it is a list with at least two // items. - if ((chanpart->ch_mode == CH_MODE_LSP || chanpart->ch_mode == CH_MODE_DAP) + if (!socketserver && (chanpart->ch_mode == CH_MODE_LSP + || chanpart->ch_mode == CH_MODE_DAP) && listtv.v_type != VAR_DICT) { ch_error(channel, "Did not receive a LSP dict, discarding"); clear_tv(&listtv); } - else if (chanpart->ch_mode != CH_MODE_LSP && chanpart->ch_mode != CH_MODE_DAP - && (listtv.v_type != VAR_LIST || listtv.vval.v_list->lv_len < 2)) + else if (!socketserver && chanpart->ch_mode != CH_MODE_LSP + && chanpart->ch_mode != CH_MODE_DAP + && (listtv.v_type != VAR_LIST || listtv.vval.v_list->lv_len < 2)) { if (listtv.v_type != VAR_LIST) ch_error(channel, "Did not receive a list, discarding"); @@ -2668,7 +2714,7 @@ remove_cb_node(cbq_T *head, cbq_T *node) * Remove "node" from the queue that it is in and free it. * Caller should have freed or used node->jq_value. */ - static void + void remove_json_node(jsonq_T *head, jsonq_T *node) { if (node->jq_prev == NULL) @@ -3227,8 +3273,12 @@ may_invoke_callback(channel_T *channel, ch_part_T part) int called_otc; // one time callbackup int raw_len = 0; - if (channel->ch_nb_close_cb != NULL) - // this channel is handled elsewhere (netbeans) + if (channel->ch_nb_close_cb != NULL +#ifdef FEAT_SOCKETSERVER + || channel->ch_socketserver +#endif + ) + // this channel is handled elsewhere (netbeans or socketserver) return FALSE; // Use a message-specific callback, part callback or channel callback @@ -3267,7 +3317,7 @@ may_invoke_callback(channel_T *channel, ch_part_T part) (void)channel_collapse(channel, part, FALSE); // Parse readahead, return when there is still no message. - channel_parse_json(channel, part); + channel_parse_json(channel, part, false); if (channel_get_json(channel, part, -1, FALSE, &listtv) == FAIL) return FALSE; } @@ -3595,7 +3645,7 @@ channel_readahead_pointer(channel_T *channel, ch_part_T part) if (head->jq_next == NULL) // Parse json from readahead, there might be a complete message to // process. - channel_parse_json(channel, part); + channel_parse_json(channel, part, false); return head->jq_next; } @@ -3842,6 +3892,11 @@ channel_close(channel_T *channel, int invoke_close_cb) } channel->ch_nb_close_cb = NULL; +#ifdef FEAT_SOCKETSERVER + channel->ch_socketserver = false; + channel->ch_ss_accept_cb = NULL; + channel->ch_ss_close_cb = NULL; +#endif #ifdef FEAT_TERMINAL term_channel_closed(channel); @@ -4181,6 +4236,10 @@ channel_close_now(channel_T *channel) ch_log(channel, "Closing channel because all readable fds are closed"); if (channel->ch_nb_close_cb != NULL) (*channel->ch_nb_close_cb)(); +#ifdef FEAT_SOCKETSERVER + if (channel->ch_ss_close_cb != NULL) + channel->ch_ss_close_cb(channel); +#endif channel_close(channel, TRUE); } @@ -4252,6 +4311,14 @@ channel_read(channel_T *channel, ch_part_T part, char *func) channel_gui_register_one(newchannel, PART_SOCK); #endif +#ifdef FEAT_SOCKETSERVER + if (channel->ch_ss_accept_cb != NULL) + { + channel->ch_ss_accept_cb(newchannel); + return; + } +#endif + if (client.ss_family == AF_INET) { #ifdef HAVE_INET_NTOP @@ -4323,6 +4390,16 @@ channel_read(channel_T *channel, ch_part_T part, char *func) #endif } +#ifdef FEAT_SOCKETSERVER + + void +channel_check(channel_T *channel, ch_part_T part) +{ + channel_read(channel, part, "channel_check"); +} + +#endif + /* * Read from RAW or NL "channel"/"part". Blocks until there is something to * read or the timeout expires. @@ -4465,7 +4542,7 @@ channel_read_json_block( // received messages. (void)channel_collapse(channel, part, FALSE); - more = channel_parse_json(channel, part); + more = channel_parse_json(channel, part, false); // search for message "id" if (channel_get_json(channel, part, id, TRUE, rettv) == OK) @@ -5915,4 +5992,17 @@ channel_to_string_buf(typval_T *varp, char_u *buf) return buf; } +/* + * Return the channel with the given ID. Returns NULL if not found. + */ + channel_T * +channel_find(int ch_id) +{ + channel_T *ch; + FOR_ALL_CHANNELS(ch) + if (ch->ch_id == ch_id) + return ch; + return NULL; +} + #endif // FEAT_JOB_CHANNEL diff --git a/src/clientserver.c b/src/clientserver.c index 9186f4db16..ae72a694f1 100644 --- a/src/clientserver.c +++ b/src/clientserver.c @@ -15,11 +15,6 @@ #if defined(FEAT_CLIENTSERVER) -# ifdef FEAT_SOCKETSERVER -# include -# include "sys/un.h" -# endif - static void cmdsrv_main(int *argc, char **argv, char_u *serverName_arg, char_u **serverStr); static char_u *serverMakeName(char_u *arg, char *cmd); @@ -206,23 +201,6 @@ exec_on_server(mparm_T *parmp) serverInitMessaging(); # endif -# ifdef FEAT_SOCKETSERVER - // If servername is specified and we are using sockets, always init the - // sockt server. We may need to receive replies back to us. If --serverlist - // is passed, the socket server will be uninitialized before listing - // sockets then initialized after. This is so we don't add our own socket - // in the list. This does not happen in serverlist(). - if ((parmp->serverArg || parmp->serverName_arg != NULL) && - clientserver_method == CLIENTSERVER_METHOD_SOCKET) - { - parmp->servername = serverMakeName(parmp->serverName_arg, - parmp->argv[0]); - if (socket_server_init(parmp->servername) == OK) - TIME_MSG("initialize socket server"); - made_name = TRUE; - } -# endif - /* * When a command server argument was found, execute it. This may * exit Vim when it was successful. Otherwise it's executed further @@ -242,10 +220,12 @@ exec_on_server(mparm_T *parmp) parmp->servername = serverMakeName(parmp->serverName_arg, parmp->argv[0]); # ifdef MSWIN - if (parmp->servername != NULL) + if (parmp->servername != NULL && clientserver_method == CLIENTSERVER_METHOD_MSWIN) { serverSetName(parmp->servername); +# ifndef FEAT_SOCKETSERVER vim_free(parmp->servername); +# endif } # endif } @@ -262,16 +242,31 @@ prepare_server(mparm_T *parmp) * Only register nongui-vim's with an explicit --servername argument, * or when compiling with autoservername. * When running as root --servername is also required. + * + * If we are Windows and socketserver is being used, then don't use + * autoservername, since traditional naming isn't supported. */ if ( -# ifdef FEAT_X11 - X_DISPLAY != NULL && +# if defined(FEAT_X11) || (defined(FEAT_SOCKETSERVER) && !defined(MSWIN)) + ( +# ifdef FEAT_X11 + X_DISPLAY != NULL +# endif +# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER) && !defined(MSWIN) + || +# endif +# if defined(FEAT_SOCKETSERVER) && !defined(MSWIN) + clientserver_method == CLIENTSERVER_METHOD_SOCKET +# endif + ) && # endif - parmp->servername != NULL && ( # if defined(FEAT_AUTOSERVERNAME) || defined(FEAT_GUI) ( +# if defined(FEAT_SOCKETSERVER) && defined(MSWIN) + clientserver_method != CLIENTSERVER_METHOD_SOCKET && +# endif # if defined(FEAT_AUTOSERVERNAME) 1 # else @@ -284,12 +279,10 @@ prepare_server(mparm_T *parmp) # endif parmp->serverName_arg != NULL)) { + # ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) - { - if (socket_server_init(parmp->servername) == OK) - TIME_MSG("initialize socket server"); - } + socketserver_start(parmp->servername, false); # endif # ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11) @@ -300,10 +293,15 @@ prepare_server(mparm_T *parmp) # endif vim_free(parmp->servername); } -# ifdef FEAT_X11 else - serverDelayedStartName = parmp->servername; + { +# ifdef FEAT_X11 + if (clientserver_method == CLIENTSERVER_METHOD_X11) + serverDelayedStartName = parmp->servername; + else # endif + vim_free(parmp->servername); + } # endif /* @@ -343,9 +341,6 @@ cmdsrv_main( # define ARGTYPE_SEND 3 int silent = FALSE; int tabs = FALSE; -# ifdef FEAT_SOCKETSERVER - char_u *receiver; -# endif # ifdef MSWIN HWND srv; # elif defined(FEAT_X11) @@ -353,6 +348,9 @@ cmdsrv_main( setup_term_clip(); # endif +# ifdef FEAT_SOCKETSERVER + channel_T *ch = NULL; +# endif sname = serverMakeName(serverName_arg, argv[0]); if (sname == NULL) @@ -434,9 +432,8 @@ cmdsrv_main( # ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) - ret = socket_server_send( - sname, *serverStr, NULL, &receiver, - 0, -1, silent); + ret = socketserver_send(sname, *serverStr, NULL, false, -1, + silent, &ch); # endif # ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11) @@ -452,8 +449,10 @@ cmdsrv_main( } # endif # ifdef MSWIN - // Win32 always works? - ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0, silent); + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN) + // Win32 always works? + ret = serverSendToVim(sname, *serverStr, NULL, &srv, 0, 0, + silent); # endif if (ret < 0) { @@ -513,23 +512,23 @@ cmdsrv_main( char_u *p = NULL; int j; # ifdef MSWIN - p = serverGetReply(srv, NULL, TRUE, TRUE, 0); - if (p == NULL) - break; -# else -# ifdef FEAT_SOCKETSERVER + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN) + p = serverGetReply(srv, NULL, TRUE, TRUE, 0); +# endif +# ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET - && socket_server_read_reply(receiver, &p, -1) == FAIL) + && (ch == NULL + || socketserver_read_reply(sname, &p, -1, true) + == FAIL)) break; -# endif -# ifdef FEAT_X11 +# endif +# ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11 && serverReadReply(xterm_dpy, srv, &p, TRUE, -1) < 0) break; -# endif +# endif if (p == NULL) break; -# endif j = atoi((char *)p); vim_free(p); if (j >= 0 && j < numFiles) @@ -543,6 +542,13 @@ cmdsrv_main( done[j] = 1; } } +# ifdef FEAT_SOCKETSERVER + if (ch != NULL) + { + channel_close(ch, false); + channel_clear(ch); + } +# endif # ifdef FEAT_GUI_MSWIN Shell_NotifyIcon(NIM_DELETE, &ni); # endif @@ -551,25 +557,24 @@ cmdsrv_main( } else if (STRICMP(argv[i], "--remote-expr") == 0) { + int status = OK; + if (i == *argc - 1) mainerr_arg_missing((char_u *)argv[i]); # ifdef MSWIN // Win32 always works? - if (serverSendToVim(sname, (char_u *)argv[i + 1], + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN + && serverSendToVim(sname, (char_u *)argv[i + 1], &res, NULL, 1, 0, FALSE) < 0) -# else -# ifdef FEAT_SOCKETSERVER - if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) - { - if (!socket_server_valid()) - mch_errmsg(_("Socket server not online:" - "Send expression failed")); - else if (socket_server_send(sname, (char_u *)argv[i + 1], - &res, NULL, 1, 0, FALSE) < 0) - goto expr_fail; - } -# endif -# ifdef FEAT_X11 + status = FAIL; +# endif +# ifdef FEAT_SOCKETSERVER + if (clientserver_method == CLIENTSERVER_METHOD_SOCKET + && socketserver_send(sname, (char_u *)argv[i + 1], + &res, 1, 0, false, NULL) < 0) + status = FAIL; +# endif +# ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11) { if (xterm_dpy == NULL) @@ -577,15 +582,11 @@ cmdsrv_main( else if (serverSendToVim(xterm_dpy, sname, (char_u *)argv[i + 1], &res, NULL, 1, 0, 1, FALSE) < 0) - goto expr_fail; + status = FAIL; } -# endif - if (FALSE) # endif + if (status == FAIL) { -# if !defined(MSWIN) -expr_fail: -# endif if (res != NULL && *res != NUL) { // Output error from remote @@ -598,28 +599,22 @@ cmdsrv_main( else if (STRICMP(argv[i], "--serverlist") == 0) { # ifdef MSWIN - // Win32 always works? - res = serverGetVimNames(); -# else -# ifdef FEAT_SOCKETSERVER + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN) + // Win32 always works? + res = serverGetVimNames(); +# endif +# ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) - { - int was_init = socket_server_valid(); - - // Don't want to add ourselves to the list. So shutdown the - // server before listing then startup back again. - socket_server_uninit(); - res = socket_server_list_sockets(); - - if (was_init) - socket_server_init(NULL); - } +# ifdef MSWIN + res = vim_strsave((char_u *)""); +# else + res = socketserver_list(); # endif -# ifdef FEAT_X11 +# endif +# ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11 && xterm_dpy != NULL) res = serverGetVimNames(xterm_dpy); -# endif # endif if (did_emsg) mch_errmsg("\n"); @@ -648,9 +643,6 @@ cmdsrv_main( if (didone) { -# ifdef FEAT_SOCKETSERVER - socket_server_uninit(); -# endif display_errors(); // display any collected messages exit(exiterr); // Mission accomplished - get out } @@ -807,12 +799,12 @@ serverMakeName(char_u *arg, char *cmd) if (arg != NULL && *arg != NUL) { # ifdef FEAT_SOCKETSERVER - // If we are using a socket server, we want to preserve the original - // name if it is a path, else uppercase it if its just a generic name. + // When using socketserver backend, do not change the name if path or + // channel address. if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) { - if (arg[0] == '/' || STRNCMP(arg, "./", 2) == 0 || - STRNCMP(arg, "../", 3) == 0) + if (STRNICMP(arg, "channel:", 8) == 0 || arg[0] == '/' || + STRNCMP(arg, "./", 2) == 0 || STRNCMP(arg, "../", 3) == 0) p = vim_strsave(arg); else p = vim_strsave_up(arg); @@ -853,6 +845,8 @@ make_connection(void) static int check_connection(void) { + if (clientserver_method != CLIENTSERVER_METHOD_X11) + return OK; make_connection(); if (X_DISPLAY == NULL) { @@ -878,10 +872,8 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr) # ifdef FEAT_X11 Window w; # endif -# ifdef FEAT_SOCKETSERVER - char_u *client = NULL; -# endif # endif + int ret = OK; if (check_restricted() || check_secure()) return; @@ -899,35 +891,29 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr) return; // type error; errmsg already given keys = tv_get_string_buf(&argvars[1], buf); # ifdef MSWIN - if (serverSendToVim(server_name, keys, &r, &w, expr, timeout, TRUE) < 0) -# else -# ifdef FEAT_SOCKETSERVER - if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) - if (socket_server_send(server_name, keys, &r, &client, expr, - timeout * 1000, TRUE) < 0) - goto fail; -# endif -# ifdef FEAT_X11 + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN + && serverSendToVim(server_name, keys, &r, &w, expr, + timeout, TRUE) < 0) + ret = FAIL; +# endif +# ifdef FEAT_SOCKETSERVER + if (clientserver_method == CLIENTSERVER_METHOD_SOCKET + && socketserver_send(server_name, keys, &r, expr, + timeout * 1000, true, NULL) < 0) + ret = FAIL; +# endif +# ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11) if (serverSendToVim(X_DISPLAY, server_name, keys, &r, &w, expr, timeout, 0, TRUE) < 0) - goto fail; -# endif + ret = FAIL; # endif -# if !defined(MSWIN) - if (FALSE) - { -fail: -# else + if (ret == FAIL) { -# endif if (r != NULL) { emsg((char *)r); // sending worked but evaluation failed vim_free(r); -# ifdef FEAT_SOCKETSERVER - vim_free(client); -# endif } else semsg(_(e_unable_to_send_to_str), server_name); @@ -939,40 +925,42 @@ remote_common(typval_T *argvars, typval_T *rettv, int expr) if (argvars[2].v_type != VAR_UNKNOWN) { dictitem_T v; -# if defined(FEAT_SOCKETSERVER) - struct sockaddr_un addr; - char_u str[sizeof(addr.sun_path)]; -# else - char_u str[30]; +# if defined(MSWIN) || defined(FEAT_X11) + char_u sbuf[30]; # endif + char_u *str = NULL; char_u *idvar; idvar = tv_get_string_chk(&argvars[2]); if (idvar != NULL && *idvar != NUL) { - str[0] = NUL; # ifdef MSWIN - sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w); -# else -# ifdef FEAT_X11 + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN) + { + sprintf((char *)sbuf, PRINTF_HEX_LONG_U, (long_u)w); + str = sbuf; + } +# endif +# ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11) - sprintf((char *)str, PRINTF_HEX_LONG_U, (long_u)w); -# endif -# ifdef FEAT_SOCKETSERVER + { + sprintf((char *)sbuf, PRINTF_HEX_LONG_U, (long_u)w); + str = sbuf; + } +# endif +# ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) - vim_snprintf((char *)str, sizeof(addr.sun_path), - "%s", client); -# endif + str = server_name; # endif + if (str == NULL) + str = (char_u *)""; + v.di_tv.v_type = VAR_STRING; v.di_tv.vval.v_string = vim_strsave(str); set_var(idvar, &v.di_tv, FALSE); vim_free(v.di_tv.vval.v_string); } } -# ifdef FEAT_SOCKETSERVER - vim_free(client); -# endif } #endif @@ -1054,20 +1042,23 @@ f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv) if (serverid == NULL) return; // type error; errmsg already given # ifdef MSWIN - sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n); - if (n == 0) - rettv->vval.v_number = -1; - else + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN) { - s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0); - rettv->vval.v_number = (s != NULL); + sscanf((const char *)serverid, SCANF_HEX_LONG_U, &n); + if (n == 0) + rettv->vval.v_number = -1; + else + { + s = serverGetReply((HWND)n, FALSE, FALSE, FALSE, 0); + rettv->vval.v_number = (s != NULL); + } } -# else -# ifdef FEAT_SOCKETSERVER +# endif +# ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) - rettv->vval.v_number = socket_server_peek_reply(serverid, &s); -# endif -# ifdef FEAT_X11 + rettv->vval.v_number = socketserver_peek_reply(serverid, &s); +# endif +# ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11) { if (check_connection() == FAIL) @@ -1076,7 +1067,6 @@ f_remote_peek(typval_T *argvars UNUSED, typval_T *rettv) rettv->vval.v_number = serverPeekReply(X_DISPLAY, serverStrToWin(serverid), &s); } -# endif # endif if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0) @@ -1121,24 +1111,27 @@ f_remote_read(typval_T *argvars UNUSED, typval_T *rettv) timeout = tv_get_number(&argvars[1]); # ifdef MSWIN - sscanf((char *)serverid, SCANF_HEX_LONG_U, &n); - if (n != 0) - r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout); - if (r == NULL) - emsg(_(e_unable_to_read_server_reply)); -# else -# ifdef FEAT_SOCKETSERVER + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN) + { + sscanf((char *)serverid, SCANF_HEX_LONG_U, &n); + if (n != 0) + r = serverGetReply((HWND)n, FALSE, TRUE, TRUE, timeout); + if (r == NULL) + emsg(_(e_unable_to_read_server_reply)); + } +# endif +# ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET && - socket_server_read_reply(serverid, &r, timeout * 1000) == FAIL) + socketserver_read_reply(serverid, &r, timeout * 1000, false) + == FAIL) emsg(_(e_unable_to_read_server_reply)); -# endif -# ifdef FEAT_X11 +# endif +# ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11 && (check_connection() == FAIL || serverReadReply(X_DISPLAY, serverStrToWin(serverid), &r, FALSE, timeout) < 0)) emsg(_(e_unable_to_read_server_reply)); -# endif # endif } # endif @@ -1184,17 +1177,17 @@ f_remote_startserver(typval_T *argvars UNUSED, typval_T *rettv UNUSED) char_u *server = tv_get_string_chk(&argvars[0]); # ifdef MSWIN - serverSetName(server); -# else -# ifdef FEAT_SOCKETSERVER + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN) + serverSetName(server); +# endif +# ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) - socket_server_init(server); -# endif -# ifdef FEAT_X11 + socketserver_start(server, true); +# endif +# ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11 && check_connection() == OK) serverRegisterName(X_DISPLAY, server); -# endif # endif # else @@ -1209,6 +1202,7 @@ f_server2client(typval_T *argvars UNUSED, typval_T *rettv) char_u buf[NUMBUFLEN]; char_u *server; char_u *reply; + int ret = OK; rettv->vval.v_number = -1; if (check_restricted() || check_secure()) @@ -1226,28 +1220,22 @@ f_server2client(typval_T *argvars UNUSED, typval_T *rettv) # ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET && - socket_server_send_reply(server, reply) == FAIL) - goto fail; + socketserver_send_reply(server, reply) == FAIL) + ret = FAIL; # endif - # ifdef FEAT_X11 - if (clientserver_method == CLIENTSERVER_METHOD_X11 && - check_connection() == FAIL) - return; - if (clientserver_method == CLIENTSERVER_METHOD_X11 && serverSendReply(server, reply) < 0) + ret = FAIL; # endif # ifdef MSWIN - if (serverSendReply(server, reply) < 0) -# endif -# if defined(FEAT_SOCKETSERVER) && !defined(FEAT_X11) && !defined(MSWIN) - if (FALSE) + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN + && serverSendReply(server, reply) < 0) + ret = FAIL; # endif + + if (ret == FAIL) { -# ifdef FEAT_SOCKETSERVER -fail: -# endif emsg(_(e_unable_to_send_to_client)); return; } @@ -1264,20 +1252,24 @@ f_serverlist(typval_T *argvars UNUSED, typval_T *rettv) # ifdef FEAT_CLIENTSERVER # ifdef MSWIN - r = serverGetVimNames(); -# else -# ifdef FEAT_SOCKETSERVER + if (clientserver_method == CLIENTSERVER_METHOD_MSWIN) + r = serverGetVimNames(); +# endif +# ifdef FEAT_SOCKETSERVER if (clientserver_method == CLIENTSERVER_METHOD_SOCKET) - r = socket_server_list_sockets(); +# ifdef MSWIN + r = vim_strsave((char_u *)""); +# else + r = socketserver_list(); # endif -# ifdef FEAT_X11 +# endif +# ifdef FEAT_X11 if (clientserver_method == CLIENTSERVER_METHOD_X11) { make_connection(); if (X_DISPLAY != NULL) r = serverGetVimNames(X_DISPLAY); } -# endif # endif # endif rettv->v_type = VAR_STRING; diff --git a/src/configure.ac b/src/configure.ac index 6f6590f6da..27a8943bda 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -2363,22 +2363,6 @@ if test "$enable_autoservername" = "yes"; then AC_DEFINE(FEAT_AUTOSERVERNAME) fi -AC_MSG_CHECKING(--enable-socketserver argument) -AC_ARG_ENABLE(socketserver, - [ --enable-socketserver Use sockets for clientserver communication.], - [enable_socketserver=$enableval], - AS_IF([test "x$features" = xtiny], - [enable_socketserver=no_msg - AC_MSG_RESULT([cannot use socketserver with tiny features])], - [enable_socketserver=yes])) - -if test "$enable_socketserver" = "yes"; then - AC_DEFINE(WANT_SOCKETSERVER) - AC_MSG_RESULT([yes]) -elif test "$enable_socketserver" = "no"; then - AC_MSG_RESULT([no]) -fi - AC_MSG_CHECKING(--enable-multibyte argument) AC_ARG_ENABLE(multibyte, [ --enable-multibyte Include multibyte editing support.], , diff --git a/src/errors.h b/src/errors.h index 4e261c9232..0013a43ee8 100644 --- a/src/errors.h +++ b/src/errors.h @@ -1139,7 +1139,7 @@ EXTERN char e_cant_read_postscript_resource_file_str[] INIT(= N_("E457: Can't read PostScript resource file \"%s\"")); #endif // E458 unused -#if defined(UNIX) || defined(FEAT_SESSION) +#if defined(UNIX) || defined(FEAT_SESSION) || defined(FEAT_SOCKETSERVER) EXTERN char e_cannot_go_back_to_previous_directory[] INIT(= N_("E459: Cannot go back to previous directory")); #endif @@ -3777,16 +3777,14 @@ EXTERN char e_diff_anchors_with_hidden_windows[] INIT(= N_("E1562: Diff anchors cannot be used with hidden diff windows")); #endif #ifdef FEAT_SOCKETSERVER -EXTERN char e_socket_path_too_big[] - INIT(= N_("E1563: Socket path is too big")); EXTERN char e_socket_name_no_slashes[] - INIT(= N_("E1564: Socket name cannot have slashes in it without being a path")); + INIT(= N_("E1564: Socket name '%s' cannot have slashes in it without being a path")); EXTERN char e_socket_server_not_online[] INIT(= N_("E1565: Socket server is not online, call remote_startserver() first")); EXTERN char e_socket_server_failed_connecting[] - INIT(= N_("E1566: Failed connecting to socket %s: %s")); -EXTERN char e_socket_server_unavailable[] - INIT(= N_("E1567: Cannot start socket server, socket path is unavailable")); + INIT(= N_("E1566: Failed connecting to socket '%s'")); +EXTERN char e_socket_server_version_mismatch[] + INIT(= N_("E1567: Socket server protocol version mismatch, check what Vim version you are using")); #endif EXTERN char e_osc_response_timed_out[] INIT(= N_("E1568: OSC command response timed out: %.*s")); diff --git a/src/feature.h b/src/feature.h index 115db703d2..46508f74bb 100644 --- a/src/feature.h +++ b/src/feature.h @@ -953,9 +953,16 @@ #endif /* - * +socketserver Use UNIX domain sockets for clientserver communication + * The +channel feature requires +eval. + */ +#if !defined(FEAT_EVAL) && defined(FEAT_JOB_CHANNEL) +# undef FEAT_JOB_CHANNEL +#endif + +/* + * +socketserver Use channels for clientserver communication */ -#if defined(UNIX) && defined(WANT_SOCKETSERVER) +#if (defined(UNIX) || defined(MSWIN)) && defined(FEAT_JOB_CHANNEL) # define FEAT_SOCKETSERVER #endif @@ -966,6 +973,9 @@ #if (defined(MSWIN) || defined(FEAT_XCLIPBOARD) || defined(FEAT_SOCKETSERVER)) \ && defined(FEAT_EVAL) # define FEAT_CLIENTSERVER +# if defined(FEAT_SOCKETSERVER) && (defined(FEAT_XCLIPBOARD) || defined(MSWIN)) +# define FEAT_CLIENTSERVER_BACKENDS +# endif #endif /* @@ -1053,14 +1063,6 @@ * +tgetent */ - -/* - * The +channel feature requires +eval. - */ -#if !defined(FEAT_EVAL) && defined(FEAT_JOB_CHANNEL) -# undef FEAT_JOB_CHANNEL -#endif - /* * The Netbeans feature requires +eval and +job_channel */ diff --git a/src/gc.c b/src/gc.c index f15d1dffe0..f1bd6b7245 100644 --- a/src/gc.c +++ b/src/gc.c @@ -208,6 +208,9 @@ garbage_collect(int testing) #ifdef FEAT_NETBEANS_INTG abort = abort || set_ref_in_nb_channel(copyID); #endif +#ifdef FEAT_SOCKETSERVER + abort = abort || set_ref_in_socketserver_channel(copyID); +#endif #ifdef FEAT_TIMERS abort = abort || set_ref_in_timer(copyID); diff --git a/src/getchar.c b/src/getchar.c index b995698128..47ba62ad6c 100644 --- a/src/getchar.c +++ b/src/getchar.c @@ -2650,6 +2650,9 @@ parse_queued_messages(void) // Process the queued netbeans messages. netbeans_parse_messages(); # endif +# ifdef FEAT_SOCKETSERVER + socketserver_parse_messages(); +# endif # ifdef FEAT_JOB_CHANNEL // Write any buffer lines still to be written. channel_write_any_lines(); diff --git a/src/globals.h b/src/globals.h index 2fdc944648..0a4a09d65d 100644 --- a/src/globals.h +++ b/src/globals.h @@ -2122,25 +2122,28 @@ EXTERN int wayland_no_connect INIT(= FALSE); #endif -#if defined(FEAT_CLIENTSERVER) && !defined(MSWIN) +#if defined(FEAT_CLIENTSERVER) // Backend for clientserver functionality typedef enum { CLIENTSERVER_METHOD_NONE, +# ifdef FEAT_X11 CLIENTSERVER_METHOD_X11, +# endif +# ifdef MSWIN + CLIENTSERVER_METHOD_MSWIN, +# endif +# ifdef FEAT_SOCKETSERVER CLIENTSERVER_METHOD_SOCKET +# endif } clientserver_method_T; -// Default to X11 if compiled with support for it, else use socket server. -# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER) EXTERN clientserver_method_T clientserver_method -# else -// Since we aren't going to be changing clientserver_method, make it constant to -// allow compiler optimizations. -EXTERN const clientserver_method_T clientserver_method -# endif + # ifdef FEAT_X11 INIT(= CLIENTSERVER_METHOD_X11); +# elif defined(MSWIN) +INIT(= CLIENTSERVER_METHOD_MSWIN); # elif defined(FEAT_SOCKETSERVER) INIT(= CLIENTSERVER_METHOD_SOCKET); # else diff --git a/src/gui.c b/src/gui.c index 1448c86bdb..b04f132163 100644 --- a/src/gui.c +++ b/src/gui.c @@ -158,12 +158,6 @@ gui_start(char_u *arg UNUSED) choose_clipmethod(); #endif -#if defined(FEAT_SOCKETSERVER) && defined(FEAT_GUI_GTK) - // Install socket server listening socket if we are running it - if (socket_server_valid()) - gui_gtk_init_socket_server(); -#endif - #ifdef FEAT_GUI_MSWIN // Enable fullscreen mode if (vim_strchr(p_go, GO_FULLSCREEN) != NULL) diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index 2dab629394..af452c8468 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -80,13 +80,6 @@ extern void bonobo_dock_item_set_behavior(BonoboDockItem *dock_item, BonoboDockI # include #endif -#ifdef FEAT_SOCKETSERVER -# include - -// Used to track the source for the listening socket -static uint socket_server_source_id = 0; -#endif - /* * Easy-to-use macro for multihead support. */ @@ -2690,54 +2683,6 @@ global_event_filter(GdkXEvent *xev, } #endif // !USE_GNOME_SESSION -#if defined(FEAT_SOCKETSERVER) - -/* - * Callback for new events from the socket server listening socket - */ - static int -socket_server_poll_in(int fd UNUSED, GIOCondition cond, void *user_data UNUSED) -{ - if (cond & G_IO_IN) - socket_server_accept_client(); - else if (cond & (G_IO_ERR | G_IO_HUP)) - { - socket_server_uninit(); - return FALSE; - } - - return TRUE; -} - -/* - * Initialize socket server for use in the GUI (does not actually initialize the - * socket server, only attaches a source). - */ - void -gui_gtk_init_socket_server(void) -{ - if (socket_server_source_id > 0) - return; - // Register source for file descriptor to global default context - socket_server_source_id = g_unix_fd_add(socket_server_get_fd(), - G_IO_IN | G_IO_ERR | G_IO_HUP, socket_server_poll_in, NULL); -} - -/* - * Remove the source for the socket server listening socket. - */ - void -gui_gtk_uninit_socket_server(void) -{ - if (socket_server_source_id > 0) - { - g_source_remove(socket_server_source_id); - socket_server_source_id = 0; - } -} - -#endif - static GdkPixbuf * pixbuf_new_from_png_data(const unsigned char *data, unsigned int len) { diff --git a/src/json.c b/src/json.c index a3e58043d8..b9cbc0d594 100644 --- a/src/json.c +++ b/src/json.c @@ -51,6 +51,8 @@ json_encode(typval_T *val, int options) // Store bytes in the growarray. ga_init2(&ga, 1, 4000); json_encode_gap(&ga, val, options); + if (options & JSON_NL) + ga_append(&ga, NL); ga_append(&ga, NUL); return ga.ga_data; } diff --git a/src/main.c b/src/main.c index cac5db145e..d2fe22ebe5 100644 --- a/src/main.c +++ b/src/main.c @@ -1844,6 +1844,9 @@ getout(int exitval) #ifdef FEAT_NETBEANS_INTG netbeans_end(); #endif +#ifdef FEAT_SOCKETSERVER + socketserver_stop(); +#endif #ifdef FEAT_CSCOPE cs_end(); #endif @@ -1910,7 +1913,7 @@ early_arg_scan(mparm_T *parmp UNUSED) gui.dofork = false; # endif } -# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER) +# ifdef FEAT_CLIENTSERVER_BACKENDS else if (STRNICMP(argv[i], "--clientserver", 14) == 0) { char_u *arg; @@ -1920,8 +1923,14 @@ early_arg_scan(mparm_T *parmp UNUSED) if (STRICMP(arg, "socket") == 0) clientserver_method = CLIENTSERVER_METHOD_SOCKET; +# ifdef FEAT_X11 else if (STRICMP(arg, "x11") == 0) clientserver_method = CLIENTSERVER_METHOD_X11; +# endif +# ifdef MSWIN + else if (STRICMP(arg, "mswin") == 0) + clientserver_method = CLIENTSERVER_METHOD_MSWIN; +# endif else mainerr(ME_UNKNOWN_OPTION, arg); } @@ -2247,9 +2256,9 @@ command_line_scan(mparm_T *parmp) ; // already processed -- no arg else if (STRNICMP(argv[0] + argv_idx, "servername", 10) == 0 || STRNICMP(argv[0] + argv_idx, "serversend", 10) == 0 -# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER) + // Don't put this under FEAT_CLIENTSERVER_BACKENDS, just + // let it be ignored. Makes tests less complicated || STRNICMP(argv[0] + argv_idx, "clientserver", 12) == 0 -# endif ) { // already processed -- snatch the following arg @@ -3747,8 +3756,8 @@ usage(void) main_msg(_("-Y\t\t\tDo not connect to Wayland compositor")); # endif # ifdef FEAT_CLIENTSERVER -# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER) - main_msg(_("--clientserver Backend for clientserver communication")); +# ifdef FEAT_CLIENTSERVER_BACKENDS + main_msg(_("--clientserver Backend for clientserver communication")); # endif main_msg(_("--remote \tEdit in a Vim server if possible")); main_msg(_("--remote-silent Same, don't complain if there is no server")); diff --git a/src/os_unix.c b/src/os_unix.c index 5cfc422b7a..ab58dda7eb 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -147,123 +147,6 @@ Window x11_window = 0; Display *x11_display = NULL; #endif -#if defined(FEAT_SOCKETSERVER) -# include -# include - -# define SOCKET_SERVER_MAX_BACKLOG 5 -# define SOCKET_SERVER_MAX_CMD_SIZE 16384 -# define SOCKET_SERVER_MAX_MSG 6 - -static int socket_server_fd = -1; -static char_u *socket_server_path = NULL; - -typedef enum { - SS_MSG_TYPE_ENCODING = 'e', // Encoding of message. - SS_MSG_TYPE_STRING = 'c', // Script to execute or reply string. - SS_MSG_TYPE_SERIAL = 's', // Serial of pending command - SS_MSG_TYPE_CODE = 'r', // Result code for an expression sent - SS_MSG_TYPE_SENDER = 'd' // Location of socket for the client that - // sent the command. -} ss_msg_type_T; - -typedef enum { - SS_CMD_TYPE_EXPR = 'E', // An expression - SS_CMD_TYPE_KEYSTROKES = 'K', // Series of keystrokes - SS_CMD_TYPE_REPLY = 'R', // Reply from an expression - SS_CMD_TYPE_NOTIFY = 'N', // A notification - SS_CMD_TYPE_ALIVE = 'A', // Check if server is still responsive -} ss_cmd_type_T; - -// Represents a message in a command. A command can contain multiple messages. -// Each message starts with a single byte representing the type, then a uint32 -// representing the length of the contents, and then the actual contents. -// Everything is in native byte order. -// -// While contents may contain NULL characters, such as when it is a number, it -// is always NULL terminated. Note that the NULL terminator does not count in -// the length. -typedef struct { - char_u msg_type; // Type of message - uint32_t msg_len; // Total length of contents - char_u *msg_contents; // Actual contents of message -} ss_msg_T; - -// Represents a command sent over a socket. Each socket starts with a byte -// representing the type, then a uint32 representing the number of messages, -// then a uint32 representing the total size of the messages in bytes, and then -// the actual messages. Everything is in native byte order. -typedef struct { - char_u cmd_type; // Type of command - uint32_t cmd_num; // Number of messages - uint32_t cmd_len; // Combined size of all - // messages - ss_msg_T cmd_msgs[SOCKET_SERVER_MAX_MSG]; // Array of messages -} ss_cmd_T; - -# define SS_CMD_INFO_SIZE (sizeof(char_u) + (sizeof(uint32_t) * 2)) -# define SS_MSG_INFO_SIZE (sizeof(char_u) + sizeof(uint32_t)) - -// Represents a pending reply from a command sent to a Vim server. When a -// command is sent out, we generate unique serial number with it. When we -// receive any reply, we check which pending command has a matching serial -// number, and is therefore the reply for that pending command. -// -// The reason we just don't use the existing fd created by the connect() call, -// and communicate using that, is that it can't handle recursive calls, ex: -// call remote_expr('B', 'remote_expr("A", "")') -// -// This idea is taken from the existing X server functionality -typedef struct ss_pending_cmd_S { - uint32_t serial; // Serial number expected in result - char_u code; // Result code, can be 0 or -1. - char_u *result; // Result of command - - struct ss_pending_cmd_S *next; // Next in list -} ss_pending_cmd_T; - -static ss_pending_cmd_T *ss_pending_cmds; - -// Serial is always greater than zero -static uint32_t ss_serial = 0; - -// Represents a reply from a server2client call. Each client that calls a -// server2client call to us has its own ss_reply_T. Each time a client sends -// data using server2client, Vim creates a ss_reply_T if it doesn't exist and -// adds the string to the array. When remote_read is called, the server id is -// used to find the specific ss_reply_T, and a single string is popped from the -// array. -// -// This idea is taken from the existing X server functionality -typedef struct { - char_u *sender; - garray_T strings; -} ss_reply_T; - -static garray_T ss_replies; - -static char_u *socket_server_get_path_from_name(char_u *name); -static int socket_server_connect(char_u *name, char_u **path, int silent); -static void socket_server_init_pending_cmd(ss_pending_cmd_T *pending); -static void socket_server_pop_pending_cmd(ss_pending_cmd_T *pending); -static void socket_server_init_cmd(ss_cmd_T *cmd, ss_cmd_type_T type); -static int socket_server_append_msg(ss_cmd_T *cmd, char_u type, - char_u *contents, int len); -static void socket_server_free_cmd(ss_cmd_T *cmd); -static char_u *socket_server_encode_cmd(ss_cmd_T *cmd, size_t *sz); -static int socket_server_decode_cmd(ss_cmd_T *cmd, int socket_fd, int timeout); -static int socket_server_write(int sock_fd, char_u *data, size_t sz, - int timeout); -static ss_reply_T *socket_server_get_reply(char_u *sender, int *index); -static ss_reply_T *socket_server_add_reply(char_u *sender); -static void socket_server_remove_reply(char_u *sender); -static void socket_server_exec_cmd(ss_cmd_T *cmd, int fd); -static int socket_server_dispatch(int timeout); -static int socket_server_check_alive(char_u *name); -static int socket_server_name_is_valid(char_u *name); - -#endif // FEAT_SOCKETSERVER - static int ignore_sigtstp = FALSE; static int get_x11_title(int); @@ -3782,10 +3665,6 @@ mch_exit(int r) x11_export_final_selection(); #endif -#ifdef FEAT_SOCKETSERVER - socket_server_uninit(); -#endif - #ifdef FEAT_GUI if (!gui.in_use) #endif @@ -6792,9 +6671,6 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted) // each channel may use in, out and err struct pollfd fds[7 + 3 * MAX_OPEN_CHANNELS]; int nfd; -# ifdef FEAT_SOCKETSERVER - int socket_server_idx = -1; -# endif # ifdef FEAT_WAYLAND_CLIPBOARD int wayland_idx = -1; # endif @@ -6821,16 +6697,6 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted) fds[0].events = POLLIN; nfd = 1; -# ifdef FEAT_SOCKETSERVER - if (socket_server_fd != -1) - { - socket_server_idx = nfd; - fds[nfd].fd = socket_server_fd; - fds[nfd].events = POLLIN; - nfd++; - } -# endif - # ifdef FEAT_WAYLAND if ((wayland_fd = wayland_prepare_read()) >= 0) { @@ -6886,19 +6752,6 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted) finished = FALSE; # endif -# ifdef FEAT_SOCKETSERVER - if (socket_server_idx >= 0) - { - if (fds[socket_server_idx].revents & POLLIN) - { - if (socket_server_accept_client() == FAIL) - socket_server_uninit(); - } - else if (fds[socket_server_idx].revents & (POLLHUP | POLLERR)) - socket_server_uninit(); - } -# endif - # ifdef FEAT_WAYLAND if (wayland_idx >= 0) wayland_poll_check(fds[wayland_idx].revents); @@ -6984,16 +6837,6 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted) # endif maxfd = fd; -# ifdef FEAT_SOCKETSERVER - if (socket_server_fd != -1) - { - FD_SET(socket_server_fd, &rfds); - - if (maxfd < socket_server_fd) - maxfd = socket_server_fd; - } -# endif - # ifdef FEAT_WAYLAND if ((wayland_fd = wayland_prepare_read()) >= 0) { @@ -7093,13 +6936,6 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted) finished = FALSE; # endif -# ifdef FEAT_SOCKETSERVER - if (ret > 0 && socket_server_fd != -1 - && FD_ISSET(socket_server_fd, &rfds) - && socket_server_accept_client() == FAIL) - socket_server_uninit(); -# endif - # ifdef FEAT_WAYLAND if (wayland_fd != -1) wayland_select_check(ret > 0 && FD_ISSET(wayland_fd, &rfds)); @@ -7161,16 +6997,9 @@ RealWaitForChar(int fd, long msec, int *check_for_gpm UNUSED, int *interrupted) if (finished || msec == 0) break; -# if defined(FEAT_CLIENTSERVER) -# ifdef FEAT_X11 +# if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) if (clientserver_method == CLIENTSERVER_METHOD_X11 && server_waiting()) break; -# endif -# ifdef FEAT_SOCKETSERVER - if (clientserver_method == CLIENTSERVER_METHOD_SOCKET && - socket_server_waiting_accept()) - break; -# endif # endif // We're going to loop around again, find out for how long @@ -9251,1565 +9080,3 @@ mch_create_anon_file(void) } return fd; } - -#if defined(FEAT_SOCKETSERVER) - -/* - * Initialize socket server called "name" (the socket filename). If "name" is a - * path (starts with a '/', './', or '../'), it is assumed to be the path to - * the desired socket. If the socket path is already taken, append an - * incrementing number to the path until we find a socket filename that can be - * used. If NULL is passed as the name, the previous socket path is used (only - * if not NULL). Returns OK on success and FAIL on failure. - */ - int -socket_server_init(char_u *name) -{ - struct sockaddr_un addr; - char_u *path; - int num_printed; - int fd; - int i = 1; - - if (socket_server_valid() || (name == NULL && socket_server_path == NULL)) - return FAIL; - if (name == NULL) - name = socket_server_path; - - path = alloc(sizeof(addr.sun_path)); - - if (path == NULL) - return FAIL; - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - - if (fd == -1) - { - vim_free(path); - return FAIL; - } - - addr.sun_family = AF_UNIX; - - // If name is not a path, find a common directory to place the - // socket. - if (name[0] == '/' || STRNCMP(name, "./", 2) == 0 || - STRNCMP(name, "../", 3) == 0) - num_printed = - vim_snprintf((char *)path, sizeof(addr.sun_path), "%s", name); - else - { - const char_u *dir; - char_u *buf; - - // Check if there are slashes in the name - if (vim_strchr(name, '/') != NULL) - { - emsg(_(e_socket_name_no_slashes)); - goto fail; - } - - dir = mch_getenv("XDG_RUNTIME_DIR"); - - if (dir == NULL) - { - // Use $TMPDIR or /tmp if $XDG_RUNTIME_DIR is not set. - const char_u *tmpdir = mch_getenv("TMPDIR"); - size_t sz; - - if (tmpdir != NULL) - dir = tmpdir; - else - dir = (char_u *)"/tmp"; - - sz = STRLEN(dir) + 25; - buf = alloc(sz); - - if (buf == NULL) - goto fail; - - vim_snprintf((char *)buf, sz, "%s/vim-%lu", dir, - (unsigned long int)getuid()); - } - else - { - buf = alloc(STRLEN(dir) + STRLEN("vim") + 2); - - if (buf == NULL) - goto fail; - - sprintf((char *)buf, "%s/vim", dir); - } - - // Always set directory permissions to 0700 for security - if (vim_mkdir(buf, 0700) == -1 && errno != EEXIST) - { - semsg(_("Failed creating socket directory: %s"), strerror(errno)); - vim_free(buf); - goto fail; - } - - num_printed = vim_snprintf((char *)path, sizeof(addr.sun_path), - "%s/%s", buf, name); - - vim_free(buf); - } - - // Check if path was too big - if ((size_t)num_printed >= sizeof(addr.sun_path)) - { - emsg(_(e_socket_path_too_big)); - goto fail; - } - - vim_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", path); - - // Bind to a suitable path/address - while (i < 1000) - { - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) - == -1) - { - int fd2; - - if (errno != EADDRINUSE) - { - emsg(_(e_socket_server_unavailable)); - goto fail; - } - - // If the socket is dead, remove it and try again - fd2 = socket_server_connect((char_u *)addr.sun_path, NULL, TRUE); - - if (fd2 == -1) - { - mch_remove(addr.sun_path); - continue; - } - else - close(fd2); - } - else - break; - - num_printed = vim_snprintf(addr.sun_path, sizeof(addr.sun_path), - "%s%d", path, i); - - if ((size_t)num_printed >= sizeof(addr.sun_path)) - { - // Address too big - emsg(_(e_socket_path_too_big)); - goto fail; - } - - i++; - } - - if (i >= 1000) - { - emsg(_(e_socket_server_unavailable)); - goto fail; - } - - // Start listening for connections - if (listen(fd, SOCKET_SERVER_MAX_BACKLOG) == -1) - goto fail; - - // Set global path and vvar to the absolute path - if ((socket_server_path = alloc(MAXPATHL)) == NULL) - goto fail; - - socket_server_path[0] = NUL; - - if (mch_FullName((char_u *)addr.sun_path, socket_server_path, - MAXPATHL, FALSE) == FAIL) - { - vim_free(socket_server_path); - goto fail; - } - - serverName = vim_strsave(socket_server_path); -# ifdef FEAT_EVAL - set_vim_var_string(VV_SEND_SERVER, serverName, -1); -# endif - - socket_server_fd = fd; - -# ifdef FEAT_GUI_GTK - if (gui.in_use) - // Initialize source for GUI if we are using it - gui_gtk_init_socket_server(); -# endif - - vim_free(path); - return OK; -fail: - close(fd); - vim_free(path); - socket_server_uninit(); - return FAIL; -} - - void -socket_server_uninit(void) -{ - if (socket_server_fd != -1) - { - close(socket_server_fd); - socket_server_fd = -1; - } - - if (socket_server_path != NULL) - { - mch_remove(socket_server_path); - vim_free(socket_server_path); - socket_server_path = NULL; - } -# ifdef FEAT_GUI_GTK - if (gui.in_use) - gui_gtk_uninit_socket_server(); -# endif -} - -/* - * List available sockets that can be connected to, only in common directories - * that Vim knows about. Vim instances with custom socket paths will not be - * detected. Returns a newline separated string on success and NULL on failure. - */ - char_u * -socket_server_list_sockets(void) -{ - garray_T str; - string_T buf; - string_T path; - DIR *dirp; - struct dirent *dp; - struct sockaddr_un addr; - char_u *known_dirs[] = { - mch_getenv("XDG_RUNTIME_DIR"), - mch_getenv("TMPDIR"), - (char_u *)"/tmp" - }; - - if ((buf.string = alloc(sizeof(addr.sun_path))) == NULL) - return NULL; - if ((path.string = alloc(sizeof(addr.sun_path))) == NULL) - { - vim_free(buf.string); - return NULL; - } - buf.length = 0; - path.length = 0; - - ga_init2(&str, 1, 100); - - for (size_t i = 0 ; i < ARRAY_LENGTH(known_dirs); i++) - { - char_u *dir = known_dirs[i]; - - if (dir == NULL) - continue; - - if (STRCMP(dir, "/tmp") == 0 || - (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0)) - path.length = vim_snprintf_safelen((char *)path.string, sizeof(addr.sun_path), - "%s/vim-%lu", dir, (unsigned long int)getuid()); - else - path.length = vim_snprintf_safelen((char *)path.string, sizeof(addr.sun_path), - "%s/vim", dir); - - dirp = opendir((char *)path.string); - if (dirp == NULL) - continue; - - // Loop through directory - while ((dp = readdir(dirp)) != NULL) - { - if (STRCMP(dp->d_name, ".") == 0 || STRCMP(dp->d_name, "..") == 0) - continue; - - buf.length = vim_snprintf_safelen((char *)buf.string, sizeof(addr.sun_path), - "%s/%s", path.string, dp->d_name); - - // Don't want to send to ourselves, but we do want to list our - // server name (if we are a server). - if (socket_server_path == NULL - || STRCMP(socket_server_path, buf.string) != 0) - { - // Try sending an ALIVE command. This is more assuring than a - // simple connect, and *also seems to make tests less flaky*. - // - // We could also use a lock file which may be better, but this - // has worked fine so far... - 64bitman - if (!socket_server_check_alive(buf.string)) - continue; - } - - ga_concat_len(&str, (char_u *)dp->d_name, buf.length - (path.length + 1)); - ga_append(&str, '\n'); - } - - closedir(dirp); - - break; - } - - vim_free(path.string); - vim_free(buf.string); - - ga_append(&str, NUL); - - return str.ga_data; -} - -/* - * Called when the server has received a new command. If so, parse it and do the - * stuff it says, and possibly send back a reply. Returns OK if client was - * accepted, else FAIL. - */ - int -socket_server_accept_client(void) -{ - int fd = accept(socket_server_fd, NULL, NULL); - ss_cmd_T cmd; - - if (fd == -1) - return FAIL; - - if (socket_server_decode_cmd(&cmd, fd, 1000) == FAIL) - goto exit; - -# ifdef FEAT_EVAL - ch_log(NULL, "accepted new client on socket %s", socket_server_path); -# endif - - socket_server_exec_cmd(&cmd, fd); - socket_server_free_cmd(&cmd); - -exit: - close(fd); - return OK; -} - -/* - * Check if socket server is able to be used - */ - int -socket_server_valid(void) -{ - return socket_server_fd != -1 && socket_server_path != NULL; -} - -/* - * If "name" is a pathless name such as "VIM", search known directories for the - * socket named "name", and return the alloc'ed path to it. If "name" starts - * with a '/', './' or '../', then a copy of "name" is returned. Returns NULL - * on failure or if no socket was found. - */ - static char_u * -socket_server_get_path_from_name(char_u *name) -{ - char_u *buf; - stat_T s; - const char_u *known_dirs[] = { - mch_getenv("XDG_RUNTIME_DIR"), - mch_getenv("TMPDIR"), - (char_u *)"/tmp" - }; - - if (name == NULL) - return NULL; - - // Ignore if name is a path - if (name[0] == '/' || STRNCMP(name, "./", 2) == 0 || - STRNCMP(name, "../", 3) == 0) - return vim_strsave(name); - - buf = alloc(MAXPATHL); - - if (buf == NULL) - return NULL; - - for (size_t i = 0; i < ARRAY_LENGTH(known_dirs); i++) - { - const char_u *dir = known_dirs[i]; - - if (dir == NULL) - continue; - else if (STRCMP(dir, "/tmp") == 0 || - (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0)) - vim_snprintf((char *)buf, MAXPATHL, "%s/vim-%lu/%s", dir, - (unsigned long int)getuid(), name); - else - vim_snprintf((char *)buf, MAXPATHL, "%s/vim/%s", dir, name); - - if (mch_stat((char *)buf,&s) == 0 && S_ISSOCK(s.st_mode)) - { - if (STRCMP(buf, socket_server_path) == 0) - // Can't connect to itself - break; - return buf; - } - } - - vim_free(buf); - return NULL; -} - -/* - * Send command to socket named "name". Returns 0 for OK, -1 on error. - */ - int -socket_server_send( - char_u *name, // Socket path or a general name - char_u *str, // What to send - char_u **result, // Set to result of expr - char_u **receiver, // Full path of "name" - int is_expr, // Is it an expression or keystrokes? - int timeout, // In milliseconds - int silent) // Don't complain if socket doesn't exist -{ - ss_cmd_T cmd; - int socket_fd; - size_t sz; - char_u *final; - char_u *path; -# ifdef ELAPSED_FUNC - elapsed_T start_tv; -# endif - - if (!socket_server_valid()) - { - emsg(_(e_socket_server_not_online)); - return -1; - } - - socket_fd = socket_server_connect(name, &path, silent); - - if (socket_fd == -1) - return -1; - -# ifdef FEAT_EVAL - ch_log(NULL, "socket_server_send(%s, %s)", path, str); -# endif - - // Execute locally if target is ourselves - if (serverName != NULL && STRICMP(path, serverName) == 0) - { - vim_free(path); - close(socket_fd); - return sendToLocalVim(str, is_expr, result); - } - - socket_server_init_cmd(&cmd, - is_expr ? SS_CMD_TYPE_EXPR : SS_CMD_TYPE_KEYSTROKES); - - socket_server_append_msg(&cmd, SS_MSG_TYPE_ENCODING, p_enc, STRLEN(p_enc)); - - // Add +1 in case of empty string - socket_server_append_msg(&cmd, SS_MSG_TYPE_STRING, str, STRLEN(str) + 1); - - // Tell server who we are so it can save our socket path internally for - // later use with server2client - socket_server_append_msg(&cmd, SS_MSG_TYPE_SENDER, socket_server_path, - STRLEN(socket_server_path)); - - if (is_expr) - { - ss_serial++; - socket_server_append_msg(&cmd, SS_MSG_TYPE_SERIAL, - (char_u *)&ss_serial, sizeof(ss_serial)); - } - - final = socket_server_encode_cmd(&cmd, &sz); - - if (final == NULL || - socket_server_write(socket_fd, final, sz, 1000) == FAIL) - { - if (final != NULL) - emsg(_(e_failed_to_send_command_to_destination_program)); - - vim_free(path); - socket_server_free_cmd(&cmd); - close(socket_fd); - vim_free(final); - return -1; - } - socket_server_free_cmd(&cmd); - vim_free(final); - - - close(socket_fd); - if (!is_expr) - { - if (receiver != NULL) - *receiver = path; - else - vim_free(path); - - // Exit, we aren't waiting for a response - return 0; - } - - ss_pending_cmd_T pending; - - socket_server_init_pending_cmd(&pending); - -# ifdef ELAPSED_FUNC - ELAPSED_INIT(start_tv); -# endif - - // Wait for server to send back result - while (socket_server_dispatch(500) >= 0) - { - if (pending.result != NULL) - break; - -# ifdef ELAPSED_FUNC - if (ELAPSED_FUNC(start_tv) >= (timeout > 0 ? timeout : 1000)) - break; -# endif - } - - if (pending.result == NULL) - { - socket_server_pop_pending_cmd(&pending); - vim_free(path); - return -1; - } - - if (result != NULL) - *result = pending.result; - else - vim_free(pending.result); - - if (receiver != NULL) - *receiver = path; - else - vim_free(path); - - socket_server_pop_pending_cmd(&pending); - - return pending.code == 0 ? 0 : -1; -} - -/* - * Wait for replies from "client" and place result in "str". Returns OK on - * success and FAIL on failure. Timeout is in milliseconds - */ - int -socket_server_read_reply(char_u *client, char_u **str, int timeout UNUSED) -{ - ss_reply_T *reply = NULL; -# ifdef ELAPSED_FUNC - elapsed_T start_tv; -# endif - - if (!socket_server_name_is_valid(client)) - return -1; - - if (!socket_server_valid()) - return -1; - -# ifdef ELAPSED_FUNC - if (timeout > 0) - ELAPSED_INIT(start_tv); -# endif - - // Try seeing if there already is a reply in the queue - goto get_reply; - - while (socket_server_dispatch(500) >= 0) - { - int fd; - -# ifdef ELAPSED_FUNC - if (timeout > 0 && ELAPSED_FUNC(start_tv) >= timeout) - break; -# endif - -get_reply: - reply = socket_server_get_reply(client, NULL); - - if (reply != NULL) - break; - - // Check if sender is down by connecting to it as a test. A simple - // connect will do. - fd = socket_server_connect(client, NULL, TRUE); - - if (fd == -1) - return FAIL; - else - close(fd); - } - - if (reply == NULL || reply->strings.ga_data == NULL || - reply->strings.ga_len <= 0) - { - return FAIL; - } - - // Consume the string - *str = ((char_u **)reply->strings.ga_data)[0]; - - for (int i = 1; i < reply->strings.ga_len; i++) - { - ((char_u **)reply->strings.ga_data)[i - 1] = - ((char_u **)reply->strings.ga_data)[i]; - } - reply->strings.ga_len--; - - if (reply->strings.ga_len < 1) - // Last string removed, remove the reply - socket_server_remove_reply(client); - - - return OK; -} - -/* - * Check for any replies for "sender". Returns 1 if there is and places the - * reply in "str" without consuming it. Returns 0 if otherwise and -1 on - * error. - */ - int -socket_server_peek_reply(char_u *sender, char_u **str) -{ - ss_reply_T *reply; - - if (!socket_server_name_is_valid(sender)) - return -1; - - if (!socket_server_valid()) - return 0; - - reply = socket_server_get_reply(sender, NULL); - - if (reply != NULL && reply->strings.ga_len > 0) - { - if (str != NULL) - *str = ((char_u **)reply->strings.ga_data)[0]; - return 1; - } - - return 0; -} - -/* - * Send a string to "client" as a reply (notification). Returns OK on success - * and FAIL on failure. - */ - int -socket_server_send_reply(char_u *client, char_u *str) -{ - int socket_fd; - ss_cmd_T cmd; - size_t sz; - char_u *final; - - if (!socket_server_name_is_valid(client)) - return FAIL; - - if (!socket_server_valid()) - { - emsg(_(e_socket_server_not_online)); - return FAIL; - } - - socket_fd = socket_server_connect(client, NULL, TRUE); - - if (socket_fd == -1) - return FAIL; - - socket_server_init_cmd(&cmd, SS_CMD_TYPE_NOTIFY); - - socket_server_append_msg(&cmd, SS_MSG_TYPE_ENCODING, p_enc, STRLEN(p_enc)); - socket_server_append_msg(&cmd, SS_MSG_TYPE_STRING, str, STRLEN(str)); - socket_server_append_msg(&cmd, SS_MSG_TYPE_SENDER, - socket_server_path, STRLEN(socket_server_path)); - - final = socket_server_encode_cmd(&cmd, &sz); - - if (final == NULL || - socket_server_write(socket_fd, final, sz, 1000) == FAIL) - { - socket_server_free_cmd(&cmd); - vim_free(final); - close(socket_fd); - return FAIL; - } - - socket_server_free_cmd(&cmd); - vim_free(final); - close(socket_fd); - - return OK; -} - -/* - * Connect to a socket using "name". "path" is set to the full path of "name" - * used to create the socket, only if its not NULL. Returns fd on success and -1 - * on failure. - */ - static int -socket_server_connect(char_u *name, char_u **path, int silent) -{ - int socket_fd; - int res; - struct sockaddr_un addr; - - char_u *socket_path = socket_server_get_path_from_name(name); - - if (socket_path == NULL) - { - if (!silent) - semsg(_(e_no_registered_server_named_str), name); - return -1; - } - if (STRLEN(socket_path) >= sizeof(addr.sun_path)) - { - // Path too big - vim_free(socket_path); - return -1; - } - - socket_fd = socket(AF_UNIX, SOCK_STREAM, 0); - - if (socket_fd == -1) - { - vim_free(socket_path); - return -1; - } - - addr.sun_family = AF_UNIX; - vim_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); - - res = connect(socket_fd, (struct sockaddr *)&addr, sizeof(addr)); - - if (res == -1) - { - if (!silent) - semsg(_(e_socket_server_failed_connecting), socket_path, - strerror(errno)); - close(socket_fd); - vim_free(socket_path); - return -1; - } - - if (path != NULL) - *path = socket_path; - else - vim_free(socket_path); - - return socket_fd; - -} - -/* - * Add a new pending command to the list of pending commands. Returns OK on - * success and FAIL on failure - */ - static void -socket_server_init_pending_cmd(ss_pending_cmd_T *pending) -{ - pending->code = 0; - pending->result = NULL; - pending->serial = ss_serial; - pending->next = ss_pending_cmds; - ss_pending_cmds = pending; -} - -/* - * Remove pending command from the list, does not free the result string. - */ - static void -socket_server_pop_pending_cmd(ss_pending_cmd_T *pending) -{ - if (ss_pending_cmds == pending) - { - ss_pending_cmds = pending->next; - return; - } - - for (ss_pending_cmd_T *cmd = ss_pending_cmds; cmd != NULL; cmd = cmd->next) - { - if (cmd->next == pending) - { - cmd->next = pending->next; - return; - } - } -} - -/* - * Initialize command structure to empty state - */ - static void -socket_server_init_cmd(ss_cmd_T *cmd, ss_cmd_type_T type) -{ - cmd->cmd_len = 0; - cmd->cmd_num = 0; - cmd->cmd_type = type; -} - -/* - * Append a message to a command. Note that "len" is the length of contents. - * Returns OK on success and FAIL on failure - */ - static int -socket_server_append_msg(ss_cmd_T *cmd, char_u type, char_u *contents, int len) -{ - ss_msg_T *msg = cmd->cmd_msgs + cmd->cmd_num; - - if (cmd->cmd_num >= SOCKET_SERVER_MAX_MSG) - return FAIL; - - // Check if command will be too big. - if (SS_CMD_INFO_SIZE + cmd->cmd_len + SS_MSG_INFO_SIZE + len - > SOCKET_SERVER_MAX_CMD_SIZE) - return FAIL; - - msg->msg_contents = alloc(len); - - if (msg->msg_contents == NULL) - return FAIL; - - msg->msg_type = type; - msg->msg_len = len; - memcpy(msg->msg_contents, contents, len); - - cmd->cmd_len += SS_MSG_INFO_SIZE + len; - cmd->cmd_num++; - - return OK; -} - -/* - * Free all resources associated with a command object. - */ - static void -socket_server_free_cmd(ss_cmd_T *cmd) -{ - for (uint32_t i = 0; i < cmd->cmd_num; i++) - { - ss_msg_T *msg = cmd->cmd_msgs + i; - - vim_free(msg->msg_contents); - } -} - -/* - * Encode command struct and return the final message to send. Returns NULL on - * failure. - */ - static char_u * -socket_server_encode_cmd(ss_cmd_T *cmd, size_t *sz) -{ - size_t size; - char_u *buf; - char_u *start; - - size = SS_CMD_INFO_SIZE + cmd->cmd_len; - buf = alloc(size); - - if (buf == NULL) - return NULL; - - start = buf; - memcpy(start, &cmd->cmd_type, sizeof(cmd->cmd_type)); - start += sizeof(cmd->cmd_type); - memcpy(start, &cmd->cmd_num, sizeof(cmd->cmd_num)); - start += sizeof(cmd->cmd_num); - memcpy(start, &cmd->cmd_len, sizeof(cmd->cmd_len)); - start += sizeof(cmd->cmd_len); - - // Append messages to buffer - for (uint32_t i = 0; i < cmd->cmd_num; i++) - { - ss_msg_T *msg = cmd->cmd_msgs + i; - - memcpy(start, &msg->msg_type, sizeof(msg->msg_type)); - start += sizeof(msg->msg_type); - memcpy(start, &msg->msg_len, sizeof(msg->msg_len)); - start += sizeof(msg->msg_len); - - memcpy(start, msg->msg_contents, msg->msg_len); - start += msg->msg_len; - } - - *sz = size; - - return buf; -} - -/* - * Read from "socket_fd" an entire command and return the result in "cmd". The - * socket fd should be at the start of the command. Returns OK on success and - * FAIL on failure. - */ - static int -socket_server_decode_cmd(ss_cmd_T *cmd, int socket_fd, int timeout) -{ - int got_cmd_info = FALSE; // Consists of type, num, and len - size_t total_r = 0; - char_u *buf; - char_u *cur; -# ifdef ELAPSED_FUNC - elapsed_T start_tv; -# endif - - // We also poll the socket server listening file descriptor to handle - // recursive remote calls between Vim instances, such as when one Vim - // instance calls remote_expr for an expression that calls remote_expr to - // itself again. -# ifndef HAVE_SELECT - struct pollfd pfd; - - pfd.fd = socket_fd; - pfd.events = POLLIN; -# else - fd_set rfds; - struct timeval tv; - - FD_ZERO(&rfds); - FD_SET(socket_fd, &rfds); -# endif - - buf = alloc(SS_CMD_INFO_SIZE); - - if (buf == NULL) - return FAIL; - - // We may exit in the middle of the loop and free the messages, we don't - // want to free an uninitialized pointer. - memset(cmd, 0, sizeof(*cmd)); - -# ifdef ELAPSED_FUNC - ELAPSED_INIT(start_tv); -# endif - - while (TRUE) - { - int ret; - ssize_t r = 0; - -# ifndef HAVE_SELECT - ret = poll(&pfd, 1, timeout); -# else - tv.tv_sec = 0; - tv.tv_usec = 500 * 1000; - ret = select(socket_fd + 1, &rfds, NULL, NULL, &tv); -# endif - if (ret < 0) - goto fail; - if (ret == 0) - goto continue_loop; - - // Get cmd info first so we know the total size of all messages, and - // can read it all in one go. - if (!got_cmd_info) - { - r = read(socket_fd, buf + total_r, SS_CMD_INFO_SIZE - total_r); - - if ((size_t)r >= SS_CMD_INFO_SIZE - total_r) - { - char_u *tmp; - - got_cmd_info = TRUE; - - memcpy(&cmd->cmd_type, buf, sizeof(cmd->cmd_type)); - memcpy(&cmd->cmd_num, buf + sizeof(cmd->cmd_type), - sizeof(cmd->cmd_num)); - memcpy(&cmd->cmd_len, - buf + sizeof(cmd->cmd_type) + sizeof(cmd->cmd_num), - sizeof(cmd->cmd_len)); - - if (cmd->cmd_num > SOCKET_SERVER_MAX_MSG) - // Too many messages to handle or invalid number - goto fail; - - if (cmd->cmd_num == 0) - // No messages to read - goto exit; - - // Now that we now the total size of messages, we can realloc - // the buffer to contain all data - tmp = vim_realloc(buf, SS_CMD_INFO_SIZE + cmd->cmd_len); - - if (tmp == NULL) - goto fail; - - buf = tmp; - cur = buf + SS_CMD_INFO_SIZE; - - continue; - } - } - else - { - // Read message data - r = read(socket_fd, cur + total_r, cmd->cmd_len - total_r); - - if ((size_t)r >= cmd->cmd_len - total_r) - break; - } - - if (r == -1 || r == 0) - goto fail; - - total_r += r; - -continue_loop: -# ifdef ELAPSED_FUNC - if (ELAPSED_FUNC(start_tv) >= timeout) - goto fail; -# endif - } - - // Parse message data - for (uint32_t i = 0; i < cmd->cmd_num; i++) - { - ss_msg_T *msg = cmd->cmd_msgs + i; - - memcpy(&msg->msg_type, cur, sizeof(msg->msg_type)); - cur += sizeof(msg->msg_type); - memcpy(&msg->msg_len, cur, sizeof(msg->msg_len)); - cur += sizeof(msg->msg_len); - - msg->msg_contents = alloc(msg->msg_len + 1); - - if (msg->msg_contents == NULL) - goto fail; - - memcpy(msg->msg_contents, cur, msg->msg_len); - msg->msg_contents[msg->msg_len] = 0; // NULL terminate it - - // Move pointer to start of next message - cur += msg->msg_len; - } - -exit: - vim_free(buf); - return OK; -fail: - socket_server_free_cmd(cmd); - vim_free(buf); - return FAIL; -} - -/* - * Low level function that writes to a socket with a timeout in milliseconds. - * Returns OK on success and FAIL on failure. - */ - static int -socket_server_write(int socket_fd, char_u *data, size_t sz, int timeout) -{ - char_u *cur = data; - size_t total_w = 0; -# ifdef ELAPSED_FUNC - elapsed_T start_tv; -# endif -# ifndef HAVE_SELECT - struct pollfd pfd; - - pfd.fd = socket_fd; - pfd.events = POLLOUT; -# else - fd_set wfds; - struct timeval tv; - - FD_ZERO(&wfds); - FD_SET(socket_fd, &wfds); -# endif - -# ifdef ELAPSED_FUNC - ELAPSED_INIT(start_tv); -# endif - - while (total_w < sz) - { - int ret; - ssize_t written; - - errno = 0; -# ifndef HAVE_SELECT - ret = poll(&pfd, 1, timeout); -# else - tv.tv_sec = 0; - tv.tv_usec = 500 * 1000; - ret = select(socket_fd + 1, NULL, &wfds, NULL, &tv); -# endif - if (ret < 0) - return FAIL; - else if (ret == 0) - goto continue_loop; - - written = write(socket_fd, cur, sz - total_w); - - if (written == -1) - return FAIL; - - total_w += written; - -continue_loop: -# ifdef ELAPSED_FUNC - if (ELAPSED_FUNC(start_tv) >= timeout) - return FAIL; -# endif - } - - return OK; -} - - static ss_reply_T * -socket_server_get_reply(char_u *sender, int *index) -{ - for (int i = 0; i < ss_replies.ga_len; i++) - { - ss_reply_T *reply = ((ss_reply_T *)ss_replies.ga_data) + i; - - if (STRCMP(reply->sender, sender) == 0) - { - if (index != NULL) - *index = i; - return reply; - } - } - return NULL; -} - -/* - * Add reply to list of replies. Returns a pointer to the ss_reply_T that was - * initialized or was found. - */ - static ss_reply_T * -socket_server_add_reply(char_u *sender) -{ - ss_reply_T *reply; - - if (ss_replies.ga_growsize == 0) - ga_init2(&ss_replies, sizeof(ss_reply_T), 1); - - reply = socket_server_get_reply(sender, NULL); - - if (reply == NULL && ga_grow(&ss_replies, 1) == OK) - { - reply = ((ss_reply_T *)ss_replies.ga_data) + ss_replies.ga_len++; - - reply->sender = vim_strsave(sender); - - if (reply->sender == NULL) - return NULL; - - ga_init2(&reply->strings, sizeof(char_u *), 5); - } - - return reply; -} - - static void -socket_server_remove_reply(char_u *sender) -{ - int index; - ss_reply_T *reply = socket_server_get_reply(sender, &index); - - if (reply != NULL) - { - ss_reply_T *arr = ss_replies.ga_data; - - // Free strings - vim_free(reply->sender); - ga_clear_strings(&reply->strings); - - // Move all elements after the removed reply forward by one - for (int i = index + 1; i < ss_replies.ga_len; i++) - arr[i - 1] = arr[i]; - ss_replies.ga_len--; - } -} - -/* - * Execute the actions given by command. "fd" is the socket of the client that - * sent the command. - */ - static void -socket_server_exec_cmd(ss_cmd_T *cmd, int fd) -{ - char_u *str = NULL; - char_u *enc = NULL; - char_u *sender = NULL; - uint32_t serial = 0; - char_u rcode = 0; - char_u *to_free; - char_u *to_free2; - - for (uint32_t i = 0; i < cmd->cmd_num; i++) - { - ss_msg_T *msg = cmd->cmd_msgs + i; - - if (msg->msg_type == SS_MSG_TYPE_STRING) - str = msg->msg_contents; - if (msg->msg_type == SS_MSG_TYPE_ENCODING) - enc = msg->msg_contents; - if (msg->msg_type == SS_MSG_TYPE_SERIAL) - memcpy(&serial, msg->msg_contents, sizeof(serial)); - if (msg->msg_type == SS_MSG_TYPE_CODE) - memcpy(&rcode, msg->msg_contents, sizeof(rcode)); - else if (msg->msg_type == SS_MSG_TYPE_SENDER) - { - sender = msg->msg_contents; - - // Save in global - vim_free(client_socket); - client_socket = vim_strsave(sender); - } - } - -# ifdef FEAT_EVAL - ch_log(NULL, "socket_server_exec_cmd(): encoding: %s, result: %s", - enc == NULL ? (char_u *)"(null)" : enc, - str == NULL ? (char_u *)"(null)" : str); -# endif - - if (cmd->cmd_type == SS_CMD_TYPE_EXPR || - cmd->cmd_type == SS_CMD_TYPE_KEYSTROKES) - { - // Either an expression or keystrokes. - if (socket_server_valid() && enc != NULL && str != NULL) - { - str = serverConvert(enc, str, &to_free); - - if (cmd->cmd_type == SS_CMD_TYPE_KEYSTROKES) - server_to_input_buf(str); - else if (sender != NULL) - { - // Evaluate expression and send reply containing result - char_u *result; - size_t sz; - char_u *buf; - char_u code; - - result = eval_client_expr_to_string(str); - - code = result == NULL ? -1 : 0; - - // Send reply - ss_cmd_T rcmd; - - socket_server_init_cmd(&rcmd, SS_CMD_TYPE_REPLY); - - // Don't care about errors, server will just ignore command if - // its missing something. - if (result != NULL) - socket_server_append_msg(&rcmd, SS_MSG_TYPE_STRING, result, - STRLEN(result) + 1); // We add +1 in case "result" - // is an empty string. - else - // An error occurred, return an error msg instead - socket_server_append_msg(&rcmd, SS_MSG_TYPE_STRING, - (char_u *)_(e_invalid_expression_received), - STRLEN(e_invalid_expression_received)); - - socket_server_append_msg(&rcmd, SS_MSG_TYPE_CODE, - &code, sizeof(code)); - - socket_server_append_msg(&rcmd, SS_MSG_TYPE_ENCODING, p_enc, - STRLEN(p_enc)); - - socket_server_append_msg(&rcmd, SS_MSG_TYPE_SERIAL, - (char_u *)&serial, sizeof(serial)); - - buf = socket_server_encode_cmd(&rcmd, &sz); - - if (buf != NULL) - { - int fd2 = socket_server_connect(sender, NULL, TRUE); - - if (fd2 >= 0) - socket_server_write(fd2, buf, sz, 1000); - vim_free(buf); - close(fd2); - } - - socket_server_free_cmd(&rcmd); - vim_free(result); - } - vim_free(to_free); - } - return; - } - else if (cmd->cmd_type == SS_CMD_TYPE_REPLY) - { - // A reply from a previous command we set up, update the corresponding - // pending command. - if (serial > 0 && str != NULL) - { - for (ss_pending_cmd_T *pending = ss_pending_cmds; pending != NULL; - pending = pending->next) - { - if (serial == pending->serial && pending->result == NULL) - { - str = serverConvert(enc, str, &to_free); - - pending->code = rcode; - - if (to_free == NULL) - pending->result = vim_strsave(str); - else - pending->result = str; - break; - } - } - } - return; - } - else if (cmd->cmd_type == SS_CMD_TYPE_NOTIFY) - { - // Notification, execute autocommands and save the reply for later use - if (sender != NULL && str != NULL && enc != NULL) - { - ss_reply_T *reply; - - str = serverConvert(enc, str, &to_free); - sender = serverConvert(enc, sender, &to_free2); - - reply = socket_server_add_reply(sender); - - if (reply != NULL) - ga_copy_string(&reply->strings, str); - - apply_autocmds(EVENT_REMOTEREPLY, sender, str, TRUE, curbuf); - - vim_free(to_free); - vim_free(to_free2); - } - return; - } - else if (cmd->cmd_type == SS_CMD_TYPE_ALIVE) - { - // Client wants to check if we are still responsive, send back a single - // byte as a YES. - char_u buf[1] = {1}; -# ifndef HAVE_SELECT - struct pollfd pfd; - - pfd.fd = fd; - pfd.events = POLLIN; -# else - fd_set rfds; - struct timeval tv; - - FD_ZERO(&rfds); - FD_SET(fd, &rfds); -# endif - - if (write(fd, buf, 1) == -1) - return; - - // Poll until client closes their end - -# ifndef HAVE_SELECT - poll(&pfd, 1, 1000); -# else - tv.tv_sec = 1; - tv.tv_usec = 0; - select(fd + 1, &rfds, NULL, NULL, &tv); -# endif - return; - } - - // Command type is invalid, do nothing - return; -} - -/* - * Poll the socket server fd until a new connection is accepted. Returns 0 on - * success, 1 if it timed out or if poll returned empty, and -1 on error. - */ - static int -socket_server_dispatch(int timeout) -{ - int ret; -# ifndef HAVE_SELECT - struct pollfd pfd; - - pfd.fd = socket_server_fd; - pfd.events = POLLIN; -# else - fd_set rfds; - fd_set efds; - struct timeval tv; - - FD_ZERO(&rfds); - FD_ZERO(&efds); - FD_SET(socket_server_fd, &rfds); - FD_SET(socket_server_fd, &efds); -# endif - -# ifndef HAVE_SELECT - ret = poll(&pfd, 1, timeout); -# else - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - ret = select(socket_server_fd + 1, &rfds, NULL, &efds, &tv); -# endif - - if (ret < 0) - return -1; - else if (ret == 0) - return 1; - -# ifndef HAVE_SELECT - if (pfd.revents & POLLIN) -# else - if (FD_ISSET(socket_server_fd, &rfds)) -# endif - { - socket_server_accept_client(); - return 0; - } -# ifndef HAVE_SELECT - else if (pfd.revents & (POLLHUP | POLLERR)) -# else - else if (FD_ISSET(socket_server_fd, &efds)) -# endif - // Connection was closed - return -1; - else - return 1; - - return -1; -} - -/* - * Check if socket "name" is responsive by sending an ALIVE command. This does - * not require the socket server to be active. - */ - static int -socket_server_check_alive(char_u *name) -{ - int socket_fd; - int ret; - size_t sz; - char_u *final; - char_u buf[1] = {0}; -# ifndef HAVE_SELECT - struct pollfd pfd; -# else - fd_set rfds; - struct timeval tv; -# endif - - socket_fd = socket_server_connect(name, NULL, TRUE); - - if (socket_fd == -1) - return FALSE; - -# ifndef HAVE_SELECT - pfd.fd = socket_fd; - pfd.events = POLLIN; -# else - FD_ZERO(&rfds); - FD_SET(socket_fd, &rfds); -# endif - - ss_cmd_T cmd; - - socket_server_init_cmd(&cmd, SS_CMD_TYPE_ALIVE); - - final = socket_server_encode_cmd(&cmd, &sz); - - if (final == NULL || - socket_server_write(socket_fd, final, sz, 1000) == FAIL) - { - vim_free(final); - close(socket_fd); - return FALSE; - } - vim_free(final); - - // Poll for response -# ifndef HAVE_SELECT - ret = poll(&pfd, 1, 1000); -# else - tv.tv_sec = 1; - tv.tv_usec = 0; - ret = select(socket_fd + 1, &rfds, NULL, NULL, &tv); -# endif - - if (ret > 0) - if (read(socket_fd, buf, 1) == -1) - { - close(socket_fd); - return FALSE; - } - - close(socket_fd); - return buf[0] == 1; -} - -/* - * Get file descriptor of listening socket - */ - int -socket_server_get_fd(void) -{ - return socket_server_fd; -} - - -/* - * Check if socket name is a valid name - */ - static int -socket_server_name_is_valid(char_u *name) -{ - if (STRLEN(name) == 0 || (name[0] != '/' && vim_strchr(name, '/') != NULL)) - { - semsg(_(e_invalid_server_id_used_str), name); - return FALSE; - } - return TRUE; -} - -/* - * Returns TRUE if there are clients queued in the listening socket waiting to - * be accepted - */ - int -socket_server_waiting_accept(void) -{ - int ret; -# ifndef HAVE_SELECT - struct pollfd pfd; - - pfd.fd = socket_server_fd; - pfd.events = POLLIN; - - ret = poll(&pfd, 1, 0); - - if (ret > 0 && pfd.revents & POLLIN) - return TRUE; -# else - fd_set rfds; - struct timeval tv; - - if (socket_server_fd == -1) - return FALSE; - - FD_ZERO(&rfds); - FD_SET(socket_server_fd, &rfds); - - tv.tv_sec = 0; - tv.tv_usec = 0; - ret = select(socket_server_fd + 1, &rfds, NULL, NULL, &tv); - - if (ret > 0 && FD_ISSET(socket_server_fd, &rfds)) - return TRUE; -# endif - - return FALSE; -} - -#endif // FEAT_SOCKETSERVER diff --git a/src/po/vim.pot b/src/po/vim.pot index ad805b30b7..946ce08831 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -254,9 +254,6 @@ msgstr "" msgid "%d of %d edited" msgstr "" -msgid "Socket server not online:Send expression failed" -msgstr "" - msgid "No display: Send expression failed.\n" msgstr "" @@ -1722,7 +1719,8 @@ msgstr "" msgid "-Y\t\t\tDo not connect to Wayland compositor" msgstr "" -msgid "--clientserver Backend for clientserver communication" +msgid "" +"--clientserver Backend for clientserver communication" msgstr "" msgid "--remote \tEdit in a Vim server if possible" @@ -2531,10 +2529,6 @@ msgstr "" msgid "XSMP SmcOpenConnection failed: %s" msgstr "" -#, c-format -msgid "Failed creating socket directory: %s" -msgstr "" - msgid "At line" msgstr "" @@ -2806,6 +2800,10 @@ msgstr "" msgid " (not supported)" msgstr "" +#, c-format +msgid "Failed creating socket directory: %s" +msgstr "" + #, c-format msgid "Warning: Cannot find word list \"%s_%s.spl\" or \"%s_ascii.spl\"" msgstr "" @@ -8834,20 +8832,20 @@ msgstr "" msgid "E1562: Diff anchors cannot be used with hidden diff windows" msgstr "" -msgid "E1563: Socket path is too big" -msgstr "" - -msgid "E1564: Socket name cannot have slashes in it without being a path" +#, c-format +msgid "E1564: Socket name '%s' cannot have slashes in it without being a path" msgstr "" msgid "E1565: Socket server is not online, call remote_startserver() first" msgstr "" #, c-format -msgid "E1566: Failed connecting to socket %s: %s" +msgid "E1566: Failed connecting to socket '%s'" msgstr "" -msgid "E1567: Cannot start socket server, socket path is unavailable" +msgid "" +"E1567: Socket server protocol version mismatch, check what Vim version you " +"are using" msgstr "" #, c-format diff --git a/src/proto.h b/src/proto.h index ed7a5a054e..8dd9c30cbe 100644 --- a/src/proto.h +++ b/src/proto.h @@ -277,6 +277,9 @@ void mbyte_im_set_active(int active_arg); # include "job.pro" # include "channel.pro" # endif +# ifdef FEAT_SOCKETSERVER +# include "socketserver.pro" +# endif # ifdef FEAT_EVAL // Not generated automatically so that we can add an extra attribute. diff --git a/src/proto/channel.pro b/src/proto/channel.pro index 0b5d17a137..cd58f2c317 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -7,15 +7,18 @@ int channel_unref(channel_T *channel); int free_unused_channels_contents(int copyID, int mask); void free_unused_channels(int copyID, int mask); void channel_gui_register_all(void); +channel_T *channel_open_unix(const char *path, void (*nb_close_cb)(void)); channel_T *channel_open(const char *hostname, int port, int waittime, void (*nb_close_cb)(void)); +int channel_parse_socketserver_address(char_u *address, int *port, char_u **unix_path, bool quiet); channel_T *channel_listen_func(typval_T *argvars); channel_T *channel_listen(int port_in, void (*nb_close_cb)(void)); -channel_T *channel_listen_unix(char *path, void (*nb_close_cb)(void)); +channel_T *channel_listen_unix(char *path, void (*nb_close_cb)(void), bool replace); void ch_close_part(channel_T *channel, ch_part_T part); void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_job(channel_T *channel, job_T *job, jobopt_T *options); void channel_write_in(channel_T *channel); void channel_buffer_free(buf_T *buf); +void channel_write_input(channel_T *channel); void channel_write_any_lines(void); void channel_write_new_lines(buf_T *buf); readq_T *channel_peek(channel_T *channel, ch_part_T part); @@ -23,11 +26,15 @@ char_u *channel_first_nl(readq_T *node); char_u *channel_get(channel_T *channel, ch_part_T part, int *outlen); void channel_consume(channel_T *channel, ch_part_T part, int len); int channel_collapse(channel_T *channel, ch_part_T part, int want_nl); +int channel_fill(js_read_T *reader); +int channel_parse_json(channel_T *channel, ch_part_T part, bool socketserver); +void remove_json_node(jsonq_T *head, jsonq_T *node); int channel_can_write_to(channel_T *channel); int channel_is_open(channel_T *channel); void channel_close(channel_T *channel, int invoke_close_cb); void channel_clear(channel_T *channel); void channel_free_all(void); +void channel_check(channel_T *channel, ch_part_T part); int channel_in_blocking_wait(void); channel_T *get_channel_arg(typval_T *tv, int check_open, int reading, ch_part_T part); void channel_handle_events(int only_keep_open); @@ -59,4 +66,5 @@ void f_ch_sendraw(typval_T *argvars, typval_T *rettv); void f_ch_setoptions(typval_T *argvars, typval_T *rettv); void f_ch_status(typval_T *argvars, typval_T *rettv); char_u *channel_to_string_buf(typval_T *varp, char_u *buf); +channel_T *channel_find(int ch_id); /* vim: set ft=c : */ diff --git a/src/proto/gui_gtk_x11.pro b/src/proto/gui_gtk_x11.pro index 5a57240135..696f6a07af 100644 --- a/src/proto/gui_gtk_x11.pro +++ b/src/proto/gui_gtk_x11.pro @@ -8,8 +8,6 @@ void gui_mch_stop_blink(int may_call_gui_update_cursor); void gui_mch_start_blink(void); int gui_mch_early_init_check(int give_message); int gui_mch_init_check(void); -void gui_gtk_init_socket_server(void); -void gui_gtk_uninit_socket_server(void); void gui_mch_set_dark_theme(int dark); void gui_mch_show_tabline(int showit); int gui_mch_showing_tabline(void); diff --git a/src/proto/os_unix.pro b/src/proto/os_unix.pro index 7513e400f0..aca289d1f7 100644 --- a/src/proto/os_unix.pro +++ b/src/proto/os_unix.pro @@ -96,15 +96,4 @@ void stop_timeout(void); volatile sig_atomic_t *start_timeout(long msec); void delete_timer(void); int mch_create_anon_file(void); -int socket_server_init(char_u *name); -void socket_server_uninit(void); -char_u *socket_server_list_sockets(void); -int socket_server_accept_client(void); -int socket_server_valid(void); -int socket_server_send(char_u *name, char_u *str, char_u **result, char_u **receiver, int is_expr, int timeout, int silent); -int socket_server_read_reply(char_u *client, char_u **str, int timeout); -int socket_server_peek_reply(char_u *sender, char_u **str); -int socket_server_send_reply(char_u *client, char_u *str); -int socket_server_get_fd(void); -int socket_server_waiting_accept(void); /* vim: set ft=c : */ diff --git a/src/proto/socketserver.pro b/src/proto/socketserver.pro new file mode 100644 index 0000000000..69d292c3ab --- /dev/null +++ b/src/proto/socketserver.pro @@ -0,0 +1,11 @@ +/* socketserver.c */ +int socketserver_start(char_u *name, bool quiet); +void socketserver_stop(void); +char_u *socketserver_list(void); +int set_ref_in_socketserver_channel(int copyID); +void socketserver_parse_messages(void); +int socketserver_send(char_u *name, char_u *str, char_u **result, bool is_expr, int timeout, bool silent, channel_T **ch); +int socketserver_send_reply(char_u *client, char_u *str); +int socketserver_read_reply(char_u *client, char_u **str, int timeout, bool remotewait); +int socketserver_peek_reply(char_u *sender, char_u **str); +/* vim: set ft=c : */ diff --git a/src/socketserver.c b/src/socketserver.c new file mode 100644 index 0000000000..7c4e19b536 --- /dev/null +++ b/src/socketserver.c @@ -0,0 +1,1404 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + */ + +/* + * socketserver.c: Socketserver clientserver functionality + */ + +#include "vim.h" + +#ifdef FEAT_SOCKETSERVER + +// Always greater than one. +# define PROTOCOL_VER 1 + +/* + * Any message sent to a Vim server or received from a Vim server is a simple + * JSON object with the following fields: + * { + * "version": PROTOCOL_VER -- Protocol version, if different, then ignore. + * + * "type": "expr"|"keystrokes"|"notify"|"reply"|"ver" -- The type of message + * + * "str": string -- What to execute for the command or contents of result + * + * ?"code": number -- Return code for expression + * + * ?"sender": string -- Address of client that sent command if it is a server. + * + * ?"wait": bool -- Used by --remote-wait since the client is not a server. + * } + */ + +/* + * Represents a reply from a server2client call. Each client that calls a + * server2client call to us has its own ss_reply_T. Each time a client sends + * data using server2client, Vim creates a ss_reply_T if it doesn't exist and + * adds the string to the array. When remote_read is called, the server id is + * used to find the specific ss_reply_T, and a single string is popped from the + * array. + */ +typedef struct +{ + char_u *sender; // Includes "type:" prefix if any + garray_T strings; +} ss_reply_T; + +static channel_T *server_channel = NULL; +static char_u *server_address = NULL; // Includes "type:" prefix if any +static bool server_is_unix = false; +static char_u *server_addr_cwd = NULL; // CWD when server was started, used + // to handle relative file paths. +static garray_T server_replies; + +static channel_T *client_channels = NULL; + +# define FOR_ALL_CLIENTS(ch) \ + for (ch = client_channels; ch != NULL; ch = ch->ch_ss_next) + +static void socketserver_cleanup(void); +static char_u *socketserver_create_path(char_u *name, bool quiet); +static char_u *socketserver_get_path(char_u *name, bool new, bool quiet, bool *fatal); +static void socketserver_accept(channel_T *channel); +static void socketserver_close(channel_T *channel); +static ss_reply_T *socketserver_add_reply(char_u *sender); + +/* + * Start the socketserver using the given name. Returns OK on success and FAIL + * on failure. + */ + int +socketserver_start(char_u *name, bool quiet) +{ + char_u *address = NULL; + int port; + bool is_unix = false; + channel_T *channel; + char_u *buf; + char_u dirbuf[MAXPATHL]; + + if (server_channel != NULL) + return OK; + + if (STRNICMP(name, "channel:", 8) == 0) + { + if (channel_parse_socketserver_address(name + 8, &port, &address, quiet) + == FAIL) + return FAIL; + if (address != NULL) + is_unix = true; + } + else + { + address = socketserver_create_path(name, quiet); + if (address == NULL) + return FAIL; + is_unix = true; + } + + if (is_unix) + channel = channel_listen_unix((char *)address, NULL, false); + else + channel = channel_listen(port, NULL); + + if (channel == NULL) + { + vim_free(address); + return FAIL; + } + + channel->ch_socketserver = true; + channel->ch_ss_accept_cb = socketserver_accept; + channel->ch_ss_close_cb = socketserver_close; + + server_channel = channel; + server_is_unix = is_unix; + + VIM_CLEAR(serverName); + + if (STRNICMP(name, "channel:", 8) == 0) + buf = vim_strsave(name); + else + { + buf = alloc(MAXPATHL + 1); + if (buf != NULL) + { + buf[0] = NUL; + mch_FullName(address, buf, MAXPATHL, false); + } + } + vim_free(address); + + if (buf != NULL) + { + server_address = vim_strsave(buf); + serverName = buf; + set_vim_var_string(VV_SEND_SERVER, serverName, -1); + } + + vim_free(server_addr_cwd); + if (mch_dirname(dirbuf, sizeof(dirbuf)) == OK) + server_addr_cwd = vim_strsave(dirbuf); + else + server_addr_cwd = NULL; + + ga_init2(&server_replies, sizeof(ss_reply_T), 2); + + ch_log(NULL, "socketserver: started server at %s", name); + + return OK; +} + +/* + * Stop running the socketserver if it is. Note that this does not stop Vim from + * becoming a client. + */ + void +socketserver_stop(void) +{ + if (server_channel == NULL) + return; + + channel_close(server_channel, false); + channel_clear(server_channel); + + socketserver_cleanup(); + + ch_log(NULL, "socketserver: shutting down server"); +} + +/* + * Cleanup server stuff. Do not close client channels, as those are not part of + * the actual server. + */ + static void +socketserver_cleanup(void) +{ +# ifdef UNIX + if (server_is_unix && server_address != NULL) + { + char_u *path = server_address; + char_u dirbuf[MAXPATHL]; + + if (STRNICMP(path, "channel:unix:", 13) == 0) + path += 13; + else if (STRNICMP(path, "name:", 5) == 0) + path += 5; + + if (*path == '/') + mch_remove(path); + // Go to the directory where the server was started. This is to handle + // when Vim changes directories and the servername is a relative path. + else if (server_addr_cwd != NULL + && mch_dirname(dirbuf, sizeof(dirbuf)) == OK) + { + if (mch_chdir((char *)server_addr_cwd) == 0) + { + mch_remove(path); + if (mch_chdir((char *)dirbuf) < 0) + emsg(_(e_cannot_go_back_to_previous_directory)); + } + } + } +# endif + + server_channel = NULL; + VIM_CLEAR(server_address); + VIM_CLEAR(server_addr_cwd); + + // Free all replies + for (int i = 0; i < server_replies.ga_len; i++) + { + ss_reply_T *reply = (ss_reply_T *)server_replies.ga_data + i; + + vim_free(reply->sender); + ga_clear_strings(&reply->strings); + } + ga_clear(&server_replies); +} + +/* + * List available sockets that can be connected to, only in common directories + * that Vim knows about. Vim instances with custom socket paths will not be + * detected. Returns a newline separated string on success and NULL on failure. + */ + char_u * +socketserver_list(void) +{ +# ifdef MSWIN + // Only support addresses on Windows + return vim_strsave((char_u *)""); +# else + garray_T str; + string_T buf; + string_T path; + DIR *dirp; + struct dirent *dp; + const char_u *known_dirs[] = { + mch_getenv("XDG_RUNTIME_DIR"), + mch_getenv("TMPDIR"), + (char_u *)"/tmp" + }; + + if ((buf.string = alloc(MAXPATHL)) == NULL) + return NULL; + if ((path.string = alloc(MAXPATHL)) == NULL) + { + vim_free(buf.string); + return NULL; + } + buf.length = 0; + path.length = 0; + + ga_init2(&str, 1, 100); + + for (size_t i = 0 ; i < ARRAY_LENGTH(known_dirs); i++) + { + const char_u *dir = known_dirs[i]; + + if (dir == NULL) + continue; + + if (STRCMP(dir, "/tmp") == 0 || + (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0)) + path.length = vim_snprintf_safelen((char *)path.string, MAXPATHL, + "%s/vim-%lu", dir, (unsigned long int)getuid()); + else + path.length = vim_snprintf_safelen((char *)path.string, MAXPATHL, + "%s/vim", dir); + + dirp = opendir((char *)path.string); + if (dirp == NULL) + continue; + + // Loop through directory + while ((dp = readdir(dirp)) != NULL) + { + if (STRCMP(dp->d_name, ".") == 0 || STRCMP(dp->d_name, "..") == 0) + continue; + + buf.length = vim_snprintf_safelen((char *)buf.string, MAXPATHL, + "%s/%s", path.string, dp->d_name); + + ga_concat_len(&str, (char_u *)dp->d_name, + buf.length - (path.length + 1)); + ga_append(&str, '\n'); + } + + closedir(dirp); + + break; + } + + vim_free(path.string); + vim_free(buf.string); + + ga_append(&str, NUL); + + return str.ga_data; +# endif +} + +/* + * If "name" is a path (starts with a '/', './', or '../'), it is assumed to be + * the path to the desired socket. If the socket path is already taken, append + * an incrementing number to the path until we find a socket filename that can + * be used. Returns alloced string or NULL on failure. + */ + static char_u * +socketserver_create_path(char_u *name, bool quiet) +{ +# ifdef MSWIN + // Only support channel addresses on Windows + if (STRNICMP(name, "channel:", 8) == 0 && STRLEN(name) > 8) + return vim_strsave(name + 8); + else + { + if (!quiet) + semsg(_(e_invalid_argument_str), name); + return NULL; + } +# else + char_u *buf = NULL; + int buflen = STRLEN(name) + NUMBUFLEN; + char_u *path = NULL; + bool fatal = false; + + if (STRNICMP(name, "channel:", 8) == 0 && STRLEN(name) > 8) + return vim_strsave(name + 8); + if (STRNICMP(name, "name:", 5) == 0) + name += 5; + + for (int i = 0; i < 1000; i++) + { + if (buf != NULL) + { + vim_snprintf((char *)buf, buflen, "%s%d", name, i); + path = socketserver_get_path(buf, true, quiet, &fatal); + } + else + path = socketserver_get_path(name, true, quiet, &fatal); + + if (fatal) + break; + + if (path == NULL) + { + if (buf == NULL) + { + buf = alloc(buflen); + if (buf == NULL) + { + semsg(_(e_out_of_memory_allocating_nr_bytes), buflen); + return NULL; + } + } + continue; + } + break; + } + vim_free(buf); + + return path; +# endif +} + +/* + * If "name" is a pathless name such as "VIM", search known directories for the + * socket named "name", and return the alloc'ed path to it. If "name" starts + * with a '/', './' or '../', then a copy of "name" is returned. + * + * If "name" starts with "channel:", then return the address part + * + * If "new" is true, then return a path if the name does not exist in the known + * location. + * + * If "fatal" is not NULL, then it is set to true if error is fatal. + * + * Returns path on success and NULL on failure. + */ + static char_u * +socketserver_get_path(char_u *name, bool new UNUSED, bool quiet, bool *fatal) +{ +# ifdef MSWIN + // Only support channel addresses on Windows + if (STRNICMP(name, "channel:", 8) == 0 && STRLEN(name) > 8) + return vim_strsave(name + 8); + else + { + if (!quiet) + semsg(_(e_invalid_argument_str), name); + if (fatal != NULL) + *fatal = true; + return NULL; + } +# else + char_u *buf; + bool res = false; + channel_T *channel; + const char_u *known_dirs[] = { + mch_getenv("XDG_RUNTIME_DIR"), + mch_getenv("TMPDIR"), + (char_u *)"/tmp" + }; + + if (name == NULL) + { + if (fatal != NULL) + *fatal = true; + return NULL; + } + + // Ignore if name is a path + if (name[0] == '/' || STRNCMP(name, "./", 2) == 0 || + STRNCMP(name, "../", 3) == 0) + return vim_strsave(name); + + if (STRNICMP(name, "channel:", 8) == 0 && STRLEN(name) > 8) + return vim_strsave(name + 8); + + if (STRNICMP(name, "name:", 5) == 0) + name += 5; + + if (vim_strchr(name, '/') != NULL) + { + if (!quiet) + semsg(_(e_socket_name_no_slashes), name); + if (fatal != NULL) + *fatal = true; + return NULL; + } + + buf = alloc(MAXPATHL); + + if (buf == NULL) + { + if (fatal != NULL) + *fatal = true; + semsg(_(e_out_of_memory_allocating_nr_bytes), MAXPATHL); + return NULL; + } + + for (size_t i = 0; i < ARRAY_LENGTH(known_dirs); i++) + { + const char_u *dir = known_dirs[i]; + bool got = false; + + if (dir == NULL) + continue; + else if (STRCMP(dir, "/tmp") == 0 || + (known_dirs[1] != NULL && STRCMP(dir, known_dirs[1]) == 0)) + { + // "/tmp" or $TMPDIR, must suffix dir with uid + vim_snprintf((char *)buf, MAXPATHL, "%s/vim-%lu", dir, + (unsigned long int)getuid()); + if (vim_mkdir(buf, 0700) == -1 && errno != EEXIST) + continue; + + vim_snprintf((char *)buf, MAXPATHL, "%s/vim-%lu/%s", dir, + (unsigned long int)getuid(), name); + } + else + { + vim_snprintf((char *)buf, MAXPATHL, "%s/vim", dir); + if (vim_mkdir(buf, 0700) == -1 && errno != EEXIST) + continue; + + vim_snprintf((char *)buf, MAXPATHL, "%s/vim/%s", dir, name); + } + + // If looking for a new socket path, and "buf" currently exists, check + // if it is a dead socket, if it is then remove it. + if (new) + { + emsg_silent++; + channel = channel_open_unix((char *)buf, NULL); + emsg_silent--; + + if (channel != NULL) + { + channel_close(channel, false); + channel_clear(channel); + } + else + { + mch_remove(buf); + got = true; + } + } + + res = true; + if (got || (!new && mch_access((char *)buf, F_OK) == 0)) + { + if (server_address != NULL && STRCMP(buf, server_address) == 0) + // Can't connect to itself + break; + return buf; + } + break; + } + + if (!quiet) + { + if (!res) + semsg(_("Failed creating socket directory: %s"), strerror(errno)); + else + semsg(_(e_invalid_server_id_used_str), name); + } + if (!res && fatal != NULL) + *fatal = true; + + vim_free(buf); + return NULL; +# endif +} + +/* + * Callback for when client channel is closed + */ + static void +socketserver_client_close(channel_T *channel) +{ + if (channel == client_channels) + client_channels = channel->ch_ss_next; + if (channel->ch_ss_prev != NULL) + channel->ch_ss_prev->ch_ss_next = channel->ch_ss_next; + if (channel->ch_ss_next != NULL) + channel->ch_ss_next->ch_ss_prev = channel->ch_ss_prev; + + ch_log(NULL, "socketserver: client channel closed"); +} + +/* + * Callback for when server channel accepted new client. + */ + static void +socketserver_accept(channel_T *channel) +{ + channel->ch_socketserver = true; + channel->ch_ss_close_cb = socketserver_client_close; + + channel->ch_ss_next = client_channels; + channel->ch_ss_prev = NULL; + if (client_channels != NULL) + client_channels->ch_ss_prev = channel; + client_channels = channel; + + // We will read the command from the client later in the input loop. + ch_log(NULL, "socketserver: accepted new client"); +} + +/* + * Callback for when server channel is closed + */ + static void +socketserver_close(channel_T *channel UNUSED) +{ + socketserver_cleanup(); + + ch_log(NULL, "socketserver: server channel closed"); +} + +/* + * Mark references to socketserver channels + */ + int +set_ref_in_socketserver_channel(int copyID) +{ + bool abort = false; + channel_T *channel; + typval_T tv; + + tv.v_type = VAR_CHANNEL; + tv.vval.v_channel = server_channel; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL); + + FOR_ALL_CLIENTS(channel) + { + if (abort) + break; + + tv.v_type = VAR_CHANNEL; + tv.vval.v_channel = channel; + abort = abort || set_ref_in_item(&tv, copyID, NULL, NULL, NULL); + } + return abort; +} + +/* + * Serialize "msg" into a string and send it over the given channel "ch". Also + * adds version header to "msg", if "ver" is true. Returns OK on success and + * FAIL on failure. + */ + static int +socketserver_send_message(channel_T *ch, dict_T *msg, char *func, bool ver) +{ + typval_T tv; + char_u *buf; + int ret; + + if (ver) + dict_add_number(msg, "version", PROTOCOL_VER); + + tv.v_type = VAR_DICT; + tv.vval.v_dict = msg; + buf = json_encode(&tv, JSON_NL); + + if (buf != NULL) + { + emsg_silent++; + ret = channel_send(ch, PART_SOCK, buf, (int)STRLEN(buf), func); + emsg_silent--; + } + else + return FAIL; + vim_free(buf); + return ret; +} + +/* + * Execute the JSON message represented by "dict". + */ + static void +socketserver_exec(channel_T *channel, dict_T *message) +{ + dictitem_T *di; + char_u *type; + char_u *str = NULL; + char_u *sender = NULL; + char_u idbuf[11 + NUMBUFLEN]; + + di = dict_find(message, (char_u *)"type", -1); + if (di == NULL || di->di_tv.v_type != VAR_STRING) + return; + else + type = di->di_tv.vval.v_string; + + di = dict_find(message, (char_u *)"str", -1); + if (di != NULL && di->di_tv.v_type == VAR_STRING) + str = di->di_tv.vval.v_string; + + di = dict_find(message, (char_u *)"sender", -1); + if (di != NULL && di->di_tv.v_type == VAR_STRING) + { + sender = di->di_tv.vval.v_string; + + // Save in global + vim_free(client_socket); + client_socket = vim_strsave(sender); + } + + di = dict_find(message, (char_u *)"wait", -1); + if (di != NULL && di->di_tv.v_type == VAR_BOOL && di->di_tv.vval.v_number) + { + // Client is not a server, but still wants a response later. Save the + // ID of the channel connection that we will use to send back a response, + vim_snprintf((char *)idbuf, sizeof(idbuf), "remotewait:%d", + channel->ch_id); + + sender = idbuf; + vim_free(client_socket); + client_socket = vim_strsave(sender); + } + + ch_log(NULL, "socketserver_exec(): result: %s", + str == NULL ? (char_u *)"(null)" : str); + + if (STRCMP(type, "expr") == 0 && str != NULL) + { + // Evaluate expression and send back reply + dict_T *dict; + char_u *result; + int code; + + dict = dict_alloc(); + if (dict == NULL) + return; + + result = eval_client_expr_to_string(str); + code = result == NULL ? -1 : 0; + + dict_add_string(dict, "type", (char_u *)"reply"); + if (result != NULL) + { + dict_add_string(dict, "str", result); + vim_free(result); + } + else + // Error occured, return error message + dict_add_string(dict, "str", + (char_u *)_(e_invalid_expression_received)); + + dict_add_number(dict, "code", code); + + socketserver_send_message(channel, dict, "socketserver_exec", true); + dict_unref(dict); + } + else if (STRCMP(type, "keystrokes") == 0 && str != NULL) + { + // Execute keystrokes + server_to_input_buf(str); + } + else if (STRCMP(type, "notify") == 0) + { + // Notification, execute autocommands and save the reply for later use + if (sender != NULL && str != NULL) + { + ss_reply_T *reply; + + reply = socketserver_add_reply(sender); + + if (reply != NULL) + ga_copy_string(&reply->strings, str); + + apply_autocmds(EVENT_REMOTEREPLY, sender, str, TRUE, curbuf); + } + } + else if (STRCMP(type, "ver") == 0) + { + // A message we sent previously had the wrong version. Emit an error + // message to alert the user. + emsg(_(e_socket_server_version_mismatch)); + } + else + ch_error(NULL, "socketserver: unknown command type '%s'", type); +} + + static int +socketserver_get_message(channel_T *channel, typval_T **tv) +{ + jsonq_T *head = &channel->ch_part[PART_SOCK].ch_json_head; + jsonq_T *json_msg = head->jq_next; + int ver; + + if (json_msg == NULL) + { + // Check the readahead buffer + channel_parse_json(channel, PART_SOCK, true); + json_msg = head->jq_next; + } + if (json_msg == NULL) + return FAIL; + *tv = json_msg->jq_value; + remove_json_node(head, json_msg); + + if ((*tv)->v_type != VAR_DICT) + { + ch_error(NULL, "socketserver: message is not a JSON object"); + free_tv(*tv); + return FAIL; + } + + ver = dict_get_number((*tv)->vval.v_dict, "version"); + + if (ver != 0 && ver != PROTOCOL_VER) + { + dict_T *dict; + + ch_error(NULL, "socketserver: message has different version %d", ver); + free_tv(*tv); + + // Send back a special error + dict = dict_alloc(); + if (dict != NULL) + { + dict_add_string(dict, "type", (char_u *)"ver"); + socketserver_send_message(channel, dict, + "socketserver_get_message", false); + dict_unref(dict); + } + return FAIL; + } + + return OK; +} + +/* + * Parse any commands in the queue and execute them. + */ + void +socketserver_parse_messages(void) +{ + typval_T *tv; + + for (channel_T *ch = client_channels; ch != NULL;) + { + // Make sure to save next channel in case "ch" is freed. Not sure if + // this can actually happen but be safe. + channel_T *next = ch->ch_ss_next; + + // Get the JSON message if there is any from the queue for this channel. + if (socketserver_get_message(ch, &tv) == FAIL) + { + ch = next; + continue; + } + + socketserver_exec(ch, tv->vval.v_dict); + free_tv(tv); + ch = next; + } +} + + +/* + * Poll until there is something to read on "channel". Also handle other + * socketserver channels in the meantime. If "channel" is NULL, then poll all + * channels once then exit. If "timeout" is -1, then wait forever unless + * interrupted. + * + * Return OK on success and FAIL on failure or timeout. + */ + static int +socketserver_wait(channel_T *channel, int timeout) +{ + while (true) + { + int ret; + channel_T *ch; +# ifdef HAVE_SELECT + fd_set rfds; + struct timeval tv; + int maxfd = -1; + + if (timeout != -1) + { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + } + + FD_ZERO(&rfds); + + if (channel != NULL) + { + if (channel->CH_SOCK_FD == INVALID_FD) + // Shouldn't happen + return FAIL; + + maxfd = channel->CH_SOCK_FD; + FD_SET(channel->CH_SOCK_FD, &rfds); + } + if (server_channel != NULL && server_channel->CH_SOCK_FD != INVALID_FD) + { + FD_SET(server_channel->CH_SOCK_FD, &rfds); + if (server_channel->CH_SOCK_FD > maxfd) + maxfd = server_channel->CH_SOCK_FD; + } + + FOR_ALL_CLIENTS(ch) + { + if (ch->CH_SOCK_FD != INVALID_FD) + { + FD_SET(ch->CH_SOCK_FD, &rfds); + if (maxfd < (int)ch->CH_SOCK_FD) + maxfd = (int)ch->CH_SOCK_FD; + } + } + + if (maxfd == -1) + return FAIL; + + ret = select(maxfd + 1, &rfds, NULL, NULL, timeout == -1 ? NULL : &tv); + +# ifdef EINTR + if (ret == -1 && errno == EINTR) + { + if (got_int) + break; + continue; + } +# endif + + if (ret > 0) + { + if (server_channel != NULL + && server_channel->CH_SOCK_FD != INVALID_FD + && FD_ISSET(server_channel->CH_SOCK_FD, &rfds)) + channel_check(server_channel, PART_SOCK); + + FOR_ALL_CLIENTS(ch) + if (ch->CH_SOCK_FD != INVALID_FD + && FD_ISSET(ch->CH_SOCK_FD, &rfds)) + channel_check(ch, PART_SOCK); + + socketserver_parse_messages(); + + if (channel == NULL) + return OK; + + if (channel->CH_SOCK_FD != INVALID_FD + && FD_ISSET(channel->CH_SOCK_FD, &rfds)) + { + channel_check(channel, PART_SOCK); + return OK; + } + continue; + } +# else + struct pollfd fds[MAX_OPEN_CHANNELS + 1]; + int nfd = 0; + int channel_idx = -1; + int server_idx = -1; + + if (channel != NULL) + { + if (channel->CH_SOCK_FD == INVALID_FD) + // Shouldn't happen + return FAIL; + + channel_idx = nfd; + fds[nfd].fd = channel->CH_SOCK_FD; + fds[nfd++].events = POLLIN; + } + if (server_channel != NULL && server_channel->CH_SOCK_FD != INVALID_FD) + { + server_idx = nfd; + fds[nfd].fd = server_channel->CH_SOCK_FD; + fds[nfd++].events = POLLIN; + } + + FOR_ALL_CLIENTS(ch) + if (ch->CH_SOCK_FD != INVALID_FD) + { + fds[nfd].fd = ch->CH_SOCK_FD; + fds[nfd].events = POLLIN; + ch->ch_part[PART_SOCK].ch_poll_idx = nfd; + nfd++; + } + + ret = poll(fds, nfd, timeout); + +# ifdef EINTR + if (ret == -1 && errno == EINTR) + { + if (got_int) + break; + continue; + } +# endif + + if (ret > 0) + { + if (server_channel != NULL + && server_channel->CH_SOCK_FD != INVALID_FD + && fds[server_idx].revents & POLLIN) + channel_check(server_channel, PART_SOCK); + + FOR_ALL_CLIENTS(ch) + if (ch->CH_SOCK_FD != INVALID_FD + && fds[ch->ch_part[PART_SOCK].ch_poll_idx] + .revents & POLLIN) + channel_check(ch, PART_SOCK); + + socketserver_parse_messages(); + + if (channel == NULL) + return OK; + + if (channel->CH_SOCK_FD != INVALID_FD + && fds[channel_idx].revents & POLLIN) + { + channel_check(channel, PART_SOCK); + return OK; + } + continue; + } +# endif + break; + } + return FAIL; +} + +/* + * Parse "name" and create or get the channel connection for it. If "wait" is + * not NULL, then if the client name is a channel ID, then it will be set to + * true. Returns NULL on + * failure. + */ + static channel_T * +socketserver_get_channel(char_u *name, bool quiet, bool *wait) +{ + char_u *address = NULL; + int port; + bool is_unix = false; + channel_T *channel; + + if (STRNICMP(name, "channel:", 8) == 0) + { + if (channel_parse_socketserver_address(name + 8, &port, &address, true) + == FAIL) + return NULL; + if (address != NULL) + is_unix = true; + } + else if (STRNICMP(name, "remotewait:", 11) == 0) + { + // Channel ID name, find channel with that ID. + int id; + + if (name[11] == NUL) + return NULL; + id = strtol((char *)name + 11, NULL, 10); + if (wait != NULL) + *wait = true; + + return channel_find(id); + } + else + { + address = socketserver_get_path(name, false, quiet, NULL); + if (address == NULL) + return NULL; + is_unix = true; + } + + if (is_unix) + channel = channel_open_unix((char *)address, NULL); + else + channel = channel_open("localhost", port, 1000, NULL); + + if (channel == NULL && !quiet) + semsg(_(e_socket_server_failed_connecting), name); + + vim_free(address); + + return channel; +} + +/* + * Send command to address "name". If "ch" is not NULL, it is set to the channel + * for the connection between us and the server, and the channel will not just + * be closed immediately, this is used for --remote-wait. Returns 0 for OK, -1 + * on error. + */ + int +socketserver_send( + char_u *name, + char_u *str, + char_u **result, + bool is_expr, + int timeout, + bool silent, + channel_T **ch) +{ + int rcode = -1; + channel_T *channel; + dict_T *dict; + dictitem_T *di; + typval_T *resp_tv = NULL; + + if (*name == NUL) + { + semsg(_(e_unable_to_send_to_str), name); + return -1; + } + + // Execute locally if target is ourselves + if (serverName != NULL && STRICMP(name, serverName) == 0) + return sendToLocalVim(str, is_expr, result); + + channel = socketserver_get_channel(name, silent, NULL); + if (channel == NULL) + return -1; + + dict = dict_alloc(); + if (dict == NULL) + goto exit; + + dict_add_string(dict, "type", (char_u *)(is_expr ? "expr" : "keystrokes")); + dict_add_string(dict, "str", str); + + // Tell server who we are so it can save our socket path internally for + // later use with server2client. Only do this if we are actually a server. + // + // If we are not a server, then --remote-wait will not work. To handle this + // case, we add "wait" to the JSON message set to true, so that the server + // will create an internal address for our connection to it. This is + // only used for --remote-wait, not exposed to user. + if (server_address != NULL) + dict_add_string(dict, "sender", server_address); + else if (ch != NULL) + dict_add_bool(dict, "wait", true); + + if (socketserver_send_message(channel, dict, "socketserver_send", true) + == FAIL) + { + semsg(_(e_unable_to_send_to_str), name); + dict_unref(dict); + goto exit; + } + dict_unref(dict); + + if (!is_expr) + { + // Exit, we aren't waiting for a response + rcode = 0; + + if (ch != NULL) + { + *ch = channel; + + channel->ch_ss_next = client_channels; + channel->ch_ss_prev = NULL; + channel->ch_ss_close_cb = socketserver_client_close; + client_channels = channel; + + return rcode; + } + goto exit; + } + + if (timeout == 0) + timeout = 1000; + + // To handle recursive calls, we must handle any socketserver channels as + // well. + while (socketserver_wait(channel, timeout) == OK) + if (socketserver_get_message(channel, &resp_tv) == OK) + break; + + if (resp_tv == NULL) + { + // Channel closed, don't make this an error, because this may be the + // result of the expression (e.g. --remote-expr 'execute("qa!")') + rcode = 0; + goto exit; + } + + dict = resp_tv->vval.v_dict; + + di = dict_find(dict, (char_u *)"type", -1); + if (di == NULL || di->di_tv.v_type != VAR_STRING || + (STRCMP(di->di_tv.vval.v_string, "reply") != 0 + && STRCMP(di->di_tv.vval.v_string, "ver") != 0)) + { + ch_error(NULL, "socketserver: unknown reply type"); + free_tv(resp_tv); + goto exit; + } + + if (STRCMP(di->di_tv.vval.v_string, "ver") == 0) + { + emsg(_(e_socket_server_version_mismatch)); + free_tv(resp_tv); + goto exit; + } + + if (result != NULL) + { + di = dict_find(dict, (char_u *)"str", -1); + if (di != NULL && di->di_tv.v_type == VAR_STRING) + *result = vim_strsave(di->di_tv.vval.v_string); + else + { + free_tv(resp_tv); + goto exit; + } + } + + di = dict_find(dict, (char_u *)"code", -1); + if (di != NULL && di->di_tv.v_type == VAR_NUMBER) + rcode = di->di_tv.vval.v_number; + + free_tv(resp_tv); + +exit: + channel_close(channel, false); + channel_clear(channel); + return rcode; +} + + static ss_reply_T * +socketserver_get_reply(char_u *sender, int *index) +{ + for (int i = 0; i < server_replies.ga_len; i++) + { + ss_reply_T *reply = ((ss_reply_T *)server_replies.ga_data) + i; + + if (STRCMP(reply->sender, sender) == 0) + { + if (index != NULL) + *index = i; + return reply; + } + } + return NULL; +} + +/* + * Add reply to list of replies. Returns a pointer to the ss_reply_T that was + * initialized or was found. + */ + static ss_reply_T * +socketserver_add_reply(char_u *sender) +{ + ss_reply_T *reply; + + if (server_replies.ga_growsize == 0) + ga_init2(&server_replies, sizeof(ss_reply_T), 1); + + reply = socketserver_get_reply(sender, NULL); + + if (reply == NULL && ga_grow(&server_replies, 1) == OK) + { + reply = ((ss_reply_T *)server_replies.ga_data) + server_replies.ga_len++; + + reply->sender = vim_strsave(sender); + + if (reply->sender == NULL) + return NULL; + + ga_init2(&reply->strings, sizeof(char_u *), 5); + } + + return reply; +} + + static void +socketserver_remove_reply(char_u *sender) +{ + int index; + ss_reply_T *reply = socketserver_get_reply(sender, &index); + + if (reply != NULL) + { + ss_reply_T *arr = server_replies.ga_data; + int len = server_replies.ga_len; + + // Free strings + vim_free(reply->sender); + ga_clear_strings(&reply->strings); + + // Move all elements after the removed reply forward by one + if (len > 1) + mch_memmove(arr + index, arr + index + 1, + sizeof(ss_reply_T) * (len - index - 1)); + server_replies.ga_len--; + } +} + +/* + * Send a string to "client" as a reply (notification). Returns OK on success + * and FAIL on failure. + */ + int +socketserver_send_reply(char_u *client, char_u *str) +{ + dict_T *dict; + channel_T *channel; + int ret = OK; + bool wait = false; + + if (*client == NUL) + { + semsg(_(e_invalid_server_id_used_str), client); + return FAIL; + } + + if (server_channel == NULL || server_address == NULL) + { + emsg(_(e_socket_server_not_online)); + return FAIL; + } + + channel = socketserver_get_channel(client, false, &wait); + if (channel == NULL) + return FAIL; + + dict = dict_alloc(); + if (dict == NULL) + { + ret = FAIL; + goto exit; + } + + dict_add_string(dict, "type", (char_u *)"notify"); + dict_add_string(dict, "str", str); + if (server_address != NULL) + dict_add_string(dict, "sender", server_address); + + ret = socketserver_send_message(channel, dict, + "socketserver_send_reply", true); + dict_unref(dict); + +exit: + // Don't want to close the channel if client is referenced by channel ID. + // This allows --remote-wait to work with multiple files. + if (!wait) + { + channel_close(channel, false); + channel_clear(channel); + } + + return ret; +} + +/* + * Wait for reply from "client" and place result in "str". Returns OK on success + * and FAIL on failure. Timeout is in milliseconds + */ + int +socketserver_read_reply( + char_u *client, + char_u **str, + int timeout, + bool remotewait) +{ + ss_reply_T *reply = NULL; + char_u *actual; + + if (*client == NUL) + { + semsg(_(e_invalid_server_id_used_str), client); + return FAIL; + } + + if (!remotewait && (server_channel == NULL || server_address == NULL)) + { + emsg(_(e_socket_server_not_online)); + return FAIL; + } + + actual = socketserver_get_path(client, false, false, NULL); + if (actual == NULL) + return FAIL; + + while (true) + { + reply = socketserver_get_reply(actual, NULL); + if (reply != NULL) + break; + if (socketserver_wait(NULL, timeout) == FAIL) + break; + } + + if (reply == NULL || reply->strings.ga_len == 0) + { + vim_free(actual); + return FAIL; + } + + // Consume the string + *str = ((char_u **)reply->strings.ga_data)[0]; + + if (reply->strings.ga_len > 1) + mch_memmove((char_u **)reply->strings.ga_data, + ((char_u **)reply->strings.ga_data) + 1, + sizeof(char_u *) * (reply->strings.ga_len - 1)); + reply->strings.ga_len--; + + if (reply->strings.ga_len < 1) + // Last string removed, remove the reply + socketserver_remove_reply(actual); + + vim_free(actual); + + return OK; +} + +/* + * Check for any replies for "sender". Returns 1 if there is and places the + * reply in "str" without consuming it (note that a copy is not created). + * Returns 0 if otherwise and -1 on + * error. + */ + int +socketserver_peek_reply(char_u *sender, char_u **str) +{ + ss_reply_T *reply; + char_u *actual; + + if (*sender == NUL) + { + semsg(_(e_invalid_server_id_used_str), sender); + return FAIL; + } + + if (server_channel == NULL || server_address == NULL) + { + emsg(_(e_socket_server_not_online)); + return FAIL; + } + + actual = socketserver_get_path(sender, false, false, NULL); + if (actual == NULL) + return FAIL; + + reply = socketserver_get_reply(actual, NULL); + vim_free(actual); + + if (reply != NULL && reply->strings.ga_len > 0) + { + if (str != NULL) + *str = ((char_u **)reply->strings.ga_data)[0]; + return 1; + } + return 0; +} + +#endif // FEAT_SOCKETSERVER diff --git a/src/structs.h b/src/structs.h index 6c6ea8c0e4..8cb9b3f91d 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2795,6 +2795,13 @@ struct channel_S { void (*ch_nb_close_cb)(void); // callback for Netbeans when channel is // closed +#ifdef FEAT_SOCKETSERVER + bool ch_socketserver; // If channel is used by socketserver + void (*ch_ss_close_cb)(channel_T *); + void (*ch_ss_accept_cb)(channel_T *); + channel_T *ch_ss_next; + channel_T *ch_ss_prev; +#endif #ifdef MSWIN int ch_named_pipe; // using named pipe instead of pty diff --git a/src/testdir/dumps/Test_clientserver_1.dump b/src/testdir/dumps/Test_clientserver_1.dump new file mode 100644 index 0000000000..a4a0b42564 --- /dev/null +++ b/src/testdir/dumps/Test_clientserver_1.dump @@ -0,0 +1,8 @@ +|~+0#4040ff13#ffffff0| @73 +|~| @73 +|~| @73 +|E+0#ffffff16#e000002|1|5|6|7|:| |S|o|c|k|e|t| |s|e|r|v|e|r| |p|r|o|t|o|c|o|l| |v|e|r|s|i|o|n| |m|i|s|m|a|t|c|h|,| |c|h|e|c|k| |w|h|a|t| |V|i|m| |v|e|r|s|i|o|n| |y|o|u| +|a|r|e| |u|s|i|n|g| +0#0000000#ffffff0@65 +|E+0#ffffff16#e000002|2|4|1|:| |U|n|a|b|l|e| |t|o| |s|e|n|d| |t|o| |c|h|a|n@1|e|l|:|2|0@2| +0#0000000#ffffff0@38 +@75 +|P+0#00e0003&|r|e|s@1| |E|N|T|E|R| |o|r| |t|y|p|e| |c|o|m@1|a|n|d| |t|o| |c|o|n|t|i|n|u|e> +0#0000000&@35 diff --git a/src/testdir/test_clientserver.vim b/src/testdir/test_clientserver.vim index 0ced9cbc41..15918f7267 100644 --- a/src/testdir/test_clientserver.vim +++ b/src/testdir/test_clientserver.vim @@ -1,5 +1,6 @@ " Tests for the +clientserver feature. +source util/screendump.vim CheckFeature job if !has('clientserver') @@ -10,10 +11,6 @@ CheckFeature clientserver source util/shared.vim -" Unlike X11, we need the socket server running if we want to send commands to -" a server via sockets. -RunSocketServer - func Check_X11_Connection() if has('x11') CheckX11 @@ -192,9 +189,9 @@ func Test_client_server() " When using socket server, server id is not a number, but the path to the " socket. - if has('socketserver') && !has('X11') - call assert_fails("let x = remote_read('vim/10')", ['E573:.*vim/10']) - call assert_fails("call server2client('a/b/c', 'xyz')", ['E573:.*a/b/c']) + if (has('socketserver') && !has('X11') && !has('win32')) || index(v:argv, "socket") != -1 + call assert_fails("let x = remote_read('vim/10')", ['E1564:']) + call assert_fails("call server2client('x/b/c', 'xyz')", ['E1564:']) else call assert_fails("let x = remote_read('vim10')", \ has('unix') ? ['E573:.*vim10'] : 'E277:') @@ -246,11 +243,65 @@ func Test_client_server_stopinsert() endtry endfunc -" Test if socket server and X11 backends can be chosen and work properly. -func Test_client_server_x11_and_socket_server() - CheckNotMSWindows +" Test if socket server, X11, and mswin backends can be chosen and work properly. +func Test_client_server_multiple_backends() + CheckFeature socketserver + + let g:test_is_flaky = 1 + let cmd = GetVimCommand() + + if cmd == '' + throw 'GetVimCommand() failed' + endif + call Check_X11_Connection() + + let types = [ + \ ['socket', "channel:2000"], + \ ['x11', "TEST"], + \ ['mswin', "TEST"], + \ ] + + for [type, expected] in types + if (type == 'x11' && (!has('x11') || !empty($WAYLAND_DISPLAY) || empty($DISPLAY)) + \ || (type == 'mswin' && !has('win32'))) + continue + endif + if has('win32') && has('gui_running') + " Windows gVim --remote-expr shows a dialog window, which blocks tests + " from running. Using --gui-dialog-file does not seem to work either. + continue + endif + + let actual_cmd = cmd .. ' --clientserver ' .. type + let actual_cmd ..= ' --servername ' .. expected + let job = job_start(actual_cmd, {'stoponexit': 'kill', 'out_io': 'null'}) + + call WaitForAssert({-> assert_equal("run", job_status(job))}) + call assert_match(expected, system(actual_cmd .. ' --remote-expr "v:servername"')) + + " On Windows using --remote-expr causes E282, possibly due to some shell + " escaping quirk? When gtk gui is running, using system() seems to cause a + " deadlock when using the x11 backend only... don't use it for now... + if has('win32') || has('gui_running') + call job_stop(job, 'kill') + else + call system(actual_cmd .. " --remote-expr 'execute(\"qa!\")'") + endif + try + call WaitForAssert({-> assert_equal("dead", job_status(job))}) + finally + if job_status(job) != 'dead' + call assert_report('Server did not exit') + call job_stop(job, 'kill') + endif + endtry + endfor + endfunc + +" Test if custom paths work for socketserver +func Test_client_server_socketserver_custom_path() CheckFeature socketserver - CheckFeature x11 + CheckNotMSWindows let g:test_is_flaky = 1 let cmd = GetVimCommand() @@ -258,22 +309,20 @@ func Test_client_server_x11_and_socket_server() if cmd == '' throw 'GetVimCommand() failed' endif - call Check_X11_Connection() - let types = ['socket', 'x11'] + let name = 'VIMTESTSOCKET2' + + let paths = ['./' .. name, '../testdir/' .. name, getcwd(-1) .. '/' .. name] - for type in types - let name = 'VIMTEST_' .. toupper(type) - let actual_cmd = cmd .. ' --clientserver ' .. type - let actual_cmd .= ' --servername ' .. name - let job = job_start(actual_cmd, {'stoponexit': 'kill', 'out_io': 'null'}) + for path in paths + let actual = cmd .. ' --clientserver socket --servername ' .. path - call WaitForAssert({-> assert_equal("run", job_status(job))}) - call WaitForAssert({-> assert_match(name, system(cmd .. ' --clientserver ' .. type .. ' --serverlist'))}) + let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'}) - call assert_match(name, system(actual_cmd .. ' --remote-expr "v:servername"')) + call WaitForAssert({-> assert_equal("run", job_status(job))}) + call WaitForAssert({-> assert_equal(path, glob(path))}) - call system(actual_cmd .. " --remote-expr 'execute(\"qa!\")'") + call system(actual .. " --remote-expr 'execute(\"qa!\")'") try call WaitForAssert({-> assert_equal("dead", job_status(job))}) finally @@ -285,11 +334,10 @@ func Test_client_server_x11_and_socket_server() endfor endfunc -" Test if socket server works in the GUI -func Test_client_server_socket_server_gui() - CheckNotMSWindows +" Test if "channel:" prefix works correctly to use channel address for +" socketserver. +func Test_client_server_socketserver_address() CheckFeature socketserver - CheckFeature gui_gtk let g:test_is_flaky = 1 let cmd = GetVimCommand() @@ -297,23 +345,24 @@ func Test_client_server_socket_server_gui() if cmd == '' throw 'GetVimCommand() failed' endif - call Check_X11_Connection() - let name = 'VIMTESTSOCKET' - let cmd .= ' --clientserver socket' - let cmd .= ' --servername ' .. name + let actual = cmd .. ' --clientserver socket --servername channel:2000' - let job = job_start(cmd, {'stoponexit': 'kill', 'out_io': 'null'}) + let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'}) call WaitForAssert({-> assert_equal("run", job_status(job))}) - call WaitForAssert({-> assert_match(name, system(cmd .. ' --serverlist'))}) - - call system(cmd .. " --remote-expr 'execute(\"gui\")'") - call assert_match('1', system(cmd .. " --remote-expr 'has(\"gui_running\")'")) - call assert_match(name, system(cmd .. ' --remote-expr "v:servername"')) + if !has('win32') || !has('gui_running') + " Does not work with gVim on Windows because it shows an OK dialog box which + " blocks tests from running + call assert_match('channel:2000', system(actual .. ' --remote-expr "v:servername"')) + endif - call system(cmd .. " --remote-expr 'execute(\"qa!\")'") + if has('win32') + call job_stop(job, 'kill') + else + call system(actual .. " --remote-expr 'execute(\"qa!\")'") + endif try call WaitForAssert({-> assert_equal("dead", job_status(job))}) finally @@ -324,41 +373,129 @@ func Test_client_server_socket_server_gui() endtry endfunc -" Test if custom paths work for socketserver -func Test_client_socket_server_custom_path() +" Test if --remote-wait works properly with multiple files +func Test_client_server_multiple_remote_wait() + CheckRunVimInTerminal + + call Check_X11_Connection() + + let buf = RunVimInTerminal('--servername TEST', {'rows': 8}) + call TermWait(buf) + + call writefile(["1"], 'XRemoteOne', 'D') + call writefile(["2"], 'XRemoteTwo', 'D') + + let cmd = GetVimCommand() + + if cmd == '' + throw 'GetVimCommand() failed' + endif + + let actual = cmd .. ' -n --servername TEST --remote-wait ./XRemoteOne ./XRemoteTwo' + let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'}) + + sleep 500m " Wait for server to receive request + call assert_equal("run", job_status(job)) + + call term_sendkeys(buf, "\:next\") + call TermWait(buf) + call assert_equal("run", job_status(job)) + + call term_sendkeys(buf, "\:q!\") " Don't use qa! because we only want to quit one file + call WaitForAssert({-> assert_equal("dead", job_status(job))}) +endfunc + +" Check if socket is removed even if Vim changes directory +func Test_client_server_socketserver_chdir() + CheckFeature socketserver + CheckRunVimInTerminal CheckNotMSWindows + + let buf = RunVimInTerminal('--clientserver socket --servername ./TEST', + \ {'rows': 8}) + call TermWait(buf) + + call term_sendkeys(buf, "\:cd ../\") + call StopVimInTerminal(buf) + + call assert_equal("", glob("./TEST")) +endfunc + +func DummyServerCallback(ch, addr) + let msg = json_encode(#{type: "ver"}) .. "\n" + call ch_sendraw(a:ch, msg) +endfunc + +" Test if commands with different version are ignored and handled properly. +func Test_client_server_socketserver_version_mismatch() CheckFeature socketserver - CheckNotFeature x11 + CheckRunVimInTerminal + CheckScreendump - let g:test_is_flaky = 1 let cmd = GetVimCommand() if cmd == '' throw 'GetVimCommand() failed' endif - let name = 'VIMTESTSOCKET2' + let buf = RunVimInTerminal('--clientserver socket --servername + \ channel:2000', {'rows': 8}) + call TermWait(buf) - let paths = ['./' .. name, '../testdir/' .. name, getcwd(-1) .. '/' .. name] + let ch = ch_open('localhost:2000', #{mode: 'nl'}) + let msg = json_encode(#{ + \ type: "expr", + \ str: "v:servername", + \ version: 999999999 + \ }) .. "\n" - for path in paths - let actual = cmd .. ' --servername ' .. path + call ch_sendraw(ch, msg) + let resp = ch_readraw(ch) - let job = job_start(actual, {'stoponexit': 'kill', 'out_io': 'null'}) + call assert_equal(#{type: "ver"}, json_decode(resp)) - call WaitForAssert({-> assert_equal("run", job_status(job))}) - call WaitForAssert({-> assert_equal(path, glob(path))}) + call StopVimInTerminal(buf) - call system(actual .. " --remote-expr 'execute(\"qa!\")'") - try - call WaitForAssert({-> assert_equal("dead", job_status(job))}) - finally - if job_status(job) != 'dead' - call assert_report('Server did not exit') - call job_stop(job, 'kill') - endif - endtry - endfor + let ch = ch_listen("2000", #{ + \ mode: 'nl', + \ callback: function('DummyServerCallback') + \ }) + + let buf = RunVimInTerminal('--clientserver socket --servername channel:3000', {'rows': 8}) + call TermWait(buf) + + call term_sendkeys(buf, "\:echo remote_expr('channel:2000', 'v:servername', '', 1)\") + call TermWait(buf) + + call VerifyScreenDump(buf, 'Test_clientserver_1', #{wait: 3000}) + + call StopVimInTerminal(buf) +endfunc + +" Test if invalid messages do not crash Vim socketserver. +func Test_clientserver_socketserver_invalid_msg() + CheckFeature socketserver + CheckRunVimInTerminal + + let cmd = GetVimCommand() + + if cmd == '' + throw 'GetVimCommand() failed' + endif + + let buf = RunVimInTerminal('--clientserver socket --servername + \ channel:3000', {'rows': 8}) + call TermWait(buf) + + let ch = ch_open('localhost:3000', #{mode: 'nl'}) + + call ch_sendraw(ch, "wjdaljdsjalsj\n") + call ch_sendraw(ch, "{\"type\": \"unknown\"}\n") + + call assert_match("channel:3000", system(cmd .. " --clientserver socket --servername channel:3000 --remote-expr 'v:servername'")) + call assert_equal("running", term_getstatus(buf)) + + call StopVimInTerminal(buf) endfunc " Uncomment this line to get a debugging log diff --git a/src/testdir/test_remote.vim b/src/testdir/test_remote.vim index ad959e629c..9f23bb0ce1 100644 --- a/src/testdir/test_remote.vim +++ b/src/testdir/test_remote.vim @@ -16,18 +16,6 @@ func Verify_remote_feature_works() let buf = RunVimInTerminal('--servername XVIMTEST', {'rows': 8}) call TermWait(buf) - " For some reason when the socket server is being used, the terminal Vim never - " receives the `:w! XVimRemoteTest.txt` command from term_sendkeys. - if has('socketserver') && !has('X11') - if match(serverlist(), "XVIMTEST") == -1 - call StopVimInTerminal(buf) - throw s:skip - endif - - let s:remote = 1 - return - endif - let cmd = GetVimCommandCleanTerm() .. '--serverlist' call term_sendkeys(buf, ":r! " .. cmd .. "\") call TermWait(buf) @@ -71,6 +59,14 @@ func Test_remote_servername() " open XTEST.txt, if wildignore setting is not ignored, the server " will continue with the Xdummy.log file let buf2 = RunVimInTerminal('--servername XVIMTEST --remote-silent XTEST.txt', {'rows': 5, 'wait_for_ruler': 0}) + + " It may take a while for the command to be sent to XVIMTEST and be executed. + " Do a blocking --remote-expr so that is assured the command was executed. + botright new + let buf3 = RunVimInTerminal('--servername XVIMTEST --remote-expr "v:servername"', {'rows': 5, 'wait_for_ruler': 0}) + call WaitForAssert({-> assert_equal("finished", term_getstatus(buf3))}) + exe buf3 .. 'bw!' + " job should be no-longer running, so we can just close it exe buf2 .. 'bw!' call term_sendkeys(buf, ":sil :3,$d\") diff --git a/src/testdir/test_usercommands.vim b/src/testdir/test_usercommands.vim index 6ad39787e3..8b90a826e4 100644 --- a/src/testdir/test_usercommands.vim +++ b/src/testdir/test_usercommands.vim @@ -402,7 +402,6 @@ func Test_CmdCompletion() exe 'delcommand ' .. cmd endfor delcommand MissingFeature - delcommand RunSocketServer command! DoCmd1 : command! DoCmd2 : diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index b74f324aef..3f95ee8b25 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -1,7 +1,6 @@ " Test using builtin functions in the Vim9 script language. source util/screendump.vim -source util/socketserver.vim import './util/vim9.vim' as v9 " Test for passing too many or too few arguments to builtin functions @@ -3525,11 +3524,12 @@ enddef def Test_remote_expr() CheckFeature clientserver - TrySocketServer + CheckNotMSWindows - if !g:socketserver_only + if has("x11") CheckEnv DISPLAY endif + v9.CheckSourceDefAndScriptFailure(['remote_expr(1, "b")'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1']) v9.CheckSourceDefAndScriptFailure(['remote_expr("a", 2)'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2']) v9.CheckSourceDefAndScriptFailure(['remote_expr("a", "b", 3)'], ['E1013: Argument 3: type mismatch, expected string but got number', 'E1174: String required for argument 3']) @@ -3551,10 +3551,12 @@ enddef def Test_remote_peek() CheckFeature clientserver - TrySocketServer - if !g:socketserver_only + CheckNotMSWindows + + if has("x11") CheckEnv DISPLAY endif + v9.CheckSourceDefAndScriptFailure(['remote_peek(0z10)'], ['E1013: Argument 1: type mismatch, expected string but got blob', 'E1174: String required for argument 1']) v9.CheckSourceDefAndScriptFailure(['remote_peek("a5b6c7", [1])'], ['E1013: Argument 2: type mismatch, expected string but got list', 'E1174: String required for argument 2']) v9.CheckSourceDefExecAndScriptFailure(['remote_peek("")'], 'E573: Invalid server id used') @@ -3562,7 +3564,12 @@ enddef def Test_remote_read() CheckFeature clientserver - CheckEnv DISPLAY + CheckNotMSWindows + + if has("x11") + CheckEnv DISPLAY + endif + v9.CheckSourceDefAndScriptFailure(['remote_read(1)'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1']) v9.CheckSourceDefAndScriptFailure(['remote_read("a", "x")'], ['E1013: Argument 2: type mismatch, expected number but got string', 'E1210: Number required for argument 2']) v9.CheckSourceDefExecAndScriptFailure(['remote_read("")'], 'E573: Invalid server id used') @@ -3570,10 +3577,12 @@ enddef def Test_remote_send() CheckFeature clientserver - TrySocketServer - if !g:socketserver_only + CheckNotMSWindows + + if has("x11") CheckEnv DISPLAY endif + v9.CheckSourceDefAndScriptFailure(['remote_send(1, "b")'], ['E1013: Argument 1: type mismatch, expected string but got number', 'E1174: String required for argument 1']) v9.CheckSourceDefAndScriptFailure(['remote_send("a", 2)'], ['E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2']) v9.CheckSourceDefAndScriptFailure(['remote_send("a", "b", 3)'], ['E1013: Argument 3: type mismatch, expected string but got number', 'E1174: String required for argument 3']) @@ -3582,10 +3591,12 @@ enddef def Test_remote_startserver() CheckFeature clientserver - TrySocketServer - if !g:socketserver_only + CheckNotMSWindows + + if has("x11") CheckEnv DISPLAY endif + v9.CheckSourceDefAndScriptFailure(['remote_startserver({})'], ['E1013: Argument 1: type mismatch, expected string but got dict', 'E1174: String required for argument 1']) enddef diff --git a/src/testdir/util/check.vim b/src/testdir/util/check.vim index 8df985091c..c69d399441 100644 --- a/src/testdir/util/check.vim +++ b/src/testdir/util/check.vim @@ -348,17 +348,6 @@ func CheckGithubActions() endif endfunc -command RunSocketServer call RunSocketServer() -func RunSocketServer() - if has("socketserver") && v:servername == "" - try - call remote_startserver('VIMSOCKETSERVERTEST') - catch " not possible to start a remote server - throw 'Skipped: Cannot start remote server' - endtry - endif -endfunc - let &cpo = s:cpo_save unlet s:cpo_save " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/util/socketserver.vim b/src/testdir/util/socketserver.vim deleted file mode 100644 index 5492db626d..0000000000 --- a/src/testdir/util/socketserver.vim +++ /dev/null @@ -1,17 +0,0 @@ -" Check if only the socketserver backend is available for clientserver (only on -" Unix), and set g:socketserver_only to v:true along with starting the -" socketserver. -command TrySocketServer call TrySocketServer() -func TrySocketServer() - if has("socketserver") && !has("x11") - let g:socketserver_only = v:true - - if v:servername == "" - call remote_startserver('VIMSOCKETSERVERTEST') - endif - else - let g:socketserver_only = v:false - endif -endfunc - -" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/ui.c b/src/ui.c index aa985e4ee8..f277ba4075 100644 --- a/src/ui.c +++ b/src/ui.c @@ -406,20 +406,10 @@ inchar_loop( # endif if ((resize_func != NULL && resize_func(TRUE)) -# if defined(FEAT_CLIENTSERVER) && defined(UNIX) - || ( -# ifdef FEAT_X11 - (clientserver_method == CLIENTSERVER_METHOD_X11 && - server_waiting()) -# endif -# if defined(FEAT_X11) && defined(FEAT_SOCKETSERVER) - || -# endif -# ifdef FEAT_SOCKETSERVER - (clientserver_method == CLIENTSERVER_METHOD_SOCKET && - socket_server_waiting_accept()) -# endif - ) +# if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11) + || + (clientserver_method == CLIENTSERVER_METHOD_X11 && + server_waiting()) # endif # ifdef MESSAGE_QUEUE || interrupted diff --git a/src/version.c b/src/version.c index 7be1f150ba..ce8c620a92 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 512, /**/ 511, /**/ From 25e4e46c584840806b45da20edf8219cf19801a2 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Fri, 22 May 2026 21:46:57 +0000 Subject: [PATCH 287/545] patch 9.2.0513: [security]: memory safety issues in spellfile.c Problem: [security]: memory safety issues in spellfile.c (tacdm) Solution: Add recursion limit to read_tree_node(), add length limit check in tree_count_words(), use alloc_clear() in spell_read_tree(). Github Security Advisory: https://github.com/vim/vim/security/advisories/GHSA-3h95-3962-mmvf Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Christian Brabandt --- src/spell.h | 1 + src/spellfile.c | 42 ++++++++++++++++++++++------------ src/testdir/test_spell.vim | 5 +++- src/testdir/test_spellfile.vim | 18 +++++++++++++++ src/version.c | 2 ++ 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/src/spell.h b/src/spell.h index 1f473732a9..4cef842fa2 100644 --- a/src/spell.h +++ b/src/spell.h @@ -119,6 +119,7 @@ struct slang_S // Info from the .sug file. Loaded on demand. time_t sl_sugtime; // timestamp for .sug file char_u *sl_sbyts; // soundfolded word bytes + long sl_sbyts_len; // length of sl_sbyts idx_T *sl_sidxs; // soundfolded word indexes buf_T *sl_sugbuf; // buffer with word number table int sl_sugloaded; // TRUE when .sug file was loaded or failed to diff --git a/src/spellfile.c b/src/spellfile.c index 5102dad5b6..f65bb642b7 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -313,7 +313,7 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to); static void set_sal_first(slang_T *lp); static int *mb_str2wide(char_u *s); static int spell_read_tree(FILE *fd, char_u **bytsp, long *bytsp_len, idx_T **idxsp, int prefixtree, int prefixcnt); -static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx_T startidx, int prefixtree, int maxprefcondnr); +static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx_T startidx, int prefixtree, int maxprefcondnr, int depth); static void set_spell_charflags(char_u *flags, int cnt, char_u *upp); static int set_spell_chartab(char_u *fol, char_u *low, char_u *upp); static void set_map_str(slang_T *lp, char_u *map); @@ -597,7 +597,7 @@ spell_load_file( * Returns the total number of words. */ static void -tree_count_words(char_u *byts, idx_T *idxs) +tree_count_words(char_u *byts, long byts_len, idx_T *idxs) { int depth; idx_T arridx[MAXWLEN]; @@ -635,8 +635,8 @@ tree_count_words(char_u *byts, idx_T *idxs) ++wordcount[depth]; // Skip over any other NUL bytes (same word with different - // flags). - while (byts[n + 1] == 0) + // flags). But don't go over the end + while (n + 1 < byts_len && byts[n + 1] == 0) { ++n; ++curi[depth]; @@ -732,8 +732,8 @@ suggest_load_files(void) * : * Read the trie with the soundfolded words. */ - if (spell_read_tree(fd, &slang->sl_sbyts, NULL, &slang->sl_sidxs, - FALSE, 0) != 0) + if (spell_read_tree(fd, &slang->sl_sbyts, &slang->sl_sbyts_len, + &slang->sl_sidxs, FALSE, 0) != 0) { someerror: semsg(_(e_error_while_reading_sug_file_str), @@ -782,8 +782,10 @@ suggest_load_files(void) * Need to put word counts in the word tries, so that we can find * a word by its number. */ - tree_count_words(slang->sl_fbyts, slang->sl_fidxs); - tree_count_words(slang->sl_sbyts, slang->sl_sidxs); + tree_count_words(slang->sl_fbyts, slang->sl_fbyts_len, + slang->sl_fidxs); + tree_count_words(slang->sl_sbyts, slang->sl_sbyts_len, + slang->sl_sidxs); nextone: if (fd != NULL) @@ -1603,8 +1605,11 @@ spell_read_tree( if (len <= 0) return 0; - // Allocate the byte array. - bp = alloc(len); + // Allocate the byte array. Zero-initialize so that any position the + // tree does not visit reads as 0; a stray BY_INDEX shared reference + // into such a slot then behaves as end-of-word in spellsuggest() + // instead of consuming an arbitrary heap byte as a siblingcount. + bp = alloc_clear(len); if (bp == NULL) return SP_OTHERERROR; *bytsp = bp; @@ -1618,9 +1623,11 @@ spell_read_tree( *idxsp = ip; // Recursively read the tree and store it in the array. - idx = read_tree_node(fd, bp, ip, len, 0, prefixtree, prefixcnt); + idx = read_tree_node(fd, bp, ip, len, 0, prefixtree, prefixcnt, 0); if (idx < 0) return idx; + if (idx != len) + return SP_FORMERROR; return 0; } @@ -1642,7 +1649,8 @@ read_tree_node( int maxidx, // size of arrays idx_T startidx, // current index in "byts" and "idxs" int prefixtree, // TRUE for reading PREFIXTREE - int maxprefcondnr) // maximum for + int maxprefcondnr, // maximum for + int depth) // recursiong level { int len; int i; @@ -1652,6 +1660,12 @@ read_tree_node( int c2; #define SHARED_MASK 0x8000000 + // Bail out on a crafted .spl whose tree recurses beyond the maximum + // word length: each tree level corresponds to one byte of a word, so + // any well-formed file has depth <= MAXWLEN. + if (depth > MAXWLEN) + return SP_FORMERROR; + len = getc(fd); // if (len <= 0) return SP_TRUNCERROR; @@ -1737,7 +1751,7 @@ read_tree_node( { idxs[startidx + i] = idx; idx = read_tree_node(fd, byts, idxs, maxidx, idx, - prefixtree, maxprefcondnr); + prefixtree, maxprefcondnr, depth + 1); if (idx < 0) break; } @@ -5649,7 +5663,7 @@ sug_filltree(spellinfo_T *spin, slang_T *slang) spin->si_blocks_cnt = 0; // Skip over any other NUL bytes (same word with different - // flags). But don't go over the end. + // flags). But don't go over the end while (n + 1 < slang->sl_fbyts_len && byts[n + 1] == 0) { ++n; diff --git a/src/testdir/test_spell.vim b/src/testdir/test_spell.vim index 58a2d58707..3b7f727d80 100644 --- a/src/testdir/test_spell.vim +++ b/src/testdir/test_spell.vim @@ -912,7 +912,10 @@ func Test_spellsuggest_too_deep() " This was incrementing "depth" over MAXWLEN. new norm s000G00ý000000000000 - sil norm ..vzG................vvzG0 v z= + try + sil norm ..vzG................vvzG0 v z= + catch /E759:/ + endtry bwipe! endfunc diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim index 8f3ef4907d..cf75eb4ef9 100644 --- a/src/testdir/test_spellfile.vim +++ b/src/testdir/test_spellfile.vim @@ -372,6 +372,24 @@ func Test_spellfile_format_error() " LWORDTREE: incorrect sibling node count call Spellfile_Test(0zFF00000001040000000000000000, 'E759:') + " LWORDTREE: declared nodecount larger than the tree actually fills. + " Root has two siblings: 'x' (recurses into an end-of-word at idx 3..4) + " and BY_INDEX targeting position 9. Tree fills positions 0..4, leaving + " 5..9 unwritten — byts[9] would be uninitialized without the fix. + call Spellfile_Test(0zFF0000000A02780100000979010000000000000000000000, 'E759:') + + " LWORDTREE: recursion depth past MAXWLEN. A linear chain of 254 + " (siblingcount=1, byte='a') frames drives read_tree_node to depth + " MAXWLEN where the new guard rejects. The trailing (01 00) gives the + " chain a clean end-of-word so an *unguarded* parser would accept the + " file silently — that's what makes this a meaningful regression test + " for the depth check specifically (a deeper chain would also crash + " unguarded builds via stack overflow, which we don't want in CI). + let v = eval('0zFF00000200' .. repeat('0161', 255) + \ .. '0100' .. repeat('00', 8)) + + call Spellfile_Test(v, 'E759:') + " KWORDTREE: missing tree node call Spellfile_Test(0zFF0000000000000004, 'E758:') diff --git a/src/version.c b/src/version.c index ce8c620a92..f0e32a02a9 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 513, /**/ 512, /**/ From ea71d5bb01ead1f982148771799462e91136107f Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Fri, 22 May 2026 22:18:03 +0000 Subject: [PATCH 288/545] patch 9.2.0514: GTK4: build errors when socketserver is enabled Problem: GTK4: build errors when socketserver is enabled (after v9.2.0512) Solution: Drop unused functions (Foxe Chen) closes: #20293 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 60 ------------------------------------------ src/proto/gui_gtk4.pro | 2 -- src/version.c | 2 ++ 3 files changed, 2 insertions(+), 62 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index 083ac9f400..508a33d587 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -112,13 +112,6 @@ vim_parse_geometry(const char *str, int *x, int *y, return mask; } -#ifdef FEAT_SOCKETSERVER -# include - -// Used to track the source for the listening socket -static guint socket_server_source_id = 0; -#endif - #if defined(FEAT_MOUSESHAPE) // Last set mouse pointer shape static int last_shape = 0; @@ -2991,59 +2984,6 @@ gui_get_x11_windis(Window *win UNUSED, Display **dis UNUSED) return FAIL; } -#if defined(FEAT_SOCKETSERVER) - -/* - * Callback for new events from the socket server listening socket. - */ - static int -socket_server_poll_in(int fd UNUSED, GIOCondition cond, - void *user_data UNUSED) -{ - if (cond & G_IO_IN) - socket_server_accept_client(); - else if (cond & (G_IO_ERR | G_IO_HUP)) - { - socket_server_uninit(); - return FALSE; - } - - return TRUE; -} - -#endif // FEAT_SOCKETSERVER - -/* - * Initialize socket server for use in the GUI (does not actually initialize - * the socket server, only attaches a source). - */ - void -gui_gtk_init_socket_server(void) -{ -#if defined(FEAT_SOCKETSERVER) - if (socket_server_source_id > 0) - return; - // Register source for file descriptor to global default context - socket_server_source_id = g_unix_fd_add(socket_server_get_fd(), - G_IO_IN | G_IO_ERR | G_IO_HUP, socket_server_poll_in, NULL); -#endif -} - -/* - * Remove the source for the socket server listening socket. - */ - void -gui_gtk_uninit_socket_server(void) -{ -#if defined(FEAT_SOCKETSERVER) - if (socket_server_source_id > 0) - { - g_source_remove(socket_server_source_id); - socket_server_source_id = 0; - } -#endif -} - void gui_gtk_set_mnemonics(int enable UNUSED) { diff --git a/src/proto/gui_gtk4.pro b/src/proto/gui_gtk4.pro index 1f13ab6ca6..7359c750c7 100644 --- a/src/proto/gui_gtk4.pro +++ b/src/proto/gui_gtk4.pro @@ -70,8 +70,6 @@ void gui_mch_destroy_sign(void *sign); int gui_gtk_draw_string_ext(int row, int col, char_u *s, int len, int flags, int force_pango); int gui_gtk_draw_string(int row, int col, char_u *s, int len, int flags); int gui_get_x11_windis(Window *win, Display **dis); -void gui_gtk_init_socket_server(void); -void gui_gtk_uninit_socket_server(void); void gui_gtk_set_mnemonics(int enable); void gui_make_popup(char_u *path_name, int mouse_pos); int get_menu_tool_width(void); diff --git a/src/version.c b/src/version.c index f0e32a02a9..a2674417da 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 514, /**/ 513, /**/ From 3d0a6073e560dd3b06a2f05ae01f0e9c59e68224 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Fri, 22 May 2026 23:08:29 +0000 Subject: [PATCH 289/545] patch 9.2.0515: virtualedit=insert doesn't work during change operation Problem: virtualedit=insert doesn't work during change operation (after 6.1.014). Solution: Make virtual_op only affect virtualedit=block (zeertzjq). related: neovim/neovim#35391 closes: #20298 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/misc2.c | 9 ++++---- src/testdir/test_virtualedit.vim | 35 ++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/misc2.c b/src/misc2.c index 076593d6e0..25dab5ed13 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -24,15 +24,16 @@ virtual_active(void) { unsigned int cur_ve_flags = get_ve_flags(); + if (cur_ve_flags == VE_ALL + || ((cur_ve_flags & VE_INSERT) && (State & MODE_INSERT))) + return TRUE; + // While an operator is being executed we return "virtual_op", because // VIsual_active has already been reset, thus we can't check for "block" // being used. if (virtual_op != MAYBE) return virtual_op; - return (cur_ve_flags == VE_ALL - || ((cur_ve_flags & VE_BLOCK) && VIsual_active - && VIsual_mode == Ctrl_V) - || ((cur_ve_flags & VE_INSERT) && (State & MODE_INSERT))); + return (cur_ve_flags & VE_BLOCK) && VIsual_active && VIsual_mode == Ctrl_V; } /* diff --git a/src/testdir/test_virtualedit.vim b/src/testdir/test_virtualedit.vim index 7819025f6b..902bbbfd1e 100644 --- a/src/testdir/test_virtualedit.vim +++ b/src/testdir/test_virtualedit.vim @@ -753,4 +753,39 @@ func Test_virtualedit_getpos_stable_past_eol_after_visual() bwipe! endfunc +func Test_virtualedit_insert() + new + set virtualedit=insert + + call feedkeys("ifoobar\\\\baz\", 'tnix') + call assert_equal('foobar baz', getline(1)) + + call feedkeys("ccFOOBAR\\\\BAZ\", 'tnix') + call assert_equal('FOOBAR BAZ', getline(1)) + + set virtualedit& + bwipe! +endfunc + +func Test_set_virtualedit_on_mode_change() + new + set virtualedit=all + augroup testing + au ModeChanged n:* set virtualedit=onemore + au ModeChanged *:n set virtualedit=all + au ModeChanged i:* call cursor(getpos("'^")[1:]) + augroup END + + call feedkeys("ilkj\", 'tnix') + call assert_equal([0, 1, 4, 0], getpos('.')) + + call feedkeys("cclkj\", 'tnix') + call assert_equal([0, 1, 4, 0], getpos('.')) + + au! testing ModeChanged + augroup! testing + set virtualedit& + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index a2674417da..dd6dc15985 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 515, /**/ 514, /**/ From 37223f47b178d7e66173271e65d84de4413af6e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 22 May 2026 23:12:27 +0000 Subject: [PATCH 290/545] CI: Bump github/codeql-action Bumps the github-actions group with 1 update in the / directory: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 4.35.4 to 4.35.5 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v4.35.4...v4.35.5) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.35.5 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... closes: #20297 Signed-off-by: dependabot[bot] Signed-off-by: Christian Brabandt --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 856ce7f241..ae2a4a5315 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -48,7 +48,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4.35.4 + uses: github/codeql-action/init@v4.35.5 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -59,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v4.35.4 + uses: github/codeql-action/autobuild@v4.35.5 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -73,4 +73,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4.35.4 + uses: github/codeql-action/analyze@v4.35.5 From 6574102fb4a4b31c0988ea1c1de0ec4a665cc1c8 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Sat, 23 May 2026 15:47:32 +0000 Subject: [PATCH 291/545] runtime(doc): Tweak documentation style closes: #20296 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/autocmd.txt | 7 ++++--- runtime/doc/syntax.txt | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 62cd761e19..b3d1deb25f 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1,4 +1,4 @@ -*autocmd.txt* For Vim version 9.2. Last change: 2026 May 10 +*autocmd.txt* For Vim version 9.2. Last change: 2026 May 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1393,8 +1393,9 @@ TextPutPre Before text has been put in the current buffer. is valid to call |setreg()| in this autocommand, allowing you to process and modify the text in "regcontents" before it is - put. However this does not apply to |quote_#|, - |quote_=|, |quote_%|, |quote_:|, |quote_/| or |quote_.|. + put. However this does not apply to + |quote_#|, |quote_=|, |quote_%|, |quote_:|, + |quote_/| or |quote_.|. Not triggered when |quote_| is used nor when called recursively. It is not allowed to change the buffer text, diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 428e8a5522..b1a18644a1 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 May 17 +*syntax.txt* For Vim version 9.2. Last change: 2026 May 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -6130,7 +6130,8 @@ SpellRare Word that is recognized by the spellchecker as one that is StatusLine Status line of current window. The highlight at the status line's edge (StatusLine, or any %#... / %N* in 'statusline') also extends into the adjacent vertical separator cell when - the status lines are connected (no 'vert' character drawn). + the status lines are connected (The "vert" in 'fillchars' is + not being drawn). *hl-StatusLineNC* StatusLineNC status lines of not-current windows. Like |hl-StatusLine|, the edge highlight also extends into the adjacent vertical From e3cb9655d7b0fbd4c2a14f7e300fafae374e3be2 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Sat, 23 May 2026 15:55:28 +0000 Subject: [PATCH 292/545] patch 9.2.0516: socketserver: spurious error when servername is taken Problem: socketserver: when searching for a free socket path, socketserver_get_path() emits an error for each name that is already in use (after v9.2.0512). Solution: Add an "ignore" argument to socketserver_get_path() to suppress the error (Foxe Chen). Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/socketserver.c | 24 ++++++++++++++++-------- src/version.c | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/socketserver.c b/src/socketserver.c index 7c4e19b536..bc98ae4e2b 100644 --- a/src/socketserver.c +++ b/src/socketserver.c @@ -63,7 +63,7 @@ static channel_T *client_channels = NULL; static void socketserver_cleanup(void); static char_u *socketserver_create_path(char_u *name, bool quiet); -static char_u *socketserver_get_path(char_u *name, bool new, bool quiet, bool *fatal); +static char_u *socketserver_get_path(char_u *name, bool new, bool quiet, bool ignore, bool *fatal); static void socketserver_accept(channel_T *channel); static void socketserver_close(channel_T *channel); static ss_reply_T *socketserver_add_reply(char_u *sender); @@ -339,10 +339,10 @@ socketserver_create_path(char_u *name, bool quiet) if (buf != NULL) { vim_snprintf((char *)buf, buflen, "%s%d", name, i); - path = socketserver_get_path(buf, true, quiet, &fatal); + path = socketserver_get_path(buf, true, quiet, true, &fatal); } else - path = socketserver_get_path(name, true, quiet, &fatal); + path = socketserver_get_path(name, true, quiet, true, &fatal); if (fatal) break; @@ -378,12 +378,20 @@ socketserver_create_path(char_u *name, bool quiet) * If "new" is true, then return a path if the name does not exist in the known * location. * + * If "ignore" is true, then don't emit an error message if a suitable path + * could not be found. + * * If "fatal" is not NULL, then it is set to true if error is fatal. * * Returns path on success and NULL on failure. */ static char_u * -socketserver_get_path(char_u *name, bool new UNUSED, bool quiet, bool *fatal) +socketserver_get_path( + char_u *name, + bool new UNUSED, + bool quiet, + bool ignore UNUSED, + bool *fatal) { # ifdef MSWIN // Only support channel addresses on Windows @@ -507,7 +515,7 @@ socketserver_get_path(char_u *name, bool new UNUSED, bool quiet, bool *fatal) { if (!res) semsg(_("Failed creating socket directory: %s"), strerror(errno)); - else + else if (!ignore) semsg(_(e_invalid_server_id_used_str), name); } if (!res && fatal != NULL) @@ -1011,7 +1019,7 @@ socketserver_get_channel(char_u *name, bool quiet, bool *wait) } else { - address = socketserver_get_path(name, false, quiet, NULL); + address = socketserver_get_path(name, false, quiet, false, NULL); if (address == NULL) return NULL; is_unix = true; @@ -1324,7 +1332,7 @@ socketserver_read_reply( return FAIL; } - actual = socketserver_get_path(client, false, false, NULL); + actual = socketserver_get_path(client, false, false, false, NULL); if (actual == NULL) return FAIL; @@ -1385,7 +1393,7 @@ socketserver_peek_reply(char_u *sender, char_u **str) return FAIL; } - actual = socketserver_get_path(sender, false, false, NULL); + actual = socketserver_get_path(sender, false, false, false, NULL); if (actual == NULL) return FAIL; diff --git a/src/version.c b/src/version.c index dd6dc15985..92eea5baa2 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 516, /**/ 515, /**/ From ecba601e3f10fc046829877a0a4682a54cc27da0 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 23 May 2026 16:07:16 +0000 Subject: [PATCH 293/545] runtime(doc): fix a few small problems closes: #20287 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- runtime/doc/map.txt | 5 ++--- runtime/doc/syntax.txt | 2 +- runtime/doc/usr_30.txt | 6 +++--- src/socketserver.c | 2 +- src/spellfile.c | 6 +++--- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 58f27d099e..38de46c657 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1,4 +1,4 @@ -*map.txt* For Vim version 9.2. Last change: 2026 May 21 +*map.txt* For Vim version 9.2. Last change: 2026 May 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -208,8 +208,7 @@ be effective in the current buffer only. Example: > Then you can map ",w" to something else in another buffer: > :map ,w /[#&!] The local buffer mappings are used before the global ones. See below -to make a short local mapping taking effect when a longer global one -exists. +to make a short local mapping take effect when a longer global one exists. The "" argument can also be used to clear mappings: > :unmap ,w :mapclear diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index b1a18644a1..b9013eee69 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -852,7 +852,7 @@ See |ft-ada-syntax| ALGOL 68 *algol68* *ft-algol68-syntax* This syntax file currently targets the Algol 68 Genie project using the -default UPPER stropping regime. It should also be usuable in other +default UPPER stropping regime. It should also be usable in other environments using the UPPER stropping regime, though somewhat less complete. Highlighting can be further configured with the following variables. diff --git a/runtime/doc/usr_30.txt b/runtime/doc/usr_30.txt index 7b9dbf1cb4..cae3749969 100644 --- a/runtime/doc/usr_30.txt +++ b/runtime/doc/usr_30.txt @@ -1,4 +1,4 @@ -*usr_30.txt* For Vim version 9.2. Last change: 2026 May 15 +*usr_30.txt* For Vim version 9.2. Last change: 2026 May 23 VIM USER MANUAL by Bram Moolenaar @@ -531,8 +531,8 @@ they do when using only tab characters. Vim 5.4 introduced the 'softtabstop' option. On top of the (hard) tab stops used to display the horizontal tab characters in the text, Vim adds extra soft tab stops dedicated only to the cursor. When 'softtabstop' is set to a -positive value, the key will push the cursor to the next soft tab -stop. Vim will insert the correct combination of tab characters and spaces to +positive value, the key will push the cursor to the next soft tab stop. +Vim will insert the correct combination of tab characters and spaces to achieve this effect. Likewise pressing will have the cursor try to reach the nearest soft tab stop. The following example uses `:set softtabstop=4` diff --git a/src/socketserver.c b/src/socketserver.c index bc98ae4e2b..48c00c3580 100644 --- a/src/socketserver.c +++ b/src/socketserver.c @@ -698,7 +698,7 @@ socketserver_exec(channel_T *channel, dict_T *message) vim_free(result); } else - // Error occured, return error message + // Error occurred, return error message dict_add_string(dict, "str", (char_u *)_(e_invalid_expression_received)); diff --git a/src/spellfile.c b/src/spellfile.c index f65bb642b7..9ee7031e55 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -635,7 +635,7 @@ tree_count_words(char_u *byts, long byts_len, idx_T *idxs) ++wordcount[depth]; // Skip over any other NUL bytes (same word with different - // flags). But don't go over the end + // flags). But don't go over the end. while (n + 1 < byts_len && byts[n + 1] == 0) { ++n; @@ -1650,7 +1650,7 @@ read_tree_node( idx_T startidx, // current index in "byts" and "idxs" int prefixtree, // TRUE for reading PREFIXTREE int maxprefcondnr, // maximum for - int depth) // recursiong level + int depth) // recursion level { int len; int i; @@ -5663,7 +5663,7 @@ sug_filltree(spellinfo_T *spin, slang_T *slang) spin->si_blocks_cnt = 0; // Skip over any other NUL bytes (same word with different - // flags). But don't go over the end + // flags). But don't go over the end. while (n + 1 < slang->sl_fbyts_len && byts[n + 1] == 0) { ++n; From cb8510d4703c13b34e178067ffe48a24c9a3ad32 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sat, 23 May 2026 18:16:22 +0000 Subject: [PATCH 294/545] patch 9.2.0517: quickfix: can set quickfixtextfunc in restricted/sandbox mode Problem: quickfix: can set quickfixtextfunc in restricted/sandbox mode (tacdm) Solution: Disallow setting the quickfixtextfunc option from a sandbox and restricted mode (Yegappan Lakshmanan). closes: #20305 Co-Authored-by: tacdm Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- src/quickfix.c | 5 ++++- src/testdir/test_quickfix.vim | 34 +++++++++++++++++++++++++++++++++ src/testdir/test_restricted.vim | 1 + src/version.c | 2 ++ 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/quickfix.c b/src/quickfix.c index feeec18125..3fe015ee58 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -8173,13 +8173,16 @@ qf_setprop_curidx(qf_info_T *qi, qf_list_T *qfl, dictitem_T *di) } /* - * Set the current index in the specified quickfix list + * Set the 'quickfixtextfunc' in the specified quickfix/location list */ static int qf_setprop_qftf(qf_info_T *qi UNUSED, qf_list_T *qfl, dictitem_T *di) { callback_T cb; + if (check_restricted() || check_secure()) + return FAIL; + free_callback(&qfl->qf_qftf_cb); cb = get_callback(&di->di_tv); if (cb.cb_name == NULL || *cb.cb_name == NUL) diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index 64ec97f504..e1dbaa7c59 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -7028,4 +7028,38 @@ func Test_efm_overlongline() call setqflist([], 'f') endfunc +func Xtest_set_qftf_in_sandbox(cchar) + call s:setup_commands(a:cchar) + + call g:Xsetlist([{'filename': 'test.c', 'lnum': 1, 'text': 'trigger'}]) + let g:qftf_fn_called = v:false + func Qftf_Fn(d) + let g:qftf_fn_called = v:true + return [] + endfunc + + let g:caught_exception = v:false + try + sandbox call g:Xsetlist([], 'a', #{quickfixtextfunc: 'g:Qftf_Fn'}) + catch /E48:/ + let g:caught_exception = v:true + endtry + copen + cclose + + call assert_equal(v:true, g:caught_exception) + call assert_equal(v:false, g:qftf_fn_called) + + delfunc Qftf_Fn + unlet g:caught_exception + unlet g:qftf_fn_called + %bw! +endfunc + +" Test for setting the 'quickfixtextfunc' in a sandbox +func Test_set_qftf_in_sandbox() + call Xtest_set_qftf_in_sandbox('c') + call Xtest_set_qftf_in_sandbox('l') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_restricted.vim b/src/testdir/test_restricted.vim index d9810826a0..21133089ee 100644 --- a/src/testdir/test_restricted.vim +++ b/src/testdir/test_restricted.vim @@ -95,6 +95,7 @@ func Test_restricted_mode() if has('unix') call assert_fails('cd `pwd`', 'E145:') endif + call assert_fails("call setqflist([], 'a', {'id': 1, 'quickfixtextfunc': 'tr'})", 'E145:') call writefile(v:errors, 'Xresult') qa! diff --git a/src/version.c b/src/version.c index 92eea5baa2..f3dd63e775 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 517, /**/ 516, /**/ From f42ce78f770a68a8bb15209cc6def9b7763062f6 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sat, 23 May 2026 18:25:16 +0000 Subject: [PATCH 295/545] patch 9.2.0518: GTK4: input method cannot compose text Problem: GTK4: input method cannot compose text (lilydjwg, after v9.2.0501) Solution: Render the over-the-spot preedit with a GtkPopover instead of a separate toplevel, so the compositor keeps keyboard focus on the drawing area and does not disable text-input-v3; attach the key controller to gui.drawarea and filter key events manually with gtk_im_context_filter_keypress() (Yasuhiro Matsumoto) fixes: #20257 closes: #20266 Co-Authored-by: lilydjwg Co-authored-by: Claude Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 16 ++- src/gui_xim.c | 293 +++++++++++++++++++++++++++++++++++++++++-------- src/version.c | 2 + 3 files changed, 263 insertions(+), 48 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index 508a33d587..bc66ba9e7f 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -528,7 +528,10 @@ gui_mch_init(void) G_CALLBACK(key_press_event), NULL); g_signal_connect(key_ctrl, "key-released", G_CALLBACK(key_release_event), NULL); - gtk_widget_add_controller(gui.mainwin, key_ctrl); + gtk_widget_add_controller(gui.drawarea, key_ctrl); +#ifdef FEAT_XIM + xim_init(); +#endif } { @@ -1103,6 +1106,10 @@ gui_mch_init_font(char_u *font_name, int fontset UNUSED) get_styled_font_variants(); ascii_glyph_table_init(); + // im window position depends on cursor size which depends on font metrics + // update the position after we've initialized font + im_set_position(gui.row, gui.col); + return OK; } @@ -1859,15 +1866,14 @@ drawarea_realize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED) gui.surface = create_backing_surface(w, h); gui_mch_new_colors(); - -#ifdef FEAT_XIM - xim_init(); -#endif } static void drawarea_unrealize_cb(GtkWidget *widget UNUSED, gpointer data UNUSED) { +#ifdef FEAT_XIM + im_shutdown(); +#endif if (gui.surface != NULL) { cairo_surface_destroy(gui.surface); diff --git a/src/gui_xim.c b/src/gui_xim.c index acc6351936..ec4620f38b 100644 --- a/src/gui_xim.c +++ b/src/gui_xim.c @@ -198,6 +198,15 @@ static unsigned int im_activatekey_state = 0; static GtkWidget *preedit_window = NULL; static GtkWidget *preedit_label = NULL; +# ifdef USE_GTK4 +static int preedit_label_width = 0; // last measured popover natural width +static int preedit_popover_height = 0; // last measured popover natural height +// CSS provider that styles the preedit popover frame; rebuilt whenever +// gui.back_pixel changes so the popover background matches the editor's +// background colour and fully covers the cursor cell. +static GtkCssProvider *preedit_css_provider = NULL; +static guicolor_T preedit_css_cached_bg = INVALCOLOR; +# endif static void im_preedit_window_set_position(void); @@ -235,9 +244,28 @@ im_set_position(int row, int col) area.x = FILL_X(col); area.y = FILL_Y(row); - area.width = gui.char_width * (mb_lefthalve(row, col) ? 2 : 1); + // The screen buffer may not be allocated yet when this is called from + // xim_init() during gui_mch_init() to seed the IM's initial cursor + // location, so guard mb_lefthalve() against a NULL LineOffset[]. + area.width = gui.char_width + * ((ScreenLines != NULL && mb_lefthalve(row, col)) ? 2 : 1); area.height = gui.char_height; +# ifdef USE_GTK4 + // Stretch the cursor rect height to cover the preedit popover so the + // IM's candidate window is placed below the popover rather than on + // top of it. Coordinates stay in client-widget (drawarea) space; + // GTK4's IM module translates to the surface internally. + if (p_imst == IM_OVER_THE_SPOT) + { + int popover_h = preedit_popover_height; + if (popover_h <= 0) + popover_h = gui.char_height; + if (popover_h > area.height) + area.height = popover_h; + } +# endif + gtk_im_context_set_cursor_location(xic, &area); if (p_imst == IM_OVER_THE_SPOT) @@ -274,25 +302,46 @@ im_add_to_input(char_u *str, int len) static void im_preedit_window_set_position(void) { - int x, y, width, height; - int screen_x, screen_y, screen_width, screen_height; - if (preedit_window == NULL) return; # ifdef USE_GTK4 - // GTK4: positioning popup windows is limited. - // Use a simpler approach - just place near the cursor. - x = FILL_X(gui.col); - y = FILL_Y(gui.row) + gui.char_height; - width = 0; - height = 0; - screen_x = 0; - screen_y = 0; - screen_width = 0; - screen_height = 0; - // GTK4 doesn't have gtk_window_move; preedit is shown in-place. + // GTK4: The popover is parented to gui.mainwin (the toplevel) rather + // than gui.drawarea, because GtkDrawingArea is a leaf widget that does + // not support children and causes snapshot/redraw artifacts when used + // as a popover parent. Translate the cursor cell coordinates from + // drawarea space to mainwin space, and use the cached label width as + // the anchor rect width so the popover's left edge aligns with the + // cursor cell: + // popup_origin.x = anchor.x + anchor.w/2 - popup_w/2 + // With anchor.w == label_w and popup_w ≈ label_w (after CSS zeroing + // popover frame padding), popup_origin.x ≈ anchor.x. + GdkRectangle rect; + graphene_point_t pt_in, pt_out; + + pt_in.x = FILL_X(gui.col); + pt_in.y = FILL_Y(gui.row); + if (!gtk_widget_compute_point(gui.drawarea, gui.mainwin, + &pt_in, &pt_out)) + { + pt_out.x = pt_in.x; + pt_out.y = pt_in.y; + } + rect.x = (int)pt_out.x; + rect.y = (int)pt_out.y; + rect.width = preedit_label_width; + rect.height = 0; + // make up for the difference between GVim's line height and GtkLabel's line height + int offset = gui.char_height - preedit_popover_height; + gtk_popover_set_offset(GTK_POPOVER(preedit_window), 0, offset); + // make the arrow pointer at start so that it only starts sliding + // horizontally when screen border is reached + gtk_widget_set_halign(preedit_window, GTK_ALIGN_START); + gtk_popover_set_pointing_to(GTK_POPOVER(preedit_window), &rect); # else + int x, y, width, height; + int screen_x, screen_y, screen_width, screen_height; + gui_gtk_get_screen_geom_of_win(gui.drawarea, 0, 0, &screen_x, &screen_y, &screen_width, &screen_height); gdk_window_get_origin(gtk_widget_get_window(gui.drawarea), &x, &y); @@ -328,27 +377,99 @@ im_preedit_window_open(void) if (preedit_window == NULL) { # ifdef USE_GTK4 - preedit_window = gtk_window_new(); + // GTK4: use GtkPopover (xdg_popup) so the compositor keeps keyboard + // focus on the drawing area and does not disable text-input-v3. + // See issue #20257. Parent the popover to gui.mainwin (a real + // container) rather than gui.drawarea: GtkDrawingArea is a leaf + // widget and using it as a popover parent causes snapshot artifacts + // (white-out) on the drawing area. + preedit_window = gtk_popover_new(); + gtk_popover_set_autohide(GTK_POPOVER(preedit_window), FALSE); + gtk_popover_set_has_arrow(GTK_POPOVER(preedit_window), FALSE); + // Pin the popover below the anchor. Together with anchor.h == 0 in + // im_preedit_window_set_position() this lands the popover's top edge + // exactly on FILL_Y(row), so the popover covers the cursor cell rather + // than landing above it (the default GTK_POS_TOP would do that and + // only flip to bottom when there is no room above, which is flaky). + gtk_popover_set_position(GTK_POPOVER(preedit_window), GTK_POS_BOTTOM); + gtk_widget_set_can_focus(preedit_window, FALSE); + gtk_widget_set_focusable(preedit_window, FALSE); + gtk_widget_set_can_target(preedit_window, FALSE); + gtk_widget_set_parent(preedit_window, gui.mainwin); # else preedit_window = gtk_window_new(GTK_WINDOW_POPUP); -# endif gtk_window_set_transient_for(GTK_WINDOW(preedit_window), GTK_WINDOW(gui.mainwin)); +# endif preedit_label = gtk_label_new(""); gtk_widget_set_name(preedit_label, "vim-gui-preedit-area"); # ifdef USE_GTK4 - gtk_window_set_child(GTK_WINDOW(preedit_window), preedit_label); + gtk_label_set_xalign(GTK_LABEL(preedit_label), 0.0); + gtk_widget_set_halign(preedit_label, GTK_ALIGN_START); + gtk_widget_set_valign(preedit_label, GTK_ALIGN_START); + // Tag the popover with a CSS class so the minimal frame CSS below + // (no border, no rounded corners, no shadow, no padding) applies + // only to our preedit popover and not other popovers in the app. + gtk_widget_add_css_class(preedit_window, "vim-preedit-popover"); + gtk_popover_set_child(GTK_POPOVER(preedit_window), preedit_label); # else gtk_container_add(GTK_CONTAINER(preedit_window), preedit_label); # endif } -# if GTK_CHECK_VERSION(3,16,0) +# ifdef USE_GTK4 + // Build (or rebuild on background-colour change) the CSS that flattens + // the popover frame and paints its background with gui.back_pixel. + // Using a solid background instead of `background: transparent` makes + // the popover surface fully cover the cursor cell even when the popover + // surface is allocated a pixel or two larger than the glyph bounding + // box (e.g. due to residual theme insets) -- otherwise the blinking + // text cursor at the cell origin shows through the popover frame. + if (preedit_css_provider == NULL || preedit_css_cached_bg != gui.back_pixel) + { + GdkDisplay *display = gdk_display_get_default(); + gchar *css; + + if (preedit_css_provider != NULL) + { + gtk_style_context_remove_provider_for_display(display, + GTK_STYLE_PROVIDER(preedit_css_provider)); + g_object_unref(preedit_css_provider); + } + preedit_css_provider = gtk_css_provider_new(); + css = g_strdup_printf( + "popover.vim-preedit-popover,\n" + "popover.vim-preedit-popover > contents,\n" + "popover.vim-preedit-popover label {\n" + " border: none;\n" + " border-radius: 0;\n" + " box-shadow: none;\n" + " padding: 0;\n" + " margin: 0;\n" + " min-width: 0;\n" + " min-height: 0;\n" + " background-color: #%02lx%02lx%02lx;\n" + "}\n" + "popover.vim-preedit-popover > arrow {\n" + " background: transparent;\n" + " border: none;\n" + "}\n", + (unsigned long)((gui.back_pixel >> 16) & 0xff), + (unsigned long)((gui.back_pixel >> 8) & 0xff), + (unsigned long)(gui.back_pixel & 0xff)); + gtk_css_provider_load_from_string(preedit_css_provider, css); + g_free(css); + gtk_style_context_add_provider_for_display(display, + GTK_STYLE_PROVIDER(preedit_css_provider), G_MAXUINT); + preedit_css_cached_bg = gui.back_pixel; + } +# endif + +# ifndef USE_GTK4 +# if GTK_CHECK_VERSION(3,16,0) { -# ifndef USE_GTK4 GtkStyleContext * const context = gtk_widget_get_style_context(preedit_label); -# endif GtkCssProvider * const provider = gtk_css_provider_new(); gchar *css = NULL; const char * const fontname @@ -361,15 +482,10 @@ im_preedit_window_open(void) { // fontsize was given in points. Convert it into that in pixels // to use with CSS. -# ifdef USE_GTK4 - // GTK4: assume 96 DPI as default - fontsize = 96 * fontsize / 72; -# else GdkScreen * const screen = gdk_window_get_screen(gtk_widget_get_window(gui.mainwin)); const gdouble dpi = gdk_screen_get_resolution(screen); fontsize = dpi * fontsize / 72; -# endif } if (fontsize > 0) fontsize_propval = g_strdup_printf("%dpx", fontsize); @@ -392,22 +508,15 @@ im_preedit_window_open(void) (gui.back_pixel >> 8) & 0xff, gui.back_pixel & 0xff); -# ifdef USE_GTK4 - gtk_css_provider_load_from_string(provider, css); - gtk_style_context_add_provider_for_display( - gdk_display_get_default(), - GTK_STYLE_PROVIDER(provider), G_MAXUINT); -# else gtk_css_provider_load_from_data(provider, css, -1, NULL); gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider), G_MAXUINT); -# endif g_free(css); g_free(fontsize_propval); g_object_unref(provider); } -# elif GTK_CHECK_VERSION(3,0,0) +# elif GTK_CHECK_VERSION(3,0,0) gtk_widget_override_font(preedit_label, gui.norm_font); vim_snprintf(buf, sizeof(buf), "#%06X", gui.norm_pixel); @@ -418,7 +527,7 @@ im_preedit_window_open(void) gdk_rgba_parse(&color, buf); gtk_widget_override_background_color(preedit_label, GTK_STATE_FLAG_NORMAL, &color); -# else +# else gtk_widget_modify_font(preedit_label, gui.norm_font); vim_snprintf(buf, sizeof(buf), "#%06X", (unsigned)gui.norm_pixel); @@ -428,12 +537,49 @@ im_preedit_window_open(void) vim_snprintf(buf, sizeof(buf), "#%06X", (unsigned)gui.back_pixel); gdk_color_parse(buf, &color); gtk_widget_modify_bg(preedit_window, GTK_STATE_NORMAL, &color); -# endif +# endif +# endif // !USE_GTK4 gtk_im_context_get_preedit_string(xic, &preedit_string, &attr_list, NULL); if (preedit_string[0] != NUL) { +# ifdef USE_GTK4 + // GTK4: drive all styling (font + foreground + background) via + // PangoAttributes on the label rather than CSS. This pins the + // preedit text to gui.norm_font (family + weight + style + size) + // and the editor colors without going through CSS DPI conversion + // or selector matching. These attributes are merged with whatever + // the IM gave us (e.g. underline for composing range). + PangoAttribute *pa; + + if (attr_list == NULL) + attr_list = pango_attr_list_new(); + + if (gui.norm_font != NULL) + { + pa = pango_attr_font_desc_new(gui.norm_font); + pa->start_index = 0; + pa->end_index = G_MAXUINT; + pango_attr_list_insert(attr_list, pa); + } + + pa = pango_attr_foreground_new( + ((gui.norm_pixel >> 16) & 0xff) * 257, + ((gui.norm_pixel >> 8) & 0xff) * 257, + (gui.norm_pixel & 0xff) * 257); + pa->start_index = 0; + pa->end_index = G_MAXUINT; + pango_attr_list_insert(attr_list, pa); + + pa = pango_attr_background_new( + ((gui.back_pixel >> 16) & 0xff) * 257, + ((gui.back_pixel >> 8) & 0xff) * 257, + (gui.back_pixel & 0xff) * 257); + pa->start_index = 0; + pa->end_index = G_MAXUINT; + pango_attr_list_insert(attr_list, pa); +# endif gtk_label_set_text(GTK_LABEL(preedit_label), preedit_string); gtk_label_set_attributes(GTK_LABEL(preedit_label), attr_list); @@ -441,14 +587,47 @@ im_preedit_window_open(void) pango_layout_get_pixel_size(layout, &w, &h); h = MAX(h, gui.char_height); # ifdef USE_GTK4 - gtk_window_set_default_size(GTK_WINDOW(preedit_window), w, h); - gtk_widget_set_visible(preedit_window, TRUE); + // Cache label/popover size derived from the Pango layout, which is + // available without mapping the popover. Using these for positioning + // avoids calling gtk_popover_set_pointing_to() after popup, which + // triggers a GDK "compositor doesn't support moving popups" warning + // on compositors without xdg_popup.reposition (e.g. Weston). + preedit_label_width = w; + preedit_popover_height = h; + + // Report an enlarged cursor rectangle that covers both the cursor + // cell and the preedit popover. This way the IM (e.g. fcitx5) + // places its candidate window below the popover instead of on top + // of it. Coordinates are in client-widget (drawarea) space. + if (xic != NULL) + { + GdkRectangle area; + + area.x = FILL_X(gui.col); + area.y = FILL_Y(gui.row); + area.width = preedit_label_width; + area.height = MAX(gui.char_height, preedit_popover_height); + gtk_im_context_set_cursor_location(xic, &area); + } + + // Pop down before re-anchoring: on compositors that lack + // xdg_popup.reposition (e.g. Weston), calling set_pointing_to() on + // a mapped popover triggers GDK's remap fallback and a warning. + // Doing the popdown ourselves makes the next popup land at the new + // anchor cleanly without that warning. + if (gtk_widget_get_mapped(preedit_window)) + gtk_popover_popdown(GTK_POPOVER(preedit_window)); + + im_preedit_window_set_position(); + + gtk_widget_queue_resize(preedit_window); + gtk_popover_popup(GTK_POPOVER(preedit_window)); # else gtk_window_resize(GTK_WINDOW(preedit_window), w, h); gtk_widget_show_all(preedit_window); -# endif im_preedit_window_set_position(); +# endif } g_free(preedit_string); @@ -458,11 +637,12 @@ im_preedit_window_open(void) static void im_preedit_window_close(void) { - if (preedit_window != NULL) + if (preedit_window == NULL) + return; # ifdef USE_GTK4 - gtk_widget_set_visible(preedit_window, FALSE); + gtk_popover_popdown(GTK_POPOVER(preedit_window)); # else - gtk_widget_hide(preedit_window); + gtk_widget_hide(preedit_window); # endif } @@ -931,7 +1111,6 @@ xim_init(void) # endif xic = gtk_im_multicontext_new(); - g_object_ref(xic); im_commit_handler_id = g_signal_connect(G_OBJECT(xic), "commit", G_CALLBACK(&im_commit_cb), NULL); @@ -944,6 +1123,15 @@ xim_init(void) # ifdef USE_GTK4 gtk_im_context_set_client_widget(xic, gui.drawarea); + + // Seed the cursor location immediately. GTK4's Wayland IM module + // batches text-input-v3 state and sends it on the next commit cycle + // (typically triggered by focus_in). Without this seed the very + // first commit would carry the protocol default (0, 0, 0, 0) for the + // cursor rectangle, and the IM's candidate window for the very first + // composition would appear at the top-left of the surface instead of + // next to the cursor. + im_set_position(gui.row, gui.col); # else gtk_im_context_set_client_window(xic, gtk_widget_get_window(gui.drawarea)); # endif @@ -962,6 +1150,25 @@ im_shutdown(void) g_object_unref(xic); xic = NULL; } +# ifdef USE_GTK4 + // GTK4: a widget added via gtk_widget_set_parent() must be detached + // with gtk_widget_unparent() before its parent is finalized, otherwise + // GTK prints "Finalizing ..., but it still has children left". + if (preedit_window != NULL) + { + gtk_widget_unparent(preedit_window); + preedit_window = NULL; + preedit_label = NULL; + } + if (preedit_css_provider != NULL) + { + gtk_style_context_remove_provider_for_display(gdk_display_get_default(), + GTK_STYLE_PROVIDER(preedit_css_provider)); + g_object_unref(preedit_css_provider); + preedit_css_provider = NULL; + preedit_css_cached_bg = INVALCOLOR; + } +# endif im_is_active = FALSE; im_commit_handler_id = 0; if (p_imst == IM_ON_THE_SPOT) diff --git a/src/version.c b/src/version.c index f3dd63e775..caec10bb1a 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 518, /**/ 517, /**/ From 3a54de8f441730dfe617892c7a08817252618a1c Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Sat, 23 May 2026 18:35:07 +0000 Subject: [PATCH 296/545] patch 9.2.0519: GTK4: GUI tabline is not displayed correctly Problem: GTK4: GUI tabline is not displayed correctly, and double increments the index in the for() loop Solution: Drop the additional increment (Foxe Chen) closes: #20299 Co-Authored-by: Hirohito Higashi Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 4 ++-- src/gui_gtk_x11.c | 2 +- src/version.c | 2 ++ 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index bc66ba9e7f..4b02fe1a71 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -477,7 +477,7 @@ gui_mch_init(void) #ifdef FEAT_GUI_TABLINE gui.tabline = gtk_notebook_new(); gtk_notebook_set_show_border(GTK_NOTEBOOK(gui.tabline), FALSE); - gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), FALSE); + gtk_notebook_set_show_tabs(GTK_NOTEBOOK(gui.tabline), TRUE); gtk_notebook_set_scrollable(GTK_NOTEBOOK(gui.tabline), TRUE); gtk_widget_set_visible(gui.tabline, FALSE); gtk_box_append(GTK_BOX(vbox), gui.tabline); @@ -2328,7 +2328,7 @@ gui_mch_update_tabline(void) gtk_box_append(GTK_BOX(event_box), label); gtk_widget_set_visible(label, TRUE); gtk_notebook_insert_page(GTK_NOTEBOOK(gui.tabline), - page, event_box, nr++); + page, event_box, nr); gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(gui.tabline), page, TRUE); } diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index af452c8468..b47941acba 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -3581,7 +3581,7 @@ gui_mch_update_tabline(void) gtk_notebook_insert_page(GTK_NOTEBOOK(gui.tabline), page, event_box, - nr++); + nr); # if GTK_CHECK_VERSION(2,10,0) gtk_notebook_set_tab_reorderable(GTK_NOTEBOOK(gui.tabline), page, diff --git a/src/version.c b/src/version.c index caec10bb1a..10480a3c80 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 519, /**/ 518, /**/ From d7e6ce7a767209ed046c8cb04091b47128892954 Mon Sep 17 00:00:00 2001 From: Shad Date: Sat, 23 May 2026 18:43:09 +0000 Subject: [PATCH 297/545] patch 9.2.0520: Reversed text opacity in popup when termguicolor is set Problem: When termguicolor is set, popup opacity seems reversed for the underlying text: when opacity go from 1 to 99, the greater opacity is, the more underlying text is readable. Solution: Invert popup_color and base_fg when calling blend_colors (Shad). fixes: #20283 closes: #20284 Signed-off-by: Shad Signed-off-by: Christian Brabandt --- src/highlight.c | 6 +++--- .../Test_popupwin_opacity_textprop_undercurl.dump | 6 +++--- .../dumps/Test_popupwin_opacity_vsplit_1.dump | 2 +- .../dumps/Test_popupwin_opacity_vsplit_2.dump | 2 +- src/testdir/dumps/Test_popupwin_opacity_wide_1.dump | 12 ++++++------ src/testdir/dumps/Test_popupwin_opacity_wide_2.dump | 12 ++++++------ src/version.c | 2 ++ 7 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/highlight.c b/src/highlight.c index 3b1886be0e..2f92a2e52b 100644 --- a/src/highlight.c +++ b/src/highlight.c @@ -3303,7 +3303,7 @@ blend_cterm_colors(int popup_c, guicolor_T popup_rgb, #if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) /* * Blend two RGB colors based on blend value (0-100). - * blend: 0=use popup color, 100=use background color + * blend: 0=return first argument color, 100=return second argument color * If bg_color is INVALCOLOR, high blend means more visible (return INVALCOLOR). */ static guicolor_T @@ -3397,7 +3397,7 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED) && char_aep->ae_u.gui.fg_color != INVALCOLOR) base_fg = char_aep->ae_u.gui.fg_color; new_en.ae_u.gui.fg_color = blend_colors( - base_fg, popup_aep->ae_u.gui.bg_color, blend); + popup_aep->ae_u.gui.bg_color, base_fg, blend); } } else @@ -3537,7 +3537,7 @@ hl_blend_attr(int char_attr, int popup_attr, int blend, int blend_fg UNUSED) && !COLOR_INVALID(char_aep->ae_u.cterm.fg_rgb)) base_fg = char_aep->ae_u.cterm.fg_rgb; new_en.ae_u.cterm.fg_rgb = blend_colors( - base_fg, popup_bg, blend); + popup_bg, base_fg, blend); } } else diff --git a/src/testdir/dumps/Test_popupwin_opacity_textprop_undercurl.dump b/src/testdir/dumps/Test_popupwin_opacity_textprop_undercurl.dump index 2d29cfdeaf..489b0dbbc1 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_textprop_undercurl.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_textprop_undercurl.dump @@ -1,6 +1,6 @@ ->a+0&#ffffff0@2| |b@2| |c|P+0#e5e9f0255#03050b255|O|P|U|P|d+0#61646f255&|C+0#e5e9f0255&|O|N|T|E|N|T|f+0#61646f255&| |g@2| |h@2| @7| +0#0000000#ffffff0@10 -|i@2| |j@2| |k|k+0#61646f255#03050b255@1| |l@2| |m@2| |n@2| |o@2| |p@2| @7| +0#0000000#ffffff0@10 -|q@2| |r@2| |s|s+0#61646f255#03050b255@1| |m+0#e5e9f0255&|i|d@1|l|e|u+0#61646f255&| |v@2| |w@2| |x@2| @7| +0#0000000#ffffff0@10 +>a+0&#ffffff0@2| |b@2| |c|P+0#e5e9f0255#03050b255|O|P|U|P|d+0#a9abb1255&|C+0#e5e9f0255&|O|N|T|E|N|T|f+0#a9abb1255&| |g@2| |h@2| @7| +0#0000000#ffffff0@10 +|i@2| |j@2| |k|k+0#a9abb1255#03050b255@1| |l@2| |m@2| |n@2| |o@2| |p@2| @7| +0#0000000#ffffff0@10 +|q@2| |r@2| |s|s+0#a9abb1255#03050b255@1| |m+0#e5e9f0255&|i|d@1|l|e|u+0#a9abb1255&| |v@2| |w@2| |x@2| @7| +0#0000000#ffffff0@10 |y@2| |z@2| |1@2| |2@2| |3@2| |4@2| |5@2| |6@2| @18 |~+0#0000ff255&| @48 |~| @48 diff --git a/src/testdir/dumps/Test_popupwin_opacity_vsplit_1.dump b/src/testdir/dumps/Test_popupwin_opacity_vsplit_1.dump index 825706cef4..d6179c2499 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_vsplit_1.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_vsplit_1.dump @@ -1,6 +1,6 @@ >r+0&#ffffff0|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 |r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 -|r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |o+0#ffffff255#000045255|p|a|c|i|t|y|x+0#8080c5255&|o+0#ffffff255&|v|e|r||+1#8080c5255&|v+0#ffffff255&|s|p|l|i|t|i+0#8080c5255&|n|d|o|w| |t+0#0000000#ffffff0|e|x|t| |h|e|r|e| |x@3| @2 +|r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |o+0#ffffff255#000045255|p|a|c|i|t|y|x+0#7f7fc5255&|o+0#ffffff255&|v|e|r||+1#7f7fc5255&|v+0#ffffff255&|s|p|l|i|t|i+0#7f7fc5255&|n|d|o|w| |t+0#0000000#ffffff0|e|x|t| |h|e|r|e| |x@3| @2 |r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 |r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 |r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 diff --git a/src/testdir/dumps/Test_popupwin_opacity_vsplit_2.dump b/src/testdir/dumps/Test_popupwin_opacity_vsplit_2.dump index 4b766456da..3fa171461d 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_vsplit_2.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_vsplit_2.dump @@ -1,6 +1,6 @@ |r+0&#ffffff0|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 |r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 -|r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |o+0#ffffff255#000045255|p|a|c|i|t|y|x+0#8080c5255&|o+0#ffffff255&|v|e|r||+1#8080c5255&|v+0#ffffff255&|s|p|l|i|t|i+0#8080c5255&|n|d|o|w| |t+0#0000000#ffffff0|e|x|t| |h|e|r|e| |x@3| @2 +|r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |o+0#ffffff255#000045255|p|a|c|i|t|y|x+0#7f7fc5255&|o+0#ffffff255&|v|e|r||+1#7f7fc5255&|v+0#ffffff255&|s|p|l|i|t|i+0#7f7fc5255&|n|d|o|w| |t+0#0000000#ffffff0|e|x|t| |h|e|r|e| |x@3| @2 |r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 |r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 >r|i|g|h|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2||+1&&|l+0&&|e|f|t| |w|i|n|d|o|w| |t|e|x|t| |h|e|r|e| |x@3| @2 diff --git a/src/testdir/dumps/Test_popupwin_opacity_wide_1.dump b/src/testdir/dumps/Test_popupwin_opacity_wide_1.dump index 875430044b..c712253f47 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_wide_1.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_wide_1.dump @@ -1,9 +1,9 @@ ->い*0&#ffffff0|え*0#e08080255#600000255|ー@6| +&| +0#0000000#ffffff0|ー*&@7|い|!+&| |1| @3 -|い*&| +0#e08080255#600000255|カ*0#ffffff255&|ラ|フ|ル|な| +0#e08080255&|ー*&@1| +&| +0#0000000#ffffff0|ー*&@7|い|!+&| |2| @3 -|い*&| +0#e08080255#600000255|ポ*0#ffffff255#600030255|ッ|プ|ア|ッ|プ|で|─+0#e08080255&|╮| +0#0000000#ffffff0|ー*&@7|い|!+&| |3| @3 -|い*&| +0#e08080255#600000255|最*0#ffffff255#600030255|上|川| +0#e08080255&|ぼ*&|赤|い|な|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |4| @3 -|い*&| +0#e08080255#600000255|│+0�|あ*&|い|う|え|お|ー*0#a04070255&@1|│+0#e08080255&| +0#0000000#ffffff0|ー*&@7|い|!+&| |5| @3 -|い*&| +&|│+0#ffffff255#000060255|ー*0#8080e0255&@6|│+0#ffffff255&| +0#0000000#ffffff0|ー*&@7|い|!+&| |6| @3 +>い*0&#ffffff0|え*0#df7f7f255#600000255|ー@6| +&| +0#0000000#ffffff0|ー*&@7|い|!+&| |1| @3 +|い*&| +0#df7f7f255#600000255|カ*0#ffffff255&|ラ|フ|ル|な| +0#df7f7f255&|ー*&@1| +&| +0#0000000#ffffff0|ー*&@7|い|!+&| |2| @3 +|い*&| +0#df7f7f255#600000255|ポ*0#ffffff255#600030255|ッ|プ|ア|ッ|プ|で|─+0#df7f7f255&|╮| +0#0000000#ffffff0|ー*&@7|い|!+&| |3| @3 +|い*&| +0#df7f7f255#600000255|最*0#ffffff255#600030255|上|川| +0#df7f7f255&|ぼ*&|赤|い|な|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |4| @3 +|い*&| +0#df7f7f255#600000255|│+0�|あ*&|い|う|え|お|ー*0#a03f6f255&@1|│+0#df7f7f255&| +0#0000000#ffffff0|ー*&@7|い|!+&| |5| @3 +|い*&| +&|│+0#ffffff255#000060255|ー*0#7f7fdf255&@6|│+0#ffffff255&| +0#0000000#ffffff0|ー*&@7|い|!+&| |6| @3 |い*&| +&|╰+0#ffffff255#000060255|─@13|╯| +0#0000000#ffffff0|ー*&@7|い|!+&| |7| @3 |い*&|え|ー@15|い|!+&| |8| @3 |い*&|え|ー@15|い|!+&| |9| @3 diff --git a/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump b/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump index df2e6c09b8..40c5dca85c 100644 --- a/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump +++ b/src/testdir/dumps/Test_popupwin_opacity_wide_2.dump @@ -2,12 +2,12 @@ |い*&|え|ー@15|い|!+&| |2| @3 |い*&| +&|╭+0#ffffff255#000060255|─@13|╮| +0#0000000#ffffff0|ー*&@7|い|!+&| |3| @3 |い*&| +&|│+0#ffffff255#000060255|あ*&|め|ん|ぼ|赤|い|な|│+&| +0#0000000#ffffff0|ー*&@7|い|!+&| |4| @3 -|い*&| +&|│+0#ffffff255#000060255|あ*&|い|う|え|お|ー*0#8080e0255&@1|│+0#ffffff255&| +0#0000000#ffffff0|ー*&@7|い|!+&| |5| @3 -|い*&| +&|│+0#ffffff255#000060255|ー*0#8080e0255&@4| +&| +0#a04070255#600030255|ー*&|│+0#e08080255&| +0�|ー*&@5|ー*0#0000000#ffffff0@1|い|!+&| |6| @3 -|い*&| +&|╰+0#ffffff255#000060255|─@10|─+0#e08080255#600030255|カ*0#ffffff255&|ラ*0�|フ|ル|な|ー*0#e08080255&@2|ー*0#0000000#ffffff0@1|い|!+&| |7| @3 -|い*&|え|ー@4| +&| +0#e08080255#600000255|ポ*0#ffffff255&|ッ|プ|ア|ッ|プ|で|ー*0#e08080255&|ー*0#0000000#ffffff0@1|い|!+&| |8| @3 -|い*&|え|ー@4| +&| +0#e08080255#600000255|最*0#ffffff255&|上|川|ー*0#e08080255&@4|ー*0#0000000#ffffff0@1|い|!+&| |9| @3 -|い*&|え|ー@4| +&| +0#e08080255#600000255|ー*&@7|ー*0#0000000#ffffff0@1|い|!+&| |1|0| @2 +|い*&| +&|│+0#ffffff255#000060255|あ*&|い|う|え|お|ー*0#7f7fdf255&@1|│+0#ffffff255&| +0#0000000#ffffff0|ー*&@7|い|!+&| |5| @3 +|い*&| +&|│+0#ffffff255#000060255|ー*0#7f7fdf255&@4| +&| +0#a03f6f255#600030255|ー*&|│+0#df7f7f255&| +0�|ー*&@5|ー*0#0000000#ffffff0@1|い|!+&| |6| @3 +|い*&| +&|╰+0#ffffff255#000060255|─@10|─+0#df7f7f255#600030255|カ*0#ffffff255&|ラ*0�|フ|ル|な|ー*0#df7f7f255&@2|ー*0#0000000#ffffff0@1|い|!+&| |7| @3 +|い*&|え|ー@4| +&| +0#df7f7f255#600000255|ポ*0#ffffff255&|ッ|プ|ア|ッ|プ|で|ー*0#df7f7f255&|ー*0#0000000#ffffff0@1|い|!+&| |8| @3 +|い*&|え|ー@4| +&| +0#df7f7f255#600000255|最*0#ffffff255&|上|川|ー*0#df7f7f255&@4|ー*0#0000000#ffffff0@1|い|!+&| |9| @3 +|い*&|え|ー@4| +&| +0#df7f7f255#600000255|ー*&@7|ー*0#0000000#ffffff0@1|い|!+&| |1|0| @2 |い*&|え|ー@15|い|!+&| |1@1| @2 |い*&|え|ー@15|い|!+&| |1|2| @2 |い*&|え|ー@15|い|!+&| |1|3| @2 diff --git a/src/version.c b/src/version.c index 10480a3c80..2b51332c9c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 520, /**/ 519, /**/ From b34fa59abbd4f70b595ccab435446744443914d6 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sat, 23 May 2026 18:49:43 +0000 Subject: [PATCH 298/545] patch 9.2.0521: GTK4: cannot resize shell after the window is shown Problem: GTK4: cannot resize shell after the window is shown (Maxim Kim, after v9.2.0501) Solution: Always apply the requested size with gtk_window_set_default_size(), regardless of realized state (Yasuhiro Matsumoto). fixes: #20264 closes: #20269 Co-Authored-by: Claude Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 12 +++--------- src/version.c | 2 ++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index 4b02fe1a71..a9c9de7caf 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -772,15 +772,9 @@ gui_mch_set_shellsize(int width, int height, int base_width UNUSED, int base_height UNUSED, int direction UNUSED) { - // Only set window size if it hasn't been shown yet (initial sizing). - // After that, the window size is controlled by the user/WM and - // Vim adapts to it via form_size_allocate -> gui_resize_shell. - if (!gtk_widget_get_realized(gui.mainwin)) - { - width += get_menu_tool_width(); - height += get_menu_tool_height(); - gtk_window_set_default_size(GTK_WINDOW(gui.mainwin), width, height); - } + width += get_menu_tool_width(); + height += get_menu_tool_height(); + gtk_window_set_default_size(GTK_WINDOW(gui.mainwin), width, height); } void diff --git a/src/version.c b/src/version.c index 2b51332c9c..9decaeea3c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 521, /**/ 520, /**/ From da1f41d5d33152d7ab90a817bd99803c5727b937 Mon Sep 17 00:00:00 2001 From: John Marriott Date: Sat, 23 May 2026 19:00:12 +0000 Subject: [PATCH 299/545] patch 9.2.0522: event_nr2name() in autocmd.c can be improved Problem: event_nr2name() in autocmd.c can be improved Solution: Refactor it so that event_nr2name() returns a string_T type (John Marriott). Additionally: - change the define to an enum. - in function auto_next_pat(), move some variables closer to where they are used; rename s to fmt along the way. closes: #20272 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/autocmd.c | 49 ++++++++++++++++++++++++++++--------------------- src/version.c | 2 ++ 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/src/autocmd.c b/src/autocmd.c index 3b16a6b5b3..2e7f1efd01 100644 --- a/src/autocmd.c +++ b/src/autocmd.c @@ -261,7 +261,7 @@ static int current_augroup = AUGROUP_DEFAULT; static int au_need_clean = FALSE; // need to delete marked patterns static event_T event_name2nr(char_u *start, char_u **end); -static char_u *event_nr2name(event_T event); +static string_T *event_nr2name(event_T event); static int au_get_grouparg(char_u **argp); static int do_autocmd_event(event_T event, char_u *pat, int once, int nested, char_u *cmd, int forceit, int group, int flags); static int apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, int force, int group, buf_T *buf, exarg_T *eap); @@ -305,6 +305,8 @@ show_autocmd(AutoPat *ap, event_T event) goto theend; if (event != last_event || ap->group != last_group) { + string_T *event_name; + if (ap->group != AUGROUP_DEFAULT) { if (AUGROUP_NAME(ap->group) == NULL) @@ -313,7 +315,8 @@ show_autocmd(AutoPat *ap, event_T event) msg_puts_attr((char *)AUGROUP_NAME(ap->group), HL_ATTR(HLF_T)); msg_puts(" "); } - msg_puts_attr((char *)event_nr2name(event), HL_ATTR(HLF_T)); + event_name = event_nr2name(event); + msg_puts_attr((char *)event_name->string, HL_ATTR(HLF_T)); last_event = event; last_group = ap->group; msg_putchar('\n'); @@ -485,9 +488,12 @@ aubuflocal_remove(buf_T *buf) au_remove_pat(ap); if (p_verbose >= 6) { + string_T *event_name; + verbose_enter(); + event_name = event_nr2name(event); smsg(_("auto-removing autocommand: %s "), - event_nr2name(event), buf->b_fnum); + event_name->string, buf->b_fnum); verbose_leave(); } } @@ -710,13 +716,14 @@ event_name2nr(char_u *start, char_u **end) /* * Return the name for event "event". */ - static char_u * + static string_T * event_nr2name(event_T event) { int i; -#define CACHE_SIZE 12 + enum {CACHE_SIZE = 12}; static int cache_tab[CACHE_SIZE]; static int cache_last_index = -1; + static string_T unknown = STR_LITERAL_INIT("Unknown"); if (cache_last_index < 0) { @@ -731,7 +738,7 @@ event_nr2name(event_T event) for (i = cache_last_index; cache_tab[i] >= 0; ) { if ((event_T)abs(event_tab[cache_tab[i]].key) == event) - return event_tab[cache_tab[i]].value.string; + return &event_tab[cache_tab[i]].value; if (i == 0) i = CACHE_SIZE - 1; @@ -755,12 +762,11 @@ event_nr2name(event_T event) else ++cache_last_index; cache_tab[cache_last_index] = i; - break; + return &event_tab[i].value; } } - return (i == NUM_EVENTS) ? (char_u *)"Unknown" : - event_tab[i].value.string; + return &unknown; } /* @@ -2629,11 +2635,7 @@ auto_next_pat( int stop_at_last) // stop when 'last' flag is set { AutoPat *ap; - AutoCmd *cp; - char_u *name; - char *s; estack_T *entry; - char_u *namep; entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1; @@ -2657,12 +2659,17 @@ auto_next_pat( apc->sfname, apc->tail, ap->allow_dirs)) : ap->buflocal_nr == apc->arg_bufnr) { - name = event_nr2name(apc->event); - s = _("%s Autocommands for \"%s\""); - namep = alloc(STRLEN(s) + STRLEN(name) + ap->patlen + 1); + string_T *event_name; + char *fmt; + char_u *namep; + AutoCmd *cp; + + event_name = event_nr2name(apc->event); + fmt = _("%s Autocommands for \"%s\""); + namep = alloc(STRLEN(fmt) + event_name->length + ap->patlen + 1); if (namep != NULL) { - sprintf((char *)namep, s, (char *)name, (char *)ap->pat); + sprintf((char *)namep, fmt, (char *)event_name->string, (char *)ap->pat); if (p_verbose >= 8) { verbose_enter(); @@ -3364,7 +3371,6 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) AutoCmd *ac; list_T *event_list; dict_T *event_dict; - string_T event_name = {NULL, 0}; char_u *pat = NULL; char_u *name = NULL; int group = AUGROUP_ALL; @@ -3443,11 +3449,12 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) for (event = (event_T)0; (int)event < NUM_EVENTS; event = (event_T)((int)event + 1)) { + string_T *event_name; + if (event_arg != NUM_EVENTS && event != event_arg) continue; - event_name.string = event_nr2name(event); - event_name.length = STRLEN(event_name.string); + event_name = event_nr2name(event); // iterate through all the patterns for this autocmd event FOR_ALL_AUTOCMD_PATTERNS(event, ap) @@ -3482,7 +3489,7 @@ f_autocmd_get(typval_T *argvars, typval_T *rettv) } if (dict_add_string_len(event_dict, "event", - event_name.string, (int)event_name.length) == FAIL + event_name->string, (int)event_name->length) == FAIL || dict_add_string_len(event_dict, "group", group_name.string, (int)group_name.length) == FAIL || (ap->buflocal_nr != 0 diff --git a/src/version.c b/src/version.c index 9decaeea3c..95434f97f8 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 522, /**/ 521, /**/ From fccc2adc98c3d6664f1f2d8ddab17b096e647986 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 23 May 2026 19:05:28 +0000 Subject: [PATCH 300/545] patch 9.2.0523: tests: no test for using shellescape() in combination with :! Problem: tests: no test for using shellescape() in combination with :! Solution: Add a test that checks runtime files for using wrong combination of shellescape() with ! ex command This has lead to a few security relevant issues, so add a test that checks all runtime files for any ! followed by a shellescape() that does not use the {special} arg. related: Commit: 3fb5e58fbc63d86a3e65f1a141b0d67af2 (patch 9.2.0479: [security]: runtime(tar): command injection in tar plugin) closes: #20286 Supported by AI Signed-off-by: Christian Brabandt --- src/testdir/test_codestyle.vim | 23 +++++++++++++++++++++++ src/version.c | 2 ++ 2 files changed, 25 insertions(+) diff --git a/src/testdir/test_codestyle.vim b/src/testdir/test_codestyle.vim index da08e71ffd..1dcca61eab 100644 --- a/src/testdir/test_codestyle.vim +++ b/src/testdir/test_codestyle.vim @@ -195,4 +195,27 @@ def Test_indent_of_source_files() endfor enddef +def Test_runtime_wrong_shellescape() + # Check that shellescape() is called with the {special} argument (a second, + # non-zero argument) when its result is used in a ":!" ex command. + # This could cause code injection! + var pattern = '\ Date: Sat, 23 May 2026 19:21:42 +0000 Subject: [PATCH 301/545] translation(it): Update Italian manpage Signed-off-by: Antonio Giovanni Colombo Signed-off-by: Christian Brabandt --- runtime/doc/vim-it.1 | 9 +++++---- runtime/doc/vim-it.UTF-8.1 | 9 +++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/runtime/doc/vim-it.1 b/runtime/doc/vim-it.1 index 329f1e64f1..32b9093ffa 100644 --- a/runtime/doc/vim-it.1 +++ b/runtime/doc/vim-it.1 @@ -519,10 +519,11 @@ Quando si sta usando il server a socket [socketserver backend], se il nome inizia con "/", "./", o "../", è considerato come il percorso, assoluto o relativo per arrivare al socket. .TP -\-\-clientserver {backend} -Usare {backend} come interfaccia per la funzionalità client/server, -rispettivamente, o "socket" o "x11". Disponibile solo se Vim è stato -compilato con le due funzionalità socketserver e X11. +\-\-clientserver {metodo} +Usare {metodo} come interfaccia per la funzionalità client/server. Può valere +"socket" o "x11" o "mswin". Disponibile solo se Vim è stato compilato sia con +la funzionalità socketserver che con quella X11, o se compilato con la +funzionalità socketserver in ambiente MS-Windows. .TP \-\-socketid {id} Solo per GUI GTK: Usare meccanismo GtkPlug per eseguire gVim in un'altra diff --git a/runtime/doc/vim-it.UTF-8.1 b/runtime/doc/vim-it.UTF-8.1 index ffe30b3cbb..e291820360 100644 --- a/runtime/doc/vim-it.UTF-8.1 +++ b/runtime/doc/vim-it.UTF-8.1 @@ -519,10 +519,11 @@ Quando si sta usando il server a socket [socketserver backend], se il nome inizia con "/", "./", o "../", è considerato come il percorso, assoluto o relativo per arrivare al socket. .TP -\-\-clientserver {backend} -Usare {backend} come interfaccia per la funzionalità client/server, -rispettivamente, o "socket" o "x11". Disponibile solo se Vim è stato -compilato con le due funzionalità socketserver e X11. +\-\-clientserver {metodo} +Usare {metodo} come interfaccia per la funzionalità client/server. Può valere +"socket" o "x11" o "mswin". Disponibile solo se Vim è stato compilato sia con +la funzionalità socketserver che con quella X11, o se compilato con la +funzionalità socketserver in ambiente MS-Windows. .TP \-\-socketid {id} Solo per GUI GTK: Usare meccanismo GtkPlug per eseguire gVim in un'altra From 9a920e82546dace49a0a88dced80d29a39efbdb0 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sat, 23 May 2026 19:56:10 +0000 Subject: [PATCH 302/545] patch 9.2.0524: spell: buffer overflow with many affix or compound flags Problem: spell: a word in a .dic file with many postponed prefix or compound flags overflows the fixed-size store_afflist[MAXWLEN] buffer in get_pfxlist() and get_compflags(). Solution: Add bounds checks (Yasuhiro Matsumoto). closes: #20286 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/spell.txt | 7 +- runtime/doc/tags | 1 + src/errors.h | 4 + src/po/vim.pot | 5 +- src/spellfile.c | 129 ++++++++++++++++++++++++++------- src/testdir/test_spellfile.vim | 32 ++++++++ src/version.c | 2 + 7 files changed, 150 insertions(+), 30 deletions(-) diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt index 27f97548b3..774f0777c3 100644 --- a/runtime/doc/spell.txt +++ b/runtime/doc/spell.txt @@ -1,4 +1,4 @@ -*spell.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*spell.txt* For Vim version 9.2. Last change: 2026 May 23 VIM REFERENCE MANUAL by Bram Moolenaar @@ -562,6 +562,11 @@ then Vim will try to guess. avoid running out of memory compression will be done now and then. This can be tuned with the 'mkspellmem' option. + *E1578* + There is a limit on how many postponed prefix and + compound flags can be stored for one word. Reduce the + number of affix/compound flags on a word in the .dic + file that exceeds it. After the spell file was written and it was being used in a buffer it will be reloaded automatically. diff --git a/runtime/doc/tags b/runtime/doc/tags index 9c0298aedd..7cd0074a6a 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -4776,6 +4776,7 @@ E1574 channel.txt /*E1574* E1575 builtin.txt /*E1575* E1576 tagsrch.txt /*E1576* E1577 options.txt /*E1577* +E1578 spell.txt /*E1578* E158 sign.txt /*E158* E159 sign.txt /*E159* E16 cmdline.txt /*E16* diff --git a/src/errors.h b/src/errors.h index 0013a43ee8..9dbd7ac4db 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3812,3 +3812,7 @@ EXTERN char e_tag_file_entry_must_not_be_url[] INIT(= N_("E1576: Tag file entry must not be a URL")); EXTERN char e_invalid_format_string_single_percent_s[] INIT(= N_("E1577: Invalid format string, only one \"%s\" is allowed")); +#ifdef FEAT_SPELL +EXTERN char e_too_many_postponed_prefixes_spell[] + INIT(= N_("E1578: Too many postponed prefixes and/or compound flags")); +#endif diff --git a/src/po/vim.pot b/src/po/vim.pot index 946ce08831..00c192a621 100644 --- a/src/po/vim.pot +++ b/src/po/vim.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Vim\n" "Report-Msgid-Bugs-To: vim-dev@vim.org\n" -"POT-Creation-Date: 2026-05-21 19:38+0000\n" +"POT-Creation-Date: 2026-05-23 19:49+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -8880,6 +8880,9 @@ msgstr "" msgid "E1577: Invalid format string, only one \"%s\" is allowed" msgstr "" +msgid "E1578: Too many postponed prefixes and/or compound flags" +msgstr "" + #. type of cmdline window or 0 #. result of cmdline window or 0 #. buffer of cmdline window or NULL diff --git a/src/spellfile.c b/src/spellfile.c index 9ee7031e55..f1841ddf37 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -2013,8 +2013,8 @@ static int str_equal(char_u *s1, char_u *s2); static void add_fromto(spellinfo_T *spin, garray_T *gap, char_u *from, char_u *to); static int sal_to_bool(char_u *s); static int get_affix_flags(afffile_T *affile, char_u *afflist); -static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist); -static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_afflist); +static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist, int *cntp); +static int get_compflags(afffile_T *affile, char_u *afflist, char_u *store_afflist, int *cntp); static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afffile_T *affile, hashtab_T *ht, hashtab_T *xht, int condit, int flags, char_u *pfxlist, int pfxlen); static void *getroom(spellinfo_T *spin, size_t len, int align); static char_u *getroom_save(spellinfo_T *spin, char_u *s); @@ -3357,6 +3357,26 @@ check_renumber(spellinfo_T *spin) } } +/* + * Append one affix or compound ID to "store_afflist". + * Returns FAIL when this would overrun the fixed-size buffer. + */ + static int +store_afflist_add( + char_u *store_afflist, + int *cntp, + int id) +{ + if (*cntp >= MAXWLEN - 1) + { + emsg(_(e_too_many_postponed_prefixes_spell)); + return FAIL; + } + store_afflist[(*cntp)++] = id; + store_afflist[*cntp] = NUL; + return OK; +} + /* * Return TRUE if flag "flag" appears in affix list "afflist". */ @@ -3516,6 +3536,7 @@ spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) char_u *afflist; char_u store_afflist[MAXWLEN]; int pfxlen; + int totlen; int need_affix; char_u *dw; char_u *pc; @@ -3665,6 +3686,7 @@ spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) flags = 0; store_afflist[0] = NUL; pfxlen = 0; + totlen = 0; need_affix = FALSE; if (afflist != NULL) { @@ -3676,13 +3698,28 @@ spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) need_affix = TRUE; if (affile->af_pfxpostpone) + { // Need to store the list of prefix IDs with the word. - pfxlen = get_pfxlist(affile, afflist, store_afflist); + if (get_pfxlist(affile, afflist, store_afflist, &totlen) + == FAIL) + { + retval = FAIL; + break; + } + pfxlen = totlen; + } if (spin->si_compflags != NULL) + { // Need to store the list of compound flags with the word. // Concatenate them to the list of prefix IDs. - get_compflags(affile, afflist, store_afflist + pfxlen); + if (get_compflags(affile, afflist, store_afflist, &totlen) + == FAIL) + { + retval = FAIL; + break; + } + } } // Add the word to the word tree(s). @@ -3753,18 +3790,18 @@ get_affix_flags(afffile_T *affile, char_u *afflist) /* * Get the list of prefix IDs from the affix list "afflist". * Used for PFXPOSTPONE. - * Put the resulting flags in "store_afflist[MAXWLEN]" with a terminating NUL - * and return the number of affixes. + * Put the resulting flags in "store_afflist[MAXWLEN]" with a terminating NUL. + * Returns FAIL when the fixed-size buffer would overflow. */ static int get_pfxlist( afffile_T *affile, char_u *afflist, - char_u *store_afflist) + char_u *store_afflist, + int *cntp) { char_u *p; char_u *prevp; - int cnt = 0; int id; char_u key[AH_KEY_LEN]; hashitem_T *hi; @@ -3781,32 +3818,32 @@ get_pfxlist( if (!HASHITEM_EMPTY(hi)) { id = HI2AH(hi)->ah_newID; - if (id != 0) - store_afflist[cnt++] = id; + if (id != 0 && store_afflist_add(store_afflist, cntp, id) == FAIL) + return FAIL; } } if (affile->af_flagtype == AFT_NUM && *p == ',') ++p; } - store_afflist[cnt] = NUL; - return cnt; + return OK; } /* * Get the list of compound IDs from the affix list "afflist" that are used * for compound words. * Puts the flags in "store_afflist[]". + * Returns FAIL when the fixed-size buffer would overflow. */ - static void + static int get_compflags( afffile_T *affile, char_u *afflist, - char_u *store_afflist) + char_u *store_afflist, + int *cntp) { char_u *p; char_u *prevp; - int cnt = 0; char_u key[AH_KEY_LEN]; hashitem_T *hi; @@ -3818,14 +3855,16 @@ get_compflags( // A flag is a compound flag if it appears in "af_comp". vim_strncpy(key, prevp, p - prevp); hi = hash_find(&affile->af_comp, key); - if (!HASHITEM_EMPTY(hi)) - store_afflist[cnt++] = HI2CI(hi)->ci_newID; + if (!HASHITEM_EMPTY(hi) + && store_afflist_add(store_afflist, cntp, + HI2CI(hi)->ci_newID) == FAIL) + return FAIL; } if (affile->af_flagtype == AFT_NUM && *p == ',') ++p; } - store_afflist[cnt] = NUL; + return OK; } /* @@ -3983,10 +4022,20 @@ store_aff_word( if (affile->af_pfxpostpone || spin->si_compflags != NULL) { + int listlen = 0; + if (affile->af_pfxpostpone) + { // Get prefix IDS from the affix list. - use_pfxlen = get_pfxlist(affile, - ae->ae_flags, store_afflist); + if (get_pfxlist(affile, ae->ae_flags, + store_afflist, &listlen) + == FAIL) + { + retval = FAIL; + break; + } + use_pfxlen = listlen; + } else use_pfxlen = 0; use_pfxlist = store_afflist; @@ -3998,14 +4047,30 @@ store_aff_word( for (j = 0; j < use_pfxlen; ++j) if (pfxlist[i] == use_pfxlist[j]) break; - if (j == use_pfxlen) - use_pfxlist[use_pfxlen++] = pfxlist[i]; + if (j == use_pfxlen + && store_afflist_add(use_pfxlist, + &listlen, pfxlist[i]) + == FAIL) + { + retval = FAIL; + break; + } + use_pfxlen = listlen; } + if (retval == FAIL) + break; if (spin->si_compflags != NULL) // Get compound IDS from the affix list. - get_compflags(affile, ae->ae_flags, - use_pfxlist + use_pfxlen); + if (get_compflags(affile, ae->ae_flags, + use_pfxlist, &listlen) + == FAIL) + { + retval = FAIL; + break; + } + if (retval == FAIL) + break; // Combine the list of compound flags. // Concatenate them to the prefix IDs list. @@ -4016,12 +4081,17 @@ store_aff_word( use_pfxlist[j] != NUL; ++j) if (pfxlist[i] == use_pfxlist[j]) break; - if (use_pfxlist[j] == NUL) + if (use_pfxlist[j] == NUL + && store_afflist_add(use_pfxlist, + &listlen, pfxlist[i]) + == FAIL) { - use_pfxlist[j++] = pfxlist[i]; - use_pfxlist[j] = NUL; + retval = FAIL; + break; } } + if (retval == FAIL) + break; } } @@ -6236,6 +6306,7 @@ spell_add_word( char_u line[MAXWLEN * 2]; long fpos, fpos_next = 0; int i; + size_t linelen; char_u *spf; if (!valid_spell_word(word, word + len)) @@ -6312,7 +6383,9 @@ spell_add_word( fpos_next = ftell(fd); if (fpos_next < 0) break; // should never happen - if (STRNCMP(word, line, len) == 0 + linelen = STRLEN(line); + if (linelen >= (size_t)len + && STRNCMP(word, line, len) == 0 && (line[len] == '/' || line[len] < ' ')) { // Found duplicate word. Remove it by writing a '#' at diff --git a/src/testdir/test_spellfile.vim b/src/testdir/test_spellfile.vim index cf75eb4ef9..07156818d9 100644 --- a/src/testdir/test_spellfile.vim +++ b/src/testdir/test_spellfile.vim @@ -1214,5 +1214,37 @@ func Test_mkspell_no_buffer_overflow() defer delete('Xbof2.spl') endfunc +func Test_mkspell_no_affixlist_overflow() + let aff_lines = [ + \ 'SET ISO8859-1', + \ 'PFXPOSTPONE', + \ 'PFX A Y 1', + \ 'PFX A 0 pre .', + \ ] + call writefile(aff_lines, 'Xaffbof.aff', 'D') + call writefile(['1', 'word/' .. repeat('A', 300)], 'Xaffbof.dic', 'D') + + call assert_fails('mkspell! Xaffbof.spl Xaffbof', + \ 'Too many postponed prefixes and/or compound flags') + call assert_false(filereadable('Xaffbof.spl')) +endfunc + +func Test_mkspell_no_compflag_overflow() + " Overflow the compound-flag path in get_compflags(): a word whose + " affix list repeats a compound flag many times accumulates one ID per + " occurrence, overrunning store_afflist[MAXWLEN]. + let aff_lines = [ + \ 'SET ISO8859-1', + \ 'COMPOUNDFLAG c', + \ ] + call writefile(aff_lines, 'Xcompbof.aff', 'D') + + " Repeat the compound flag 'c' far past MAXWLEN. + call writefile(['1', 'word/' .. repeat('c', 300)], 'Xcompbof.dic', 'D') + + call assert_fails('mkspell! Xcompbof.spl Xcompbof', + \ 'Too many postponed prefixes and/or compound flags') + call assert_false(filereadable('Xcompbof.spl')) +endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index af37e33da1..f3cfeb60a8 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 524, /**/ 523, /**/ From e60d4b4ff309cab96522f4ad565dd7dc9478b89c Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 24 May 2026 08:56:17 +0000 Subject: [PATCH 303/545] runtime(doc): update netrws "mt" command description fixes: #20302 Signed-off-by: Christian Brabandt --- runtime/doc/pi_netrw.txt | 7 ++++--- runtime/pack/dist/opt/netrw/doc/netrw.txt | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 6f20073d5a..fd9339f1d5 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -2284,9 +2284,10 @@ Set the marked file copy/move-to target (see |netrw-mc| and |netrw-mm|): displayed directory is used for the copy/move-to target. * Also, if the cursor is in the banner, then the netrw window's currently - displayed directory is used for the copy/move-to target. - Unless the target already is the current directory. In which case, - typing "mf" clears the target. + displayed directory is used for the copy/move-to target - unless that + directory is already the target, in which case typing "mt" again clears + it. Repeating "mt" in the banner thus toggles the current directory as + the target. * However, if the cursor is atop a directory name, then that directory is used for the copy/move-to target diff --git a/runtime/pack/dist/opt/netrw/doc/netrw.txt b/runtime/pack/dist/opt/netrw/doc/netrw.txt index 2c5d6d822e..41e61cfad8 100644 --- a/runtime/pack/dist/opt/netrw/doc/netrw.txt +++ b/runtime/pack/dist/opt/netrw/doc/netrw.txt @@ -2284,9 +2284,10 @@ Set the marked file copy/move-to target (see |netrw-mc| and |netrw-mm|): displayed directory is used for the copy/move-to target. * Also, if the cursor is in the banner, then the netrw window's currently - displayed directory is used for the copy/move-to target. - Unless the target already is the current directory. In which case, - typing "mf" clears the target. + displayed directory is used for the copy/move-to target - unless that + directory is already the target, in which case typing "mt" again clears + it. Repeating "mt" in the banner thus toggles the current directory as + the target. * However, if the cursor is atop a directory name, then that directory is used for the copy/move-to target From 6c8e125395cc77a67c7ddb95422684ff84a54e58 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 24 May 2026 09:03:59 +0000 Subject: [PATCH 304/545] patch 9.2.0525: spell: memory leak in spell_read_dic() Problem: spell: memory leak in spell_read_dic() (after 9.2.0524). Solution: Free "pc" before breaking out of the loop (zeertzjq). closes: #20309 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- src/spellfile.c | 2 ++ src/version.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/spellfile.c b/src/spellfile.c index f1841ddf37..8a373f343f 100644 --- a/src/spellfile.c +++ b/src/spellfile.c @@ -3704,6 +3704,7 @@ spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) == FAIL) { retval = FAIL; + vim_free(pc); break; } pfxlen = totlen; @@ -3717,6 +3718,7 @@ spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) == FAIL) { retval = FAIL; + vim_free(pc); break; } } diff --git a/src/version.c b/src/version.c index f3cfeb60a8..175d7ab57c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 525, /**/ 524, /**/ From 2c23dde0b09971324906dcf179fb129ffe19d76c Mon Sep 17 00:00:00 2001 From: John Marriott Date: Sun, 24 May 2026 09:08:59 +0000 Subject: [PATCH 305/545] patch 9.2.0526: missing out-of-memory check in ex_substitute() Problem: missing out-of-memory check in ex_substitute() Solution: Bail out in case of out-of-memory (John Marriott) closes: #20308 Signed-off-by: John Marriott Signed-off-by: Christian Brabandt --- src/ex_cmds.c | 13 +++++++++++++ src/version.c | 2 ++ 2 files changed, 15 insertions(+) diff --git a/src/ex_cmds.c b/src/ex_cmds.c index 7cf63adaf9..3a598ddfa0 100644 --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -4144,6 +4144,8 @@ ex_substitute(exarg_T *eap) pat = NULL; // search_regcomp() will use previous pattern patlen = 0; sub = vim_strsave(old_sub); + if (sub == NULL) + return; // Vi compatibility quirk: repeating with ":s" keeps the cursor in the // last column after using "$". @@ -5003,6 +5005,12 @@ ex_substitute(exarg_T *eap) vim_free(sub_firstline); sub_firstline = vim_strnsave(ml_get(sub_firstlnum), ml_get_len(sub_firstlnum)); + if (sub_firstline == NULL) + { + vim_free(new_start); + goto outofmem; + } + // When going beyond the last line, stop substituting. if (sub_firstlnum <= line2) do_again = TRUE; @@ -5019,6 +5027,11 @@ ex_substitute(exarg_T *eap) // less than what it ought to be. vim_free(sub_firstline); sub_firstline = vim_strsave((char_u *)""); + if (sub_firstline == NULL) + { + vim_free(new_start); + goto outofmem; + } copycol = 0; } diff --git a/src/version.c b/src/version.c index 175d7ab57c..91bfa5761c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 526, /**/ 525, /**/ From 07c8b4712f84daa5cfd0c96f134710e6f4865b95 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 24 May 2026 15:25:03 +0000 Subject: [PATCH 306/545] patch 9.2.0527: Possible double free in fill_partial_and_closure() Problem: Possible double free in fill_partial_and_closure() (xuqing yang) Solution: Let the caller handle the free() Signed-off-by: Christian Brabandt --- src/version.c | 2 ++ src/vim9execute.c | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/version.c b/src/version.c index 91bfa5761c..708cd1746b 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 527, /**/ 526, /**/ diff --git a/src/vim9execute.c b/src/vim9execute.c index e1ddb7c1de..68ca777d06 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2157,10 +2157,8 @@ fill_partial_and_closure( // and local variables) so that the closure can use it later. // Store a reference to the partial so we can handle that. if (GA_GROW_FAILS(&ectx->ec_funcrefs, 1)) - { - vim_free(pt); + // caller needs to free pt return FAIL; - } // Extra variable keeps the count of closures created in the current // function call. ++(((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx @@ -5123,7 +5121,10 @@ exec_instructions(ectx_T *ectx) if (fill_partial_and_closure(pt, ufunc, extra == NULL ? NULL : &extra->fre_loopvar_info, ectx) == FAIL) + { + vim_free(pt); goto theend; + } tv = STACK_TV_BOT(0); ++ectx->ec_stack.ga_len; tv->vval.v_partial = pt; From 31c739458acc397ea3684d4504e6036846f2a708 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 24 May 2026 16:47:27 +0000 Subject: [PATCH 307/545] patch 9.2.0528: possible overflow in XIM resource handling Problem: possible overflow in XIM resource handling (Venukamatchi) Solution: use vim_strncpy() with the buffer size Signed-off-by: Christian Brabandt --- src/gui_xim.c | 6 ++++-- src/version.c | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/gui_xim.c b/src/gui_xim.c index ec4620f38b..a04667e2dc 100644 --- a/src/gui_xim.c +++ b/src/gui_xim.c @@ -1750,7 +1750,8 @@ xim_real_init(Window x11_window, Display *x11_display) if (gui.rsrc_input_method != NULL && *gui.rsrc_input_method != NUL) { - strcpy(tmp, gui.rsrc_input_method); + vim_strncpy((char_u *)tmp, (char_u *)gui.rsrc_input_method, + sizeof(tmp) - 1); for (ns = s = tmp; ns != NULL && *s != NUL;) { s = (char *)skipwhite((char_u *)s); @@ -1817,7 +1818,8 @@ xim_real_init(Window x11_window, Display *x11_display) } found = False; - strcpy(tmp, gui.rsrc_preedit_type_name); + vim_strncpy((char_u *)tmp, (char_u *)gui.rsrc_preedit_type_name, + sizeof(tmp) - 1); for (s = tmp; s && !found; ) { while (*s && SAFE_isspace(*s)) diff --git a/src/version.c b/src/version.c index 708cd1746b..19dbebbdb4 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 528, /**/ 527, /**/ From 06ef3a54bf0f740d1ada5524599c7d05f0603b20 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 24 May 2026 17:29:04 +0000 Subject: [PATCH 308/545] patch 9.2.0529: GTK4: clipboard returns empty after a foreign app takes the selection Problem: GTK4: clipboard read returns empty after a foreign app takes the selection (lilydjwg, after v9.2.0501) Solution: Skip the set_content call unless gdk_clipboard_is_local() still says we own the clipboard (Yasuhiro Matsumoto). clipboard_changed_cb calls clip_lose_selection() which cascades into clip_mch_lose_selection(), and that unconditionally called gdk_clipboard_set_content(clipboard, NULL). When the signal fires because *another* app took the selection, this re-claims ownership with NULL content, so the next gdk_clipboard_read_text_async() returns empty and the user sees "Nothing in register *". fixes: #20256 closes: #20261 Co-Authored-by: Claude Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 38 ++++++++++++++++++++++++++++++++++++-- src/version.c | 2 ++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index a9c9de7caf..c12256790e 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -285,6 +285,7 @@ static void drawarea_unrealize_cb(GtkWidget *widget, gpointer data); static void drawarea_resize_cb(GtkDrawingArea *area, int width, int height, gpointer data); static void drawarea_scale_factor_cb(GObject *object, GParamSpec *pspec, gpointer data); static cairo_surface_t *create_backing_surface(int width, int height); +static void clipboard_changed_cb(GdkClipboard *clipboard, gpointer user_data); /* * Parse the GUI related command-line arguments. Any arguments used are @@ -589,6 +590,19 @@ gui_mch_init(void) // Create a blank (invisible) cursor for hiding the mouse pointer. gui.blank_pointer = gdk_cursor_new_from_name("none", NULL); + { + GdkDisplay *display = gtk_widget_get_display(gui.mainwin); + GdkClipboard *primary = gdk_display_get_primary_clipboard(display); + GdkClipboard *board = gdk_display_get_clipboard(display); + + if (primary != NULL) + g_signal_connect(primary, "changed", + G_CALLBACK(clipboard_changed_cb), &clip_star); + if (board != NULL) + g_signal_connect(board, "changed", + G_CALLBACK(clipboard_changed_cb), &clip_plus); + } + return OK; } @@ -3130,6 +3144,8 @@ clip_mch_request_selection(Clipboard_T *cbd) g_main_context_iteration(NULL, TRUE); } +static int in_clipboard_set = FALSE; + /* * Send the current selection to the clipboard. */ @@ -3174,7 +3190,9 @@ clip_mch_set_selection(Clipboard_T *cbd) { mch_memmove(nul_str, str, len); nul_str[len] = NUL; + in_clipboard_set = TRUE; gdk_clipboard_set_text(clipboard, (const char *)nul_str); + in_clipboard_set = FALSE; vim_free(nul_str); } } @@ -3182,6 +3200,18 @@ clip_mch_set_selection(Clipboard_T *cbd) vim_free(str); } + static void +clipboard_changed_cb(GdkClipboard *clipboard, gpointer user_data) +{ + Clipboard_T *cbd = (Clipboard_T *)user_data; + + if (in_clipboard_set) + return; + if (gdk_clipboard_is_local(clipboard)) + return; + clip_lose_selection(cbd); +} + /* * Own the selection. In GTK4, ownership is implicit when content is set * on the clipboard. Return OK to indicate we can own it. @@ -3205,8 +3235,12 @@ clip_mch_lose_selection(Clipboard_T *cbd) if (clipboard == NULL) return; - // Setting NULL content provider releases ownership. - gdk_clipboard_set_content(clipboard, NULL); + // Only release ownership if we still own it. Otherwise we would + // clobber another application's clipboard content with NULL, which + // happens when this is called from clipboard_changed_cb after a + // foreign app took the selection. + if (gdk_clipboard_is_local(clipboard)) + gdk_clipboard_set_content(clipboard, NULL); } // Balloon eval - use GTK4 tooltip diff --git a/src/version.c b/src/version.c index 19dbebbdb4..ca5b3ec12f 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 529, /**/ 528, /**/ From 53d97c93b73252c02ed12b350532da8a2c19eb11 Mon Sep 17 00:00:00 2001 From: bennyyip Date: Sun, 24 May 2026 17:41:58 +0000 Subject: [PATCH 309/545] runtime(compiler): Remove wrong escape in zig compiler files closes: #20312 Signed-off-by: bennyyip Signed-off-by: Christian Brabandt --- runtime/compiler/zig.vim | 3 ++- runtime/compiler/zig_build_exe.vim | 3 ++- runtime/compiler/zig_cc.vim | 3 ++- runtime/compiler/zig_test.vim | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/runtime/compiler/zig.vim b/runtime/compiler/zig.vim index 5f08423daf..f6144c9f52 100644 --- a/runtime/compiler/zig.vim +++ b/runtime/compiler/zig.vim @@ -3,6 +3,7 @@ " Upstream: https://github.com/ziglang/zig.vim " Last Change: " 2026 May 12 by the Vim project (set errormformat) +" 2026 May 24 by the Vim project (do not escape vars for makeprg) if exists("current_compiler") finish @@ -13,7 +14,7 @@ let s:save_cpo = &cpo set cpo&vim " a subcommand must be provided for the this compiler (test, build-exe, etc) -CompilerSet makeprg=zig\ \$*\ \%:S +CompilerSet makeprg=zig\ $*\ %:S CompilerSet errorformat= \%-G, diff --git a/runtime/compiler/zig_build_exe.vim b/runtime/compiler/zig_build_exe.vim index ab63bfffe8..2f1681b501 100644 --- a/runtime/compiler/zig_build_exe.vim +++ b/runtime/compiler/zig_build_exe.vim @@ -3,6 +3,7 @@ " Upstream: https://github.com/ziglang/zig.vim " Last Change: 2025 Nov 16 by the Vim Project (set errorformat) " 2026 May 12 by the Vim project (remove errorformat) +" 2026 May 24 by the Vim project (do not escape vars for makeprg) if exists('current_compiler') finish @@ -13,7 +14,7 @@ let current_compiler = 'zig_build_exe' let s:save_cpo = &cpo set cpo&vim -CompilerSet makeprg=zig\ build-exe\ \%:S\ \$* +CompilerSet makeprg=zig\ build-exe\ %:S\ $* let &cpo = s:save_cpo unlet s:save_cpo diff --git a/runtime/compiler/zig_cc.vim b/runtime/compiler/zig_cc.vim index 331d44751a..d86bb48efc 100644 --- a/runtime/compiler/zig_cc.vim +++ b/runtime/compiler/zig_cc.vim @@ -1,6 +1,7 @@ " Vim compiler file " Compiler: Zig Compiler (zig cc) " Last Change: 2026 May 12 +" 2026 May 24 by the Vim project (do not escape vars for makeprg) if exists('current_compiler') finish @@ -11,7 +12,7 @@ let current_compiler = 'zig_cc' let s:save_cpo = &cpo set cpo&vim -CompilerSet makeprg=zig\ cc\ \%:S\ \$* +CompilerSet makeprg=zig\ cc\ %:S\ $* let &cpo = s:save_cpo unlet s:save_cpo diff --git a/runtime/compiler/zig_test.vim b/runtime/compiler/zig_test.vim index 6dee38b2f1..07cb88eb4a 100644 --- a/runtime/compiler/zig_test.vim +++ b/runtime/compiler/zig_test.vim @@ -3,6 +3,7 @@ " Upstream: https://github.com/ziglang/zig.vim " Last Change: 2025 Nov 16 by the Vim Project (set errorformat) " 2026 May 12 by the Vim Project (remove error format) +" 2026 May 24 by the Vim project (do not escape vars for makeprg) if exists('current_compiler') finish @@ -13,7 +14,7 @@ let current_compiler = 'zig_test' let s:save_cpo = &cpo set cpo&vim -CompilerSet makeprg=zig\ test\ \%:S\ \$* +CompilerSet makeprg=zig\ test\ %:S\ $* let &cpo = s:save_cpo unlet s:save_cpo From 24678d31c88c1b6e1a2880e8ee29a44c192cb3c3 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Sun, 24 May 2026 17:43:32 +0000 Subject: [PATCH 310/545] patch 9.2.0530: WinBar row vertical separator not refreshed on window change Problem: After the current window changes, the vertical separator cell on the WinBar row keeps its previous VertSplit / VertSplitNC highlight. Content rows and status line rows of the same window are refreshed correctly; only the WinBar row is left behind, so the WinBar's separator no longer matches the surrounding cells (Mao-Yining) Solution: Include the WinBar row in draw_vsep_win() when redrawing from the top of the window. When called with row == 0 the loop now starts at wp->w_winrow (the WinBar row when present) instead of W_WINROW(wp) (the content start), so the separator highlight on the WinBar row is updated together with the rest of the window (Hirohito Higashi). fixes: #20304 closes: #20310 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/screen.c | 7 +++-- .../dumps/Test_winbar_vsep_active.dump | 20 +++++++++++++ .../dumps/Test_winbar_vsep_inactive.dump | 20 +++++++++++++ src/testdir/test_winbar.vim | 30 +++++++++++++++++++ src/version.c | 2 ++ 5 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 src/testdir/dumps/Test_winbar_vsep_active.dump create mode 100644 src/testdir/dumps/Test_winbar_vsep_inactive.dump diff --git a/src/screen.c b/src/screen.c index 8c1b8da0a7..9042d90ded 100644 --- a/src/screen.c +++ b/src/screen.c @@ -1222,8 +1222,11 @@ draw_vsep_win(win_T *wp, int row) int content_end = W_WINROW(wp) + wp->w_height; - // Content rows: VertSplit/VertSplitNC based on adjacency. - for (int r = W_WINROW(wp) + row; r < content_end; ++r) + // Content rows: VertSplit/VertSplitNC based on adjacency. Include the + // WinBar row (above the content) when redrawing from the top so that + // the separator highlight is updated on current-window changes. + int start_row = (row == 0) ? wp->w_winrow : W_WINROW(wp) + row; + for (int r = start_row; r < content_end; ++r) { int hl; int c = fillchar_vsep(&hl, wp, r); diff --git a/src/testdir/dumps/Test_winbar_vsep_active.dump b/src/testdir/dumps/Test_winbar_vsep_active.dump new file mode 100644 index 0000000000..9a0e234045 --- /dev/null +++ b/src/testdir/dumps/Test_winbar_vsep_active.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@19||+0#000000255&| +0#0000000&@18||+0#000000255&| +0#0000000&@18 +|~+0#4040ff13&| @18||+0#000000255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+0#000000255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+0#000000255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+0#000000255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+0#000000255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+0#000000255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+0#000000255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+0#000000255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|<+1#0000000&|N|o| |N|a|m|e|]| |0|,|0|-|1| @1|A|l@1| |<|N|o| |N|a|m|e|]| |0|,|0|-|1| |A|l@1| |<|N|o| |N|a|m|e|]| |0|,|0|-|1| |A|l@1 +| +0&#e0e0e08| +2#ffffff16#6c6c6c255|S|t|e|p| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|N|e|x|t| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|F|i|n|i|s|h| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|C|o||+1#87afff255#ffffff0| +0#0000000&@28 +> @29||+1#87afff255&|~+0#4040ff13&| @27 +|~| @28||+1#87afff255&|~+0#4040ff13&| @27 +|~| @28||+1#87afff255&|~+0#4040ff13&| @27 +|~| @28||+1#87afff255&|~+0#4040ff13&| @27 +|~| @28||+1#87afff255&|~+0#4040ff13&| @27 +|~| @28||+1#87afff255&|~+0#4040ff13&| @27 +|~| @28||+1#87afff255&|~+0#4040ff13&| @27 +|[+3#0000000&|N|o| |N|a|m|e|]| @5|0|,|0|-|1| @6|A|l@1| |[+1&&|N|o| |N|a|m|e|]| @5|0|,|0|-|1| @5|A|l@1 +| +0&&@59 diff --git a/src/testdir/dumps/Test_winbar_vsep_inactive.dump b/src/testdir/dumps/Test_winbar_vsep_inactive.dump new file mode 100644 index 0000000000..2e6f17fe78 --- /dev/null +++ b/src/testdir/dumps/Test_winbar_vsep_inactive.dump @@ -0,0 +1,20 @@ +> +0&#ffffff0@19||+1#87afff255&| +0#0000000&@18||+0#000000255&| +0#0000000&@18 +|~+0#4040ff13&| @18||+1#87afff255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+1#87afff255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+1#87afff255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+1#87afff255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+1#87afff255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+1#87afff255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+1#87afff255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|~| @18||+1#87afff255&|~+0#4040ff13&| @17||+0#000000255&|~+0#4040ff13&| @17 +|<+3#0000000&|N|o| |N|a|m|e|]| |0|,|0|-|1| @1|A|l@1| |<+1&&|N|o| |N|a|m|e|]| |0|,|0|-|1| |A|l@1| |<|N|o| |N|a|m|e|]| |0|,|0|-|1| |A|l@1 +| +0&#e0e0e08| +2#ffffff16#6c6c6c255|S|t|e|p| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|N|e|x|t| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|F|i|n|i|s|h| | +0#0000000#e0e0e08@1| +2#ffffff16#6c6c6c255|C|o||+0#000000255#ffffff0| +0#0000000&@28 +@30||+0#000000255&|~+0#4040ff13&| @27 +|~| @28||+0#000000255&|~+0#4040ff13&| @27 +|~| @28||+0#000000255&|~+0#4040ff13&| @27 +|~| @28||+0#000000255&|~+0#4040ff13&| @27 +|~| @28||+0#000000255&|~+0#4040ff13&| @27 +|~| @28||+0#000000255&|~+0#4040ff13&| @27 +|~| @28||+0#000000255&|~+0#4040ff13&| @27 +|[+1#0000000&|N|o| |N|a|m|e|]| @5|0|,|0|-|1| @6|A|l@1| |[|N|o| |N|a|m|e|]| @5|0|,|0|-|1| @5|A|l@1 +| +0&&@59 diff --git a/src/testdir/test_winbar.vim b/src/testdir/test_winbar.vim index 59241746b9..3a9ca68e8d 100644 --- a/src/testdir/test_winbar.vim +++ b/src/testdir/test_winbar.vim @@ -157,6 +157,36 @@ func Test_winbar_not_visible_custom_statusline() call StopVimInTerminal(buf) endfunction +" The vertical separator on the WinBar row must follow VertSplit/VertSplitNC +" when the current window changes. +func Test_winbar_vsep_highlight_after_focus_change() + CheckScreendump + + let lines =<< trim END + vim9script + wincmd s + wincmd v + wincmd v + wincmd j + wincmd v + nnoremenu 1.10 WinBar.Step :Step + nnoremenu 1.20 WinBar.Next :Next + nnoremenu 1.30 WinBar.Finish :Finish + nnoremenu 1.40 WinBar.Cont :Continue + hi Vertsplit term=reverse ctermfg=111 + hi VertsplitNC term=reverse ctermfg=16 + END + call writefile(lines, 'XtestWinbarVsep', 'D') + let buf = RunVimInTerminal('-S XtestWinbarVsep', #{rows: 20, cols: 60}) + + call VerifyScreenDump(buf, 'Test_winbar_vsep_active', {}) + + call term_sendkeys(buf, "\k") + call VerifyScreenDump(buf, 'Test_winbar_vsep_inactive', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_drag_statusline_with_winbar() call SetupWinbar() let save_mouse = &mouse diff --git a/src/version.c b/src/version.c index ca5b3ec12f..ca41d42a53 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 530, /**/ 529, /**/ From 010a71bb6072c6c99d311222f4960d403c982c47 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 25 May 2026 15:15:09 +0000 Subject: [PATCH 311/545] runtime(zig): Update upstream repo related: #20312 Signed-off-by: Christian Brabandt --- runtime/compiler/zig.vim | 2 +- runtime/compiler/zig_build.vim | 2 +- runtime/compiler/zig_build_exe.vim | 2 +- runtime/compiler/zig_test.vim | 2 +- runtime/ftplugin/zig.vim | 2 +- runtime/indent/zig.vim | 2 +- runtime/syntax/zig.vim | 2 +- runtime/syntax/zir.vim | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/runtime/compiler/zig.vim b/runtime/compiler/zig.vim index f6144c9f52..0993874f58 100644 --- a/runtime/compiler/zig.vim +++ b/runtime/compiler/zig.vim @@ -1,6 +1,6 @@ " Vim compiler file " Compiler: Zig Compiler -" Upstream: https://github.com/ziglang/zig.vim +" Upstream: https://codeberg.org/ziglang/zig.vim " Last Change: " 2026 May 12 by the Vim project (set errormformat) " 2026 May 24 by the Vim project (do not escape vars for makeprg) diff --git a/runtime/compiler/zig_build.vim b/runtime/compiler/zig_build.vim index 79308afe34..27788836f0 100644 --- a/runtime/compiler/zig_build.vim +++ b/runtime/compiler/zig_build.vim @@ -1,6 +1,6 @@ " Vim compiler file " Compiler: Zig Compiler (zig build) -" Upstream: https://github.com/ziglang/zig.vim +" Upstream: https://codeberg.org/ziglang/zig.vim " Last Change: 2024 Apr 05 by the Vim Project (removed :CompilerSet definition) " 2026 May 12 by the Vim Project (removed comment) diff --git a/runtime/compiler/zig_build_exe.vim b/runtime/compiler/zig_build_exe.vim index 2f1681b501..f49304feb1 100644 --- a/runtime/compiler/zig_build_exe.vim +++ b/runtime/compiler/zig_build_exe.vim @@ -1,6 +1,6 @@ " Vim compiler file " Compiler: Zig Compiler (zig build-exe) -" Upstream: https://github.com/ziglang/zig.vim +" Upstream: https://codeberg.org/ziglang/zig.vim " Last Change: 2025 Nov 16 by the Vim Project (set errorformat) " 2026 May 12 by the Vim project (remove errorformat) " 2026 May 24 by the Vim project (do not escape vars for makeprg) diff --git a/runtime/compiler/zig_test.vim b/runtime/compiler/zig_test.vim index 07cb88eb4a..f1992cbffd 100644 --- a/runtime/compiler/zig_test.vim +++ b/runtime/compiler/zig_test.vim @@ -1,6 +1,6 @@ " Vim compiler file " Compiler: Zig Compiler (zig test) -" Upstream: https://github.com/ziglang/zig.vim +" Upstream: https://codeberg.org/ziglang/zig.vim " Last Change: 2025 Nov 16 by the Vim Project (set errorformat) " 2026 May 12 by the Vim Project (remove error format) " 2026 May 24 by the Vim project (do not escape vars for makeprg) diff --git a/runtime/ftplugin/zig.vim b/runtime/ftplugin/zig.vim index ea584ed086..55c3008c8f 100644 --- a/runtime/ftplugin/zig.vim +++ b/runtime/ftplugin/zig.vim @@ -2,7 +2,7 @@ " Language: Zig " Maintainer: Mathias Lindgren " Last Change: 2024 Oct 04 -" Based on: https://github.com/ziglang/zig.vim +" Based on: https://codeberg.org/ziglang/zig.vim if exists("b:did_ftplugin") finish diff --git a/runtime/indent/zig.vim b/runtime/indent/zig.vim index e3ce8aa410..9901ad8537 100644 --- a/runtime/indent/zig.vim +++ b/runtime/indent/zig.vim @@ -1,6 +1,6 @@ " Vim filetype indent file " Language: Zig -" Upstream: https://github.com/ziglang/zig.vim +" Upstream: https://codeberg.org/ziglang/zig.vim " Only load this indent file when no other was loaded. if exists("b:did_indent") diff --git a/runtime/syntax/zig.vim b/runtime/syntax/zig.vim index 121b0195b0..bf09977a5a 100644 --- a/runtime/syntax/zig.vim +++ b/runtime/syntax/zig.vim @@ -1,6 +1,6 @@ " Vim syntax file " Language: Zig -" Upstream: https://github.com/ziglang/zig.vim +" Upstream: https://codeberg.org/ziglang/zig.vim if exists("b:current_syntax") finish diff --git a/runtime/syntax/zir.vim b/runtime/syntax/zir.vim index 6553d322b7..998e6a3ed6 100644 --- a/runtime/syntax/zir.vim +++ b/runtime/syntax/zir.vim @@ -1,6 +1,6 @@ " Vim syntax file " Language: Zir -" Upstream: https://github.com/ziglang/zig.vim +" Upstream: https://codeberg.org/ziglang/zig.vim if exists("b:current_syntax") finish From 98401467a996e174d2cb741c9c7d597003e53248 Mon Sep 17 00:00:00 2001 From: "K.Takata" Date: Mon, 25 May 2026 15:16:56 +0000 Subject: [PATCH 312/545] patch 9.2.0531: tests: Test_cd_completion() fails on MS-Windows with E344 Problem: tests: Test_cd_completion() fails on MS-Windows with E344 Solution: Catch E344 in addition to E472 (Ken Takata) Sometimes, Test_cd_completion fails with E344: https://github.com/vim/vim-win32-installer/issues/446#issuecomment-4526975345 closes: #20328 Signed-off-by: K.Takata Signed-off-by: Christian Brabandt --- src/testdir/test_cd.vim | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_cd.vim b/src/testdir/test_cd.vim index e95c7c2e0e..a175cd2748 100644 --- a/src/testdir/test_cd.vim +++ b/src/testdir/test_cd.vim @@ -263,7 +263,7 @@ func Test_cd_completion() let dir = d " Yay! We found a suitable dir! break - catch /:E472:/ + catch /:\(E472\|E344\):/ " Just skip directories where "cd" fails continue finally diff --git a/src/version.c b/src/version.c index ca41d42a53..4db22e069b 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 531, /**/ 530, /**/ From 3b8ac8f8a4b3bab5637cf53507c7f77ebbadc51e Mon Sep 17 00:00:00 2001 From: Muraoka Taro Date: Mon, 25 May 2026 15:23:47 +0000 Subject: [PATCH 313/545] CI: Split platform specific CI configurations into separated files - ci-linux.yml for Linux - ci-linux_asan.yml for Linux ASan - ci-macos.yml for macOS - ci-windows.yml for Windows closes: #20325 Signed-off-by: Muraoka Taro Signed-off-by: Christian Brabandt --- .github/workflows/ci-linux.yml | 176 ++++++++ .github/workflows/ci-linux_asan.yml | 82 ++++ .github/workflows/ci-macos.yml | 103 +++++ .github/workflows/ci-windows.yml | 362 +++++++++++++++ .github/workflows/ci.yml | 668 +--------------------------- Filelist | 4 + 6 files changed, 740 insertions(+), 655 deletions(-) create mode 100644 .github/workflows/ci-linux.yml create mode 100644 .github/workflows/ci-linux_asan.yml create mode 100644 .github/workflows/ci-macos.yml create mode 100644 .github/workflows/ci-windows.yml diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml new file mode 100644 index 0000000000..987fcecc8b --- /dev/null +++ b/.github/workflows/ci-linux.yml @@ -0,0 +1,176 @@ +name: CI for Linux + +on: + workflow_call: + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for + # pull requests or the commit hash for any other events. + group: ${{ github.workflow }}-linux-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + linux: + runs-on: ${{ matrix.architecture == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} + + env: + CC: ${{ matrix.compiler }} + GCC_VER: 14 + CLANG_VER: 22 + TEST: test + SRCDIR: ./src + LEAK_CFLAGS: -DEXITFREE + LOG_DIR: ${{ github.workspace }}/logs + TERM: xterm + DISPLAY: ":99" + DEBIAN_FRONTEND: noninteractive + + strategy: + fail-fast: false + matrix: + features: [tiny, normal, huge] + compiler: [clang, gcc] + extra: [[]] + # Only use non-native architecture when features != huge. + # features=huge tries to install python3-dev, which fails to install + # for the non-native architecture. + architecture: [native] + include: + - features: tiny + compiler: clang + extra: [nogui] + - features: tiny + compiler: gcc + extra: [nogui, syn_test_execs] + - features: tiny + compiler: gcc + extra: [nogui] + architecture: arm64 + - features: normal + shadow: ./src/shadow + compiler: gcc + architecture: i386 + - features: huge + coverage: true + - features: huge + compiler: clang + interface: dynamic + python3: stable-abi + - features: huge + compiler: gcc + coverage: true + interface: dynamic + extra: [uchar, testgui] + - features: huge + compiler: gcc + coverage: true + extra: [unittests] + - features: huge + compiler: gcc + coverage: true + extra: [unittests] + architecture: arm64 + - features: normal + compiler: gcc + extra: [vimtags, proto, preproc_indent, encoding, codestyle] + - features: huge + compiler: gcc + extra: [no_x11_wl] + + steps: + - name: Checkout repository from GitHub + uses: actions/checkout@v6 + + - name: Build + timeout-minutes: 15 + uses: ./.github/actions/build_vim_on_linux + with: + features: ${{ matrix.features }} + compiler: ${{ matrix.compiler }} + architecture: ${{ matrix.architecture }} + extra: ${{ toJSON(matrix.extra) }} + shadow: ${{ matrix.shadow }} + interface: ${{ matrix.interface }} + lua_ver: ${{ matrix.lua_ver }} + python3: ${{ matrix.python3 }} + coverage: ${{ matrix.coverage }} + + - name: Test + timeout-minutes: 20 + run: make ${SHADOWOPT} ${TEST} + + # Enable to debug failing tests live and ssh into the CI runners + # - name: Setup tmate session + # if: ${{ failure() }} + # uses: mxschmitt/action-tmate@v3 + # with: + # limit-access-to-actor: true + + - name: Upload failed test artifacts + if: ${{ !cancelled() }} + uses: ./.github/actions/test_artifacts + + - name: Vim tags + if: contains(matrix.extra, 'vimtags') + run: | + # This will exit with an error code if the generated vim tags differs from source. + ( + cd runtime/doc + git diff --exit-code -- tags + make html; rm *.html tags.ref; + test -f errors.log && exit 3; + true + ) + + - name: Generate Proto files + if: contains(matrix.extra, 'proto') + run: | + # This will exit with an error code if the generated proto files differ from source + ( + git diff --exit-code -- src/proto/ + true + ) + + - name: Check Source Code style + if: contains(matrix.extra, 'codestyle') + run: | + make -C src/testdir codestyle + + - name: Check preprocessor indent + if: contains(matrix.extra, 'preproc_indent') + run: | + # This will exit with an error code if the files differ from source + ( + "${SRCDIR}"/vim -u NONE --not-a-term -esNX +"cd runtime/tools" -S preproc_indent.vim + git diff --exit-code -- src/*.[ch] src/xxd/xxd.c + true + ) + + - name: Check encoding of utf-8 runtime files + if: contains(matrix.extra, 'encoding') + run: | + # This will exit with an error code if utf-8 runtime files are not in utf-8 encoding + ( + find . -type f -name "*utf-8*.vim" -exec sh -c \ + 'iconv -f utf-8 -t utf-8 "$1" >/dev/null 2>&1 || echo "non utf-8 encoding detected in $1"' \ + find-sh {} \; |grep "non utf-8 encoding" && exit 3 + true + ) + + - name: Generate gcov files + if: matrix.coverage + run: | + cd "${SRCDIR}" + find . -type f -name '*.gcno' -exec gcov -pb {} + || true + + - name: Codecov + timeout-minutes: 20 + if: matrix.coverage + uses: codecov/codecov-action@v6 + with: + flags: linux,${{ matrix.features }}-${{ matrix.compiler }}-${{ join(matrix.extra, '-') }} + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/ci-linux_asan.yml b/.github/workflows/ci-linux_asan.yml new file mode 100644 index 0000000000..daa1e25937 --- /dev/null +++ b/.github/workflows/ci-linux_asan.yml @@ -0,0 +1,82 @@ +name: CI for Linux ASan + +on: + workflow_call: + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for + # pull requests or the commit hash for any other events. + group: ${{ github.workflow }}-linux_asan-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + linux-asan: + runs-on: ubuntu-24.04 + + env: + CC: clang + CLANG_VER: 21 + SRCDIR: ./src + LEAK_CFLAGS: -DEXITFREE + LOG_DIR: ${{ github.workspace }}/logs + TERM: xterm + DISPLAY: ":99" + DEBIAN_FRONTEND: noninteractive + + strategy: + fail-fast: false + matrix: + testset: + - tinytests + - newtests.1 + - newtests.2 + + steps: + - name: Checkout repository from GitHub + uses: actions/checkout@v6 + + - name: Build + timeout-minutes: 15 + uses: ./.github/actions/build_vim_on_linux + with: + features: huge + compiler: clang + extra: '["asan"]' + # Lua5.1 is the most widely used version (since it's what LuaJIT is + # compatible with), so ensure it works + lua_ver: "5.1" + + - name: Test (tinytests) + if: matrix.testset == 'tinytests' + timeout-minutes: 20 + run: make ${TEST} NEW_TESTS_RES= + + - name: Test (newtests) + if: startsWith(matrix.testset, 'newtests') + timeout-minutes: 20 + env: + JOB_INDEX: ${{ strategy.job-index }} + JOB_TOTAL: ${{ strategy.job-total }} + run: | + set -x + make -C src/testdir SCRIPTS_TINY_OUT= \ + NEW_TESTS_RES="$(python3 ci/gen_testset.py $((JOB_TOTAL-1)) | jq -r --argjson i $((JOB_INDEX-1)) '.[$i]|join(" ")')" + + - name: ASan logs + if: ${{ !cancelled() }} + run: | + for f in $(grep -lR '#[[:digit:]]* *0x[[:xdigit:]]*' "${LOG_DIR}"); do + ( + echo "$f" + asan_symbolize -l "$f" + ) | tee "$f".symbolized + false # in order to fail a job + done + + - name: Upload failed test artifacts + if: ${{ !cancelled() }} + uses: ./.github/actions/test_artifacts diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml new file mode 100644 index 0000000000..390f27effa --- /dev/null +++ b/.github/workflows/ci-macos.yml @@ -0,0 +1,103 @@ +name: CI for macOS + +on: + workflow_call: + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for + # pull requests or the commit hash for any other events. + group: ${{ github.workflow }}-macos-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + macos: + runs-on: ${{ matrix.runner }} + + env: + CC: clang + TEST: test + SRCDIR: ./src + LEAK_CFLAGS: -DEXITFREE + TERM: xterm + + strategy: + fail-fast: false + matrix: + features: [tiny, normal, huge] + runner: [macos-15-intel, macos-26] + + steps: + - name: Checkout repository from GitHub + uses: actions/checkout@v6 + + - name: Install packages + if: matrix.features == 'huge' + run: | + brew install lua libtool + echo "LUA_PREFIX=$(brew --prefix)" >> $GITHUB_ENV + + - name: Set up environment + run: | + ( + echo "NPROC=$(getconf _NPROCESSORS_ONLN)" + case "${{ matrix.features }}" in + tiny) + echo "TEST=testtiny" + echo "CONFOPT=--disable-gui" + ;; + normal) + ;; + huge) + echo "CONFOPT=--enable-perlinterp --enable-python3interp --enable-rubyinterp --enable-luainterp --enable-tclinterp" + ;; + esac + ) >> $GITHUB_ENV + + - name: Configure + run: | + ./configure --with-features=${{ matrix.features }} ${CONFOPT} --enable-fail-if-missing + # Append various warning flags to CFLAGS. + # BSD sed needs backup extension specified. + sed -i.bak -f ci/config.mk.sed ${SRCDIR}/auto/config.mk + # On macOS, the entity of gcc is clang. + sed -i.bak -f ci/config.mk.clang.sed ${SRCDIR}/auto/config.mk + # Suppress some warnings produced by clang 12 and later. + if clang --version | grep -qs 'Apple clang version \(1[3-9]\|[2-9]\)\.'; then + sed -i.bak -f ci/config.mk.clang-12.sed ${SRCDIR}/auto/config.mk + fi + + - name: Build + env: + LC_ALL: C + run: | + make -j${NPROC} + + - name: Check version + run: | + "${SRCDIR}"/vim --version + "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-1.vim -c quit + "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-2.vim -c quit + if ${{ matrix.features == 'huge' }}; then + # Also check that optional and dynamic features are configured and working + "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 \ + -c "let g:required=['sound', 'perl', 'python3', 'lua', 'ruby', 'tcl']" \ + -S ci/if_feat_check.vim -c quit + fi + + - name: Install packages for testing + run: | + # Apple diff is broken. Use GNU diff instead. See #14032. + brew install diffutils + + - name: Test + timeout-minutes: 25 + run: | + make ${TEST} + + - name: Upload failed test artifacts + if: ${{ !cancelled() }} + uses: ./.github/actions/test_artifacts diff --git a/.github/workflows/ci-windows.yml b/.github/workflows/ci-windows.yml new file mode 100644 index 0000000000..1f676b2ca9 --- /dev/null +++ b/.github/workflows/ci-windows.yml @@ -0,0 +1,362 @@ +name: CI for Windows + +on: + workflow_call: + +# Cancels all previous workflow runs for pull requests that have not completed. +concurrency: + # The concurrency group contains the workflow name and the branch name for + # pull requests or the commit hash for any other events. + group: ${{ github.workflow }}-windows-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +jobs: + windows: + runs-on: windows-2022 + + env: + # Interfaces + # Lua + LUA_VER: 54 + LUA_VER_DOT: "5.4" + LUA_RELEASE: 5.4.2 + LUA32_URL: https://downloads.sourceforge.net/luabinaries/lua-%LUA_RELEASE%_Win32_dllw6_lib.zip + LUA64_URL: https://downloads.sourceforge.net/luabinaries/lua-%LUA_RELEASE%_Win64_dllw6_lib.zip + LUA_DIR: D:\Lua + # do not want \L to end up in pathdef.c and compiler complaining about unknown escape sequences \l + LUA_DIR_SLASH: D:/Lua + # Python 2 + PYTHON_VER: 27 + PYTHON_VER_DOT: "2.7" + PYTHON_DIR: 'C:\Python27' + # Python 3 + PYTHON3_VER: 313 + PYTHON3_VER_DOT: "3.13" + # Other dependencies + # winpty + WINPTY_URL: https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip + # libsodium + SODIUM_VER: "1.0.20" + # SODIUM_MSVC_URL: https://download.libsodium.org/libsodium/releases/libsodium-%SODIUM_VER%-stable-msvc.zip + SODIUM_MSVC_URL: https://github.com/jedisct1/libsodium/releases/download/%SODIUM_VER%-RELEASE/libsodium-%SODIUM_VER%-msvc.zip + SODIUM_MSVC_VER: v143 + # SODIUM_MINGW_URL: https://download.libsodium.org/libsodium/releases/libsodium-%SODIUM_VER%-stable-mingw.tar.gz + SODIUM_MINGW_URL: https://github.com/jedisct1/libsodium/releases/download/%SODIUM_VER%-RELEASE/libsodium-%SODIUM_VER%-mingw.tar.gz + SODIUM_MINGW_VER: 26 + # Gettext-tools, iconv and libraries + GETTEXT32_URL: https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.21-v1.16/gettext0.21-iconv1.16-shared-32.zip + GETTEXT64_URL: https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.21-v1.16/gettext0.21-iconv1.16-shared-64.zip + # Escape sequences + COL_RED: "\x1b[31m" + COL_GREEN: "\x1b[32m" + COL_YELLOW: "\x1b[33m" + COL_RESET: "\x1b[m" + + strategy: + fail-fast: false + matrix: + include: + - { features: HUGE, toolchain: msvc, VIMDLL: no, GUI: no, arch: x64, python3: stable } + - { features: HUGE, toolchain: mingw, VIMDLL: yes, GUI: yes, arch: x86, python3: stable, coverage: yes } + - { features: HUGE, toolchain: msvc, VIMDLL: no, GUI: yes, arch: x86 } + - { features: HUGE, toolchain: mingw, VIMDLL: yes, GUI: no, arch: x64, coverage: yes } + - { features: HUGE, toolchain: msvc, VIMDLL: no, GUI: no, arch: x64, ttytype: conpty } + - { features: NORMAL, toolchain: msvc, VIMDLL: yes, GUI: no, arch: x86 } + - { features: NORMAL, toolchain: mingw, VIMDLL: no, GUI: yes, arch: x64 } + - { features: TINY, toolchain: msvc, VIMDLL: yes, GUI: yes, arch: x64 } + - { features: TINY, toolchain: mingw, VIMDLL: no, GUI: no, arch: x86 } + + steps: + - name: Initialize + id: init + shell: bash + run: | + # Show Windows version + cmd /c ver + + if ${{ matrix.arch == 'x64' }}; then + cygreg=registry + pyreg= + echo "VCARCH=amd64" >> $GITHUB_ENV + echo "WARCH=x64" >> $GITHUB_ENV + echo "BITS=64" >> $GITHUB_ENV + echo "MSYSTEM=MINGW64" >> $GITHUB_ENV + else + cygreg=registry32 + pyreg=-32 + echo "VCARCH=x86" >> $GITHUB_ENV + echo "WARCH=ia32" >> $GITHUB_ENV + echo "BITS=32" >> $GITHUB_ENV + echo "MSYSTEM=MINGW32" >> $GITHUB_ENV + fi + + echo "VCVARSALL=$(vswhere -products \* -latest -property installationPath)\\VC\\Auxiliary\\Build\\vcvarsall.bat" >> $GITHUB_ENV + if ${{ matrix.features != 'TINY' }}; then + if ${{ matrix.arch == 'x86' }}; then + choco install python2 --no-progress --forcex86 + else + choco install python2 --no-progress + fi + fi + python3_dir=$(cat "/proc/$cygreg/HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${PYTHON3_VER_DOT}$pyreg/InstallPath/@") + echo "PYTHON3_DIR=$python3_dir" >> $GITHUB_ENV + + if ${{ matrix.toolchain == 'msvc' }}; then + SODIUM_DIR=D:\\libsodium + echo "SODIUM_LIB=${SODIUM_DIR}\\${{ matrix.arch == 'x64' && 'x64' || 'Win32' }}\\Release\\${SODIUM_MSVC_VER}\\dynamic" >> $GITHUB_ENV + else + SODIUM_DIR=D:\\libsodium-win${{ matrix.arch == 'x64' && '64' || '32' }} + # do not want \L to end up in pathdef.c and compiler complaining about unknown escape sequences \l + SODIUM_DIR_SLASH=D:/libsodium-win${{ matrix.arch == 'x64' && '64' || '32' }} + echo "SODIUM_LIB=${SODIUM_DIR}\\bin" >> $GITHUB_ENV + echo "SODIUM_DIR_SLASH=${SODIUM_DIR_SLASH}" >> $GITHUB_ENV + fi + echo "SODIUM_DIR=${SODIUM_DIR}" >> $GITHUB_ENV + echo "GETTEXT_PATH=D:\gettext${{ matrix.arch == 'x64' && '64' || '32' }}" >> $GITHUB_ENV + + - uses: msys2/setup-msys2@v2.31.1 + if: matrix.toolchain == 'mingw' + with: + update: true + install: tar + pacboy: >- + make:p gcc:p + msystem: ${{ env.MSYSTEM }} + release: false + + - name: Checkout repository from GitHub + uses: actions/checkout@v6 + + - name: Create a list of download URLs + shell: cmd + run: | + type NUL > urls.txt + echo %LUA_RELEASE%>> urls.txt + echo %WINPTY_URL%>> urls.txt + echo %SODIUM_VER%>> urls.txt + echo %GETTEXT32_URL%>> urls.txt + echo %GETTEXT64_URL%>> urls.txt + + - name: Cache downloaded files + uses: actions/cache@v5.0.5 + with: + path: downloads + key: ${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('urls.txt') }} + + - name: Download dependencies + shell: cmd + run: | + path C:\Program Files\7-Zip;%path% + if not exist downloads mkdir downloads + + echo %COL_GREEN%Download Lua%COL_RESET% + call :downloadfile %LUA${{ env.BITS }}_URL% downloads\lua.zip + 7z x downloads\lua.zip -o%LUA_DIR% > nul || exit 1 + + if not "${{ matrix.ttytype }}" == "conpty" ( + echo %COL_GREEN%Download winpty%COL_RESET% + call :downloadfile %WINPTY_URL% downloads\winpty.zip + 7z x -y downloads\winpty.zip -oD:\winpty > nul || exit 1 + copy /Y D:\winpty\%WARCH%\bin\winpty.dll src\winpty%BITS%.dll + copy /Y D:\winpty\%WARCH%\bin\winpty-agent.exe src\ + ) + + echo %COL_GREEN%Download libsodium%COL_RESET% + if "${{ matrix.toolchain }}"=="msvc" ( + call :downloadfile %SODIUM_MSVC_URL% downloads\libsodium.zip + 7z x -y downloads\libsodium.zip -oD:\ > nul || exit 1 + ) else ( + call :downloadfile %SODIUM_MINGW_URL% downloads\libsodium.tar.gz + 7z x -y downloads\libsodium.tar.gz -so | 7z x -si -ttar -oD:\ > nul || exit 1 + mklink %SODIUM_LIB%\libsodium.dll %SODIUM_LIB%\libsodium-%SODIUM_MINGW_VER%.dll + ) + + echo %COL_GREEN%Download Gettext%COL_RESET% + call :downloadfile %GETTEXT${{ env.BITS }}_URL% downloads\gettext${{ env.BITS }}.zip + 7z e -y downloads\gettext${{ env.BITS }}.zip -oD:\gettext${{ env.BITS }} > nul || exit 1 + copy /y D:\gettext${{ env.BITS }}\libintl-8.dll src\ || exit 1 + copy /y D:\gettext${{ env.BITS }}\libiconv-2.dll src\ || exit 1 + + goto :eof + + :downloadfile + :: call :downloadfile + if not exist %2 ( + curl -f -L %1 -o %2 + ) + if ERRORLEVEL 1 ( + rem Retry once. + curl -f -L %1 -o %2 || exit 1 + ) + goto :eof + + - name: Build (MSVC) + if: matrix.toolchain == 'msvc' + shell: cmd + run: | + call "%VCVARSALL%" %VCARCH% + cd src + if "${{ matrix.VIMDLL }}"=="yes" ( + set GUI=yes + ) else ( + set GUI=${{ matrix.GUI }} + ) + if "${{ matrix.python3 }}"=="stable" ( + set PYTHON3_STABLE=yes + ) else ( + set PYTHON3_STABLE=no + ) + if "${{ matrix.features }}"=="HUGE" ( + nmake -nologo -f Make_mvc.mak ^ + FEATURES=${{ matrix.features }} ^ + GUI=%GUI% IME=yes ICONV=yes VIMDLL=${{ matrix.VIMDLL }} ^ + DYNAMIC_LUA=yes LUA=%LUA_DIR% ^ + DYNAMIC_PYTHON=yes PYTHON=%PYTHON_DIR% ^ + DYNAMIC_PYTHON3=yes PYTHON3=%PYTHON3_DIR% ^ + DYNAMIC_PYTHON3_STABLE_ABI=%PYTHON3_STABLE% ^ + DYNAMIC_SODIUM=yes SODIUM=%SODIUM_DIR% ^ + CI_FLAGS=/we4267 + ) else ( + nmake -nologo -f Make_mvc.mak ^ + FEATURES=${{ matrix.features }} ^ + GUI=%GUI% IME=yes ICONV=yes VIMDLL=${{ matrix.VIMDLL }} ^ + CI_FLAGS=/we4267 + ) + + - name: Build (MinGW) + if: matrix.toolchain == 'mingw' + shell: msys2 {0} + run: | + cd src + if [ "${{ matrix.VIMDLL }}" = "yes" ]; then + GUI=yes + else + GUI=${{ matrix.GUI }} + fi + if [ "${{ matrix.python3 }}" = "stable" ]; then + PYTHON3_STABLE=yes + else + PYTHON3_STABLE=no + fi + if [ "${{ matrix.features }}" = "HUGE" ]; then + mingw32-make -f Make_ming.mak -j2 \ + FEATURES=${{ matrix.features }} \ + GUI=$GUI IME=yes ICONV=yes VIMDLL=${{ matrix.VIMDLL }} \ + DYNAMIC_LUA=yes LUA=${LUA_DIR_SLASH} \ + DYNAMIC_PYTHON=yes PYTHON=${PYTHON_DIR} \ + DYNAMIC_PYTHON3=yes PYTHON3=${PYTHON3_DIR} \ + DYNAMIC_PYTHON3_STABLE_ABI=${PYTHON3_STABLE} \ + DYNAMIC_SODIUM=yes SODIUM=${SODIUM_DIR_SLASH} \ + STATIC_STDCPLUS=yes COVERAGE=${{ matrix.coverage }} + else + mingw32-make -f Make_ming.mak -j2 \ + FEATURES=${{ matrix.features }} \ + GUI=$GUI IME=yes ICONV=yes VIMDLL=${{ matrix.VIMDLL }} \ + STATIC_STDCPLUS=yes + fi + + - name: Check version + shell: cmd + run: | + PATH %LUA_DIR%;C:\msys64\%MSYSTEM%\bin;%PYTHON3_DIR%;%PATH% + if "${{ matrix.GUI }}"=="yes" ( + start /wait src\gvim -u NONE -i NONE -c "redir > version.txt | ver | q" || exit 1 + type version.txt + echo. + start /wait src\gvim -u NONE -i NONE -c "redir! > version.txt | so ci\if_ver-1.vim | q" + start /wait src\gvim -u NONE -i NONE -c "redir >> version.txt | so ci\if_ver-2.vim | q" + type version.txt + del version.txt + ) else ( + src\vim --version || exit 1 + src\vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-1.vim -c quit + src\vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-2.vim -c quit + if "${{ matrix.features }}"=="HUGE" ( + src\vim -u NONE -i NONE --not-a-term -esNX -V1 ^ + -c "let g:required=['gettext', 'sodium', 'sound', 'python3', 'lua']" ^ + -S ci/if_feat_check.vim -c quit + ) + ) + + #- name: Prepare Artifact + # shell: cmd + # run: | + # mkdir artifacts + # copy src\*vim.exe artifacts + # copy src\vim*.dll artifacts + # + #- name: Upload Artifact + # uses: actions/upload-artifact@v7 + # with: + # name: vim${{ matrix.bits }}-${{ matrix.toolchain }} + # path: ./artifacts + + # disabled because of https://github.com/tunisiano187/Chocolatey-packages/issues/3916 + #- 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 + timeout-minutes: 15 + run: | + PATH %LUA_DIR%;C:\msys64\%MSYSTEM%\bin;%PYTHON3_DIR%;%PATH%;%SODIUM_LIB% + call "%VCVARSALL%" %VCARCH% + + echo %COL_GREEN%Test gVim:%COL_RESET% + cd src\testdir + if "${{ matrix.GUI }}"=="yes" ( + nmake -nologo -f Make_mvc.mak VIMPROG=..\gvim || exit 1 + ) else ( + @rem Run only tiny tests. + nmake -nologo -f Make_mvc.mak tiny VIMPROG=..\gvim || exit 1 + ) + + - name: Test and show the result of testing Vim + if: matrix.GUI == 'no' || matrix.VIMDLL == 'yes' + shell: cmd + timeout-minutes: 15 + run: | + PATH %LUA_DIR%;C:\msys64\%MSYSTEM%\bin;%PYTHON3_DIR%;%PATH%;%SODIUM_LIB% + call "%VCVARSALL%" %VCARCH% + + echo %COL_GREEN%Test Vim:%COL_RESET% + cd src\testdir + nmake -nologo -f Make_mvc.mak clean + if "${{ matrix.GUI }}"=="no" ( + nmake -nologo -f Make_mvc.mak VIMPROG=..\vim || exit 1 + ) else ( + @rem Run only tiny tests. + nmake -nologo -f Make_mvc.mak tiny VIMPROG=..\vim || exit 1 + ) + + - name: Upload failed test artifacts + if: ${{ !cancelled() }} + uses: ./.github/actions/test_artifacts + + - name: Generate gcov files + if: matrix.coverage + shell: msys2 {0} + run: | + cd src + find . -type f -name '*.gcno' -exec gcov -pb {} + || true + + - name: Codecov + timeout-minutes: 20 + if: matrix.coverage + uses: codecov/codecov-action@v6 + with: + directory: src + flags: windows,${{ matrix.toolchain }}-${{ matrix.arch }}-${{ matrix.features }} + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6a3e619279..c725027b88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,665 +16,23 @@ permissions: contents: read # to fetch code (actions/checkout) jobs: - linux: - runs-on: ${{ matrix.architecture == 'arm64' && 'ubuntu-24.04-arm' || 'ubuntu-24.04' }} - - env: - CC: ${{ matrix.compiler }} - GCC_VER: 14 - CLANG_VER: 22 - TEST: test - SRCDIR: ./src - LEAK_CFLAGS: -DEXITFREE - LOG_DIR: ${{ github.workspace }}/logs - TERM: xterm - DISPLAY: ":99" - DEBIAN_FRONTEND: noninteractive - - strategy: - fail-fast: false - matrix: - features: [tiny, normal, huge] - compiler: [clang, gcc] - extra: [[]] - # Only use non-native architecture when features != huge. - # features=huge tries to install python3-dev, which fails to install - # for the non-native architecture. - architecture: [native] - include: - - features: tiny - compiler: clang - extra: [nogui] - - features: tiny - compiler: gcc - extra: [nogui, syn_test_execs] - - features: tiny - compiler: gcc - extra: [nogui] - architecture: arm64 - - features: normal - shadow: ./src/shadow - compiler: gcc - architecture: i386 - - features: huge - coverage: true - - features: huge - compiler: clang - interface: dynamic - python3: stable-abi - - features: huge - compiler: gcc - coverage: true - interface: dynamic - extra: [uchar, testgui] - - features: huge - compiler: gcc - coverage: true - extra: [unittests] - - features: huge - compiler: gcc - coverage: true - extra: [unittests] - architecture: arm64 - - features: normal - compiler: gcc - extra: [vimtags, proto, preproc_indent, encoding, codestyle] - - features: huge - compiler: gcc - extra: [no_x11_wl] - - steps: - - name: Checkout repository from GitHub - uses: actions/checkout@v6 - - - name: Build - timeout-minutes: 15 - uses: ./.github/actions/build_vim_on_linux - with: - features: ${{ matrix.features }} - compiler: ${{ matrix.compiler }} - architecture: ${{ matrix.architecture }} - extra: ${{ toJSON(matrix.extra) }} - shadow: ${{ matrix.shadow }} - interface: ${{ matrix.interface }} - lua_ver: ${{ matrix.lua_ver }} - python3: ${{ matrix.python3 }} - coverage: ${{ matrix.coverage }} - - - name: Test - timeout-minutes: 20 - run: make ${SHADOWOPT} ${TEST} - - # Enable to debug failing tests live and ssh into the CI runners - # - name: Setup tmate session - # if: ${{ failure() }} - # uses: mxschmitt/action-tmate@v3 - # with: - # limit-access-to-actor: true - - - name: Upload failed test artifacts - if: ${{ !cancelled() }} - uses: ./.github/actions/test_artifacts - - name: Vim tags - if: contains(matrix.extra, 'vimtags') - run: | - # This will exit with an error code if the generated vim tags differs from source. - ( - cd runtime/doc - git diff --exit-code -- tags - make html; rm *.html tags.ref; - test -f errors.log && exit 3; - true - ) - - - name: Generate Proto files - if: contains(matrix.extra, 'proto') - run: | - # This will exit with an error code if the generated proto files differ from source - ( - git diff --exit-code -- src/proto/ - true - ) - - - name: Check Source Code style - if: contains(matrix.extra, 'codestyle') - run: | - make -C src/testdir codestyle - - - name: Check preprocessor indent - if: contains(matrix.extra, 'preproc_indent') - run: | - # This will exit with an error code if the files differ from source - ( - "${SRCDIR}"/vim -u NONE --not-a-term -esNX +"cd runtime/tools" -S preproc_indent.vim - git diff --exit-code -- src/*.[ch] src/xxd/xxd.c - true - ) - - - name: Check encoding of utf-8 runtime files - if: contains(matrix.extra, 'encoding') - run: | - # This will exit with an error code if utf-8 runtime files are not in utf-8 encoding - ( - find . -type f -name "*utf-8*.vim" -exec sh -c \ - 'iconv -f utf-8 -t utf-8 "$1" >/dev/null 2>&1 || echo "non utf-8 encoding detected in $1"' \ - find-sh {} \; |grep "non utf-8 encoding" && exit 3 - true - ) - - - name: Generate gcov files - if: matrix.coverage - run: | - cd "${SRCDIR}" - find . -type f -name '*.gcno' -exec gcov -pb {} + || true - - - name: Codecov - timeout-minutes: 20 - if: matrix.coverage - uses: codecov/codecov-action@v6 - with: - flags: linux,${{ matrix.features }}-${{ matrix.compiler }}-${{ join(matrix.extra, '-') }} - token: ${{ secrets.CODECOV_TOKEN }} + linux: + name: Linux + uses: ./.github/workflows/ci-linux.yml + secrets: inherit linux-asan: - runs-on: ubuntu-24.04 - - env: - CC: clang - CLANG_VER: 21 - SRCDIR: ./src - LEAK_CFLAGS: -DEXITFREE - LOG_DIR: ${{ github.workspace }}/logs - TERM: xterm - DISPLAY: ":99" - DEBIAN_FRONTEND: noninteractive - - strategy: - fail-fast: false - matrix: - testset: - - tinytests - - newtests.1 - - newtests.2 - - steps: - - name: Checkout repository from GitHub - uses: actions/checkout@v6 - - - name: Build - timeout-minutes: 15 - uses: ./.github/actions/build_vim_on_linux - with: - features: huge - compiler: clang - extra: '["asan"]' - # Lua5.1 is the most widely used version (since it's what LuaJIT is - # compatible with), so ensure it works - lua_ver: "5.1" - - - name: Test (tinytests) - if: matrix.testset == 'tinytests' - timeout-minutes: 20 - run: make ${TEST} NEW_TESTS_RES= - - - name: Test (newtests) - if: startsWith(matrix.testset, 'newtests') - timeout-minutes: 20 - env: - JOB_INDEX: ${{ strategy.job-index }} - JOB_TOTAL: ${{ strategy.job-total }} - run: | - set -x - make -C src/testdir SCRIPTS_TINY_OUT= \ - NEW_TESTS_RES="$(python3 ci/gen_testset.py $((JOB_TOTAL-1)) | jq -r --argjson i $((JOB_INDEX-1)) '.[$i]|join(" ")')" - - - name: ASan logs - if: ${{ !cancelled() }} - run: | - for f in $(grep -lR '#[[:digit:]]* *0x[[:xdigit:]]*' "${LOG_DIR}"); do - ( - echo "$f" - asan_symbolize -l "$f" - ) | tee "$f".symbolized - false # in order to fail a job - done - - - name: Upload failed test artifacts - if: ${{ !cancelled() }} - uses: ./.github/actions/test_artifacts + name: Linux ASan + uses: ./.github/workflows/ci-linux_asan.yml + secrets: inherit macos: - runs-on: ${{ matrix.runner }} - - env: - CC: clang - TEST: test - SRCDIR: ./src - LEAK_CFLAGS: -DEXITFREE - TERM: xterm - - strategy: - fail-fast: false - matrix: - features: [tiny, normal, huge] - runner: [macos-15-intel, macos-26] - - steps: - - name: Checkout repository from GitHub - uses: actions/checkout@v6 - - - name: Install packages - if: matrix.features == 'huge' - run: | - brew install lua libtool - echo "LUA_PREFIX=$(brew --prefix)" >> $GITHUB_ENV - - - name: Set up environment - run: | - ( - echo "NPROC=$(getconf _NPROCESSORS_ONLN)" - case "${{ matrix.features }}" in - tiny) - echo "TEST=testtiny" - echo "CONFOPT=--disable-gui" - ;; - normal) - ;; - huge) - echo "CONFOPT=--enable-perlinterp --enable-python3interp --enable-rubyinterp --enable-luainterp --enable-tclinterp" - ;; - esac - ) >> $GITHUB_ENV - - - name: Configure - run: | - ./configure --with-features=${{ matrix.features }} ${CONFOPT} --enable-fail-if-missing - # Append various warning flags to CFLAGS. - # BSD sed needs backup extension specified. - sed -i.bak -f ci/config.mk.sed ${SRCDIR}/auto/config.mk - # On macOS, the entity of gcc is clang. - sed -i.bak -f ci/config.mk.clang.sed ${SRCDIR}/auto/config.mk - # Suppress some warnings produced by clang 12 and later. - if clang --version | grep -qs 'Apple clang version \(1[3-9]\|[2-9]\)\.'; then - sed -i.bak -f ci/config.mk.clang-12.sed ${SRCDIR}/auto/config.mk - fi - - - name: Build - env: - LC_ALL: C - run: | - make -j${NPROC} - - - name: Check version - run: | - "${SRCDIR}"/vim --version - "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-1.vim -c quit - "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-2.vim -c quit - if ${{ matrix.features == 'huge' }}; then - # Also check that optional and dynamic features are configured and working - "${SRCDIR}"/vim -u NONE -i NONE --not-a-term -esNX -V1 \ - -c "let g:required=['sound', 'perl', 'python3', 'lua', 'ruby', 'tcl']" \ - -S ci/if_feat_check.vim -c quit - fi - - - name: Install packages for testing - run: | - # Apple diff is broken. Use GNU diff instead. See #14032. - brew install diffutils - - - name: Test - timeout-minutes: 25 - run: | - make ${TEST} - - - name: Upload failed test artifacts - if: ${{ !cancelled() }} - uses: ./.github/actions/test_artifacts + name: macOS + uses: ./.github/workflows/ci-macos.yml + secrets: inherit windows: - runs-on: windows-2022 - - env: - # Interfaces - # Lua - LUA_VER: 54 - LUA_VER_DOT: "5.4" - LUA_RELEASE: 5.4.2 - LUA32_URL: https://downloads.sourceforge.net/luabinaries/lua-%LUA_RELEASE%_Win32_dllw6_lib.zip - LUA64_URL: https://downloads.sourceforge.net/luabinaries/lua-%LUA_RELEASE%_Win64_dllw6_lib.zip - LUA_DIR: D:\Lua - # do not want \L to end up in pathdef.c and compiler complaining about unknown escape sequences \l - LUA_DIR_SLASH: D:/Lua - # Python 2 - PYTHON_VER: 27 - PYTHON_VER_DOT: "2.7" - PYTHON_DIR: 'C:\Python27' - # Python 3 - PYTHON3_VER: 313 - PYTHON3_VER_DOT: "3.13" - # Other dependencies - # winpty - WINPTY_URL: https://github.com/rprichard/winpty/releases/download/0.4.3/winpty-0.4.3-msvc2015.zip - # libsodium - SODIUM_VER: "1.0.20" - # SODIUM_MSVC_URL: https://download.libsodium.org/libsodium/releases/libsodium-%SODIUM_VER%-stable-msvc.zip - SODIUM_MSVC_URL: https://github.com/jedisct1/libsodium/releases/download/%SODIUM_VER%-RELEASE/libsodium-%SODIUM_VER%-msvc.zip - SODIUM_MSVC_VER: v143 - # SODIUM_MINGW_URL: https://download.libsodium.org/libsodium/releases/libsodium-%SODIUM_VER%-stable-mingw.tar.gz - SODIUM_MINGW_URL: https://github.com/jedisct1/libsodium/releases/download/%SODIUM_VER%-RELEASE/libsodium-%SODIUM_VER%-mingw.tar.gz - SODIUM_MINGW_VER: 26 - # Gettext-tools, iconv and libraries - GETTEXT32_URL: https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.21-v1.16/gettext0.21-iconv1.16-shared-32.zip - GETTEXT64_URL: https://github.com/mlocati/gettext-iconv-windows/releases/download/v0.21-v1.16/gettext0.21-iconv1.16-shared-64.zip - # Escape sequences - COL_RED: "\x1b[31m" - COL_GREEN: "\x1b[32m" - COL_YELLOW: "\x1b[33m" - COL_RESET: "\x1b[m" - - strategy: - fail-fast: false - matrix: - include: - - { features: HUGE, toolchain: msvc, VIMDLL: no, GUI: no, arch: x64, python3: stable } - - { features: HUGE, toolchain: mingw, VIMDLL: yes, GUI: yes, arch: x86, python3: stable, coverage: yes } - - { features: HUGE, toolchain: msvc, VIMDLL: no, GUI: yes, arch: x86 } - - { features: HUGE, toolchain: mingw, VIMDLL: yes, GUI: no, arch: x64, coverage: yes } - - { features: HUGE, toolchain: msvc, VIMDLL: no, GUI: no, arch: x64, ttytype: conpty } - - { features: NORMAL, toolchain: msvc, VIMDLL: yes, GUI: no, arch: x86 } - - { features: NORMAL, toolchain: mingw, VIMDLL: no, GUI: yes, arch: x64 } - - { features: TINY, toolchain: msvc, VIMDLL: yes, GUI: yes, arch: x64 } - - { features: TINY, toolchain: mingw, VIMDLL: no, GUI: no, arch: x86 } - - steps: - - name: Initialize - id: init - shell: bash - run: | - # Show Windows version - cmd /c ver - - if ${{ matrix.arch == 'x64' }}; then - cygreg=registry - pyreg= - echo "VCARCH=amd64" >> $GITHUB_ENV - echo "WARCH=x64" >> $GITHUB_ENV - echo "BITS=64" >> $GITHUB_ENV - echo "MSYSTEM=MINGW64" >> $GITHUB_ENV - else - cygreg=registry32 - pyreg=-32 - echo "VCARCH=x86" >> $GITHUB_ENV - echo "WARCH=ia32" >> $GITHUB_ENV - echo "BITS=32" >> $GITHUB_ENV - echo "MSYSTEM=MINGW32" >> $GITHUB_ENV - fi - - echo "VCVARSALL=$(vswhere -products \* -latest -property installationPath)\\VC\\Auxiliary\\Build\\vcvarsall.bat" >> $GITHUB_ENV - if ${{ matrix.features != 'TINY' }}; then - if ${{ matrix.arch == 'x86' }}; then - choco install python2 --no-progress --forcex86 - else - choco install python2 --no-progress - fi - fi - python3_dir=$(cat "/proc/$cygreg/HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${PYTHON3_VER_DOT}$pyreg/InstallPath/@") - echo "PYTHON3_DIR=$python3_dir" >> $GITHUB_ENV - - if ${{ matrix.toolchain == 'msvc' }}; then - SODIUM_DIR=D:\\libsodium - echo "SODIUM_LIB=${SODIUM_DIR}\\${{ matrix.arch == 'x64' && 'x64' || 'Win32' }}\\Release\\${SODIUM_MSVC_VER}\\dynamic" >> $GITHUB_ENV - else - SODIUM_DIR=D:\\libsodium-win${{ matrix.arch == 'x64' && '64' || '32' }} - # do not want \L to end up in pathdef.c and compiler complaining about unknown escape sequences \l - SODIUM_DIR_SLASH=D:/libsodium-win${{ matrix.arch == 'x64' && '64' || '32' }} - echo "SODIUM_LIB=${SODIUM_DIR}\\bin" >> $GITHUB_ENV - echo "SODIUM_DIR_SLASH=${SODIUM_DIR_SLASH}" >> $GITHUB_ENV - fi - echo "SODIUM_DIR=${SODIUM_DIR}" >> $GITHUB_ENV - echo "GETTEXT_PATH=D:\gettext${{ matrix.arch == 'x64' && '64' || '32' }}" >> $GITHUB_ENV - - - uses: msys2/setup-msys2@v2.31.1 - if: matrix.toolchain == 'mingw' - with: - update: true - install: tar - pacboy: >- - make:p gcc:p - msystem: ${{ env.MSYSTEM }} - release: false - - - name: Checkout repository from GitHub - uses: actions/checkout@v6 - - - name: Create a list of download URLs - shell: cmd - run: | - type NUL > urls.txt - echo %LUA_RELEASE%>> urls.txt - echo %WINPTY_URL%>> urls.txt - echo %SODIUM_VER%>> urls.txt - echo %GETTEXT32_URL%>> urls.txt - echo %GETTEXT64_URL%>> urls.txt - - - name: Cache downloaded files - uses: actions/cache@v5.0.5 - with: - path: downloads - key: ${{ runner.os }}-${{ matrix.arch }}-${{ hashFiles('urls.txt') }} - - - name: Download dependencies - shell: cmd - run: | - path C:\Program Files\7-Zip;%path% - if not exist downloads mkdir downloads - - echo %COL_GREEN%Download Lua%COL_RESET% - call :downloadfile %LUA${{ env.BITS }}_URL% downloads\lua.zip - 7z x downloads\lua.zip -o%LUA_DIR% > nul || exit 1 - - if not "${{ matrix.ttytype }}" == "conpty" ( - echo %COL_GREEN%Download winpty%COL_RESET% - call :downloadfile %WINPTY_URL% downloads\winpty.zip - 7z x -y downloads\winpty.zip -oD:\winpty > nul || exit 1 - copy /Y D:\winpty\%WARCH%\bin\winpty.dll src\winpty%BITS%.dll - copy /Y D:\winpty\%WARCH%\bin\winpty-agent.exe src\ - ) - - echo %COL_GREEN%Download libsodium%COL_RESET% - if "${{ matrix.toolchain }}"=="msvc" ( - call :downloadfile %SODIUM_MSVC_URL% downloads\libsodium.zip - 7z x -y downloads\libsodium.zip -oD:\ > nul || exit 1 - ) else ( - call :downloadfile %SODIUM_MINGW_URL% downloads\libsodium.tar.gz - 7z x -y downloads\libsodium.tar.gz -so | 7z x -si -ttar -oD:\ > nul || exit 1 - mklink %SODIUM_LIB%\libsodium.dll %SODIUM_LIB%\libsodium-%SODIUM_MINGW_VER%.dll - ) - - echo %COL_GREEN%Download Gettext%COL_RESET% - call :downloadfile %GETTEXT${{ env.BITS }}_URL% downloads\gettext${{ env.BITS }}.zip - 7z e -y downloads\gettext${{ env.BITS }}.zip -oD:\gettext${{ env.BITS }} > nul || exit 1 - copy /y D:\gettext${{ env.BITS }}\libintl-8.dll src\ || exit 1 - copy /y D:\gettext${{ env.BITS }}\libiconv-2.dll src\ || exit 1 - - goto :eof - - :downloadfile - :: call :downloadfile - if not exist %2 ( - curl -f -L %1 -o %2 - ) - if ERRORLEVEL 1 ( - rem Retry once. - curl -f -L %1 -o %2 || exit 1 - ) - goto :eof - - - name: Build (MSVC) - if: matrix.toolchain == 'msvc' - shell: cmd - run: | - call "%VCVARSALL%" %VCARCH% - cd src - if "${{ matrix.VIMDLL }}"=="yes" ( - set GUI=yes - ) else ( - set GUI=${{ matrix.GUI }} - ) - if "${{ matrix.python3 }}"=="stable" ( - set PYTHON3_STABLE=yes - ) else ( - set PYTHON3_STABLE=no - ) - if "${{ matrix.features }}"=="HUGE" ( - nmake -nologo -f Make_mvc.mak ^ - FEATURES=${{ matrix.features }} ^ - GUI=%GUI% IME=yes ICONV=yes VIMDLL=${{ matrix.VIMDLL }} ^ - DYNAMIC_LUA=yes LUA=%LUA_DIR% ^ - DYNAMIC_PYTHON=yes PYTHON=%PYTHON_DIR% ^ - DYNAMIC_PYTHON3=yes PYTHON3=%PYTHON3_DIR% ^ - DYNAMIC_PYTHON3_STABLE_ABI=%PYTHON3_STABLE% ^ - DYNAMIC_SODIUM=yes SODIUM=%SODIUM_DIR% ^ - CI_FLAGS=/we4267 - ) else ( - nmake -nologo -f Make_mvc.mak ^ - FEATURES=${{ matrix.features }} ^ - GUI=%GUI% IME=yes ICONV=yes VIMDLL=${{ matrix.VIMDLL }} ^ - CI_FLAGS=/we4267 - ) - - - name: Build (MinGW) - if: matrix.toolchain == 'mingw' - shell: msys2 {0} - run: | - cd src - if [ "${{ matrix.VIMDLL }}" = "yes" ]; then - GUI=yes - else - GUI=${{ matrix.GUI }} - fi - if [ "${{ matrix.python3 }}" = "stable" ]; then - PYTHON3_STABLE=yes - else - PYTHON3_STABLE=no - fi - if [ "${{ matrix.features }}" = "HUGE" ]; then - mingw32-make -f Make_ming.mak -j2 \ - FEATURES=${{ matrix.features }} \ - GUI=$GUI IME=yes ICONV=yes VIMDLL=${{ matrix.VIMDLL }} \ - DYNAMIC_LUA=yes LUA=${LUA_DIR_SLASH} \ - DYNAMIC_PYTHON=yes PYTHON=${PYTHON_DIR} \ - DYNAMIC_PYTHON3=yes PYTHON3=${PYTHON3_DIR} \ - DYNAMIC_PYTHON3_STABLE_ABI=${PYTHON3_STABLE} \ - DYNAMIC_SODIUM=yes SODIUM=${SODIUM_DIR_SLASH} \ - STATIC_STDCPLUS=yes COVERAGE=${{ matrix.coverage }} - else - mingw32-make -f Make_ming.mak -j2 \ - FEATURES=${{ matrix.features }} \ - GUI=$GUI IME=yes ICONV=yes VIMDLL=${{ matrix.VIMDLL }} \ - STATIC_STDCPLUS=yes - fi - - - name: Check version - shell: cmd - run: | - PATH %LUA_DIR%;C:\msys64\%MSYSTEM%\bin;%PYTHON3_DIR%;%PATH% - if "${{ matrix.GUI }}"=="yes" ( - start /wait src\gvim -u NONE -i NONE -c "redir > version.txt | ver | q" || exit 1 - type version.txt - echo. - start /wait src\gvim -u NONE -i NONE -c "redir! > version.txt | so ci\if_ver-1.vim | q" - start /wait src\gvim -u NONE -i NONE -c "redir >> version.txt | so ci\if_ver-2.vim | q" - type version.txt - del version.txt - ) else ( - src\vim --version || exit 1 - src\vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-1.vim -c quit - src\vim -u NONE -i NONE --not-a-term -esNX -V1 -S ci/if_ver-2.vim -c quit - if "${{ matrix.features }}"=="HUGE" ( - src\vim -u NONE -i NONE --not-a-term -esNX -V1 ^ - -c "let g:required=['gettext', 'sodium', 'sound', 'python3', 'lua']" ^ - -S ci/if_feat_check.vim -c quit - ) - ) - - #- name: Prepare Artifact - # shell: cmd - # run: | - # mkdir artifacts - # copy src\*vim.exe artifacts - # copy src\vim*.dll artifacts - # - #- name: Upload Artifact - # uses: actions/upload-artifact@v7 - # with: - # name: vim${{ matrix.bits }}-${{ matrix.toolchain }} - # path: ./artifacts - - # disabled because of https://github.com/tunisiano187/Chocolatey-packages/issues/3916 - #- 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 - timeout-minutes: 15 - run: | - PATH %LUA_DIR%;C:\msys64\%MSYSTEM%\bin;%PYTHON3_DIR%;%PATH%;%SODIUM_LIB% - call "%VCVARSALL%" %VCARCH% - - echo %COL_GREEN%Test gVim:%COL_RESET% - cd src\testdir - if "${{ matrix.GUI }}"=="yes" ( - nmake -nologo -f Make_mvc.mak VIMPROG=..\gvim || exit 1 - ) else ( - @rem Run only tiny tests. - nmake -nologo -f Make_mvc.mak tiny VIMPROG=..\gvim || exit 1 - ) - - - name: Test and show the result of testing Vim - if: matrix.GUI == 'no' || matrix.VIMDLL == 'yes' - shell: cmd - timeout-minutes: 15 - run: | - PATH %LUA_DIR%;C:\msys64\%MSYSTEM%\bin;%PYTHON3_DIR%;%PATH%;%SODIUM_LIB% - call "%VCVARSALL%" %VCARCH% - - echo %COL_GREEN%Test Vim:%COL_RESET% - cd src\testdir - nmake -nologo -f Make_mvc.mak clean - if "${{ matrix.GUI }}"=="no" ( - nmake -nologo -f Make_mvc.mak VIMPROG=..\vim || exit 1 - ) else ( - @rem Run only tiny tests. - nmake -nologo -f Make_mvc.mak tiny VIMPROG=..\vim || exit 1 - ) - - - name: Upload failed test artifacts - if: ${{ !cancelled() }} - uses: ./.github/actions/test_artifacts - - - name: Generate gcov files - if: matrix.coverage - shell: msys2 {0} - run: | - cd src - find . -type f -name '*.gcno' -exec gcov -pb {} + || true - - - name: Codecov - timeout-minutes: 20 - if: matrix.coverage - uses: codecov/codecov-action@v6 - with: - directory: src - flags: windows,${{ matrix.toolchain }}-${{ matrix.arch }}-${{ matrix.features }} - token: ${{ secrets.CODECOV_TOKEN }} + name: Windows + uses: ./.github/workflows/ci-windows.yml + secrets: inherit diff --git a/Filelist b/Filelist index 4ea946c841..53113b456a 100644 --- a/Filelist +++ b/Filelist @@ -9,6 +9,10 @@ SRC_ALL = \ .github/MAINTAINERS \ .github/ISSUE_TEMPLATE/bug_report.yml \ .github/ISSUE_TEMPLATE/feature_request.md \ + .github/workflows/ci-linux.yml \ + .github/workflows/ci-linux_asan.yml \ + .github/workflows/ci-macos.yml \ + .github/workflows/ci-windows.yml \ .github/workflows/ci.yml \ .github/workflows/codeql-analysis.yml \ .github/workflows/coverity.yml \ From e323740b568e14f87fb29f123556cec69d30f80b Mon Sep 17 00:00:00 2001 From: Muraoka Taro Date: Mon, 25 May 2026 15:26:08 +0000 Subject: [PATCH 314/545] patch 9.2.0532: GTK: preedit font size is wrong for fractional point sizes Problem: GTK: preedit font size is wrong for fractional point sizes Solution: Compute the font size as a double and round to the nearest integer (Muraoka Taro). closes: #20316 Co-authored-by: Christian Brabandt Signed-off-by: Muraoka Taro Signed-off-by: Christian Brabandt --- src/gui_xim.c | 8 +++++--- src/version.c | 2 ++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/gui_xim.c b/src/gui_xim.c index a04667e2dc..0700ce41a5 100644 --- a/src/gui_xim.c +++ b/src/gui_xim.c @@ -474,8 +474,10 @@ im_preedit_window_open(void) gchar *css = NULL; const char * const fontname = pango_font_description_get_family(gui.norm_font); - gint fontsize - = pango_font_description_get_size(gui.norm_font) / PANGO_SCALE; + // Since font sizes can be specified as non-integer values like 10.5, + // they must be handled as floating-point (gdouble). + gdouble fontsize + = (gdouble)pango_font_description_get_size(gui.norm_font) / PANGO_SCALE; gchar *fontsize_propval = NULL; if (!pango_font_description_get_size_is_absolute(gui.norm_font)) @@ -488,7 +490,7 @@ im_preedit_window_open(void) fontsize = dpi * fontsize / 72; } if (fontsize > 0) - fontsize_propval = g_strdup_printf("%dpx", fontsize); + fontsize_propval = g_strdup_printf("%dpx", (gint)(fontsize + 0.5)); else fontsize_propval = g_strdup_printf("inherit"); diff --git a/src/version.c b/src/version.c index 4db22e069b..69f0ef22b8 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 532, /**/ 531, /**/ From bc7805323f54405e2e746efde3901b0b6aefe6ff Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Mon, 25 May 2026 15:36:30 +0000 Subject: [PATCH 315/545] patch 9.2.0533: '[ mark moved to end of inserted text after CTRL-R CTRL-P paste Problem: After CTRL-R CTRL-P (or CTRL-R CTRL-O) pastes a register into Insert mode, a follow-up edit such as backspace makes stop_arrow() rewrite Insstart with the post-paste cursor position. As a result the '[ mark points at the end of the inserted text instead of its start (agguser, after 9.2.0384) Solution: In stop_arrow(), only pull Insstart back when the cursor moved above the previous Insstart, so a line-start backspace can still save the joined range (#20031) without disturbing the start position for inserts that advance the cursor (Hirohito Higashi). related: #20031 fixes: #20130 closes: #20322 Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- src/edit.c | 15 ++++++++++----- src/testdir/test_edit.vim | 14 ++++++++++++++ src/version.c | 2 ++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/edit.c b/src/edit.c index 512c0b23c4..d59db1afd3 100644 --- a/src/edit.c +++ b/src/edit.c @@ -2512,11 +2512,16 @@ stop_arrow(void) { if (u_save_cursor() == OK) { - // A command or event may have moved the cursor or edited the - // buffer. Update Insstart so that later edits can properly decide - // whether an extra undo entry is needed. - Insstart = curwin->w_cursor; - Insstart_textlen = (colnr_T)linetabsize_str(ml_get_curline()); + // A command or event may have moved the cursor before the next + // edit. Pull Insstart back only when the cursor moved above it, + // so that later edits can properly decide whether an extra undo + // entry is needed. Advancing Insstart would mis-place '[ after a + // register paste. + if (LT_POS(curwin->w_cursor, Insstart)) + { + Insstart = curwin->w_cursor; + Insstart_textlen = (colnr_T)linetabsize_str(ml_get_curline()); + } ins_need_undo = FALSE; } } diff --git a/src/testdir/test_edit.vim b/src/testdir/test_edit.vim index 6511e178cf..af8359e0d4 100644 --- a/src/testdir/test_edit.vim +++ b/src/testdir/test_edit.vim @@ -2482,4 +2482,18 @@ func Test_autoindent_no_strip_after_cursorholdi() bwipe! endfunc +" Issue #20130: '[ must mark the start of the paste after CTRL-R CTRL-P + edit. +func Test_open_square_mark_after_ctrl_r_ctrl_p_paste() + new + call setline(1, ['a', 'b', 'c', 'd']) + call cursor(4, 1) + + call feedkeys("Vggyjo\\\"\\", 'xt') + + call assert_equal(['a', 'b', 'a', 'b', 'c', 'd', 'c', 'd'], + \ getline(1, '$')) + call assert_equal([0, 3, 1, 0], getpos("'[")) + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 69f0ef22b8..2056d71108 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 533, /**/ 532, /**/ From 14c6730a3984e49c37301b747f775f0a3f33f7cb Mon Sep 17 00:00:00 2001 From: Muraoka Taro Date: Mon, 25 May 2026 15:48:43 +0000 Subject: [PATCH 316/545] patch 9.2.0534: GTK UI does not support fullscreen mode Problem: GTK UI does not support fullscreen mode Solution: Add support for fullscreen (guioptions+=s) mode (Muraoka Taro). closes: #20303 Co-authored-by: Yasuhiro Matsumoto Signed-off-by: Muraoka Taro Signed-off-by: Christian Brabandt --- runtime/doc/gui.txt | 28 +++++++++++++++++++++++++++- runtime/doc/gui_w32.txt | 23 +---------------------- runtime/doc/options.txt | 10 +++++----- runtime/doc/tags | 2 +- runtime/doc/version9.txt | 3 ++- src/gui.c | 8 +++++++- src/gui_gtk.c | 9 +++++++++ src/gui_gtk4.c | 23 +++++++++++++++++++++++ src/gui_gtk_x11.c | 15 +++++++++++++++ src/proto/gui_gtk.pro | 1 + src/proto/gui_gtk4.pro | 1 + src/version.c | 2 ++ 12 files changed, 94 insertions(+), 31 deletions(-) diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 9965e3dd40..3a9feac715 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -1,4 +1,4 @@ -*gui.txt* For Vim version 9.2. Last change: 2026 Apr 07 +*gui.txt* For Vim version 9.2. Last change: 2026 May 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1254,6 +1254,32 @@ A recommended Japanese font is MS Mincho. You can find info here: https://learn.microsoft.com/en-us/typography/font-list/ms-mincho It should be distributed with Windows. + +Full Screen *gui-fullscreen* + +Fullscreen mode is currently only supported in the Windows and GTK GUI +versions of Vim. + +To enable fullscreen mode in the GUI version of Vim, add the 's' flag +to the 'guioptions' setting. + +For convenience, you can define a command or mapping to toggle fullscreen +mode: +> + command ToggleFullscreen { + if &guioptions =~# 's' + set guioptions-=s + else + set guioptions+=s + endif + } + + map &go =~# 's' ? ":se go-=s" : ":se go+=s" + +The fullscreen mode will occupy the entire screen area while hiding window +decorations such as the title bar and borders. + + ============================================================================== 8. Shell Commands *gui-shell* diff --git a/runtime/doc/gui_w32.txt b/runtime/doc/gui_w32.txt index 2e2e2601d0..26c516d895 100644 --- a/runtime/doc/gui_w32.txt +++ b/runtime/doc/gui_w32.txt @@ -1,4 +1,4 @@ -*gui_w32.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*gui_w32.txt* For Vim version 9.2. Last change: 2026 May 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -505,25 +505,4 @@ To use the system's default title bar colors, set highlighting groups to hi TitleBarNC guibg=NONE guifg=NONE < -Full Screen *gui-w32-fullscreen* - -To enable fullscreen mode in the Windows GUI version of Vim, add the 's' flag -to the 'guioptions' setting. - -For convenience, you can define a command or mapping to toggle fullscreen -mode: -> - command ToggleFullscreen { - if &guioptions =~# 's' - set guioptions-=s - else - set guioptions+=s - endif - } - - map &go =~# 's' ? ":se go-=s" : ":se go+=s" - -The fullscreen mode will occupy the entire screen area while hiding window -decorations such as the title bar and borders. - vim:tw=78:sw=4:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index bfab0a87b4..6dc518cc75 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1,4 +1,4 @@ -*options.txt* For Vim version 9.2. Last change: 2026 May 22 +*options.txt* For Vim version 9.2. Last change: 2026 May 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -4613,10 +4613,10 @@ A jump table for the options with a short description can be found at |Q_op|. Photon GUIs. *'go-s'* 's' Enable fullscreen mode. Currently only supported in the - MS-Windows GUI version. When set, the window will occupy the - entire screen and remove window decorations. Define custom - mappings to toggle this mode conveniently. For detailed usage - instructions, see |gui-w32-fullscreen|. + MS-Windows and GTK GUI version. When set, the window will + occupy the entire screen and remove window decorations. + Define custom mappings to toggle this mode conveniently. + For detailed usage instructions, see |gui-fullscreen|. *'go-r'* 'r' Right-hand scrollbar is always present. *'go-R'* diff --git a/runtime/doc/tags b/runtime/doc/tags index 7cd0074a6a..878f0f520a 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -8257,6 +8257,7 @@ gui-font gui.txt /*gui-font* gui-fontwide gui.txt /*gui-fontwide* gui-footer debugger.txt /*gui-footer* gui-fork gui_x11.txt /*gui-fork* +gui-fullscreen gui.txt /*gui-fullscreen* gui-functions usr_41.txt /*gui-functions* gui-gnome gui_x11.txt /*gui-gnome* gui-gnome-session gui_x11.txt /*gui-gnome-session* @@ -8286,7 +8287,6 @@ gui-vert-scroll gui.txt /*gui-vert-scroll* gui-w32 gui_w32.txt /*gui-w32* gui-w32-cmdargs gui_w32.txt /*gui-w32-cmdargs* gui-w32-dialogs gui_w32.txt /*gui-w32-dialogs* -gui-w32-fullscreen gui_w32.txt /*gui-w32-fullscreen* gui-w32-printing gui_w32.txt /*gui-w32-printing* gui-w32-start gui_w32.txt /*gui-w32-start* gui-w32-title-bar gui_w32.txt /*gui-w32-title-bar* diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 4f7b9b62da..2bea93a323 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 May 22 +*version9.txt* For Vim version 9.2. Last change: 2026 May 25 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52589,6 +52589,7 @@ GUI ~ ----- - Added support for the GTK 4 GUI (via patch 9.2.0501). Enable with `--enable-gui=gtk4` configure switch at build time. +- Add support for fullscreen mode in the GTK UI |'go-s'|. Popups ~ ------ diff --git a/src/gui.c b/src/gui.c index b04f132163..8cf4c8fa48 100644 --- a/src/gui.c +++ b/src/gui.c @@ -158,7 +158,7 @@ gui_start(char_u *arg UNUSED) choose_clipmethod(); #endif -#ifdef FEAT_GUI_MSWIN +#if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) // Enable fullscreen mode if (vim_strchr(p_go, GO_FULLSCREEN) != NULL) gui_mch_set_fullscreen(TRUE); @@ -3512,7 +3512,9 @@ gui_init_which_components(char_u *oldval UNUSED) #ifdef FEAT_GUI_MSWIN static int prev_titlebar = FALSE; int using_titlebar = FALSE; +#endif +#if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) static int prev_fullscreen = FALSE; int using_fullscreen = FALSE; #endif @@ -3589,6 +3591,8 @@ gui_init_which_components(char_u *oldval UNUSED) case GO_TITLEBAR: using_titlebar = TRUE; break; +#endif +#if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) case GO_FULLSCREEN: using_fullscreen = TRUE; break; @@ -3620,7 +3624,9 @@ gui_init_which_components(char_u *oldval UNUSED) gui_mch_set_titlebar_colors(); prev_titlebar = using_titlebar; } +#endif +#if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_GTK) if (using_fullscreen != prev_fullscreen) { gui_mch_set_fullscreen(using_fullscreen); diff --git a/src/gui_gtk.c b/src/gui_gtk.c index 77f96179e1..84eae6d41b 100644 --- a/src/gui_gtk.c +++ b/src/gui_gtk.c @@ -2611,3 +2611,12 @@ recent_func_log_func(const gchar *log_domain UNUSED, // http://bugzilla.gnome.org/show_bug.cgi?id=664587 } #endif + + void +gui_mch_set_fullscreen(int flag) +{ + if (flag) + gtk_window_fullscreen(GTK_WINDOW(gui.mainwin)); + else + gtk_window_unfullscreen(GTK_WINDOW(gui.mainwin)); +} diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index c12256790e..9cbffb3c9e 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -280,6 +280,7 @@ static gboolean drop_cb(GtkDropTarget *target, const GValue *value, double x, do #endif static void mainwin_destroy_cb(GObject *object, gpointer data); static gboolean delete_event_cb(GtkWindow *window, gpointer data); +static void mainwin_fullscreened_cb(GObject *obj, GParamSpec *pspec, gpointer user_data); static void drawarea_realize_cb(GtkWidget *widget, gpointer data); static void drawarea_unrealize_cb(GtkWidget *widget, gpointer data); static void drawarea_resize_cb(GtkDrawingArea *area, int width, int height, gpointer data); @@ -449,6 +450,8 @@ gui_mch_init(void) g_signal_connect(G_OBJECT(gui.mainwin), "close-request", G_CALLBACK(delete_event_cb), NULL); + g_signal_connect(G_OBJECT(gui.mainwin), "notify::fullscreened", + G_CALLBACK(mainwin_fullscreened_cb), NULL); // A vertical box holds the menubar, toolbar and main text window. vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); @@ -747,6 +750,26 @@ gui_mch_unmaximize(void) gtk_window_unmaximize(GTK_WINDOW(gui.mainwin)); } + void +gui_mch_set_fullscreen(int flag) +{ + if (gui.mainwin == NULL) + return; + if (flag) + gtk_window_fullscreen(GTK_WINDOW(gui.mainwin)); + else + gtk_window_unfullscreen(GTK_WINDOW(gui.mainwin)); +} + + static void +mainwin_fullscreened_cb(GObject *obj, + GParamSpec *pspec UNUSED, gpointer user_data UNUSED) +{ + // Force a redraw of the drawing area when entering fullscreen mode. + if (gtk_window_is_fullscreen(GTK_WINDOW(obj))) + gui_focus_change(TRUE); +} + /* * Called when the font changed while the window is maximized or GO_KEEPWINSIZE * is set. Recalculate Rows and Columns based on the current window size. diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index b47941acba..44b95345fe 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -2850,6 +2850,18 @@ mainwin_screen_changed_cb(GtkWidget *widget, } } + static gboolean +mainwin_state_event_cb(GtkWidget *widget UNUSED, + GdkEventWindowState *event, + gpointer user_data UNUSED) +{ + if (event->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) + // To redraw the text area when switching to fullscreen mode, we will + // reuse the `gui_focus_change` method. + gui_focus_change(TRUE); + return FALSE; +} + /* * After the drawing area comes up, we calculate all colors and create the * dummy blank cursor. @@ -3826,6 +3838,9 @@ gui_mch_init(void) g_signal_connect(G_OBJECT(gui.mainwin), "screen-changed", G_CALLBACK(&mainwin_screen_changed_cb), NULL); + g_signal_connect(G_OBJECT(gui.mainwin), "window-state-event", + G_CALLBACK(&mainwin_state_event_cb), NULL); + gui.accel_group = gtk_accel_group_new(); gtk_window_add_accel_group(GTK_WINDOW(gui.mainwin), gui.accel_group); diff --git a/src/proto/gui_gtk.pro b/src/proto/gui_gtk.pro index 7b87ba00bd..9b69c69418 100644 --- a/src/proto/gui_gtk.pro +++ b/src/proto/gui_gtk.pro @@ -21,4 +21,5 @@ void gui_make_popup(char_u *path_name, int mouse_pos); void gui_mch_find_dialog(exarg_T *eap); void gui_mch_replace_dialog(exarg_T *eap); void ex_helpfind(exarg_T *eap); +void gui_mch_set_fullscreen(int flag); /* vim: set ft=c : */ diff --git a/src/proto/gui_gtk4.pro b/src/proto/gui_gtk4.pro index 7359c750c7..10f3ccc202 100644 --- a/src/proto/gui_gtk4.pro +++ b/src/proto/gui_gtk4.pro @@ -16,6 +16,7 @@ int gui_mch_get_winpos(int *x, int *y); void gui_mch_set_winpos(int x, int y); int gui_mch_maximized(void); void gui_mch_unmaximize(void); +void gui_mch_set_fullscreen(int flag); void gui_mch_newfont(void); void gui_mch_settitle(char_u *title, char_u *icon); void gui_mch_set_shellsize(int width, int height, int min_width, int min_height, int base_width, int base_height, int direction); diff --git a/src/version.c b/src/version.c index 2056d71108..14ae41df75 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 534, /**/ 533, /**/ From 3a90b2ba8e63926c4c1c5225eced911c8594dcdf Mon Sep 17 00:00:00 2001 From: Andrey Starodubtsev Date: Mon, 25 May 2026 16:29:23 +0000 Subject: [PATCH 317/545] patch 9.2.0535: tests: matchit plugin is not tested Problem: tests: matchit plugin is not tested Solution: Add test_plugin_matchit, improve b:match_words for the html filetype plugin (Andrey Starodubtsev) `b:match_words` which contains patterns used by `matchit` plugin to find tag's counterpath, is fixed so that matching happens using the whole tag, not just its first letter. Also, it allows to find matching tag in case if there are spaces or attributes after tag name. fixes: chrisbra/matchit#51 closes: #20313 Signed-off-by: Andrey Starodubtsev Signed-off-by: Christian Brabandt --- runtime/ftplugin/html.vim | 3 +- src/testdir/Make_all.mak | 2 + src/testdir/test_plugin_matchit.vim | 58 +++++++++++++++++++++++++++++ src/version.c | 2 + 4 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/testdir/test_plugin_matchit.vim diff --git a/runtime/ftplugin/html.vim b/runtime/ftplugin/html.vim index 26b99d2bc3..172292fef0 100644 --- a/runtime/ftplugin/html.vim +++ b/runtime/ftplugin/html.vim @@ -3,6 +3,7 @@ " Maintainer: Doug Kearns " Previous Maintainer: Dan Sharp " Last Change: 2025 Sep 12 +" 2026 May 25 by Vim plugin: improve the matchit plugin #20313 if exists("b:did_ftplugin") finish @@ -41,7 +42,7 @@ if exists("loaded_matchit") && !exists("b:match_words") \ '<:>,' .. \ '<\@<=[ou]l\>[^>]*\%(>\|$\):<\@<=li\>:<\@<=/[ou]l>,' .. \ '<\@<=dl\>[^>]*\%(>\|$\):<\@<=d[td]\>:<\@<=/dl>,' .. - \ '<\@<=\([^/!][^ \t>]*\)[^>]*\%(>\|$\):<\@<=/\1>' + \ '<\@<=\([^/!][^ \t>]*\)\%([ \t]\|>\|$\):<\@<=/\1>' let b:html_set_match_words = 1 let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words b:html_set_match_words" endif diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index f8c7f8bb46..537c5250f7 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -248,6 +248,7 @@ NEW_TESTS = \ test_plugin_helpcurwin \ test_plugin_helptoc \ test_plugin_man \ + test_plugin_matchit \ test_plugin_matchparen \ test_plugin_netrw \ test_plugin_osc52 \ @@ -526,6 +527,7 @@ NEW_TESTS_RES = \ test_plugin_helpcurwin.res \ test_plugin_helptoc.res \ test_plugin_man.res \ + test_plugin_matchit.res \ test_plugin_matchparen.res \ test_plugin_netrw.res \ test_plugin_osc52.res \ diff --git a/src/testdir/test_plugin_matchit.vim b/src/testdir/test_plugin_matchit.vim new file mode 100644 index 0000000000..1c8144278d --- /dev/null +++ b/src/testdir/test_plugin_matchit.vim @@ -0,0 +1,58 @@ +" Tests for the matchit plugin + +func SetUp() + filetype plugin on + packadd matchit + call assert_equal('(MatchitNormalForward)', maparg('%', 'n', 0)) +endfunc + +func TearDown() + filetype plugin off +endfunc + +func s:Setup(lines, ft) + new + call setline(1, a:lines) + exe "setl ft=".. a:ft + call assert_true(exists('b:match_words')) + call assert_true(!empty(b:match_words)) +endfunc + +func s:PercentTo(arg) + call call('cursor', a:arg) + normal % + return line('.') +endfunc + +func Test_html_matchit_simple_tag() + call s:Setup(['', 'some text', ''], 'html') + call assert_equal(3, s:PercentTo([1, 2])) + call assert_equal(1, s:PercentTo([3, 3])) + call assert_equal(2, s:PercentTo([2, 2])) + normal % + call assert_equal(2, line('.')) + bwipe! +endfunc + +func Test_html_matchit_tag_with_attribute() + call s:Setup(['', 'some text', ''], 'html') + call assert_equal(3, s:PercentTo([1, 2])) + call assert_equal(1, s:PercentTo([3, 3])) + call assert_equal(2, s:PercentTo([2, 2])) + normal % + call assert_equal(2, line('.')) + bwipe! +endfunc + +func Test_html_matchit_tag_multiline_attributes() + call s:Setup(['', + \ 'some text', ''], 'html') + call assert_equal(6, s:PercentTo([1, 2])) + call assert_equal(1, s:PercentTo([6, 3])) + call assert_equal(5, s:PercentTo([5, 2])) + normal % + call assert_equal(5, line('.')) + bwipe! +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 14ae41df75..774aee0e7a 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 535, /**/ 534, /**/ From dfdeba16d7930e4a1fb8cf0003656b13da8ffffc Mon Sep 17 00:00:00 2001 From: Doug Kearns Date: Mon, 25 May 2026 16:33:27 +0000 Subject: [PATCH 318/545] runtime(vim): Update base syntax, fix mismtatched :def return type Anchor the return type separator ':' with a lookbehind as the relevant nextgroup options use skipwhite. closes: #20319 Signed-off-by: Doug Kearns Signed-off-by: Christian Brabandt --- runtime/syntax/generator/vim.vim.base | 10 +++++----- runtime/syntax/testdir/dumps/vim_ex_def_01.dump | 2 +- runtime/syntax/testdir/dumps/vim_ex_def_02.dump | 2 +- runtime/syntax/testdir/dumps/vim_ex_def_03.dump | 2 +- runtime/syntax/testdir/dumps/vim_ex_def_04.dump | 2 +- runtime/syntax/testdir/dumps/vim_ex_def_05.dump | 2 +- runtime/syntax/testdir/dumps/vim_ex_def_06.dump | 2 +- runtime/syntax/testdir/dumps/vim_ex_def_07.dump | 16 ++++++++-------- runtime/syntax/testdir/input/vim_ex_def.vim | 7 +++++++ runtime/syntax/vim.vim | 10 +++++----- 10 files changed, 31 insertions(+), 24 deletions(-) diff --git a/runtime/syntax/generator/vim.vim.base b/runtime/syntax/generator/vim.vim.base index 24a51167ac..48414714fc 100644 --- a/runtime/syntax/generator/vim.vim.base +++ b/runtime/syntax/generator/vim.vim.base @@ -2,7 +2,7 @@ " Language: Vim script " Maintainer: Hirohito Higashi " Doug Kearns -" Last Change: 2026 Feb 20 +" Last Change: 2026 May 24 " Former Maintainer: Charles E. Campbell " DO NOT CHANGE DIRECTLY. @@ -520,7 +520,7 @@ syn match vim9LambdaParams contained \ "(\%(\" \ skipwhite nextgroup=vim9LambdaOperator \ contains=@vim9Continue,vimDefParam,vim9LambdaParen,vim9LambdaReturnType -syn region vim9LambdaReturnType contained start=")\@<=:\s" end="\ze\s*#" end="\ze\s*=>" contains=@vim9Continue,@vimType transparent +syn region vim9LambdaReturnType contained start=")\@1<=:\s" end="\ze\s*#" end="\ze\s*=>" contains=@vim9Continue,@vimType transparent syn region vim9LambdaBlock contained matchgroup=vimSep start="{" end="^\s*\zs}" contains=@vimDefBodyList syn match vim9LambdaOperatorComment contained "#.*" skipwhite skipempty nextgroup=@vimExprList,vim9LambdaBlock,vim9LambdaOperatorComment @@ -630,7 +630,7 @@ syn match vimDelfunction "\" skipwhite nextgroup=vimDelfunct " ===== syn region vimReturnType contained - \ start=":\%(\s\|\n\)\@=" + \ start=")\@1<=:\%(\s\|\n\)\@=" \ skip=+\n\s*\%(\\\|#\\ \)\|^\s*#\\ + \ end="$" \ matchgroup=vim9Comment @@ -697,7 +697,7 @@ if s:vim9script \ contains=vim9DefTypeParam syn region vim9MethodDefReturnType contained - \ start=":\%(\s\|\n\)\@=" + \ start=")\@1<=:\%(\s\|\n\)\@=" \ skip=+\n\s*\%(\\\|#\\ \)\|^\s*#\\ + \ end="$" \ matchgroup=vim9Comment @@ -825,7 +825,7 @@ if s:vim9script \ skipwhite skipnl nextgroup=vimDefComment,vim9AbstractDefReturnType,vimCommentError \ contains=vimDefParam,vim9Comment,vimFunctionParamEquals syn region vim9AbstractDefReturnType contained - \ start=":\s" end="$" matchgroup=vim9Comment end="\ze[#"]" + \ start=")\@1<=:\s" end="$" matchgroup=vim9Comment end="\ze[#"]" \ skipwhite skipnl nextgroup=vimDefComment,vimCommentError \ contains=vimTypeSep \ transparent diff --git a/runtime/syntax/testdir/dumps/vim_ex_def_01.dump b/runtime/syntax/testdir/dumps/vim_ex_def_01.dump index f13f4f4e3c..92211445a8 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_def_01.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_def_01.dump @@ -17,4 +17,4 @@ @75 |d+0#af5f00255&|e|f| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&|#+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@55 |e+0#af5f00255&|n|d@1|e|f| +0#0000000&@68 -@57|1|9|,|1| @9|1|2|%| +@57|1|9|,|1| @9|1@1|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_def_02.dump b/runtime/syntax/testdir/dumps/vim_ex_def_02.dump index 14cffbda09..0f575f8436 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_def_02.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_def_02.dump @@ -17,4 +17,4 @@ |e+0#af5f00255&|n|d@1|e|f| +0#0000000&@68 @75 |d+0#af5f00255&|e|f| +0#0000000&|<+0#e000e06&|S|I|D|>|F+0#0000000&|o@1|(+0#e000e06&|)|:+0#0000000&| |n+0#00e0003&|u|m|b|e|r| +0#0000000&@52 -@57|3|7|,|0|-|1| @7|2|8|%| +@57|3|7|,|0|-|1| @7|2|7|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_def_03.dump b/runtime/syntax/testdir/dumps/vim_ex_def_03.dump index 4ab6ef5e9b..3c325f4809 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_def_03.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_def_03.dump @@ -17,4 +17,4 @@ |e+0#af5f00255&|n|d@1|e|f| +0#0000000&@68 @75 |d+0#af5f00255&|e|f| +0#0000000&|F|o@1|(+0#e000e06&|)|:+0#0000000&| |v+0#00e0003&|o|i|d| +0#0000000&|#+0#0000e05&| |c|o|m@1|e|n|t| +0#0000000&@49 -@57|5@1|,|3| @9|4|5|%| +@57|5@1|,|3| @9|4|2|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_def_04.dump b/runtime/syntax/testdir/dumps/vim_ex_def_04.dump index 6b5e93d8dc..cccc2bad10 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_def_04.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_def_04.dump @@ -17,4 +17,4 @@ @75 |d+0#af5f00255&|e|f| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&@65 |e+0#af5f00255&|n|d@1|e|f| +0#0000000&||| |e+0#af5f00255&|c|h|o| +0#0000000&|"+0#e000002&|F|o@1|"| +0#0000000&@55 -@57|7|3|,|0|-|1| @7|6|2|%| +@57|7|3|,|0|-|1| @7|5|8|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_def_05.dump b/runtime/syntax/testdir/dumps/vim_ex_def_05.dump index 6519226746..56950e93fa 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_def_05.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_def_05.dump @@ -17,4 +17,4 @@ @75 |"+0#0000e05&| |I|s@1|u|e| |#|1|6|2|4|3| |(|V|i|m| |s|c|r|i|p|t| |d|e|f| |p|a|r|a|m|e|t|e|r|s| |s|y|n|t|a|x| |h|i|g|h|l|i|g|h|t| |i|s| |w|r|o|n|g|)| +0#0000000&@6 @75 -@57|9|0|,|0|-|1| @7|7|8|%| +@57|9|0|,|0|-|1| @7|7|3|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_def_06.dump b/runtime/syntax/testdir/dumps/vim_ex_def_06.dump index 3c4b3ab06c..317bb43f31 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_def_06.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_def_06.dump @@ -17,4 +17,4 @@ |e+0#af5f00255&|n|d@1|e|f| +0#0000000&@68 @75 @75 -@57|1|0|8|,|1| @8|9|5|%| +@57|1|0|8|,|1| @8|8|9|%| diff --git a/runtime/syntax/testdir/dumps/vim_ex_def_07.dump b/runtime/syntax/testdir/dumps/vim_ex_def_07.dump index 87f3964bd0..381b7ae553 100644 --- a/runtime/syntax/testdir/dumps/vim_ex_def_07.dump +++ b/runtime/syntax/testdir/dumps/vim_ex_def_07.dump @@ -4,17 +4,17 @@ |s+0#af5f00255&|i|l|e|n|t|!| +0#0000000&|d+0#af5f00255&|e|f| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&@57 |e+0#af5f00255&|n|d@1|e|f| +0#0000000&@68 > @74 +@75 +|"+0#0000e05&| |U|n|r|e|p|o|r|t|e|d| |i|s@1|u|e| |(|r|e|t|u|r|n| |t|y|p|e| |c|o|l|o|n| |m|u|s|t| |f|o|l@1|o|w| |i|m@1|e|d|i|a|t|e|l|y| |a|f|t|e|r| |c|l|o|s|i|n|g +| |p|a|r|a|m|e|t|e|r|-|l|i|s|t| |p|a|r|e|n|)| +0#0000000&@52 +@75 +|d+0#af5f00255&|e|f| +0#0000000&|F|o@1|(+0#e000e06&|)| +0#0000000&@65 +|:| |e+0#af5f00255&|c|h|o| +0#0000000&|"+0#e000002&|n|o|t| |a| |m|i|s|m|a|t|c|h|e|d| |r|e|t|u|r|n| |t|y|p|e|"| +0#0000000&@37 +|e+0#af5f00255&|n|d@1|e|f| +0#0000000&@68 +@75 |~+0#4040ff13&| @73 |~| @73 |~| @73 |~| @73 |~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 -|~| @73 | +0#0000000&@56|1|2|6|,|0|-|1| @6|B|o|t| diff --git a/runtime/syntax/testdir/input/vim_ex_def.vim b/runtime/syntax/testdir/input/vim_ex_def.vim index 7b1d39f4d1..a48273f875 100644 --- a/runtime/syntax/testdir/input/vim_ex_def.vim +++ b/runtime/syntax/testdir/input/vim_ex_def.vim @@ -124,3 +124,10 @@ enddef silent! def Foo() enddef + +" Unreported issue (return type colon must follow immediately after closing parameter-list paren) + +def Foo() +: echo "not a mismatched return type" +enddef + diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 4c2c137a04..e4538dd8dc 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -2,7 +2,7 @@ " Language: Vim script " Maintainer: Hirohito Higashi " Doug Kearns -" Last Change: 2026 May 17 +" Last Change: 2026 May 24 " Former Maintainer: Charles E. Campbell " DO NOT CHANGE DIRECTLY. @@ -575,7 +575,7 @@ syn match vim9LambdaParams contained \ "(\%(\" \ skipwhite nextgroup=vim9LambdaOperator \ contains=@vim9Continue,vimDefParam,vim9LambdaParen,vim9LambdaReturnType -syn region vim9LambdaReturnType contained start=")\@<=:\s" end="\ze\s*#" end="\ze\s*=>" contains=@vim9Continue,@vimType transparent +syn region vim9LambdaReturnType contained start=")\@1<=:\s" end="\ze\s*#" end="\ze\s*=>" contains=@vim9Continue,@vimType transparent syn region vim9LambdaBlock contained matchgroup=vimSep start="{" end="^\s*\zs}" contains=@vimDefBodyList syn match vim9LambdaOperatorComment contained "#.*" skipwhite skipempty nextgroup=@vimExprList,vim9LambdaBlock,vim9LambdaOperatorComment @@ -685,7 +685,7 @@ syn match vimDelfunction "\" skipwhite nextgroup=vimDelfunct " ===== syn region vimReturnType contained - \ start=":\%(\s\|\n\)\@=" + \ start=")\@1<=:\%(\s\|\n\)\@=" \ skip=+\n\s*\%(\\\|#\\ \)\|^\s*#\\ + \ end="$" \ matchgroup=vim9Comment @@ -752,7 +752,7 @@ if s:vim9script \ contains=vim9DefTypeParam syn region vim9MethodDefReturnType contained - \ start=":\%(\s\|\n\)\@=" + \ start=")\@1<=:\%(\s\|\n\)\@=" \ skip=+\n\s*\%(\\\|#\\ \)\|^\s*#\\ + \ end="$" \ matchgroup=vim9Comment @@ -880,7 +880,7 @@ if s:vim9script \ skipwhite skipnl nextgroup=vimDefComment,vim9AbstractDefReturnType,vimCommentError \ contains=vimDefParam,vim9Comment,vimFunctionParamEquals syn region vim9AbstractDefReturnType contained - \ start=":\s" end="$" matchgroup=vim9Comment end="\ze[#"]" + \ start=")\@1<=:\s" end="$" matchgroup=vim9Comment end="\ze[#"]" \ skipwhite skipnl nextgroup=vimDefComment,vimCommentError \ contains=vimTypeSep \ transparent From 7e55dae2fe1d27c4130da03add4e8970dcd57e74 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 25 May 2026 16:59:58 +0000 Subject: [PATCH 319/545] patch 9.2.0536: tests: Test_invalid_args() fails on GTK4 without xterm_clipboard Problem: tests: Test_invalid_args() fails on GTK4 builds when xterm_clipboard is not enabled Solution: Add has('xterm_clipboard') check to the test, while at it, also document the --display argument. closes: #20318 Signed-off-by: Christian Brabandt --- runtime/doc/starting.txt | 8 +- runtime/doc/tags | 1 + runtime/doc/vim.1 | 13 +- runtime/doc/vim.man | 224 ++++++++++++++++++----------------- src/testdir/test_startup.vim | 2 +- src/version.c | 2 + 6 files changed, 138 insertions(+), 112 deletions(-) diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 816849fb9c..db37cae188 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1,4 +1,4 @@ -*starting.txt* For Vim version 9.2. Last change: 2026 Apr 29 +*starting.txt* For Vim version 9.2. Last change: 2026 May 24 VIM REFERENCE MANUAL by Bram Moolenaar @@ -642,6 +642,12 @@ a slash. Thus "-R" means recovery and "-/R" readonly. of the output is: > WID: 12345\n +--display {display} *--display* + Run Vim on {display}, connecting to that X server instead of + the one in $DISPLAY. The "--display" long form is accepted + only by the GTK+ 2 or GTK+ 3 GUI, see also |-display|. + {requires the |+xterm_clipboard| feature} + --role {role} *--role* GTK+ 2 GUI only. Set the role of the main window to {role}. The window role can be used by a window manager to uniquely diff --git a/runtime/doc/tags b/runtime/doc/tags index 878f0f520a..2291864943 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -1591,6 +1591,7 @@ $quote eval.txt /*$quote* --clean starting.txt /*--clean* --clientserver remote.txt /*--clientserver* --cmd starting.txt /*--cmd* +--display starting.txt /*--display* --echo-wid starting.txt /*--echo-wid* --gui-dialog-file starting.txt /*--gui-dialog-file* --help starting.txt /*--help* diff --git a/runtime/doc/vim.1 b/runtime/doc/vim.1 index cb1d3fc762..fbc4252501 100644 --- a/runtime/doc/vim.1 +++ b/runtime/doc/vim.1 @@ -1,4 +1,4 @@ -.TH VIM 1 "2025 Jun 27" +.TH VIM 1 "2026 May 22" .SH NAME vim \- Vi IMproved, a programmer's text editor .SH SYNOPSIS @@ -194,6 +194,17 @@ Start .B Vim in Ex mode, just like the executable was called "ex". .TP +\-display {display} +Run +.B Vim +on +.IR {display} , +connecting to that X server instead of the one in $DISPLAY. +The "\-\-display" long form is accepted only by the GTK+ 2 or GTK+ 3 GUI. +Both forms require the +.B +xterm_clipboard +feature. +.TP \-E Start .B Vim diff --git a/runtime/doc/vim.man b/runtime/doc/vim.man index ec4058f913..c3bc78c2f7 100644 --- a/runtime/doc/vim.man +++ b/runtime/doc/vim.man @@ -137,59 +137,65 @@ OPTIONS -e Start Vim in Ex mode, just like the executable was called "ex". + -display {display} + Run Vim on {display}, connecting to that X server instead + of the one in $DISPLAY. The "--display" long form is ac‐ + cepted only by the GTK+ 2 or GTK+ 3 GUI. Both forms re‐ + quire the +xterm_clipboard feature. + -E Start Vim in improved Ex mode, just like the executable was called "exim". -f Foreground. For the GUI version, Vim will not fork and de‐ - tach from the shell it was started in. On the Amiga, Vim - is not restarted to open a new window. This option should - be used when Vim is executed by a program that will wait - for the edit session to finish (e.g. mail). On the Amiga + tach from the shell it was started in. On the Amiga, Vim + is not restarted to open a new window. This option should + be used when Vim is executed by a program that will wait + for the edit session to finish (e.g. mail). On the Amiga the ":sh" and ":!" commands will not work. - -F If Vim has been compiled with FKMAP support for editing - right-to-left oriented files and Farsi keyboard mapping, - this option starts Vim in Farsi mode, i.e. 'fkmap' and - 'rightleft' are set. Otherwise an error message is given + -F If Vim has been compiled with FKMAP support for editing + right-to-left oriented files and Farsi keyboard mapping, + this option starts Vim in Farsi mode, i.e. 'fkmap' and + 'rightleft' are set. Otherwise an error message is given and Vim aborts. Note: Farsi support has been removed in patch 8.1.0932. - -g If Vim has been compiled with GUI support, this option en‐ + -g If Vim has been compiled with GUI support, this option en‐ ables the GUI. If no GUI support was compiled in, an error message is given and Vim aborts. -H If Vim has been compiled with RIGHTLEFT support for editing - right-to-left oriented files and Hebrew keyboard mapping, - this option starts Vim in Hebrew mode, i.e. 'hkmap' and - 'rightleft' are set. Otherwise an error message is given + right-to-left oriented files and Hebrew keyboard mapping, + this option starts Vim in Hebrew mode, i.e. 'hkmap' and + 'rightleft' are set. Otherwise an error message is given and Vim aborts. -i {viminfo} - Specifies the filename to use when reading or writing the - viminfo file, instead of the default "~/.viminfo". This - can also be used to skip the use of the .viminfo file, by + Specifies the filename to use when reading or writing the + viminfo file, instead of the default "~/.viminfo". This + can also be used to skip the use of the .viminfo file, by giving the name "NONE". -l Lisp mode. Sets the 'lisp' and 'showmatch' options on. -L Same as -r. - -m Modifying files is disabled. Resets the 'write' option. - You can still modify the buffer, but writing a file is not + -m Modifying files is disabled. Resets the 'write' option. + You can still modify the buffer, but writing a file is not possible. - -M Modifications not allowed. The 'modifiable' and 'write' - options will be unset, so that changes are not allowed and - files can not be written. Note that these options can be + -M Modifications not allowed. The 'modifiable' and 'write' + options will be unset, so that changes are not allowed and + files can not be written. Note that these options can be set to enable making modifications. - -n No swap file will be used. Recovery after a crash will be - impossible. Handy if you want to edit a file on a very - slow medium (e.g. floppy). Can also be done with ":set + -n No swap file will be used. Recovery after a crash will be + impossible. Handy if you want to edit a file on a very + slow medium (e.g. floppy). Can also be done with ":set uc=0". Can be undone with ":set uc=200". - -N No-compatible mode. Resets the 'compatible' option. This - will make Vim behave a bit better, but less Vi compatible, + -N No-compatible mode. Resets the 'compatible' option. This + will make Vim behave a bit better, but less Vi compatible, even though a .vimrc file does not exist. -nb Become an editor server for NetBeans. See the docs for de‐ @@ -198,144 +204,144 @@ OPTIONS -o[N] Open N windows stacked. When N is omitted, open one window for each file. - -O[N] Open N windows side by side. When N is omitted, open one + -O[N] Open N windows side by side. When N is omitted, open one window for each file. -p[N] Open N tab pages. When N is omitted, open one tab page for each file. -P {parent-title} - Win32 GUI only: Specify the title of the parent applica‐ - tion. When possible, Vim will run in an MDI window inside - the application. {parent-title} must appear in the window + Win32 GUI only: Specify the title of the parent applica‐ + tion. When possible, Vim will run in an MDI window inside + the application. {parent-title} must appear in the window title of the parent application. Make sure that it is spe‐ - cific enough. Note that the implementation is still primi‐ - tive. It won't work with all applications and the menu + cific enough. Note that the implementation is still primi‐ + tive. It won't work with all applications and the menu doesn't work. - -r List swap files, with information about using them for re‐ + -r List swap files, with information about using them for re‐ covery. - -r {file} Recovery mode. The swap file is used to recover a crashed - editing session. The swap file is a file with the same + -r {file} Recovery mode. The swap file is used to recover a crashed + editing session. The swap file is a file with the same filename as the text file with ".swp" appended. See ":help recovery". - -R Read-only mode. The 'readonly' option will be set. You + -R Read-only mode. The 'readonly' option will be set. You can still edit the buffer, but will be prevented from acci‐ dentally overwriting a file. If you do want to overwrite a - file, add an exclamation mark to the Ex command, as in - ":w!". The -R option also implies the -n option (see - above). The 'readonly' option can be reset with ":set + file, add an exclamation mark to the Ex command, as in + ":w!". The -R option also implies the -n option (see + above). The 'readonly' option can be reset with ":set noro". See ":help 'readonly'". - -s Silent mode. Only when started as "Ex" or when the "-e" + -s Silent mode. Only when started as "Ex" or when the "-e" option was given before the "-s" option. -s {scriptin} - The script file {scriptin} is read. The characters in the - file are interpreted as if you had typed them. The same + The script file {scriptin} is read. The characters in the + file are interpreted as if you had typed them. The same can be done with the command ":source! {scriptin}". If the end of the file is reached before the editor exits, further characters are read from the keyboard. - -S {file} {file} will be sourced after the first file has been read. - This is equivalent to -c "source {file}". {file} cannot + -S {file} {file} will be sourced after the first file has been read. + This is equivalent to -c "source {file}". {file} cannot start with '-'. If {file} is omitted "Session.vim" is used (only works when -S is the last argument). -T {terminal} Tells Vim the name of the terminal you are using. Only re‐ - quired when the automatic way doesn't work. Should be a - terminal known to Vim (builtin) or defined in the termcap + quired when the automatic way doesn't work. Should be a + terminal known to Vim (builtin) or defined in the termcap or terminfo file. - -u {vimrc} Use the commands in the file {vimrc} for initializations. - All the other initializations are skipped. Use this to - edit a special kind of files. It can also be used to skip - all initializations by giving the name "NONE". See ":help + -u {vimrc} Use the commands in the file {vimrc} for initializations. + All the other initializations are skipped. Use this to + edit a special kind of files. It can also be used to skip + all initializations by giving the name "NONE". See ":help initialization" within vim for more details. - -U {gvimrc} Use the commands in the file {gvimrc} for GUI initializa‐ - tions. All the other GUI initializations are skipped. It - can also be used to skip all GUI initializations by giving - the name "NONE". See ":help gui-init" within vim for more + -U {gvimrc} Use the commands in the file {gvimrc} for GUI initializa‐ + tions. All the other GUI initializations are skipped. It + can also be used to skip all GUI initializations by giving + the name "NONE". See ":help gui-init" within vim for more details. - -v Start Vim in Vi mode, just like the executable was called - "vi". This only has effect when the executable is called + -v Start Vim in Vi mode, just like the executable was called + "vi". This only has effect when the executable is called "ex". - -V[N] Verbose. Give messages about which files are sourced and - for reading and writing a viminfo file. The optional num‐ + -V[N] Verbose. Give messages about which files are sourced and + for reading and writing a viminfo file. The optional num‐ ber N is the value for 'verbose'. Default is 10. -V[N]{filename} Like -V and set 'verbosefile' to {filename}. The result is - that messages are not displayed but written to the file + that messages are not displayed but written to the file {filename}. {filename} must not start with a digit. -w{number} Set the 'window' option to {number}. -w {scriptout} - All the characters that you type are recorded in the file - {scriptout}, until you exit Vim. This is useful if you - want to create a script file to be used with "vim -s" or + All the characters that you type are recorded in the file + {scriptout}, until you exit Vim. This is useful if you + want to create a script file to be used with "vim -s" or ":source!". If the {scriptout} file exists, characters are appended. -W {scriptout} Like -w, but an existing file is overwritten. - -x If Vim has been compiled with encryption support, use en‐ + -x If Vim has been compiled with encryption support, use en‐ cryption when writing files. Will prompt for a crypt key. - -X Don't connect to the X server. Shortens startup time in a - terminal, but the window title and clipboard will not be + -X Don't connect to the X server. Shortens startup time in a + terminal, but the window title and clipboard will not be used. -Y Don't connect to the wayland compositor -y Start Vim in easy mode, just like the executable was called - "evim" or "eview". Makes Vim behave like a click-and-type + "evim" or "eview". Makes Vim behave like a click-and-type editor. - -Z Restricted mode. Works like the executable starts with + -Z Restricted mode. Works like the executable starts with "r". - -- Denotes the end of the options. Arguments after this will - be handled as a file name. This can be used to edit a + -- Denotes the end of the options. Arguments after this will + be handled as a file name. This can be used to edit a filename that starts with a '-'. - --clean Do not use any personal configuration (vimrc, plugins, - etc.). Useful to see if a problem reproduces with a clean + --clean Do not use any personal configuration (vimrc, plugins, + etc.). Useful to see if a problem reproduces with a clean Vim setup. --cmd {command} - Like using "-c", but the command is executed just before - processing any vimrc file. You can use up to 10 of these + Like using "-c", but the command is executed just before + processing any vimrc file. You can use up to 10 of these commands, independently from "-c" commands. --echo-wid GTK GUI only: Echo the Window ID on stdout. --gui-dialog-file {name} - When using the GUI, instead of showing a dialog, write the - title and message of the dialog to file {name}. The file - is created or appended to. Only useful for testing, to - avoid that the test gets stuck on a dialog that can't be + When using the GUI, instead of showing a dialog, write the + title and message of the dialog to file {name}. The file + is created or appended to. Only useful for testing, to + avoid that the test gets stuck on a dialog that can't be seen. Without the GUI the argument is ignored. --help, -h, -? Give a bit of help about the command line arguments and op‐ tions. After this Vim exits. - --literal Take file name arguments literally, do not expand wild‐ - cards. This has no effect on Unix where the shell expands + --literal Take file name arguments literally, do not expand wild‐ + cards. This has no effect on Unix where the shell expands wildcards. --log {filename} - If Vim has been compiled with eval and channel feature, - start logging and write entries to {filename}. This works + If Vim has been compiled with eval and channel feature, + start logging and write entries to {filename}. This works like calling ch_logfile({filename}, 'ao') very early during startup. @@ -345,8 +351,8 @@ OPTIONS --noplugin Skip loading plugins. Implied by -u NONE. --not-a-term - Tells Vim that the user knows that the input and/or output - is not connected to a terminal. This will avoid the warn‐ + Tells Vim that the user knows that the input and/or output + is not connected to a terminal. This will avoid the warn‐ ing and the two second delay that would happen. --remote Connect to a Vim server and make it edit the files given in @@ -354,18 +360,18 @@ OPTIONS is given and the files are edited in the current Vim. --remote-expr {expr} - Connect to a Vim server, evaluate {expr} in it and print + Connect to a Vim server, evaluate {expr} in it and print the result on stdout. --remote-send {keys} Connect to a Vim server and send {keys} to it. --remote-silent - As --remote, but without the warning when no server is + As --remote, but without the warning when no server is found. --remote-wait - As --remote, but Vim does not exit until the files have + As --remote, but Vim does not exit until the files have been edited. --remote-wait-silent @@ -376,11 +382,11 @@ OPTIONS List the names of all Vim servers that can be found. --servername {name} - Use {name} as the server name. Used for the current Vim, + Use {name} as the server name. Used for the current Vim, unless used with a --remote argument, then it's the name of - the server to connect to. If the socketserver backend is + the server to connect to. If the socketserver backend is being used, if the name starts with "/", "./", or "../", it - is taken as either an absolute, relative or relative path + is taken as either an absolute, relative or relative path to the socket. --clientserver {backend} @@ -391,37 +397,37 @@ OPTIONS dows. --socketid {id} - GTK GUI only: Use the GtkPlug mechanism to run gVim in an‐ + GTK GUI only: Use the GtkPlug mechanism to run gVim in an‐ other window. --startuptime {file} During startup write timing messages to the file {fname}. - --ttyfail When stdin or stdout is not a terminal (tty) then exit + --ttyfail When stdin or stdout is not a terminal (tty) then exit right away. --version Print version information and exit. --windowid {id} - Win32 GUI only: Make gVim try to use the window {id} as a + Win32 GUI only: Make gVim try to use the window {id} as a parent, so that it runs inside that window. ON-LINE HELP - Type ":help" in Vim to get started. Type ":help subject" to get help - on a specific subject. For example: ":help ZZ" to get help for the - "ZZ" command. Use and CTRL-D to complete subjects (":help cmd‐ - line-completion"). Tags are present to jump from one place to another + Type ":help" in Vim to get started. Type ":help subject" to get help + on a specific subject. For example: ":help ZZ" to get help for the + "ZZ" command. Use and CTRL-D to complete subjects (":help cmd‐ + line-completion"). Tags are present to jump from one place to another (sort of hypertext links, see ":help"). All documentation files can be viewed in this way, for example ":help syntax.txt". FILES /usr/local/share/vim/vim??/doc/*.txt - The Vim documentation files. Use ":help doc-file-list" + The Vim documentation files. Use ":help doc-file-list" to get the complete list. vim?? is short version number, like vim91 for Vim 9.1 /usr/local/share/vim/vim??/doc/tags - The tags file used for finding information in the docu‐ + The tags file used for finding information in the docu‐ mentation files. /usr/local/share/vim/vim??/syntax/syntax.vim @@ -434,18 +440,18 @@ FILES System wide Vim initializations. ~/.vimrc, ~/.vim/vimrc, $XDG_CONFIG_HOME/vim/vimrc - Your personal Vim initializations (first one found is + Your personal Vim initializations (first one found is used). /usr/local/share/vim/gvimrc System wide gvim initializations. ~/.gvimrc, ~/.vim/gvimrc, $XDG_CONFIG_HOME/vim/gvimrc - Your personal gVim initializations (first one found is + Your personal gVim initializations (first one found is used). /usr/local/share/vim/vim??/optwin.vim - Script used for the ":options" command, a nice way to + Script used for the ":options" command, a nice way to view and set options. /usr/local/share/vim/vim??/menu.vim @@ -455,11 +461,11 @@ FILES Script to generate a bug report. See ":help bugs". /usr/local/share/vim/vim??/filetype.vim - Script to detect the type of a file by its name. See + Script to detect the type of a file by its name. See ":help 'filetype'". /usr/local/share/vim/vim??/scripts.vim - Script to detect the type of a file by its contents. + Script to detect the type of a file by its contents. See ":help 'filetype'". /usr/local/share/vim/vim??/print/*.ps @@ -474,17 +480,17 @@ SEE ALSO AUTHOR Most of Vim was made by Bram Moolenaar, with a lot of help from others. See ":help credits" in Vim. - Vim is based on Stevie, worked on by: Tim Thompson, Tony Andrews and + Vim is based on Stevie, worked on by: Tim Thompson, Tony Andrews and G.R. (Fred) Walter. Although hardly any of the original code remains. BUGS Probably. See ":help todo" for a list of known problems. - Note that a number of things that may be regarded as bugs by some, are - in fact caused by a too-faithful reproduction of Vi's behaviour. And - if you think other things are bugs "because Vi does it differently", - you should take a closer look at the vi_diff.txt file (or type :help - vi_diff.txt when in Vim). Also have a look at the 'compatible' and + Note that a number of things that may be regarded as bugs by some, are + in fact caused by a too-faithful reproduction of Vi's behaviour. And + if you think other things are bugs "because Vi does it differently", + you should take a closer look at the vi_diff.txt file (or type :help + vi_diff.txt when in Vim). Also have a look at the 'compatible' and 'cpoptions' options. - 2025 Jun 27 VIM(1) + 2026 May 22 VIM(1) diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index 4e0d5cf98a..bb8d86d7ed 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -637,7 +637,7 @@ func Test_invalid_args() endfor endif - if has('gui_gtk') + if has('gui_gtk') && has("xterm_clipboard") let out = split(system(GetVimCommand() .. ' --display'), "\n") call assert_equal(1, v:shell_error) call assert_match('^VIM - Vi IMproved .* (.*)$', out[0]) diff --git a/src/version.c b/src/version.c index 774aee0e7a..4ea4541942 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 536, /**/ 535, /**/ From b748f4b2f01ecb30140a1979c8a78a59a1a23629 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 25 May 2026 17:08:59 +0000 Subject: [PATCH 320/545] patch 9.2.0537: GTK4: mouse popup menu does not show up at mouse pointer Problem: GTK4: mouse popup menu does not show up at mouse pointer, Drawing area blanks while popover is open (lilydjwg, after v9.2.0501) Solution: Query the pointer position, make the popover parented to the surrounding GtkOverlay and build it from buttons instead of a GMenu model(Yasuhiro Matsumoto) fixes: #20255 closes: #20260 Co-Authored-by: Claude Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++--- src/version.c | 2 + 2 files changed, 165 insertions(+), 9 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index 9cbffb3c9e..343b4f62da 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -2195,12 +2195,58 @@ gui_mch_set_foreground(void) gtk_window_present(GTK_WINDOW(gui.mainwin)); } + static int +query_pointer_pos(int *x, int *y) +{ + GtkNative *native; + GdkSurface *surface; + GdkDisplay *display; + GdkSeat *seat; + GdkDevice *pointer; + double sx, sy, nx, ny; + graphene_point_t src, dst; + + if (gui.drawarea == NULL) + return FALSE; + native = gtk_widget_get_native(gui.drawarea); + if (native == NULL) + return FALSE; + surface = gtk_native_get_surface(native); + if (surface == NULL) + return FALSE; + display = gtk_widget_get_display(gui.drawarea); + if (display == NULL) + return FALSE; + seat = gdk_display_get_default_seat(display); + if (seat == NULL) + return FALSE; + pointer = gdk_seat_get_pointer(seat); + if (pointer == NULL) + return FALSE; + + if (!gdk_surface_get_device_position(surface, pointer, &sx, &sy, NULL)) + return FALSE; + + gtk_native_get_surface_transform(native, &nx, &ny); + src.x = (float)(sx - nx); + src.y = (float)(sy - ny); + if (!gtk_widget_compute_point(GTK_WIDGET(native), gui.drawarea, + &src, &dst)) + return FALSE; + + *x = (int)dst.x; + *y = (int)dst.y; + return TRUE; +} + void gui_mch_getmouse(int *x, int *y) { - *x = 0; - *y = 0; - // GTK4: No reliable way to query pointer position synchronously. + if (!query_pointer_pos(x, y)) + { + *x = 0; + *y = 0; + } } void @@ -3706,20 +3752,128 @@ gui_mch_destroy_menu(vimmenu_T *menu) popupmenu_closed_cb(GtkPopover *popover, gpointer data UNUSED) { gtk_widget_unparent(GTK_WIDGET(popover)); + if (gui.drawarea != NULL) + gtk_widget_queue_draw(gui.drawarea); +} + +typedef struct { + GtkPopover *popover; + vimmenu_T *menu; +} popup_item_data_T; + + static void +popup_item_clicked_cb(GtkButton *button UNUSED, gpointer data) +{ + popup_item_data_T *d = data; + + if (d->popover != NULL) + gtk_popover_popdown(d->popover); + if (d->menu != NULL) + { + gui_menu_cb(d->menu); + gui_mch_flush(); + } +} + + static void +popup_item_data_free(gpointer data, GClosure *closure UNUSED) +{ + g_free(data); } void gui_mch_show_popupmenu(vimmenu_T *menu) { - GMenu *gmenu; - GtkWidget *popover; + GtkWidget *popover; + GtkWidget *box; + GtkWidget *parent; + GdkRectangle rect; + vimmenu_T *child; + int mode; + int natural_width = 0; - if (menu == NULL || menu->submenu_id == NULL) + if (menu == NULL || menu->children == NULL) return; - gmenu = (GMenu *)(gpointer)menu->submenu_id; - popover = gtk_popover_menu_new_from_model(G_MENU_MODEL(gmenu)); - gtk_widget_set_parent(popover, gui.drawarea); + // Attach the popover to drawarea's parent (the GtkOverlay) rather than + // to drawarea itself. GtkDrawingArea is a leaf widget whose snapshot + // does not iterate children, and parenting a popover to it has been + // observed to leave the drawing area blank while the popover is open. + parent = gtk_widget_get_parent(gui.drawarea); + if (parent == NULL) + parent = gui.drawarea; + + // Build the popover by hand instead of using gtk_popover_menu_new_from_model. + // GtkPopoverMenu relies on the "menu." action-group lookup walking up + // the parent chain, which has been observed to silently fail on some + // compositors when the popover is parented via gtk_widget_set_parent. Wiring + // each menu item to a plain "clicked" signal sidesteps that entirely. + popover = gtk_popover_new(); + gtk_widget_set_parent(popover, parent); + gtk_popover_set_has_arrow(GTK_POPOVER(popover), FALSE); + gtk_popover_set_position(GTK_POPOVER(popover), GTK_POS_BOTTOM); + gtk_widget_add_css_class(popover, "menu"); + + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_popover_set_child(GTK_POPOVER(popover), box); + + mode = get_menu_mode_flag(); + + for (child = menu->children; child != NULL; child = child->next) + { + GtkWidget *item; + char_u *label; + popup_item_data_T *cb_data; + + if (menu_is_separator(child->name)) + { + item = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); + gtk_box_append(GTK_BOX(box), item); + continue; + } + + label = CONVERT_TO_UTF8(child->dname); + item = gtk_button_new_with_mnemonic( + label != NULL ? (const char *)label : ""); + CONVERT_TO_UTF8_FREE(label); + + gtk_widget_add_css_class(item, "flat"); + gtk_widget_add_css_class(item, "model"); + gtk_button_set_has_frame(GTK_BUTTON(item), FALSE); + gtk_widget_set_halign(item, GTK_ALIGN_FILL); + { + GtkWidget *btn_label = gtk_button_get_child(GTK_BUTTON(item)); + if (GTK_IS_LABEL(btn_label)) + gtk_label_set_xalign(GTK_LABEL(btn_label), 0.0); + } + + if (!(child->modes & child->enabled & mode)) + gtk_widget_set_sensitive(item, FALSE); + + cb_data = g_new0(popup_item_data_T, 1); + cb_data->popover = GTK_POPOVER(popover); + cb_data->menu = child; + g_signal_connect_data(item, "clicked", + G_CALLBACK(popup_item_clicked_cb), + cb_data, popup_item_data_free, 0); + + gtk_box_append(GTK_BOX(box), item); + } + + if (!query_pointer_pos(&rect.x, &rect.y)) + { + rect.x = 0; + rect.y = 0; + } + // GtkPopover with GTK_POS_BOTTOM centres horizontally on the pointing-to + // rectangle. Use the box's natural width so the popover's left edge ends + // up at the cursor (down-and-to-the-right of the pointer). + gtk_widget_measure(box, GTK_ORIENTATION_HORIZONTAL, -1, + NULL, &natural_width, NULL, NULL); + rect.width = natural_width > 0 ? natural_width : 1; + rect.height = 1; + gtk_popover_set_pointing_to(GTK_POPOVER(popover), &rect); + g_signal_connect(popover, "closed", G_CALLBACK(popupmenu_closed_cb), NULL); gtk_popover_popup(GTK_POPOVER(popover)); diff --git a/src/version.c b/src/version.c index 4ea4541942..f86259cc9e 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 537, /**/ 536, /**/ From c175ce86fa96c34ed24443a6d0b4e87fda0f03fb Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Mon, 25 May 2026 17:21:43 +0000 Subject: [PATCH 321/545] runtime(spec): Drop obsolete s:GetRelVer() function Signed-off-by: Christian Brabandt --- runtime/ftplugin/spec.vim | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/runtime/ftplugin/spec.vim b/runtime/ftplugin/spec.vim index dcf2908911..cf8552c16c 100644 --- a/runtime/ftplugin/spec.vim +++ b/runtime/ftplugin/spec.vim @@ -6,6 +6,7 @@ " Update by Zdenek Dohnal, 2022 May 17 " 2024 Sep 10 by Vim Project: add epoch support for spec changelog, #15537 " 2024 Oct 07 by Vim Project: add comment support, #15817 +" 2026 May 25 by Vim Project: drop the python GetRelVer() func if exists("b:did_ftplugin") finish @@ -27,33 +28,7 @@ if !exists("no_plugin_maps") && !exists("no_spec_maps") endif if !hasmapto("call SpecChangelog(\"\")") - noremap " - \ ]) + # insert script closing tag if needed + if uses_script + append(style_start, [ + '', + settings.use_xhtml ? '//]]>' : '-->', + "" + ]) endif - " insert javascript to get IDs from line numbers, and to open a fold before - " jumping to any lines contained therein - if s:settings.line_ids - call append(style_start, [ - \ " /* Always jump to new location even if the line was hidden inside a fold, or", - \ " * we corrected the raw number to a line ID.", - \ " */", - \ " if (lineElem) {", - \ " lineElem.scrollIntoView(true);", - \ " }", - \ " return true;", - \ "}", - \ "if ('onhashchange' in window) {", - \ " window.onhashchange = JumpToLine;", - \ "}" - \ ]) - - if s:settings.dynamic_folds - call append(style_start, [ - \ "", - \ " /* navigate upwards in the DOM tree to open all folds containing the line */", - \ " var node = lineElem;", - \ " while (node && node.id != 'vimCodeElement"..s:settings.id_suffix.."')", - \ " {", - \ " if (node.className == 'closed-fold')", - \ " {", - \ " /* toggle open the fold ID (remove window ID) */", - \ " toggleFold(node.id.substr(4));", - \ " }", - \ " node = node.parentNode;", - \ " }", - \ ]) + # insert javascript to get IDs from line numbers, and to open a fold before + # jumping to any lines contained therein + if settings.line_ids + append(style_start, [ + " /* Always jump to new location even if the line was hidden inside a fold, or", + " * we corrected the raw number to a line ID.", + " */", + " if (lineElem) {", + " lineElem.scrollIntoView(true);", + " }", + " return true;", + "}", + "if ('onhashchange' in window) {", + " window.onhashchange = JumpToLine;", + "}" + ]) + + if settings.dynamic_folds + append(style_start, [ + "", + " /* navigate upwards in the DOM tree to open all folds containing the line */", + " var node = lineElem;", + " while (node && node.id != 'vimCodeElement" .. settings.id_suffix .. "')", + " {", + " if (node.className == 'closed-fold')", + " {", + " /* toggle open the fold ID (remove window ID) */", + " toggleFold(node.id.substr(4));", + " }", + " node = node.parentNode;", + " }", + ]) endif endif - if s:settings.line_ids - call append(style_start, [ - \ "", - \ "/* function to open any folds containing a jumped-to line before jumping to it */", - \ "function JumpToLine()", - \ "{", - \ " var lineNum;", - \ " lineNum = window.location.hash;", - \ " lineNum = lineNum.substr(1); /* strip off '#' */", - \ "", - \ " if (lineNum.indexOf('L') == -1) {", - \ " lineNum = 'L'+lineNum;", - \ " }", - \ " if (lineNum.indexOf('W') == -1) {", - \ " lineNum = 'W1'+lineNum;", - \ " }", - \ " var lineElem = document.getElementById(lineNum);" - \ ]) + if settings.line_ids + append(style_start, [ + "", + "/* function to open any folds containing a jumped-to line before jumping to it */", + "function JumpToLine()", + "{", + " var lineNum;", + " lineNum = window.location.hash;", + " lineNum = lineNum.substr(1); /* strip off '#' */", + "", + " if (lineNum.indexOf('L') == -1) {", + " lineNum = 'L'+lineNum;", + " }", + " if (lineNum.indexOf('W') == -1) {", + " lineNum = 'W1'+lineNum;", + " }", + " var lineElem = document.getElementById(lineNum);" + ]) endif - " Insert javascript to toggle matching folds open and closed in all windows, - " if dynamic folding is active. - if s:settings.dynamic_folds - call append(style_start, [ - \ " function toggleFold(objID)", - \ " {", - \ " for (win_num = 1; win_num <= "..len(a:buf_list).."; win_num++)", - \ " {", - \ " var fold;", - \ ' fold = document.getElementById("win"+win_num+objID);', - \ " if(fold.className == 'closed-fold')", - \ " {", - \ " fold.className = 'open-fold';", - \ " }", - \ " else if (fold.className == 'open-fold')", - \ " {", - \ " fold.className = 'closed-fold';", - \ " }", - \ " }", - \ " }", - \ ]) + # Insert javascript to toggle matching folds open and closed in all windows, + # if dynamic folding is active. + if settings.dynamic_folds + append(style_start, [ + " function toggleFold(objID)", + " {", + " for (win_num = 1; win_num <= " .. len(buf_list) .. "; win_num++)", + " {", + " var fold;", + ' fold = document.getElementById("win"+win_num+objID);', + " if(fold.className == 'closed-fold')", + " {", + " fold.className = 'open-fold';", + " }", + " else if (fold.className == 'open-fold')", + " {", + " fold.className = 'closed-fold';", + " }", + " }", + " }", + ]) endif - if s:uses_script - " insert script tag if needed - call append(style_start, [ - \ "", - \ s:settings.use_xhtml ? '//", + settings.use_xhtml ? '//']+ - \ style+ - \ [ s:settings.use_xhtml ? '' : '', - \ '' - \]) - endif "}}} + # Insert styles from all the generated html documents and additional styles + # for the table-based layout of the side-by-side diff. The diff should take + # up the full browser window (but not more), and be static in size, + # horizontally scrollable when the lines are too long. Otherwise, the diff + # is pretty useless for really long lines. {{{ + if settings.use_css + append(style_start, + [''] + + style + + [ settings.use_xhtml ? '' : '', + '' + ]) + endif #}}} endif endif - let &paste = s:old_paste - let &magic = s:old_magic -endfunc "}}} - -" Gets a single user option and sets it in the passed-in Dict, or gives it the -" default value if the option doesn't actually exist. -func! tohtml#GetOption(settings, option, default) "{{{ - if exists('g:html_'..a:option) - let a:settings[a:option] = g:html_{a:option} + &paste = old_paste + &magic = old_magic +enddef #}}} + +# Gets a single user option and sets it in the passed-in Dict, or gives it the +# default value if the option doesn't actually exist. +export def GetOption(_settings: dict, option: string, default: any) #{{{ + _settings[option] = get(g:, $'html_{option}', default) +enddef #}}} + +# returns a Dict containing the values of all user options for 2html, including +# default values for those not given an explicit value by the user. Discards the +# html_ prefix of the option for nicer looking code. +export def GetUserSettings(): dict #{{{ + if !empty(settings) + # just restore the known options if we've already retrieved them + return settings else - let a:settings[a:option] = a:default - endif -endfunc "}}} - -" returns a Dict containing the values of all user options for 2html, including -" default values for those not given an explicit value by the user. Discards the -" html_ prefix of the option for nicer looking code. -func! tohtml#GetUserSettings() "{{{ - if exists('s:settings') - " just restore the known options if we've already retrieved them - return s:settings - else - " otherwise figure out which options are set - let user_settings = {} + # otherwise figure out which options are set + var user_settings = {} - " Define the correct option if the old option name exists and we haven't - " already defined the correct one. + # Define the correct option if the old option name exists and we haven't + # already defined the correct one. if exists('g:use_xhtml') && !exists("g:html_use_xhtml") echohl WarningMsg echomsg "Warning: g:use_xhtml is deprecated, use g:html_use_xhtml" echohl None - let g:html_use_xhtml = g:use_xhtml + g:html_use_xhtml = g:use_xhtml endif - " get current option settings with appropriate defaults {{{ - call tohtml#GetOption(user_settings, 'no_progress', !has("statusline") ) - call tohtml#GetOption(user_settings, 'diff_one_file', 0 ) - call tohtml#GetOption(user_settings, 'number_lines', &number ) - call tohtml#GetOption(user_settings, 'pre_wrap', &wrap ) - call tohtml#GetOption(user_settings, 'use_css', 1 ) - call tohtml#GetOption(user_settings, 'ignore_conceal', 0 ) - call tohtml#GetOption(user_settings, 'ignore_folding', 0 ) - call tohtml#GetOption(user_settings, 'dynamic_folds', 0 ) - call tohtml#GetOption(user_settings, 'no_foldcolumn', user_settings.ignore_folding) - call tohtml#GetOption(user_settings, 'hover_unfold', 0 ) - call tohtml#GetOption(user_settings, 'no_pre', 0 ) - call tohtml#GetOption(user_settings, 'no_doc', 0 ) - call tohtml#GetOption(user_settings, 'no_links', 0 ) - call tohtml#GetOption(user_settings, 'no_modeline', 0 ) - call tohtml#GetOption(user_settings, 'no_invalid', 0 ) - call tohtml#GetOption(user_settings, 'whole_filler', 0 ) - call tohtml#GetOption(user_settings, 'use_xhtml', 0 ) - call tohtml#GetOption(user_settings, 'line_ids', user_settings.number_lines ) - call tohtml#GetOption(user_settings, 'use_input_for_pc', 'none') - " }}} - - " override those settings that need it {{{ - - " hover opening implies dynamic folding + # get current option settings with appropriate defaults {{{ + GetOption(user_settings, 'no_progress', !has("statusline") ) + GetOption(user_settings, 'diff_one_file', 0 ) + GetOption(user_settings, 'number_lines', &number ) + GetOption(user_settings, 'pre_wrap', &wrap ) + GetOption(user_settings, 'use_css', 1 ) + GetOption(user_settings, 'ignore_conceal', 0 ) + GetOption(user_settings, 'ignore_folding', 0 ) + GetOption(user_settings, 'dynamic_folds', 0 ) + GetOption(user_settings, 'no_foldcolumn', user_settings.ignore_folding) + GetOption(user_settings, 'hover_unfold', 0 ) + GetOption(user_settings, 'no_pre', 0 ) + GetOption(user_settings, 'no_doc', 0 ) + GetOption(user_settings, 'no_links', 0 ) + GetOption(user_settings, 'no_modeline', 0 ) + GetOption(user_settings, 'no_invalid', 0 ) + GetOption(user_settings, 'whole_filler', 0 ) + GetOption(user_settings, 'use_xhtml', 0 ) + GetOption(user_settings, 'line_ids', user_settings.number_lines ) + GetOption(user_settings, 'use_input_for_pc', 'none') + # }}} + + # override those settings that need it {{{ + + # hover opening implies dynamic folding if user_settings.hover_unfold - let user_settings.dynamic_folds = 1 + user_settings.dynamic_folds = 1 endif - " ignore folding overrides dynamic folding + # ignore folding overrides dynamic folding if user_settings.ignore_folding && user_settings.dynamic_folds - let user_settings.dynamic_folds = 0 - let user_settings.hover_unfold = 0 + user_settings.dynamic_folds = 0 + user_settings.hover_unfold = 0 endif - " dynamic folding with no foldcolumn implies hover opens + # dynamic folding with no foldcolumn implies hover opens if user_settings.dynamic_folds && user_settings.no_foldcolumn - let user_settings.hover_unfold = 1 + user_settings.hover_unfold = 1 endif - " dynamic folding implies css + # dynamic folding implies css if user_settings.dynamic_folds - let user_settings.use_css = 1 + user_settings.use_css = 1 else - let user_settings.no_foldcolumn = 1 " won't do anything but for consistency and for the test suite + user_settings.no_foldcolumn = 1 # won't do anything but for consistency and for the test suite endif - " if we're not using CSS we cannot use a pre section because tags - " aren't allowed inside a
     block
    +    # if we're not using CSS we cannot use a pre section because  tags
    +    # aren't allowed inside a 
     block
         if !user_settings.use_css
    -      let user_settings.no_pre = 1
    +      user_settings.no_pre = 1
         endif
     
    -    " pre_wrap doesn't do anything if not using pre or not using CSS
    +    # pre_wrap doesn't do anything if not using pre or not using CSS
         if user_settings.no_pre || !user_settings.use_css
    -      let user_settings.pre_wrap = 0
    +      user_settings.pre_wrap = 0
         endif
    -    "}}}
    +    #}}}
     
    -    " set up expand_tabs option after all the overrides so we know the
    -    " appropriate defaults {{{
    +    # set up expand_tabs option after all the overrides so we know the
    +    # appropriate defaults #{{{
         if user_settings.no_pre == 0
    -      call tohtml#GetOption(user_settings,
    -	    \ 'expand_tabs',
    -	    \ &expandtab || &ts != 8 || &vts != '' || user_settings.number_lines ||
    -	    \   (user_settings.dynamic_folds && !user_settings.no_foldcolumn))
    +      GetOption(user_settings,
    +	'expand_tabs',
    +	&expandtab || &ts != 8 || &vts != '' || user_settings.number_lines ||
    +	(user_settings.dynamic_folds && !user_settings.no_foldcolumn))
         else
    -      let user_settings.expand_tabs = 1
    +      user_settings.expand_tabs = 1
         endif
    -    " }}}
    -
    -    " textual options
    -    if exists("g:html_use_encoding") "{{{
    -      " user specified the desired MIME charset, figure out proper
    -      " 'fileencoding' from it or warn the user if we cannot
    -      let user_settings.encoding = g:html_use_encoding
    -      let user_settings.vim_encoding = tohtml#EncodingFromCharset(g:html_use_encoding)
    +    # }}}
    +
    +    # textual options
    +    if exists("g:html_use_encoding") #{{{
    +      # user specified the desired MIME charset, figure out proper
    +      # 'fileencoding' from it or warn the user if we cannot
    +      user_settings.encoding = g:html_use_encoding
    +      user_settings.vim_encoding = EncodingFromCharset(g:html_use_encoding)
           if user_settings.vim_encoding == ''
     	echohl WarningMsg
    -	echomsg "TOhtml: file encoding for"
    -	      \ g:html_use_encoding
    -	      \ "unknown, please set 'fileencoding'"
    +	echomsg "TOhtml: file encoding for" g:html_use_encoding "unknown, please set 'fileencoding'"
     	echohl None
           endif
         else
    -      " Figure out proper MIME charset from 'fileencoding' if possible
    -      if &l:fileencoding != '' 
    -	" If the buffer is not a "normal" type, the 'fileencoding' value may not
    -	" be trusted; since the buffer should not be written the fileencoding is
    -	" not intended to be used.
    -	if &l:buftype=='' || &l:buftype==?'help'
    -	  let user_settings.vim_encoding = &l:fileencoding
    -	  call tohtml#CharsetFromEncoding(user_settings)
    +      # Figure out proper MIME charset from 'fileencoding' if possible
    +      if &l:fileencoding != ''
    +	# If the buffer is not a "normal" type, the 'fileencoding' value may not
    +	# be trusted; since the buffer should not be written the fileencoding is
    +	# not intended to be used.
    +	if &l:buftype == '' || &l:buftype ==? 'help'
    +	  user_settings.vim_encoding = &l:fileencoding
    +	  CharsetFromEncoding(user_settings)
     	else
    -	  let user_settings.encoding = '' " trigger detection using &encoding
    +	  user_settings.encoding = '' # trigger detection using &encoding
     	endif
           endif
     
    -      " else from 'encoding' if possible
    +      # else from 'encoding' if possible
           if &l:fileencoding == '' || user_settings.encoding == ''
    -	let user_settings.vim_encoding = &encoding
    -	call tohtml#CharsetFromEncoding(user_settings)
    +	user_settings.vim_encoding = &encoding
    +	CharsetFromEncoding(user_settings)
           endif
     
    -      " else default to UTF-8 and warn user
    +      # else default to UTF-8 and warn user
           if user_settings.encoding == ''
    -	let user_settings.vim_encoding = 'utf-8'
    -	let user_settings.encoding = 'UTF-8'
    +	user_settings.vim_encoding = 'utf-8'
    +	user_settings.encoding = 'UTF-8'
     	echohl WarningMsg
     	echomsg "TOhtml: couldn't determine MIME charset, using UTF-8"
     	echohl None
           endif
    -    endif "}}}
    -
    -    " Default to making nothing uncopyable, because we default to
    -    " not-standards way of doing things, and also because Microsoft Word and
    -    " others paste the  elements anyway.
    -    "
    -    " html_prevent_copy only has an effect when using CSS.
    -    "
    -    " All options:
    -    "	  f - fold column
    -    "	  n - line numbers (also within fold text)
    -    "	  t - fold text
    -    "	  d - diff filler
    -    "	  c - concealed text (reserved future)
    -    "	  l - listchars (reserved possible future)
    -    "	  s - signs (reserved possible future)
    -    "
    -    " Normal text is always selectable.
    -    let user_settings.prevent_copy = ""
    +    endif #}}}
    +
    +    # Default to making nothing uncopyable, because we default to
    +    # not-standards way of doing things, and also because Microsoft Word and
    +    # others paste the  elements anyway.
    +    #
    +    # html_prevent_copy only has an effect when using CSS.
    +    #
    +    # All options:
    +    #	  f - fold column
    +    #	  n - line numbers (also within fold text)
    +    #	  t - fold text
    +    #	  d - diff filler
    +    #	  c - concealed text (reserved future)
    +    #	  l - listchars (reserved possible future)
    +    #	  s - signs (reserved possible future)
    +    #
    +    # Normal text is always selectable.
    +    user_settings.prevent_copy = ""
         if user_settings.use_css
           if exists("g:html_prevent_copy")
     	if user_settings.dynamic_folds && !user_settings.no_foldcolumn && g:html_prevent_copy =~# 'f'
    -	  let user_settings.prevent_copy ..= 'f'
    +	  user_settings.prevent_copy ..= 'f'
     	endif
     	if user_settings.number_lines && g:html_prevent_copy =~# 'n'
    -	  let user_settings.prevent_copy ..= 'n'
    +	  user_settings.prevent_copy ..= 'n'
     	endif
     	if &diff && g:html_prevent_copy =~# 'd'
    -	  let user_settings.prevent_copy ..= 'd'
    +	  user_settings.prevent_copy ..= 'd'
     	endif
     	if !user_settings.ignore_folding && g:html_prevent_copy =~# 't'
    -	  let user_settings.prevent_copy ..= 't'
    +	  user_settings.prevent_copy ..= 't'
     	endif
           else
    -	let user_settings.prevent_copy = ""
    +	user_settings.prevent_copy = ""
           endif
         endif
         if empty(user_settings.prevent_copy)
    -      let user_settings.no_invalid = 0
    +      user_settings.no_invalid = 0
         endif
     
    -    " enforce valid values for use_input_for_pc
    +    # enforce valid values for use_input_for_pc
         if user_settings.use_input_for_pc !~# 'fallback\|none\|all'
    -      let user_settings.use_input_for_pc = 'none'
    +      user_settings.use_input_for_pc = 'none'
           echohl WarningMsg
           echomsg '2html: "' .. g:html_use_input_for_pc .. '" is not valid for g:html_use_input_for_pc'
           echomsg '2html: defaulting to "' .. user_settings.use_input_for_pc .. '"'
    @@ -885,67 +889,64 @@ func! tohtml#GetUserSettings() "{{{
         endif
     
         if exists('g:html_id_expr')
    -      let user_settings.id_suffix = eval(g:html_id_expr)
    +      user_settings.id_suffix = eval(g:html_id_expr)
           if user_settings.id_suffix !~ '^[-_:.A-Za-z0-9]*$'
     	echohl WarningMsg
     	echomsg '2html: g:html_id_expr evaluated to invalid string for HTML id attributes'
     	echomsg '2html: Omitting user-specified suffix'
     	echohl None
     	sleep 3
    -	let user_settings.id_suffix=""
    +	user_settings.id_suffix = ""
           endif
         else
    -      let user_settings.id_suffix=""
    +      user_settings.id_suffix = ""
         endif
     
    -    " TODO: font
    +    # TODO: font
     
         return user_settings
       endif
    -endfunc "}}}
    +enddef #}}}
     
    -" get the proper HTML charset name from a Vim encoding option.
    -function! tohtml#CharsetFromEncoding(settings) "{{{
    -  let l:vim_encoding = a:settings.vim_encoding
    -  if exists('g:html_charset_override') && has_key(g:html_charset_override, l:vim_encoding)
    -    let a:settings.encoding = g:html_charset_override[l:vim_encoding]
    +# get the proper HTML charset name from a Vim encoding option.
    +export def CharsetFromEncoding(_settings: dict) #{{{
    +  var vim_encoding = _settings.vim_encoding
    +  if exists('g:html_charset_override') && has_key(g:html_charset_override, vim_encoding)
    +    _settings.encoding = g:html_charset_override[vim_encoding]
       else
    -    if l:vim_encoding =~ '^8bit\|^2byte'
    -      " 8bit- and 2byte- prefixes are to indicate encodings available on the
    -      " system that Vim will convert with iconv(), look up just the encoding name,
    -      " not Vim's prefix.
    -      let l:vim_encoding = substitute(l:vim_encoding, '^8bit-\|^2byte-', '', '')
    +    if vim_encoding =~ '^8bit\|^2byte'
    +      # 8bit- and 2byte- prefixes are to indicate encodings available on the
    +      # system that Vim will convert with iconv(), look up just the encoding name,
    +      # not Vim's prefix.
    +      vim_encoding = substitute(vim_encoding, '^8bit-\|^2byte-', '', '')
         endif
    -    if has_key(g:tohtml#encoding_to_charset, l:vim_encoding)
    -      let a:settings.encoding = g:tohtml#encoding_to_charset[l:vim_encoding]
    +    if has_key(encoding_to_charset, vim_encoding)
    +      _settings.encoding = encoding_to_charset[vim_encoding]
         else
    -      let a:settings.encoding = ""
    +      _settings.encoding = ""
         endif
       endif
    -  if a:settings.encoding != ""
    -    let l:vim_encoding = tohtml#EncodingFromCharset(a:settings.encoding)
    -    if l:vim_encoding != ""
    -      " if the Vim encoding to HTML encoding conversion is set up (by default or
    -      " by the user) to convert to a different encoding, we need to also change
    -      " the Vim encoding of the new buffer
    -      let a:settings.vim_encoding = l:vim_encoding
    +  if _settings.encoding != ""
    +    var vim_encoding2 = EncodingFromCharset(_settings.encoding)
    +    if vim_encoding2 != ""
    +      # if the Vim encoding to HTML encoding conversion is set up (by default or
    +      # by the user) to convert to a different encoding, we need to also change
    +      # the Vim encoding of the new buffer
    +      _settings.vim_encoding = vim_encoding2
         endif
       endif
    -endfun "}}}
    -
    -" Get the proper Vim encoding option setting from an HTML charset name.
    -function! tohtml#EncodingFromCharset(encoding) "{{{
    -  if exists('g:html_encoding_override') && has_key(g:html_encoding_override, a:encoding)
    -    return g:html_encoding_override[a:encoding]
    -  elseif has_key(g:tohtml#charset_to_encoding, tolower(a:encoding))
    -    return g:tohtml#charset_to_encoding[tolower(a:encoding)]
    +enddef #}}}
    +
    +# Get the proper Vim encoding option setting from an HTML charset name.
    +export def EncodingFromCharset(encoding: string): string #{{{
    +  if exists('g:html_encoding_override') && has_key(g:html_encoding_override, encoding)
    +    return g:html_encoding_override[encoding]
    +  elseif has_key(charset_to_encoding, tolower(encoding))
    +    return charset_to_encoding[tolower(encoding)]
       else
         return ""
       endif
    -endfun "}}}
    -
    -let &cpo = s:cpo_sav
    -unlet s:cpo_sav
    +enddef #}}}
     
    -" Make sure any patches will probably use consistent indent
    -"   vim: ts=8 sw=2 sts=2 noet fdm=marker
    +# Make sure any patches will probably use consistent indent
    +#   vim: ts=8 sw=2 sts=2 noet fdm=marker
    diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
    index 9a69c312ef..ca5daf1b73 100644
    --- a/runtime/doc/syntax.txt
    +++ b/runtime/doc/syntax.txt
    @@ -402,9 +402,8 @@ the desired value, or restored to their default by removing the variable using
     Remarks:
     - Some truly ancient browsers may not show the background colors.
     - From most browsers you can also print the file (in color)!
    -- The latest TOhtml may actually work with older versions of Vim, but some
    -  features such as conceal support will not function, and the colors may be
    -  incorrect for an old Vim without GUI support compiled in.
    +- Colors may be incorrect for Vim without GUI support compiled in.
    +- The latest TOhtml requires Vim 9.1 or later.
     
     Here is an example how to run the script over all .c and .h files from a
     Unix shell: >
    diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
    index 7c2ace84e7..29882b4d55 100644
    --- a/runtime/doc/version9.txt
    +++ b/runtime/doc/version9.txt
    @@ -52622,6 +52622,8 @@ Other ~
     - New "leadtab" value for the 'listchars' setting.
     - Improved |:set+=|, |:set^=| and |:set-=| handling of comma-separated "key:value"
       pairs individually (e.g. 'listchars', 'fillchars', 'diffopt').
    +- The |:TOhtml| command has been completely rewritten in Vim9 script, improving
    +  performance and maintainability.
     - |system()| and |systemlist()| functions accept a list as first argument,
       bypassing the shell completely.
     - Allow mouse clickable regions in the 'statusline', 'tabline' and the
    diff --git a/runtime/plugin/tohtml.vim b/runtime/plugin/tohtml.vim
    index 56eb2c15bf..536bc91047 100644
    --- a/runtime/plugin/tohtml.vim
    +++ b/runtime/plugin/tohtml.vim
    @@ -1,254 +1,264 @@
    -" Vim plugin for converting a syntax highlighted file to HTML.
    -" Maintainer: Ben Fritz 
    -" Last Change: 2023 Sep 07
    -"
    -" The core of the code is in $VIMRUNTIME/autoload/tohtml.vim and
    -" $VIMRUNTIME/syntax/2html.vim
    -"
    +vim9script
    +# Vim plugin for converting a syntax highlighted file to HTML.
    +# Maintainer: Ben Fritz 
    +# Last Change: 2026 Apr 9
    +#
    +# The core of the code is in $VIMRUNTIME/autoload/tohtml.vim and
    +# $VIMRUNTIME/syntax/2html.vim
    +#
     if exists('g:loaded_2html_plugin')
       finish
     endif
    -let g:loaded_2html_plugin = 'vim9.0_v2'
    +g:loaded_2html_plugin = 'vim9.2_v1'
     
    -"
    -" Changelog: {{{
    -"   9.0_v2  (this version): - Warn if using deprecated g:use_xhtml option
    -"                           - Change default g:html_use_input_for_pc to "none"
    -"                             instead of "fallback". All modern browsers support
    -"                             the "user-select: none" and "content:" CSS
    -"                             properties so the older method relying on extra
    -"                             markup and unspecified browser/app clipboard
    -"                             handling is only needed in rare special cases.
    -"                           - Fix SourceForge issue #33: generate diff filler
    -"                             correctly when new lines have been added to or
    -"                             removed from end of buffer.
    -"                           - Fix SourceForge issue #32/Vim Github issue #8547:
    -"                             use translated highlight ID for styling the
    -"                             special-use group names (e.g. LineNr) used
    -"                             directly by name in the 2html processing.
    -"                           - Fix SourceForge issue #26, refactoring to use
    -"                             :let-heredoc style string assignment and
    -"                             additional fixes for ".." vs. "." style string
    -"                             concatenation. Requires Vim v8.1.1354 or higher.
    -"   9.0_v1  (Vim 9.0.1275): - Implement g:html_no_doc and g:html_no_modeline
    -"                             for diff mode. Add tests.
    -"           (Vim 9.0.1122): NOTE: no version string update for this version!
    -"                           - Bugfix for variable name in g:html_no_doc
    -"           (Vim 9.0.0819): NOTE: no version string update for this version!
    -"                           - Add options g:html_no_doc, g:html_no_lines,
    -"                             and g:html_no_modeline (partially included in Vim
    -"                             runtime prior to version string update).
    -"                           - Updates for new Vim9 string append style (i.e. use
    -"                             ".." instead of "."). Requires Vim version
    -"                             8.1.1114 or higher.
    -"
    -"   8.1 updates: {{{
    -"   8.1_v2  (Vim 8.1.2312): - Fix SourceForge issue #19: fix calculation of tab
    -"                             stop position to use in expanding a tab, when that
    -"                             tab occurs after a syntax match which in turn
    -"                             comes after previously expanded tabs.
    -"                           - Set eventignore while splitting a window for the
    -"                             destination file to ignore FileType events;
    -"                             speeds up processing when the destination file
    -"                             already exists and HTML highlight takes too long.
    -"                           - Fix SourceForge issue #20: progress bar could not be
    -"                             seen when DiffDelete background color matched
    -"                             StatusLine background color. Added TOhtmlProgress
    -"                             highlight group for manual user override, but
    -"                             calculate it to be visible compared to StatusLine
    -"                             by default.
    -"                           - Fix SourceForge issue #1: Remove workaround for old
    -"                             browsers which don't support 'ch' CSS unit, since
    -"                             all modern browsers, including IE>=9, support it.
    -"                           - Fix SourceForge issue #10: support termguicolors
    -"                           - Fix SourceForge issue #21: default to using
    -"                             generated content instead of  tags for
    -"                             uncopyable text, so that text is correctly
    -"                             prevented from being copied in chrome. Use
    -"                             g:html_use_input_for_pc option to control the
    -"                             method used.
    -"                           - Switch to HTML5 to allow using vnu as a validator
    -"                             in unit test.
    -"                           - Fix fallback sizing of  tags for browsers
    -"                             without "ch" support.
    -"                           - Fix cursor on unselectable diff filler text.
    -"   8.1_v1  (Vim 8.1.0528): - Fix SourceForge issue #6: Don't generate empty
    -"                             script tag.
    -"                           - Fix SourceForge issue #5: javascript should
    -"                             declare variables with "var".
    -"                           - Fix SourceForge issue #13: errors thrown sourcing
    -"                             2html.vim directly when plugins not loaded.
    -"                           - Fix SourceForge issue #16: support 'vartabstop'.
    -"}}}
    -"
    -"   7.4 updates: {{{
    -"   7.4_v2  (Vim 7.4.0899): Fix error raised when converting a diff containing
    -"                           an empty buffer. Jan Stocker: allow g:html_font to
    -"                           take a list so it is easier to specfiy fallback
    -"                           fonts in the generated CSS.
    -"   7.4_v1  (Vim 7.4.0000): Fix modeline mangling for new "Vim:" format, and
    -"			    also for version-specific modelines like "vim>703:".
    -"}}}
    -"
    -"   7.3 updates: {{{
    -"   7.3_v14 (Vim 7.3.1246): Allow suppressing line number anchors using
    -"			    g:html_line_ids=0. Allow customizing
    -"			    important IDs (like line IDs and fold IDs) using
    -"			    g:html_id_expr evaluated when the buffer conversion
    -"			    is started.
    -"   7.3_v13 (Vim 7.3.1088): Keep foldmethod at manual in the generated file and
    -"			    insert modeline to set it to manual.
    -"			    Fix bug: diff mode with 2 unsaved buffers creates a
    -"			    duplicate of one buffer instead of including both.
    -"			    Add anchors to each line so you can put '#L123'
    -"			    or '#123' at the end of the URL to jump to line 123
    -"			    (idea by Andy Spencer). Add javascript to open folds
    -"			    to show the anchor being jumped to if it is hidden.
    -"			    Fix XML validation error: &nsbp; not part of XML.
    -"			    Allow TOhtml to chain together with other commands
    -"			    using |.
    -"   7.3_v12 (Vim 7.3.0616): Fix modeline mangling to also work for when multiple
    -"			    highlight groups make up the start-of-modeline text.
    -"			    Improve render time of page with uncopyable regions
    -"			    by not using one-input-per-char. Change name of
    -"			    uncopyable option from html_unselectable to
    -"			    html_prevent_copy. Added html_no_invalid option and
    -"			    default to inserting invalid markup for uncopyable
    -"			    regions to prevent MS Word from pasting undeletable
    -"			     elements. Fix 'cpo' handling (Thilo Six).
    -"		 7.3_v12b1: Add html_unselectable option. Rework logic to
    -"			    eliminate post-processing substitute commands in
    -"			    favor of doing the work up front. Remove unnecessary
    -"			    special treatment of 'LineNr' highlight group. Minor
    -"			    speed improvements. Fix modeline mangling in
    -"			    generated output so it works for text in the first
    -"			    column. Fix missing line number and fold column in
    -"			    diff filler lines. Fix that some fonts have a 1px
    -"			    gap (using a dirty hack, improvements welcome). Add
    -"			    "colorscheme" meta tag. Does NOT include support for
    -"			    the new default foldtext added in v11, as the patch
    -"			    adding it has not yet been included in Vim.
    -"   7.3_v11 ( unreleased ): Support new default foldtext from patch by Christian
    -"			    Brabandt in
    -"			    http://groups.google.com/d/topic/vim_dev/B6FSGfq9VoI/discussion.
    -"			    This patch has not yet been included in Vim, thus
    -"			    these changes are removed in the next version.
    -"   7.3_v10 (Vim 7.3.0227): Fix error E684 when converting a range wholly inside
    -"			    multiple nested folds with dynamic folding on.
    -"			    Also fix problem with foldtext in this situation.
    -"   7.3_v9  (Vim 7.3.0170): Add html_pre_wrap option active with html_use_css
    -"			    and without html_no_pre, default value same as
    -"			    'wrap' option, (Andy Spencer). Don't use
    -"			    'fileencoding' for converted document encoding if
    -"			    'buftype' indicates a special buffer which isn't
    -"			    written.
    -"   7.3_v8  (Vim 7.3.0100): Add html_expand_tabs option to allow leaving tab
    -"			    characters in generated output (Andy Spencer).
    -"			    Escape text that looks like a modeline so Vim
    -"			    doesn't use anything in the converted HTML as a
    -"			    modeline. Bugfixes: Fix folding when a fold starts
    -"			    before the conversion range. Remove fold column when
    -"			    there are no folds.
    -"   7.3_v7  (Vim 7-3-0063): see betas released on vim_dev below:
    -"		  7.3_v7b3: Fixed bug, convert Unicode to UTF-8 all the way.
    -"		  7.3_v7b2: Remove automatic detection of encodings that are not
    -"			    supported by all major browsers according to
    -"			    http://wiki.whatwg.org/wiki/Web_Encodings and
    -"			    convert to UTF-8 for all Unicode encodings. Make
    -"			    HTML encoding to Vim encoding detection be
    -"			    case-insensitive for built-in pairs.
    -"		  7.3_v7b1: Remove use of setwinvar() function which cannot be
    -"			    called in restricted mode (Andy Spencer). Use
    -"			    'fencoding' instead of 'encoding' to determine by
    -"			    charset, and make sure the 'fenc' of the generated
    -"			    file matches its indicated charset. Add charsets for
    -"			    all of Vim's natively supported encodings.
    -"   7.3_v6  (Vim 7.3.0000): Really fix bug with 'nowrapscan', 'magic' and other
    -"			    user settings interfering with diff mode generation,
    -"			    trailing whitespace (e.g. line number column) when
    -"			    using html_no_pre, and bugs when using
    -"			    html_hover_unfold.
    -"   7.3_v5  ( unreleased ): Fix bug with 'nowrapscan' and also with out-of-sync
    -"			    folds in diff mode when first line was folded.
    -"   7.3_v4  (Vim 7.3.0000): Bugfixes, especially for xhtml markup, and diff mode
    -"   7.3_v3  (Vim 7.3.0000): Refactor option handling and make html_use_css
    -"			    default to true when not set to anything. Use strict
    -"			    doctypes where possible. Rename use_xhtml option to
    -"			    html_use_xhtml for consistency. Use .xhtml extension
    -"			    when using this option. Add meta tag for settings.
    -"   7.3_v2  (Vim 7.3.0000): Fix syntax highlighting in diff mode to use both the
    -"			    diff colors and the normal syntax colors
    -"   7.3_v1  (Vim 7.3.0000): Add conceal support and meta tags in output
    -"}}}
    -"}}}
    +#
    +# Changelog: {{{
    +#   9.2_v1  (this version): - mao-yining: Convert main script to vim9script
    +#                             (SourceForge issue #28/Vim Github PR #19915).
    +#
    +#   Unspecified version:    - Inherit links colors from colorscheme styles for
    +#                             auto-generated links in HTML output (Vim Github
    +#                             issue #10191).
    +#
    +#   9.0 updates: {{{
    +#   9.0_v2  (Vim 9.0.1885): - Warn if using deprecated g:use_xhtml option
    +#                           - Change default g:html_use_input_for_pc to "none"
    +#                             instead of "fallback". All modern browsers support
    +#                             the "user-select: none" and "content:" CSS
    +#                             properties so the older method relying on extra
    +#                             markup and unspecified browser/app clipboard
    +#                             handling is only needed in rare special cases.
    +#                           - Fix SourceForge issue #33: generate diff filler
    +#                             correctly when new lines have been added to or
    +#                             removed from end of buffer.
    +#                           - Fix SourceForge issue #32/Vim Github issue #8547:
    +#                             use translated highlight ID for styling the
    +#                             special-use group names (e.g. LineNr) used
    +#                             directly by name in the 2html processing.
    +#                           - Fix SourceForge issue #26, refactoring to use
    +#                             :let-heredoc style string assignment and
    +#                             additional fixes for ".." vs. "." style string
    +#                             concatenation. Requires Vim v8.1.1354 or higher.
    +#   9.0_v1  (Vim 9.0.1275): - Implement g:html_no_doc and g:html_no_modeline
    +#                             for diff mode. Add tests.
    +#           (Vim 9.0.1122): NOTE: no version string update for this version!
    +#                           - Bugfix for variable name in g:html_no_doc
    +#           (Vim 9.0.0819): NOTE: no version string update for this version!
    +#                           - Add options g:html_no_doc, g:html_no_lines,
    +#                             and g:html_no_modeline (partially included in Vim
    +#                             runtime prior to version string update).
    +#                           - Updates for new Vim9 string append style (i.e. use
    +#                             ".." instead of "."). Requires Vim version
    +#                             8.1.1114 or higher.
    +#}}}
    +#
    +#   8.1 updates: {{{
    +#   8.1_v2  (Vim 8.1.2312): - Fix SourceForge issue #19: fix calculation of tab
    +#                             stop position to use in expanding a tab, when that
    +#                             tab occurs after a syntax match which in turn
    +#                             comes after previously expanded tabs.
    +#                           - Set eventignore while splitting a window for the
    +#                             destination file to ignore FileType events;
    +#                             speeds up processing when the destination file
    +#                             already exists and HTML highlight takes too long.
    +#                           - Fix SourceForge issue #20: progress bar could not be
    +#                             seen when DiffDelete background color matched
    +#                             StatusLine background color. Added TOhtmlProgress
    +#                             highlight group for manual user override, but
    +#                             calculate it to be visible compared to StatusLine
    +#                             by default.
    +#                           - Fix SourceForge issue #1: Remove workaround for old
    +#                             browsers which don't support 'ch' CSS unit, since
    +#                             all modern browsers, including IE>=9, support it.
    +#                           - Fix SourceForge issue #10: support termguicolors
    +#                           - Fix SourceForge issue #21: default to using
    +#                             generated content instead of  tags for
    +#                             uncopyable text, so that text is correctly
    +#                             prevented from being copied in chrome. Use
    +#                             g:html_use_input_for_pc option to control the
    +#                             method used.
    +#                           - Switch to HTML5 to allow using vnu as a validator
    +#                             in unit test.
    +#                           - Fix fallback sizing of  tags for browsers
    +#                             without "ch" support.
    +#                           - Fix cursor on unselectable diff filler text.
    +#   8.1_v1  (Vim 8.1.0528): - Fix SourceForge issue #6: Don't generate empty
    +#                             script tag.
    +#                           - Fix SourceForge issue #5: javascript should
    +#                             declare variables with "var".
    +#                           - Fix SourceForge issue #13: errors thrown sourcing
    +#                             2html.vim directly when plugins not loaded.
    +#                           - Fix SourceForge issue #16: support 'vartabstop'.
    +#}}}
    +#
    +#   7.4 updates: {{{
    +#   7.4_v2  (Vim 7.4.0899): Fix error raised when converting a diff containing
    +#                           an empty buffer. Jan Stocker: allow g:html_font to
    +#                           take a list so it is easier to specfiy fallback
    +#                           fonts in the generated CSS.
    +#   7.4_v1  (Vim 7.4.0000): Fix modeline mangling for new "Vim:" format, and
    +#			    also for version-specific modelines like "vim>703:".
    +#}}}
    +#
    +#   7.3 updates: {{{
    +#   7.3_v14 (Vim 7.3.1246): Allow suppressing line number anchors using
    +#			    g:html_line_ids=0. Allow customizing
    +#			    important IDs (like line IDs and fold IDs) using
    +#			    g:html_id_expr evaluated when the buffer conversion
    +#			    is started.
    +#   7.3_v13 (Vim 7.3.1088): Keep foldmethod at manual in the generated file and
    +#			    insert modeline to set it to manual.
    +#			    Fix bug: diff mode with 2 unsaved buffers creates a
    +#			    duplicate of one buffer instead of including both.
    +#			    Add anchors to each line so you can put '#L123'
    +#			    or '#123' at the end of the URL to jump to line 123
    +#			    (idea by Andy Spencer). Add javascript to open folds
    +#			    to show the anchor being jumped to if it is hidden.
    +#			    Fix XML validation error: &nsbp; not part of XML.
    +#			    Allow TOhtml to chain together with other commands
    +#			    using |.
    +#   7.3_v12 (Vim 7.3.0616): Fix modeline mangling to also work for when multiple
    +#			    highlight groups make up the start-of-modeline text.
    +#			    Improve render time of page with uncopyable regions
    +#			    by not using one-input-per-char. Change name of
    +#			    uncopyable option from html_unselectable to
    +#			    html_prevent_copy. Added html_no_invalid option and
    +#			    default to inserting invalid markup for uncopyable
    +#			    regions to prevent MS Word from pasting undeletable
    +#			     elements. Fix 'cpo' handling (Thilo Six).
    +#		 7.3_v12b1: Add html_unselectable option. Rework logic to
    +#			    eliminate post-processing substitute commands in
    +#			    favor of doing the work up front. Remove unnecessary
    +#			    special treatment of 'LineNr' highlight group. Minor
    +#			    speed improvements. Fix modeline mangling in
    +#			    generated output so it works for text in the first
    +#			    column. Fix missing line number and fold column in
    +#			    diff filler lines. Fix that some fonts have a 1px
    +#			    gap (using a dirty hack, improvements welcome). Add
    +#			    "colorscheme" meta tag. Does NOT include support for
    +#			    the new default foldtext added in v11, as the patch
    +#			    adding it has not yet been included in Vim.
    +#   7.3_v11 ( unreleased ): Support new default foldtext from patch by Christian
    +#			    Brabandt in
    +#			    http://groups.google.com/d/topic/vim_dev/B6FSGfq9VoI/discussion.
    +#			    This patch has not yet been included in Vim, thus
    +#			    these changes are removed in the next version.
    +#   7.3_v10 (Vim 7.3.0227): Fix error E684 when converting a range wholly inside
    +#			    multiple nested folds with dynamic folding on.
    +#			    Also fix problem with foldtext in this situation.
    +#   7.3_v9  (Vim 7.3.0170): Add html_pre_wrap option active with html_use_css
    +#			    and without html_no_pre, default value same as
    +#			    'wrap' option, (Andy Spencer). Don't use
    +#			    'fileencoding' for converted document encoding if
    +#			    'buftype' indicates a special buffer which isn't
    +#			    written.
    +#   7.3_v8  (Vim 7.3.0100): Add html_expand_tabs option to allow leaving tab
    +#			    characters in generated output (Andy Spencer).
    +#			    Escape text that looks like a modeline so Vim
    +#			    doesn't use anything in the converted HTML as a
    +#			    modeline. Bugfixes: Fix folding when a fold starts
    +#			    before the conversion range. Remove fold column when
    +#			    there are no folds.
    +#   7.3_v7  (Vim 7-3-0063): see betas released on vim_dev below:
    +#		  7.3_v7b3: Fixed bug, convert Unicode to UTF-8 all the way.
    +#		  7.3_v7b2: Remove automatic detection of encodings that are not
    +#			    supported by all major browsers according to
    +#			    http://wiki.whatwg.org/wiki/Web_Encodings and
    +#			    convert to UTF-8 for all Unicode encodings. Make
    +#			    HTML encoding to Vim encoding detection be
    +#			    case-insensitive for built-in pairs.
    +#		  7.3_v7b1: Remove use of setwinvar() function which cannot be
    +#			    called in restricted mode (Andy Spencer). Use
    +#			    'fencoding' instead of 'encoding' to determine by
    +#			    charset, and make sure the 'fenc' of the generated
    +#			    file matches its indicated charset. Add charsets for
    +#			    all of Vim's natively supported encodings.
    +#   7.3_v6  (Vim 7.3.0000): Really fix bug with 'nowrapscan', 'magic' and other
    +#			    user settings interfering with diff mode generation,
    +#			    trailing whitespace (e.g. line number column) when
    +#			    using html_no_pre, and bugs when using
    +#			    html_hover_unfold.
    +#   7.3_v5  ( unreleased ): Fix bug with 'nowrapscan' and also with out-of-sync
    +#			    folds in diff mode when first line was folded.
    +#   7.3_v4  (Vim 7.3.0000): Bugfixes, especially for xhtml markup, and diff mode
    +#   7.3_v3  (Vim 7.3.0000): Refactor option handling and make html_use_css
    +#			    default to true when not set to anything. Use strict
    +#			    doctypes where possible. Rename use_xhtml option to
    +#			    html_use_xhtml for consistency. Use .xhtml extension
    +#			    when using this option. Add meta tag for settings.
    +#   7.3_v2  (Vim 7.3.0000): Fix syntax highlighting in diff mode to use both the
    +#			    diff colors and the normal syntax colors
    +#   7.3_v1  (Vim 7.3.0000): Add conceal support and meta tags in output
    +#}}}
    +#}}}
    +#
    +# TODO: {{{
    +#   * Check the issue tracker:
    +#     https://sourceforge.net/p/vim-tohtml/issues/search/?q=%21status%3Aclosed
    +#   * Options for generating the CSS in external style sheets. New :TOcss
    +#     command to convert the current color scheme into a (mostly) generic CSS
    +#     stylesheet which can be re-used. Alternate stylesheet support? Good start
    +#     by Erik Falor
    +#     ( https://groups.google.com/d/topic/vim_use/7XTmC4D22dU/discussion ).
    +#   * Add optional argument to :TOhtml command to specify mode (gui, cterm,
    +#     term) to use for the styling. Suggestion by "nacitar".
    +#   * Add way to override or specify which RGB colors map to the color numbers
    +#     in cterm. Get better defaults than just guessing? Suggestion by "nacitar".
    +#   * Disable filetype detection until after all processing is done.
    +#   * Add option for not generating the hyperlink on stuff that looks like a
    +#     URL? Or just color the link to fit with the colorscheme (and only special
    +#     when hovering)?
    +#   * Bug: Opera does not allow printing more than one page if uncopyable
    +#     regions is turned on. Possible solution: Add normal text line numbers with
    +#     display:none, set to display:inline for print style sheets, and hide
    +#      elements for print, to allow Opera printing multiple pages (and
    +#     other uncopyable areas?). May need to make the new text invisible to IE
    +#     with conditional comments to prevent copying it, IE for some reason likes
    +#     to copy hidden text. Other browsers too?
    +#   * Bug: still a 1px gap throughout the fold column when html_prevent_copy is
    +#     "fn" in some browsers. Specifically, in Chromium on Ubuntu (but not Chrome
    +#     on Windows). Perhaps it is font related?
    +#   * Bug: still some gaps in the fold column when html_prevent_copy contains
    +#     'd' and showing the whole diff (observed in multiple browsers). Only gaps
    +#     on diff lines though.
    +#   * Undercurl support via CSS3, with fallback to dotted or something:
    +#	https://groups.google.com/d/topic/vim_use/BzXA6He1pHg/discussion
    +#   * Redo updates for modified default foldtext (v11) when/if the patch is
    +#     accepted to modify it.
    +#   * Test case +diff_one_file-dynamic_folds+expand_tabs-hover_unfold
    +#		+ignore_conceal-ignore_folding+no_foldcolumn+no_pre+no_progress
    +#		+number_lines-pre_wrap-use_css+use_xhtml+whole_filler.xhtml
    +#     does not show the whole diff filler as it is supposed to?
    +#   * Bug: when 'isprint' is wrong for the current encoding, will generate
    +#     invalid content. Can/should anything be done about this? Maybe a separate
    +#     plugin to correct 'isprint' based on encoding?
    +#   * Check to see if the windows-125\d encodings actually work in Unix without
    +#     the 8bit- prefix. Add prefix to autoload dictionaries for Unix if not.
    +#   * Font auto-detection similar to
    +#     http://www.vim.org/scripts/script.php?script_id=2384 but for a variety of
    +#     platforms.
    +#   * Pull in code from http://www.vim.org/scripts/script.php?script_id=3113 :
    +#	- listchars support
    +#	- full-line background highlight
    +#	- other?
    +#   * Make it so deleted lines in a diff don't create side-scrolling (get it
    +#     free with full-line background highlight above).
    +#   * Restore open/closed folds and cursor position after processing each file
    +#     with option not to restore for speed increase.
    +#   * Add extra meta info (generation time, etc.)?
    +#   * Tidy up so we can use strict doctype in even more situations
    +#   * Implementation detail: add threshold for writing the lines to the html
    +#     buffer before we're done (5000 or so lines should do it)
    +#   * TODO comments for code cleanup scattered throughout
    +#}}}
     
    -" TODO: {{{
    -"   * Check the issue tracker:
    -"     https://sourceforge.net/p/vim-tohtml/issues/search/?q=%21status%3Aclosed
    -"   * Options for generating the CSS in external style sheets. New :TOcss
    -"     command to convert the current color scheme into a (mostly) generic CSS
    -"     stylesheet which can be re-used. Alternate stylesheet support? Good start
    -"     by Erik Falor
    -"     ( https://groups.google.com/d/topic/vim_use/7XTmC4D22dU/discussion ).
    -"   * Add optional argument to :TOhtml command to specify mode (gui, cterm,
    -"     term) to use for the styling. Suggestion by "nacitar".
    -"   * Add way to override or specify which RGB colors map to the color numbers
    -"     in cterm. Get better defaults than just guessing? Suggestion by "nacitar".
    -"   * Disable filetype detection until after all processing is done.
    -"   * Add option for not generating the hyperlink on stuff that looks like a
    -"     URL? Or just color the link to fit with the colorscheme (and only special
    -"     when hovering)?
    -"   * Bug: Opera does not allow printing more than one page if uncopyable
    -"     regions is turned on. Possible solution: Add normal text line numbers with
    -"     display:none, set to display:inline for print style sheets, and hide
    -"      elements for print, to allow Opera printing multiple pages (and
    -"     other uncopyable areas?). May need to make the new text invisible to IE
    -"     with conditional comments to prevent copying it, IE for some reason likes
    -"     to copy hidden text. Other browsers too?
    -"   * Bug: still a 1px gap throughout the fold column when html_prevent_copy is
    -"     "fn" in some browsers. Specifically, in Chromium on Ubuntu (but not Chrome
    -"     on Windows). Perhaps it is font related?
    -"   * Bug: still some gaps in the fold column when html_prevent_copy contains
    -"     'd' and showing the whole diff (observed in multiple browsers). Only gaps
    -"     on diff lines though.
    -"   * Undercurl support via CSS3, with fallback to dotted or something:
    -"	https://groups.google.com/d/topic/vim_use/BzXA6He1pHg/discussion
    -"   * Redo updates for modified default foldtext (v11) when/if the patch is
    -"     accepted to modify it.
    -"   * Test case +diff_one_file-dynamic_folds+expand_tabs-hover_unfold
    -"		+ignore_conceal-ignore_folding+no_foldcolumn+no_pre+no_progress
    -"		+number_lines-pre_wrap-use_css+use_xhtml+whole_filler.xhtml
    -"     does not show the whole diff filler as it is supposed to?
    -"   * Bug: when 'isprint' is wrong for the current encoding, will generate
    -"     invalid content. Can/should anything be done about this? Maybe a separate
    -"     plugin to correct 'isprint' based on encoding?
    -"   * Check to see if the windows-125\d encodings actually work in Unix without
    -"     the 8bit- prefix. Add prefix to autoload dictionaries for Unix if not.
    -"   * Font auto-detection similar to
    -"     http://www.vim.org/scripts/script.php?script_id=2384 but for a variety of
    -"     platforms.
    -"   * Pull in code from http://www.vim.org/scripts/script.php?script_id=3113 :
    -"	- listchars support
    -"	- full-line background highlight
    -"	- other?
    -"   * Make it so deleted lines in a diff don't create side-scrolling (get it
    -"     free with full-line background highlight above).
    -"   * Restore open/closed folds and cursor position after processing each file
    -"     with option not to restore for speed increase.
    -"   * Add extra meta info (generation time, etc.)?
    -"   * Tidy up so we can use strict doctype in even more situations
    -"   * Implementation detail: add threshold for writing the lines to the html
    -"     buffer before we're done (5000 or so lines should do it)
    -"   * TODO comments for code cleanup scattered throughout
    -"}}}
    -
    -" Define the :TOhtml command when:
    -" - 'compatible' is not set
    -" - this plugin or user override was not already loaded
    -" - user commands are available. {{{
    +# Define the :TOhtml command when:
    +# - 'compatible' is not set
    +# - this plugin or user override was not already loaded
    +# - user commands are available. {{{
     if !&cp && !exists(":TOhtml") && has("user_commands")
       command -range=% -bar TOhtml :call tohtml#Convert2HTML(, )
     endif "}}}
     
    -" Make sure any patches will probably use consistent indent
    -"   vim: ts=8 sw=2 sts=2 noet fdm=marker
    +# Make sure any patches will probably use consistent indent
    +#   vim: ts=8 sw=2 sts=2 noet fdm=marker
    diff --git a/runtime/syntax/2html.vim b/runtime/syntax/2html.vim
    index ca40810e80..527e7d38b7 100644
    --- a/runtime/syntax/2html.vim
    +++ b/runtime/syntax/2html.vim
    @@ -1,2072 +1,2064 @@
    -" Vim syntax support file
    -" Maintainer: Ben Fritz 
    -" Last Change: 2024 Nov 02
    -"
    -" Additional contributors:
    -"
    -"             Original by Bram Moolenaar 
    -"             Modified by David Ne\v{c}as (Yeti) 
    -"             XHTML support by Panagiotis Issaris 
    -"             Made w3 compliant by Edd Barrett 
    -"             Added html_font. Edd Barrett 
    -"             Progress bar based off code from "progressbar widget" plugin by
    -"               Andreas Politz, heavily modified:
    -"               http://www.vim.org/scripts/script.php?script_id=2006
    -"
    -"             See Mercurial change logs for more!
    -
    -" Transform a file into HTML, using the current syntax highlighting.
    -
    -" this file uses line continuations
    -let s:cpo_sav = &cpo
    -let s:ls  = &ls
    -let s:ei_sav = &eventignore
    -set cpo&vim
    -
    -" HTML filetype can take a while to load/highlight if the destination file
    -" already exists.
    +vim9script
    +# Vim syntax support file
    +# Maintainer: Ben Fritz 
    +# Last Change: 2026-05-22
    +#
    +# Additional contributors:
    +#
    +#             Original by Bram Moolenaar 
    +#             Modified by David Ne\v{c}as (Yeti) 
    +#             XHTML support by Panagiotis Issaris 
    +#             Made w3 compliant by Edd Barrett 
    +#             Added html_font. Edd Barrett 
    +#             Progress bar based off code from "progressbar widget" plugin by
    +#               Andreas Politz, heavily modified:
    +#               http://www.vim.org/scripts/script.php?script_id=2006
    +#             Converted to Vim9 by Mao-Yining Vim PR #19915
    +#
    +#             See git change logs for more!
    +
    +# Transform a file into HTML, using the current syntax highlighting.
    +
    +var ls_sav = &laststatus
    +var ei_sav = &eventignore
     set eventignore+=FileType
     
    -let s:end=line('$')
    +var lnum: number
    +var end = line('$')
    +var trim_tmp: list
     
    -" Font
    +# Font
    +var htmlfont: string
     if exists("g:html_font")
       if type(g:html_font) == type([])
    -    let s:htmlfont = "'".. join(g:html_font,"','") .. "', monospace"
    +    htmlfont = "'" .. join(g:html_font, "','") .. "', monospace"
       else
    -    let s:htmlfont = "'".. g:html_font .. "', monospace"
    +    htmlfont = "'" .. g:html_font .. "', monospace"
       endif
     else
    -  let s:htmlfont = "monospace"
    +  htmlfont = "monospace"
     endif
     
    -let s:settings = tohtml#GetUserSettings()
    +var settings = tohtml#GetUserSettings()
     
    -if s:settings.use_xhtml
    -  let s:html5 = 0
    -elseif s:settings.use_css && !s:settings.no_pre
    -  let s:html5 = 1
    +var html5: number
    +if settings.use_xhtml
    +  html5 = 0
    +elseif settings.use_css && !settings.no_pre
    +  html5 = 1
     else
    -  let s:html5 = 0
    +  html5 = 0
     endif
     
    -if !exists('s:FOLDED_ID')
    -  let s:FOLDED_ID  = hlID("Folded")     | lockvar s:FOLDED_ID
    -  let s:FOLD_C_ID  = hlID("FoldColumn") | lockvar s:FOLD_C_ID
    -  let s:LINENR_ID  = hlID('LineNr')     | lockvar s:LINENR_ID
    -  let s:DIFF_D_ID  = hlID("DiffDelete") | lockvar s:DIFF_D_ID
    -  let s:DIFF_A_ID  = hlID("DiffAdd")    | lockvar s:DIFF_A_ID
    -  let s:DIFF_C_ID  = hlID("DiffChange") | lockvar s:DIFF_C_ID
    -  let s:DIFF_T_ID  = hlID("DiffText")   | lockvar s:DIFF_T_ID
    -  let s:CONCEAL_ID = hlID('Conceal')    | lockvar s:CONCEAL_ID
    -endif
    -
    -" Whitespace
    -if s:settings.pre_wrap
    -  let s:whitespace = "white-space: pre-wrap; "
    +const FOLDED_ID  = hlID("Folded")
    +const FOLD_C_ID  = hlID("FoldColumn")
    +const LINENR_ID  = hlID('LineNr')
    +const DIFF_D_ID  = hlID("DiffDelete")
    +const DIFF_A_ID  = hlID("DiffAdd")
    +const DIFF_C_ID  = hlID("DiffChange")
    +const DIFF_T_ID  = hlID("DiffText")
    +const CONCEAL_ID = hlID('Conceal')
    +
    +# Whitespace
    +var whitespace: string
    +if settings.pre_wrap
    +  whitespace = "white-space: pre-wrap; "
     else
    -  let s:whitespace = ""
    +  whitespace = ""
     endif
     
    -if !empty(s:settings.prevent_copy)
    -  if s:settings.no_invalid
    -    " User has decided they don't want invalid markup. Still works in
    -    " OpenOffice, and for text editors, but when pasting into Microsoft Word the
    -    " input elements get pasted too and they cannot be deleted (at least not
    -    " easily).
    -    let s:unselInputType = ""
    +var unselInputType: string
    +if !empty(settings.prevent_copy)
    +  if settings.no_invalid
    +    # User has decided they don't want invalid markup. Still works in
    +    # OpenOffice, and for text editors, but when pasting into Microsoft Word the
    +    # input elements get pasted too and they cannot be deleted (at least not
    +    # easily).
    +    unselInputType = ""
       else
    -    " Prevent from copy-pasting the input elements into Microsoft Word where
    -    " they cannot be deleted easily by deliberately inserting invalid markup.
    -    let s:unselInputType = " type='invalid_input_type'"
    +    # Prevent from copy-pasting the input elements into Microsoft Word where
    +    # they cannot be deleted easily by deliberately inserting invalid markup.
    +    unselInputType = " type='invalid_input_type'"
       endif
     endif
     
    -" When gui colors are not supported, we can only guess the colors.
    -" TODO - is this true anymore? Is there a way to ask the terminal what colors
    -" each number means or read them from some file?
    +# When gui colors are not supported, we can only guess the colors.
    +# TODO - is this true anymore? Is there a way to ask the terminal what colors
    +# each number means or read them from some file?
    +var whatterm: string
    +var cterm_color: dict
     if &termguicolors || has("gui_running")
    -  let s:whatterm = "gui"
    +  whatterm = "gui"
     else
    -  let s:whatterm = "cterm"
    -  if &t_Co == 8
    -    let s:cterm_color = {
    -	    \   0: "#808080", 1: "#ff6060", 2: "#00ff00", 3: "#ffff00",
    -	    \   4: "#8080ff", 5: "#ff40ff", 6: "#00ffff", 7: "#ffffff"
    -	    \ }
    +  whatterm = "cterm"
    +  if &t_Co == '8'
    +    cterm_color = {
    +      0: "#808080", 1: "#ff6060", 2: "#00ff00", 3: "#ffff00",
    +      4: "#8080ff", 5: "#ff40ff", 6: "#00ffff", 7: "#ffffff"
    +    }
       else
    -    let s:cterm_color = {
    -	    \   0: "#000000", 1: "#c00000", 2: "#008000", 3: "#804000", 
    -	    \   4: "#0000c0", 5: "#c000c0", 6: "#008080", 7: "#c0c0c0", 
    -	    \   8: "#808080", 9: "#ff6060", 10: "#00ff00", 11: "#ffff00",
    -	    \   12: "#8080ff", 13: "#ff40ff", 14: "#00ffff", 15: "#ffffff"
    -	    \ }
    -
    -    " Colors for 88 and 256 come from xterm.
    -    if &t_Co == 88
    -      call extend(s:cterm_color, {
    -	    \   16: "#000000", 17: "#00008b", 18: "#0000cd", 19: "#0000ff",
    -	    \   20: "#008b00", 21: "#008b8b", 22: "#008bcd", 23: "#008bff",
    -	    \   24: "#00cd00", 25: "#00cd8b", 26: "#00cdcd", 27: "#00cdff",
    -	    \   28: "#00ff00", 29: "#00ff8b", 30: "#00ffcd", 31: "#00ffff",
    -	    \   32: "#8b0000", 33: "#8b008b", 34: "#8b00cd", 35: "#8b00ff",
    -	    \   36: "#8b8b00", 37: "#8b8b8b", 38: "#8b8bcd", 39: "#8b8bff",
    -	    \   40: "#8bcd00", 41: "#8bcd8b", 42: "#8bcdcd", 43: "#8bcdff",
    -	    \   44: "#8bff00", 45: "#8bff8b", 46: "#8bffcd", 47: "#8bffff",
    -	    \   48: "#cd0000", 49: "#cd008b", 50: "#cd00cd", 51: "#cd00ff",
    -	    \   52: "#cd8b00", 53: "#cd8b8b", 54: "#cd8bcd", 55: "#cd8bff",
    -	    \   56: "#cdcd00", 57: "#cdcd8b", 58: "#cdcdcd", 59: "#cdcdff",
    -	    \   60: "#cdff00", 61: "#cdff8b", 62: "#cdffcd", 63: "#cdffff",
    -	    \   64: "#ff0000"
    -	    \ })
    -      call extend(s:cterm_color, {
    -	    \   65: "#ff008b", 66: "#ff00cd", 67: "#ff00ff", 68: "#ff8b00",
    -	    \   69: "#ff8b8b", 70: "#ff8bcd", 71: "#ff8bff", 72: "#ffcd00",
    -	    \   73: "#ffcd8b", 74: "#ffcdcd", 75: "#ffcdff", 76: "#ffff00",
    -	    \   77: "#ffff8b", 78: "#ffffcd", 79: "#ffffff", 80: "#2e2e2e",
    -	    \   81: "#5c5c5c", 82: "#737373", 83: "#8b8b8b", 84: "#a2a2a2",
    -	    \   85: "#b9b9b9", 86: "#d0d0d0", 87: "#e7e7e7"
    -	    \ })
    -    elseif &t_Co == 256
    -      call extend(s:cterm_color, {
    -	    \   16: "#000000", 17: "#00005f", 18: "#000087", 19: "#0000af",
    -	    \   20: "#0000d7", 21: "#0000ff", 22: "#005f00", 23: "#005f5f",
    -	    \   24: "#005f87", 25: "#005faf", 26: "#005fd7", 27: "#005fff",
    -	    \   28: "#008700", 29: "#00875f", 30: "#008787", 31: "#0087af",
    -	    \   32: "#0087d7", 33: "#0087ff", 34: "#00af00", 35: "#00af5f",
    -	    \   36: "#00af87", 37: "#00afaf", 38: "#00afd7", 39: "#00afff",
    -	    \   40: "#00d700", 41: "#00d75f", 42: "#00d787", 43: "#00d7af",
    -	    \   44: "#00d7d7", 45: "#00d7ff", 46: "#00ff00", 47: "#00ff5f",
    -	    \   48: "#00ff87", 49: "#00ffaf", 50: "#00ffd7", 51: "#00ffff",
    -	    \   52: "#5f0000", 53: "#5f005f", 54: "#5f0087", 55: "#5f00af",
    -	    \   56: "#5f00d7", 57: "#5f00ff", 58: "#5f5f00", 59: "#5f5f5f",
    -	    \   60: "#5f5f87", 61: "#5f5faf", 62: "#5f5fd7", 63: "#5f5fff",
    -	    \   64: "#5f8700"
    -	    \ })
    -      call extend(s:cterm_color, {
    -	    \   65: "#5f875f", 66: "#5f8787", 67: "#5f87af", 68: "#5f87d7",
    -	    \   69: "#5f87ff", 70: "#5faf00", 71: "#5faf5f", 72: "#5faf87",
    -	    \   73: "#5fafaf", 74: "#5fafd7", 75: "#5fafff", 76: "#5fd700",
    -	    \   77: "#5fd75f", 78: "#5fd787", 79: "#5fd7af", 80: "#5fd7d7",
    -	    \   81: "#5fd7ff", 82: "#5fff00", 83: "#5fff5f", 84: "#5fff87",
    -	    \   85: "#5fffaf", 86: "#5fffd7", 87: "#5fffff", 88: "#870000",
    -	    \   89: "#87005f", 90: "#870087", 91: "#8700af", 92: "#8700d7",
    -	    \   93: "#8700ff", 94: "#875f00", 95: "#875f5f", 96: "#875f87",
    -	    \   97: "#875faf", 98: "#875fd7", 99: "#875fff", 100: "#878700",
    -	    \   101: "#87875f", 102: "#878787", 103: "#8787af", 104: "#8787d7",
    -	    \   105: "#8787ff", 106: "#87af00", 107: "#87af5f", 108: "#87af87",
    -	    \   109: "#87afaf", 110: "#87afd7", 111: "#87afff", 112: "#87d700"
    -	    \ })
    -      call extend(s:cterm_color, {
    -	    \   113: "#87d75f", 114: "#87d787", 115: "#87d7af", 116: "#87d7d7",
    -	    \   117: "#87d7ff", 118: "#87ff00", 119: "#87ff5f", 120: "#87ff87",
    -	    \   121: "#87ffaf", 122: "#87ffd7", 123: "#87ffff", 124: "#af0000",
    -	    \   125: "#af005f", 126: "#af0087", 127: "#af00af", 128: "#af00d7",
    -	    \   129: "#af00ff", 130: "#af5f00", 131: "#af5f5f", 132: "#af5f87",
    -	    \   133: "#af5faf", 134: "#af5fd7", 135: "#af5fff", 136: "#af8700",
    -	    \   137: "#af875f", 138: "#af8787", 139: "#af87af", 140: "#af87d7",
    -	    \   141: "#af87ff", 142: "#afaf00", 143: "#afaf5f", 144: "#afaf87",
    -	    \   145: "#afafaf", 146: "#afafd7", 147: "#afafff", 148: "#afd700",
    -	    \   149: "#afd75f", 150: "#afd787", 151: "#afd7af", 152: "#afd7d7",
    -	    \   153: "#afd7ff", 154: "#afff00", 155: "#afff5f", 156: "#afff87",
    -	    \   157: "#afffaf", 158: "#afffd7"
    -	    \ })
    -      call extend(s:cterm_color, {
    -	    \   159: "#afffff", 160: "#d70000", 161: "#d7005f", 162: "#d70087",
    -	    \   163: "#d700af", 164: "#d700d7", 165: "#d700ff", 166: "#d75f00",
    -	    \   167: "#d75f5f", 168: "#d75f87", 169: "#d75faf", 170: "#d75fd7",
    -	    \   171: "#d75fff", 172: "#d78700", 173: "#d7875f", 174: "#d78787",
    -	    \   175: "#d787af", 176: "#d787d7", 177: "#d787ff", 178: "#d7af00",
    -	    \   179: "#d7af5f", 180: "#d7af87", 181: "#d7afaf", 182: "#d7afd7",
    -	    \   183: "#d7afff", 184: "#d7d700", 185: "#d7d75f", 186: "#d7d787",
    -	    \   187: "#d7d7af", 188: "#d7d7d7", 189: "#d7d7ff", 190: "#d7ff00",
    -	    \   191: "#d7ff5f", 192: "#d7ff87", 193: "#d7ffaf", 194: "#d7ffd7",
    -	    \   195: "#d7ffff", 196: "#ff0000", 197: "#ff005f", 198: "#ff0087",
    -	    \   199: "#ff00af", 200: "#ff00d7", 201: "#ff00ff", 202: "#ff5f00",
    -	    \   203: "#ff5f5f", 204: "#ff5f87"
    -	    \ })
    -      call extend(s:cterm_color, {
    -	    \   205: "#ff5faf", 206: "#ff5fd7", 207: "#ff5fff", 208: "#ff8700",
    -	    \   209: "#ff875f", 210: "#ff8787", 211: "#ff87af", 212: "#ff87d7",
    -	    \   213: "#ff87ff", 214: "#ffaf00", 215: "#ffaf5f", 216: "#ffaf87",
    -	    \   217: "#ffafaf", 218: "#ffafd7", 219: "#ffafff", 220: "#ffd700",
    -	    \   221: "#ffd75f", 222: "#ffd787", 223: "#ffd7af", 224: "#ffd7d7",
    -	    \   225: "#ffd7ff", 226: "#ffff00", 227: "#ffff5f", 228: "#ffff87",
    -	    \   229: "#ffffaf", 230: "#ffffd7", 231: "#ffffff", 232: "#080808",
    -	    \   233: "#121212", 234: "#1c1c1c", 235: "#262626", 236: "#303030",
    -	    \   237: "#3a3a3a", 238: "#444444", 239: "#4e4e4e", 240: "#585858",
    -	    \   241: "#626262", 242: "#6c6c6c", 243: "#767676", 244: "#808080",
    -	    \   245: "#8a8a8a", 246: "#949494", 247: "#9e9e9e", 248: "#a8a8a8",
    -	    \   249: "#b2b2b2", 250: "#bcbcbc", 251: "#c6c6c6", 252: "#d0d0d0",
    -	    \   253: "#dadada", 254: "#e4e4e4", 255: "#eeeeee"
    -	    \ })
    +    cterm_color = {
    +      0: "#000000", 1: "#c00000", 2: "#008000", 3: "#804000",
    +      4: "#0000c0", 5: "#c000c0", 6: "#008080", 7: "#c0c0c0",
    +      8: "#808080", 9: "#ff6060", 10: "#00ff00", 11: "#ffff00",
    +      12: "#8080ff", 13: "#ff40ff", 14: "#00ffff", 15: "#ffffff"
    +    }
    +
    +    # Colors for 88 and 256 come from xterm.
    +    if &t_Co == '88'
    +      extend(cterm_color, {
    +	16: "#000000", 17: "#00008b", 18: "#0000cd", 19: "#0000ff",
    +	20: "#008b00", 21: "#008b8b", 22: "#008bcd", 23: "#008bff",
    +	24: "#00cd00", 25: "#00cd8b", 26: "#00cdcd", 27: "#00cdff",
    +	28: "#00ff00", 29: "#00ff8b", 30: "#00ffcd", 31: "#00ffff",
    +	32: "#8b0000", 33: "#8b008b", 34: "#8b00cd", 35: "#8b00ff",
    +	36: "#8b8b00", 37: "#8b8b8b", 38: "#8b8bcd", 39: "#8b8bff",
    +	40: "#8bcd00", 41: "#8bcd8b", 42: "#8bcdcd", 43: "#8bcdff",
    +	44: "#8bff00", 45: "#8bff8b", 46: "#8bffcd", 47: "#8bffff",
    +	48: "#cd0000", 49: "#cd008b", 50: "#cd00cd", 51: "#cd00ff",
    +	52: "#cd8b00", 53: "#cd8b8b", 54: "#cd8bcd", 55: "#cd8bff",
    +	56: "#cdcd00", 57: "#cdcd8b", 58: "#cdcdcd", 59: "#cdcdff",
    +	60: "#cdff00", 61: "#cdff8b", 62: "#cdffcd", 63: "#cdffff",
    +	64: "#ff0000"
    +      })
    +      extend(cterm_color, {
    +	65: "#ff008b", 66: "#ff00cd", 67: "#ff00ff", 68: "#ff8b00",
    +	69: "#ff8b8b", 70: "#ff8bcd", 71: "#ff8bff", 72: "#ffcd00",
    +	73: "#ffcd8b", 74: "#ffcdcd", 75: "#ffcdff", 76: "#ffff00",
    +	77: "#ffff8b", 78: "#ffffcd", 79: "#ffffff", 80: "#2e2e2e",
    +	81: "#5c5c5c", 82: "#737373", 83: "#8b8b8b", 84: "#a2a2a2",
    +	85: "#b9b9b9", 86: "#d0d0d0", 87: "#e7e7e7"
    +      })
    +    elseif &t_Co == '256'
    +      extend(cterm_color, {
    +	16: "#000000", 17: "#00005f", 18: "#000087", 19: "#0000af",
    +	20: "#0000d7", 21: "#0000ff", 22: "#005f00", 23: "#005f5f",
    +	24: "#005f87", 25: "#005faf", 26: "#005fd7", 27: "#005fff",
    +	28: "#008700", 29: "#00875f", 30: "#008787", 31: "#0087af",
    +	32: "#0087d7", 33: "#0087ff", 34: "#00af00", 35: "#00af5f",
    +	36: "#00af87", 37: "#00afaf", 38: "#00afd7", 39: "#00afff",
    +	40: "#00d700", 41: "#00d75f", 42: "#00d787", 43: "#00d7af",
    +	44: "#00d7d7", 45: "#00d7ff", 46: "#00ff00", 47: "#00ff5f",
    +	48: "#00ff87", 49: "#00ffaf", 50: "#00ffd7", 51: "#00ffff",
    +	52: "#5f0000", 53: "#5f005f", 54: "#5f0087", 55: "#5f00af",
    +	56: "#5f00d7", 57: "#5f00ff", 58: "#5f5f00", 59: "#5f5f5f",
    +	60: "#5f5f87", 61: "#5f5faf", 62: "#5f5fd7", 63: "#5f5fff",
    +	64: "#5f8700"
    +      })
    +      extend(cterm_color, {
    +	65: "#5f875f", 66: "#5f8787", 67: "#5f87af", 68: "#5f87d7",
    +	69: "#5f87ff", 70: "#5faf00", 71: "#5faf5f", 72: "#5faf87",
    +	73: "#5fafaf", 74: "#5fafd7", 75: "#5fafff", 76: "#5fd700",
    +	77: "#5fd75f", 78: "#5fd787", 79: "#5fd7af", 80: "#5fd7d7",
    +	81: "#5fd7ff", 82: "#5fff00", 83: "#5fff5f", 84: "#5fff87",
    +	85: "#5fffaf", 86: "#5fffd7", 87: "#5fffff", 88: "#870000",
    +	89: "#87005f", 90: "#870087", 91: "#8700af", 92: "#8700d7",
    +	93: "#8700ff", 94: "#875f00", 95: "#875f5f", 96: "#875f87",
    +	97: "#875faf", 98: "#875fd7", 99: "#875fff", 100: "#878700",
    +	101: "#87875f", 102: "#878787", 103: "#8787af", 104: "#8787d7",
    +	105: "#8787ff", 106: "#87af00", 107: "#87af5f", 108: "#87af87",
    +	109: "#87afaf", 110: "#87afd7", 111: "#87afff", 112: "#87d700"
    +      })
    +      extend(cterm_color, {
    +	113: "#87d75f", 114: "#87d787", 115: "#87d7af", 116: "#87d7d7",
    +	117: "#87d7ff", 118: "#87ff00", 119: "#87ff5f", 120: "#87ff87",
    +	121: "#87ffaf", 122: "#87ffd7", 123: "#87ffff", 124: "#af0000",
    +	125: "#af005f", 126: "#af0087", 127: "#af00af", 128: "#af00d7",
    +	129: "#af00ff", 130: "#af5f00", 131: "#af5f5f", 132: "#af5f87",
    +	133: "#af5faf", 134: "#af5fd7", 135: "#af5fff", 136: "#af8700",
    +	137: "#af875f", 138: "#af8787", 139: "#af87af", 140: "#af87d7",
    +	141: "#af87ff", 142: "#afaf00", 143: "#afaf5f", 144: "#afaf87",
    +	145: "#afafaf", 146: "#afafd7", 147: "#afafff", 148: "#afd700",
    +	149: "#afd75f", 150: "#afd787", 151: "#afd7af", 152: "#afd7d7",
    +	153: "#afd7ff", 154: "#afff00", 155: "#afff5f", 156: "#afff87",
    +	157: "#afffaf", 158: "#afffd7"
    +      })
    +      extend(cterm_color, {
    +	159: "#afffff", 160: "#d70000", 161: "#d7005f", 162: "#d70087",
    +	163: "#d700af", 164: "#d700d7", 165: "#d700ff", 166: "#d75f00",
    +	167: "#d75f5f", 168: "#d75f87", 169: "#d75faf", 170: "#d75fd7",
    +	171: "#d75fff", 172: "#d78700", 173: "#d7875f", 174: "#d78787",
    +	175: "#d787af", 176: "#d787d7", 177: "#d787ff", 178: "#d7af00",
    +	179: "#d7af5f", 180: "#d7af87", 181: "#d7afaf", 182: "#d7afd7",
    +	183: "#d7afff", 184: "#d7d700", 185: "#d7d75f", 186: "#d7d787",
    +	187: "#d7d7af", 188: "#d7d7d7", 189: "#d7d7ff", 190: "#d7ff00",
    +	191: "#d7ff5f", 192: "#d7ff87", 193: "#d7ffaf", 194: "#d7ffd7",
    +	195: "#d7ffff", 196: "#ff0000", 197: "#ff005f", 198: "#ff0087",
    +	199: "#ff00af", 200: "#ff00d7", 201: "#ff00ff", 202: "#ff5f00",
    +	203: "#ff5f5f", 204: "#ff5f87"
    +      })
    +      extend(cterm_color, {
    +	205: "#ff5faf", 206: "#ff5fd7", 207: "#ff5fff", 208: "#ff8700",
    +	209: "#ff875f", 210: "#ff8787", 211: "#ff87af", 212: "#ff87d7",
    +	213: "#ff87ff", 214: "#ffaf00", 215: "#ffaf5f", 216: "#ffaf87",
    +	217: "#ffafaf", 218: "#ffafd7", 219: "#ffafff", 220: "#ffd700",
    +	221: "#ffd75f", 222: "#ffd787", 223: "#ffd7af", 224: "#ffd7d7",
    +	225: "#ffd7ff", 226: "#ffff00", 227: "#ffff5f", 228: "#ffff87",
    +	229: "#ffffaf", 230: "#ffffd7", 231: "#ffffff", 232: "#080808",
    +	233: "#121212", 234: "#1c1c1c", 235: "#262626", 236: "#303030",
    +	237: "#3a3a3a", 238: "#444444", 239: "#4e4e4e", 240: "#585858",
    +	241: "#626262", 242: "#6c6c6c", 243: "#767676", 244: "#808080",
    +	245: "#8a8a8a", 246: "#949494", 247: "#9e9e9e", 248: "#a8a8a8",
    +	249: "#b2b2b2", 250: "#bcbcbc", 251: "#c6c6c6", 252: "#d0d0d0",
    +	253: "#dadada", 254: "#e4e4e4", 255: "#eeeeee"
    +      })
         endif
       endif
     endif
     
    -" Return good color specification: in GUI no transformation is done, in
    -" terminal return RGB values of known colors and empty string for unknown
    -if s:whatterm == "gui"
    -  function! s:HtmlColor(color)
    -    return a:color
    -  endfun
    +# Return good color specification: in GUI no transformation is done, in
    +# terminal return RGB values of known colors and empty string for unknown
    +if whatterm == "gui"
    +  def HtmlColor(color: string): string
    +    return color
    +  enddef
     else
    -  function! s:HtmlColor(color)
    -    if has_key(s:cterm_color, a:color)
    -      return s:cterm_color[a:color]
    +  def HtmlColor(color: string): string
    +    if has_key(cterm_color, color)
    +      return cterm_color[color]
         else
           return ""
         endif
    -  endfun
    +  enddef
     endif
     
    -" Find out the background and foreground color for use later
    -let s:fgc = s:HtmlColor(synIDattr(hlID("Normal")->synIDtrans(), "fg#", s:whatterm))
    -let s:bgc = s:HtmlColor(synIDattr(hlID("Normal")->synIDtrans(), "bg#", s:whatterm))
    -if s:fgc == ""
    -  let s:fgc = ( &background == "dark" ? "#ffffff" : "#000000" )
    +# Find out the background and foreground color for use later
    +var fgc = HtmlColor(synIDattr(synIDtrans(hlID("Normal")), "fg#", whatterm))
    +var bgc = HtmlColor(synIDattr(synIDtrans(hlID("Normal")), "bg#", whatterm))
    +if fgc == ""
    +  fgc = (&background == "dark" ? "#ffffff" : "#000000")
     endif
    -if s:bgc == ""
    -  let s:bgc = ( &background == "dark" ? "#000000" : "#ffffff" )
    +if bgc == ""
    +  bgc = (&background == "dark" ? "#000000" : "#ffffff")
     endif
     
    -if !s:settings.use_css
    -  " Return opening HTML tag for given highlight id
    -  function! s:HtmlOpening(id, extra_attrs)
    -    let a = ""
    -    let translated_ID = synIDtrans(a:id)
    -    if synIDattr(translated_ID, "inverse")
    -      " For inverse, we always must set both colors (and exchange them)
    -      let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm))
    -      let a = a .. ''
    -      let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm))
    -      let a = a .. ''
    +if !settings.use_css
    +  # Return opening HTML tag for given highlight id
    +  def HtmlOpening(id: number, extra_attrs: string): string
    +    var a = ""
    +    var translated_ID = synIDtrans(id)
    +    if synIDattr(translated_ID, "inverse") == '1'
    +      # For inverse, we always must set both colors (and exchange them)
    +      var x = HtmlColor(synIDattr(translated_ID, "fg#", whatterm))
    +      a ..= ''
    +      x = HtmlColor(synIDattr(translated_ID, "bg#", whatterm))
    +      a ..= ''
         else
    -      let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm))
    +      var x = HtmlColor(synIDattr(translated_ID, "bg#", whatterm))
           if x != ""
    -	let a = a .. ''
    -      elseif !empty(a:extra_attrs)
    -	let a = a .. ''
    +	a ..= ''
    +      elseif !empty(extra_attrs)
    +	a ..= ''
           endif
    -      let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm))
    -      if x != "" | let a = a .. '' | endif
    +      x = HtmlColor(synIDattr(translated_ID, "fg#", whatterm))
    +      if x != "" | a ..= '' | endif
         endif
    -    if synIDattr(translated_ID, "bold") | let a = a .. "" | endif
    -    if synIDattr(translated_ID, "italic") | let a = a .. "" | endif
    -    if synIDattr(translated_ID, "underline") | let a = a .. "" | endif
    +    if synIDattr(translated_ID, "bold") == '1' | a ..= "" | endif
    +    if synIDattr(translated_ID, "italic") == '1' | a ..= "" | endif
    +    if synIDattr(translated_ID, "underline") == '1' | a ..= "" | endif
         return a
    -  endfun
    -
    -  " Return closing HTML tag for given highlight id
    -  function! s:HtmlClosing(id, has_extra_attrs)
    -    let a = ""
    -    let translated_ID = synIDtrans(a:id)
    -    if synIDattr(translated_ID, "underline") | let a = a .. "" | endif
    -    if synIDattr(translated_ID, "italic") | let a = a .. "" | endif
    -    if synIDattr(translated_ID, "bold") | let a = a .. "" | endif
    -    if synIDattr(translated_ID, "inverse")
    -      let a = a .. ''
    +  enddef
    +
    +  # Return closing HTML tag for given highlight id
    +  def HtmlClosing(id: number, has_extra_attrs: bool): string
    +    var a = ""
    +    var translated_ID = synIDtrans(id)
    +    if synIDattr(translated_ID, "underline") == '1' | a ..= "" | endif
    +    if synIDattr(translated_ID, "italic") == '1' | a ..= "" | endif
    +    if synIDattr(translated_ID, "bold") == '1' | a ..= "" | endif
    +    if synIDattr(translated_ID, "inverse") == '1'
    +      a ..= ''
         else
    -      let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm))
    -      if x != "" | let a = a .. '' | endif
    -      let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm))
    -      if x != "" || a:has_extra_attrs | let a = a .. '' | endif
    +      var x = HtmlColor(synIDattr(translated_ID, "fg#", whatterm))
    +      if x != "" | a ..= '' | endif
    +      x = HtmlColor(synIDattr(translated_ID, "bg#", whatterm))
    +      if x != "" || has_extra_attrs | a ..= '' | endif
         endif
         return a
    -  endfun
    +  enddef
     endif
     
    -" Use a different function for formatting based on user options. This way we
    -" can avoid a lot of logic during the actual execution.
    -"
    -" Build the function line by line containing only what is needed for the options
    -" in use for maximum code sharing with minimal branch logic for greater speed.
    -"
    -" Note, 'exec' commands do not recognize line continuations, so must concatenate
    -" lines rather than continue them.
    -if s:settings.use_css
    -  " save CSS to a list of rules to add to the output at the end of processing
    -
    -  " first, get the style names we need
    -  let s:wrapperfunc_lines = []
    -  call add(s:wrapperfunc_lines, [])
    -  let s:wrapperfunc_lines[-1] =<< trim ENDLET
    -	function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, make_unselectable, unformatted)
    -	
    -	  let l:style_name = synIDattr(a:style_id, "name", s:whatterm)
    +# Use a different function for formatting based on user options. This way we
    +# can avoid a lot of logic during the actual execution.
    +#
    +# Build the function line by line containing only what is needed for the options
    +# in use for maximum code sharing with minimal branch logic for greater speed.
    +if settings.use_css
    +  # save CSS to a list of rules to add to the output at the end of processing
    +
    +  # Note, 'exec' commands do not recognize line continuations, so must concatenate
    +  # lines rather than continue them.
    +  var wrapperfunc_lines: list
    +  # first, get the style names we need
    +  wrapperfunc_lines =<< trim eval ENDLET
    +    def BuildStyleWrapper(style_id: number, diff_style_id: number, extra_attrs: string, text: string, make_unselectable: bool, unformatted: string): string
    +      var style_name = synIDattr(style_id, "name", whatterm)
    +      var saved_style: string
       ENDLET
       if &diff
    -    call add(s:wrapperfunc_lines, [])
    -    let s:wrapperfunc_lines[-1] =<< trim ENDLET
    -	  let l:diff_style_name = synIDattr(a:diff_style_id, "name", s:whatterm)
    +    trim_tmp =<< trim eval ENDLET
    +      var diff_style_name = synIDattr(diff_style_id, "name", whatterm)
         ENDLET
    +    wrapperfunc_lines += trim_tmp
     
    -    " Add normal groups and diff groups to separate lists so we can order them to
    -    " allow diff highlight to override normal highlight
    -
    -    " if primary style IS a diff style, grab it from the diff cache instead
    -    " (always succeeds because we pre-populate it)
    -    call add(s:wrapperfunc_lines, [])
    -    let s:wrapperfunc_lines[-1] =<< trim ENDLET
    +    # Add normal groups and diff groups to separate lists so we can order them to
    +    # allow diff highlight to override normal highlight
     
    -	  if a:style_id == s:DIFF_D_ID || a:style_id == s:DIFF_A_ID || a:style_id == s:DIFF_C_ID || a:style_id == s:DIFF_T_ID
    -	    let l:saved_style = get(s:diffstylelist,a:style_id)
    -	  else
    +    # if primary style IS a diff style, grab it from the diff cache instead
    +    # (always succeeds because we pre-populate it)
    +    trim_tmp =<< trim eval ENDLET
    +      if style_id == DIFF_D_ID || style_id == DIFF_A_ID || style_id == DIFF_C_ID || style_id == DIFF_T_ID
    +	saved_style = get(diffstylelist, style_id)
    +      else
         ENDLET
    +    wrapperfunc_lines += trim_tmp
       endif
     
    -  " get primary style info from cache or build it on the fly if not found
    -  call add(s:wrapperfunc_lines, [])
    -  let s:wrapperfunc_lines[-1] =<< trim ENDLET
    -	    let l:saved_style = get(s:stylelist,a:style_id)
    -	    if type(l:saved_style) == type(0)
    -	      unlet l:saved_style
    -	      let l:saved_style = s:CSS1(a:style_id)
    -	      if l:saved_style != ""
    -	        let l:saved_style = "." .. l:style_name .. " { " .. l:saved_style .. "}"
    -	      endif
    -	      let s:stylelist[a:style_id] = l:saved_style
    -	    endif
    +  # get primary style info from cache or build it on the fly if not found
    +  trim_tmp =<< trim ENDLET
    +    if has_key(stylelist, style_id)
    +	saved_style = stylelist[style_id]
    +    else
    +	saved_style = CSS1(style_id)
    +	if !empty(saved_style)
    +	    saved_style = "." .. style_name .. " { " .. saved_style .. "}"
    +	endif
    +	stylelist[style_id] = saved_style
    +    endif
       ENDLET
    +  wrapperfunc_lines += trim_tmp
       if &diff
    -    call add(s:wrapperfunc_lines, [])
    -    let s:wrapperfunc_lines[-1] =<< trim ENDLET
    -	  endif
    +    trim_tmp =<< trim eval ENDLET
    +      endif
         ENDLET
    +    wrapperfunc_lines += trim_tmp
       endif
    -" Ignore this comment, just bypassing a highlighting issue: if
    -
    -  " Build the wrapper tags around the text. It turns out that caching these
    -  " gives pretty much zero performance gain and adds a lot of logic.
    +  # Ignore this comment, just bypassing a highlighting issue: if
     
    -  call add(s:wrapperfunc_lines, [])
    -  let s:wrapperfunc_lines[-1] =<< trim ENDLET
    +  # Build the wrapper tags around the text. It turns out that caching these
    +  # gives pretty much zero performance gain and adds a lot of logic.
     
    -	  if l:saved_style == "" && empty(a:extra_attrs)
    +  trim_tmp =<< trim eval ENDLET
    +      if saved_style == "" && empty(extra_attrs)
       ENDLET
    +  wrapperfunc_lines += trim_tmp
       if &diff
    -    call add(s:wrapperfunc_lines, [])
    -    let s:wrapperfunc_lines[-1] =<< trim ENDLET
    -	    if a:diff_style_id <= 0
    +    trim_tmp =<< trim eval ENDLET
    +	if diff_style_id <= 0
         ENDLET
    +    wrapperfunc_lines += trim_tmp
       endif
    -  " no surroundings if neither primary nor diff style has any info
    -  call add(s:wrapperfunc_lines, [])
    -  let s:wrapperfunc_lines[-1] =<< trim ENDLET
    -	      return a:text
    +  # no surroundings if neither primary nor diff style has any info
    +  trim_tmp =<< trim eval ENDLET
    +	  return text
       ENDLET
    +  wrapperfunc_lines += trim_tmp
       if &diff
    -    " no primary style, but diff style
    -    call add(s:wrapperfunc_lines, [])
    -    let s:wrapperfunc_lines[-1] =<< trim ENDLET
    -	    else
    -	      return ''..a:text..""
    -	    endif
    +    # no primary style, but diff style
    +    trim_tmp =<< trim ENDLET
    +	else
    +	  return $'{text}'
    +	endif
         ENDLET
    +    wrapperfunc_lines += trim_tmp
       endif
    -  " Ignore this comment, just bypassing a highlighting issue: if
    +  # Ignore this comment, just bypassing a highlighting issue: if
     
    -  " open tag for non-empty primary style
    -  call add(s:wrapperfunc_lines, [])
    -  let s:wrapperfunc_lines[-1] =<< trim ENDLET
    -	  else
    +  # open tag for non-empty primary style
    +  trim_tmp =<< trim eval ENDLET
    +      else
       ENDLET
    -  " non-empty primary style. handle either empty or non-empty diff style.
    -  "
    -  " separate the two classes by a space to apply them both if there is a diff
    -  " style name, unless the primary style is empty, then just use the diff style
    -  " name
    -  let s:diffstyle =
    -	  \ (&diff ? '(a:diff_style_id <= 0 ? "" : " " .. l:diff_style_name)..'
    -	  \        : '')
    -  if s:settings.prevent_copy == ""
    -    call add(s:wrapperfunc_lines, [])
    -    let s:wrapperfunc_lines[-1] =<< trim eval ENDLET
    -	    return "'..a:text..""
    +  wrapperfunc_lines += trim_tmp
    +  # non-empty primary style. handle either empty or non-empty diff style.
    +  #
    +  # separate the two classes by a space to apply them both if there is a diff
    +  # style name, unless the primary style is empty, then just use the diff style
    +  # name
    +  var diffstyle: string
    +  if &diff
    +    diffstyle = '(diff_style_id <= 0 ? "" : " " .. diff_style_name) .. '
    +  else
    +    diffstyle = ''
    +  endif
    +  if settings.prevent_copy == ""
    +    trim_tmp =<< trim eval ENDLET
    +	return "' .. text .. ""
         ENDLET
    +    wrapperfunc_lines += trim_tmp
       else
    -
    -    " New method: use generated content in the CSS. The only thing needed here
    -    " is a span with no content, with an attribute holding the desired text.
    -    "
    -    " Old method: use an  element when text is unsectable. This is still
    -    " used in conditional comments for Internet Explorer, where the new method
    -    " doesn't work.
    -    "
    -    " Wrap the  in a  to allow fixing the stupid bug in some fonts
    -    " which cause browsers to display a 1px gap between lines when these
    -    " s have a background color (maybe not really a bug, this isn't
    -    " well-defined)
    -    "
    -    " use strwidth, because we care only about how many character boxes are
    -    " needed to size the input, we don't care how many characters (including
    -    " separately counted composing chars, from strchars()) or bytes (from
    -    " len())the string contains. strdisplaywidth() is not needed because none of
    -    " the unselectable groups can contain tab characters (fold column, fold
    -    " text, line number).
    -    "
    -    " Note, if maxlength property needs to be added in the future, it will need
    -    " to use strchars(), because HTML specifies that the maxlength parameter
    -    " uses the number of unique codepoints for its limit.
    -    call add(s:wrapperfunc_lines, [])
    -    let s:wrapperfunc_lines[-1] =<< trim eval ENDLET
    -	    if a:make_unselectable
    -	      let return_span = " element when text is unsectable. This is still
    +    # used in conditional comments for Internet Explorer, where the new method
    +    # doesn't work.
    +    #
    +    # Wrap the  in a  to allow fixing the stupid bug in some fonts
    +    # which cause browsers to display a 1px gap between lines when these
    +    # s have a background color (maybe not really a bug, this isn't
    +    # well-defined)
    +    #
    +    # use strwidth, because we care only about how many character boxes are
    +    # needed to size the input, we don't care how many characters (including
    +    # separately counted composing chars, from strchars()) or bytes (from
    +    # len())the string contains. strdisplaywidth() is not needed because none of
    +    # the unselectable groups can contain tab characters (fold column, fold
    +    # text, line number).
    +    #
    +    # Note, if maxlength property needs to be added in the future, it will need
    +    # to use strchars(), because HTML specifies that the maxlength parameter
    +    # uses the number of unique codepoints for its limit.
    +    trim_tmp =<< trim eval ENDLET
    +	if make_unselectable
    +	  var return_span = "' : '>')
    +    wrapperfunc_lines += trim_tmp
    +    if settings.use_input_for_pc !=# 'none'
    +      trim_tmp =<< trim eval ENDLET
    +	  return_span ..= '' : '>')
           ENDLET
    +      wrapperfunc_lines += trim_tmp
         endif
    -    call add(s:wrapperfunc_lines, [])
    -    let s:wrapperfunc_lines[-1] =<< trim eval ENDLET
    -	      return return_span..''
    -	    else
    -	      return "'..a:text..""
    -	    endif
    +    trim_tmp =<< trim eval ENDLET
    +	  return return_span .. ''
    +	else
    +	  return "' .. text .. ""
    +	endif
         ENDLET
    +    wrapperfunc_lines += trim_tmp
       endif
    -  call add(s:wrapperfunc_lines, [])
    -  let s:wrapperfunc_lines[-1] =<< trim ENDLET
    -	  endif
    -	endfun
    +  trim_tmp =<< trim eval ENDLET
    +      endif
    +    enddef
       ENDLET
    +  wrapperfunc_lines += trim_tmp
    +
    +  # create the function we built line by line above
    +  execute join(wrapperfunc_lines, "\n")
     else
    -  " Non-CSS method just needs the wrapper.
    -  "
    -  " Functions used to get opening/closing automatically return null strings if
    -  " no styles exist.
    +  # Non-CSS method just needs the wrapper.
    +  #
    +  # Functions used to get opening/closing automatically return null strings if
    +  # no styles exist.
       if &diff
    -    let s:wrapperfunc_lines =<< trim ENDLET
    -	function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)
    -	  if a:diff_style_id <= 0
    -	    let l:diff_opening = s:HtmlOpening(a:diff_style_id, "")
    -	    let l:diff_closing = s:HtmlClosing(a:diff_style_id, 0)
    -	  else
    -	    let l:diff_opening = ""
    -	    let l:diff_closing = ""
    -	  endif
    -	  return s:HtmlOpening(a:style_id, a:extra_attrs)..l:diff_opening..a:text..l:diff_closing..s:HtmlClosing(a:style_id, !empty(a:extra_attrs))
    -	endfun
    -    ENDLET
    +    def BuildStyleWrapper(style_id: number, diff_style_id: number, extra_attrs: string, text: string, unusedarg: bool, unusedarg2: string): string
    +      var diff_opening: string
    +      var diff_closing: string
    +      if diff_style_id > 0
    +	diff_opening = HtmlOpening(diff_style_id, "")
    +	diff_closing = HtmlClosing(diff_style_id, false)
    +      endif
    +      return HtmlOpening(style_id, extra_attrs) .. diff_opening .. text .. diff_closing .. HtmlClosing(style_id, !empty(extra_attrs))
    +    enddef
       else
    -    let s:wrapperfunc_lines =<< trim ENDLET
    -	function! s:BuildStyleWrapper(style_id, diff_style_id, extra_attrs, text, unusedarg, unusedarg2)
    -	  return s:HtmlOpening(a:style_id, a:extra_attrs)..a:text..s:HtmlClosing(a:style_id, !empty(a:extra_attrs))
    -	endfun
    -    ENDLET
    +    def BuildStyleWrapper(style_id: number, diff_style_id: number, extra_attrs: string, text: string, unusedarg: bool, unusedarg2: string): string
    +      return HtmlOpening(style_id, extra_attrs) .. text .. HtmlClosing(style_id, !empty(extra_attrs))
    +    enddef
       endif
     endif
     
    -" create the function we built line by line above
    -exec join(flatten(s:wrapperfunc_lines), "\n")
    -
    -let s:diff_mode = &diff
    -
    -" Return HTML valid characters enclosed in a span of class style_name with
    -" unprintable characters expanded and double spaces replaced as necessary.
    -"
    -" TODO: eliminate unneeded logic like done for BuildStyleWrapper
    -function! s:HtmlFormat(text, style_id, diff_style_id, extra_attrs, make_unselectable)
    -  " Replace unprintable characters
    -  let unformatted = strtrans(a:text)
    -
    -  let formatted = unformatted
    -
    -  " Replace the reserved html characters
    -  let formatted = substitute(formatted, '&', '\&',  'g')
    -  let formatted = substitute(formatted, '<', '\<',   'g')
    -  let formatted = substitute(formatted, '>', '\>',   'g')
    -  let formatted = substitute(formatted, '"', '\"', 'g')
    -  " ' is not valid in HTML but it is in XHTML, so just use the numeric
    -  " reference for it instead. Needed because it could appear in quotes
    -  " especially if unselectable regions is turned on.
    -  let formatted = substitute(formatted, '"', '\'', 'g')
    -
    -  " Replace a "form feed" character with HTML to do a page break
    -  " TODO: need to prevent this in unselectable areas? Probably it should never
    -  " BE in an unselectable area...
    -  let formatted = substitute(formatted, "\x0c", '
    ', 'g') - - " Replace double spaces, leading spaces, and trailing spaces if needed - if ' ' != s:HtmlSpace - let formatted = substitute(formatted, ' ', s:HtmlSpace .. s:HtmlSpace, 'g') - let formatted = substitute(formatted, '^ ', s:HtmlSpace, 'g') - let formatted = substitute(formatted, ' \+$', s:HtmlSpace, 'g') +var diff_mode = &diff + +# Return HTML valid characters enclosed in a span of class style_name with +# unprintable characters expanded and double spaces replaced as necessary. +# +# TODO: eliminate unneeded logic like done for BuildStyleWrapper +def HtmlFormat(text: string, style_id: number, diff_style_id: number, extra_attrs: string, make_unselectable: bool): string + # Replace unprintable characters + var unformatted = strtrans(text) + + var formatted = unformatted + + # Replace the reserved html characters + formatted = substitute(formatted, '&', '\&', 'g') + formatted = substitute(formatted, '<', '\<', 'g') + formatted = substitute(formatted, '>', '\>', 'g') + formatted = substitute(formatted, '"', '\"', 'g') + # ' is not valid in HTML but it is in XHTML, so just use the numeric + # reference for it instead. Needed because it could appear in quotes + # especially if unselectable regions is turned on. + formatted = substitute(formatted, '"', '\'', 'g') + + # Replace a "form feed" character with HTML to do a page break + # TODO: need to prevent this in unselectable areas? Probably it should never + # BE in an unselectable area... + formatted = substitute(formatted, "\x0c", '
    ', 'g') + + # Replace double spaces, leading spaces, and trailing spaces if needed + if ' ' != HtmlSpace + formatted = substitute(formatted, ' ', HtmlSpace .. HtmlSpace, 'g') + formatted = substitute(formatted, '^ ', HtmlSpace, 'g') + formatted = substitute(formatted, ' \+$', HtmlSpace, 'g') endif - " Enclose in the correct format - return s:BuildStyleWrapper(a:style_id, a:diff_style_id, a:extra_attrs, formatted, a:make_unselectable, unformatted) -endfun - -" set up functions to call HtmlFormat in certain ways based on whether the -" element is supposed to be unselectable or not -if s:settings.prevent_copy =~# 'n' - if s:settings.number_lines - if s:settings.line_ids - function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) - if a:lnr > 0 - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'..(exists('g:html_diff_win_num') ? 'W'..g:html_diff_win_num : "")..'L'..a:lnr..s:settings.id_suffix..'" ', 1) + # Enclose in the correct format + return BuildStyleWrapper(style_id, diff_style_id, extra_attrs, formatted, make_unselectable, unformatted) +enddef + +# set up functions to call HtmlFormat in certain ways based on whether the +# element is supposed to be unselectable or not +if settings.prevent_copy =~# 'n' + if settings.number_lines + if settings.line_ids + def HtmlFormat_n(text: string, style_id: number, diff_style_id: number, lnr: number): string + if lnr > 0 + return HtmlFormat(text, style_id, diff_style_id, 'id="' .. (exists('g:html_diff_win_num') ? 'W' .. g:html_diff_win_num : "") .. 'L' .. lnr .. settings.id_suffix .. '" ', true) else - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) + return HtmlFormat(text, style_id, diff_style_id, "", true) endif - endfun + enddef else - function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) - endfun + def HtmlFormat_n(text: string, style_id: number, diff_style_id: number, lnr: number): string + return HtmlFormat(text, style_id, diff_style_id, "", true) + enddef endif - elseif s:settings.line_ids - " if lines are not being numbered the only reason this function gets called - " is to put the line IDs on each line; "text" will be empty but lnr will - " always be non-zero, however we don't want to use the because that - " won't work as nice for empty text - function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'..(exists('g:html_diff_win_num') ? 'W'..g:html_diff_win_num : "")..'L'..a:lnr..s:settings.id_suffix..'" ', 0) - endfun + elseif settings.line_ids + # if lines are not being numbered the only reason this function gets called + # is to put the line IDs on each line; "text" will be empty but lnr will + # always be non-zero, however we don't want to use the because that + # won't work as nice for empty text + def HtmlFormat_n(text: string, style_id: number, diff_style_id: number, lnr: number): string + return HtmlFormat(text, style_id, diff_style_id, 'id="' .. (exists('g:html_diff_win_num') ? 'W' .. g:html_diff_win_num : "") .. 'L' .. lnr .. settings.id_suffix .. '" ', false) + enddef endif else - if s:settings.line_ids - function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) - if a:lnr > 0 - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, 'id="'..(exists('g:html_diff_win_num') ? 'W'..g:html_diff_win_num : "")..'L'..a:lnr..s:settings.id_suffix..'" ', 0) + if settings.line_ids + def HtmlFormat_n(text: string, style_id: number, diff_style_id: number, lnr: number): string + if lnr > 0 + return HtmlFormat(text, style_id, diff_style_id, 'id="' .. (exists('g:html_diff_win_num') ? 'W' .. g:html_diff_win_num : "") .. 'L' .. lnr .. settings.id_suffix .. '" ', false) else - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) + return HtmlFormat(text, style_id, diff_style_id, "", false) endif - endfun + enddef else - function! s:HtmlFormat_n(text, style_id, diff_style_id, lnr) - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) - endfun + def HtmlFormat_n(text: string, style_id: number, diff_style_id: number, lnr: number): string + return HtmlFormat(text, style_id, diff_style_id, "", false) + enddef endif endif -if s:settings.prevent_copy =~# 'd' - function! s:HtmlFormat_d(text, style_id, diff_style_id) - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) - endfun + +if settings.prevent_copy =~# 'd' + def HtmlFormat_d(text: string, style_id: number, diff_style_id: number): string + return HtmlFormat(text, style_id, diff_style_id, "", true) + enddef else - function! s:HtmlFormat_d(text, style_id, diff_style_id) - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) - endfun + def HtmlFormat_d(text: string, style_id: number, diff_style_id: number): string + return HtmlFormat(text, style_id, diff_style_id, "", false) + enddef endif -if s:settings.prevent_copy =~# 'f' - if s:settings.use_input_for_pc ==# 'none' - " Simply space-pad to the desired width inside the generated content (note - " that the FoldColumn definition includes a whitespace:pre rule) - function! s:FoldColumn_build(char, len, numfill, char2, class, click) - return "" - endfun - function! s:FoldColumn_fill() - return s:HtmlFormat(repeat(' ', s:foldcolumn), s:FOLD_C_ID, 0, "", 1) - endfun +if settings.prevent_copy =~# 'f' + if settings.use_input_for_pc ==# 'none' + # If not using the method at all, define functions that will simply + # space-pad to the desired width inside the generated content (note that + # the FoldColumn definition includes a whitespace:pre rule) + def FoldColumn_build(char: string, len: number, numfill: number, char2: string, class: string, click: string): string + return "" + enddef + def FoldColumn_fill(): string + return HtmlFormat(repeat(' ', foldcolumn), FOLD_C_ID, 0, "", true) + enddef else - " Note the elements for fill spaces will have a single space for - " content, to allow active cursor CSS selection to work. - " - " Wrap the whole thing in a span for the 1px padding workaround for gaps. - " - " Build the function line by line containing only what is needed for the - " options in use for maximum code sharing with minimal branch logic for - " greater speed. - " - " Note, 'exec' commands do not recognize line continuations, so must - " concatenate lines rather than continue them. - let s:build_fun_lines = [] - call add(s:build_fun_lines, []) - let s:build_fun_lines[-1] =<< trim ENDLET - function! s:FoldColumn_build(char, len, numfill, char2, class, click) - let l:input_open = "" : "'>") - let l:return_span = "" - let l:return_span ..= l:input_open..l:common_attrs..repeat(a:char, a:len)..(a:char2) - let l:return_span ..= l:input_close + # Note the elements for fill spaces will have a single space for + # content, to allow active cursor CSS selection to work. + # + # Wrap the whole thing in a span for the 1px padding workaround for gaps. + # + # Build the function line by line containing only what is needed for the + # options in use for maximum code sharing with minimal branch logic for + # greater speed. + # + # Note, 'exec' commands do not recognize line continuations, so must + # concatenate lines rather than continue them. + var build_fun_lines: list = [] + build_fun_lines =<< trim ENDLET + def FoldColumn_build(char: string, len: number, numfill: number, char2: string, class: string, click: string): string + var input_open = "" : "'>" + var return_span = "" + return_span ..= input_open .. common_attrs .. repeat(char, len) .. char2 + return_span ..= input_close ENDLET - if s:settings.use_input_for_pc ==# 'fallback' - call add(s:build_fun_lines, []) - let s:build_fun_lines[-1] =<< trim ENDLET - let l:return_span ..= "". - \ repeat(a:char, a:len)..a:char2..repeat(' ', a:numfill). - \ "" - endfun - function! s:FoldColumn_fill() - return s:HtmlFormat(repeat(' ', s:foldcolumn), s:FOLD_C_ID, 0, "", 0) - endfun + # For normal fold columns, simply space-pad to the desired width (note that + # the FoldColumn definition includes a whitespace:pre rule) + def FoldColumn_build(char: string, len: number, numfill: number, char2: string, class: string, click: string): string + return "" .. + repeat(char, len) .. char2 .. repeat(' ', numfill) .. + "" + enddef + def FoldColumn_fill(): string + return HtmlFormat(repeat(' ', foldcolumn), FOLD_C_ID, 0, "", false) + enddef endif -if s:settings.prevent_copy =~# 't' - " put an extra empty span at the end for dynamic folds, so the linebreak can - " be surrounded. Otherwise do it as normal. - " - " TODO: isn't there a better way to do this, than placing it here and using a - " substitute later? - if s:settings.dynamic_folds - function! s:HtmlFormat_t(text, style_id, diff_style_id) - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) . - \ s:HtmlFormat("", a:style_id, 0, "", 0) - endfun +if settings.prevent_copy =~# 't' + # put an extra empty span at the end for dynamic folds, so the linebreak can + # be surrounded. Otherwise do it as normal. + # + # TODO: isn't there a better way to do this, than placing it here and using a + # substitute later? + if settings.dynamic_folds + def HtmlFormat_t(text: string, style_id: number, diff_style_id: number): string + return HtmlFormat(text, style_id, diff_style_id, "", true) .. + HtmlFormat("", style_id, 0, "", false) + enddef else - function! s:HtmlFormat_t(text, style_id, diff_style_id) - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 1) - endfun + def HtmlFormat_t(text: string, style_id: number, diff_style_id: number): string + return HtmlFormat(text, style_id, diff_style_id, "", true) + enddef endif else - function! s:HtmlFormat_t(text, style_id, diff_style_id) - return s:HtmlFormat(a:text, a:style_id, a:diff_style_id, "", 0) - endfun + def HtmlFormat_t(text: string, style_id: number, diff_style_id: number): string + return HtmlFormat(text, style_id, diff_style_id, "", false) + enddef endif -" Return CSS style describing given highlight id (can be empty) -function! s:CSS1(id) - let a = "" - let translated_ID = synIDtrans(a:id) - if synIDattr(translated_ID, "inverse") - " For inverse, we always must set both colors (and exchange them) - let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm)) - let a = a .. "color: " .. ( x != "" ? x : s:bgc ) .. "; " - let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm)) - let a = a .. "background-color: " .. ( x != "" ? x : s:fgc ) .. "; " +# Return CSS style describing given highlight id (can be empty) +def CSS1(id: number): string + var a = "" + var translated_ID = synIDtrans(id) + if synIDattr(translated_ID, "inverse") == '1' + # For inverse, we always must set both colors (and exchange them) + var x = HtmlColor(synIDattr(translated_ID, "bg#", whatterm)) + a ..= "color: " .. (x != "" ? x : bgc) .. "; " + x = HtmlColor(synIDattr(translated_ID, "fg#", whatterm)) + a ..= "background-color: " .. (x != "" ? x : fgc) .. "; " else - let x = s:HtmlColor(synIDattr(translated_ID, "fg#", s:whatterm)) - if x != "" | let a = a .. "color: " .. x .. "; " | endif - let x = s:HtmlColor(synIDattr(translated_ID, "bg#", s:whatterm)) + var x = HtmlColor(synIDattr(translated_ID, "fg#", whatterm)) + if x != "" | a ..= "color: " .. x .. "; " | endif + x = HtmlColor(synIDattr(translated_ID, "bg#", whatterm)) if x != "" - let a = a .. "background-color: " .. x .. "; " - " stupid hack because almost every browser seems to have at least one font - " which shows 1px gaps between lines which have background - let a = a .. "padding-bottom: 1px; " - elseif (translated_ID == s:FOLDED_ID || translated_ID == s:LINENR_ID || translated_ID == s:FOLD_C_ID) && !empty(s:settings.prevent_copy) - " input elements default to a different color than the rest of the page - let a = a .. "background-color: " .. s:bgc .. "; " + a ..= "background-color: " .. x .. "; " + # stupid hack because almost every browser seems to have at least one font + # which shows 1px gaps between lines which have background + a ..= "padding-bottom: 1px; " + elseif (translated_ID == FOLDED_ID || translated_ID == LINENR_ID || translated_ID == FOLD_C_ID) && !empty(settings.prevent_copy) + # input elements default to a different color than the rest of the page + a ..= "background-color: " .. bgc .. "; " endif endif - if synIDattr(translated_ID, "bold") | let a = a .. "font-weight: bold; " | endif - if synIDattr(translated_ID, "italic") | let a = a .. "font-style: italic; " | endif - if synIDattr(translated_ID, "underline") | let a = a .. "text-decoration: underline; " | endif + if synIDattr(translated_ID, "bold") == '1' | a ..= "font-weight: bold; " | endif + if synIDattr(translated_ID, "italic") == '1' | a ..= "font-style: italic; " | endif + if synIDattr(translated_ID, "underline") == '1' | a ..= "text-decoration: underline; " | endif return a -endfun - -if s:settings.dynamic_folds - " compares two folds as stored in our list of folds - " A fold is "less" than another if it starts at an earlier line number, - " or ends at a later line number, ties broken by fold level - function! s:FoldCompare(f1, f2) - if a:f1.firstline != a:f2.firstline - " put it before if it starts earlier - return a:f1.firstline - a:f2.firstline - elseif a:f1.lastline != a:f2.lastline - " put it before if it ends later - return a:f2.lastline - a:f1.lastline +enddef + +if settings.dynamic_folds + # compares two folds as stored in our list of folds + # A fold is "less" than another if it starts at an earlier line number, + # or ends at a later line number, ties broken by fold level + def FoldCompare(f1: dict, f2: dict): number + if f1.firstline != f2.firstline + # put it before if it starts earlier + return f1.firstline - f2.firstline + elseif f1.lastline != f2.lastline + # put it before if it ends later + return f2.lastline - f1.lastline else - " if folds begin and end on the same lines, put lowest fold level first - return a:f1.level - a:f2.level + # if folds begin and end on the same lines, put lowest fold level first + return f1.level - f2.level endif - endfunction - + enddef endif -" Set some options to make it work faster. -" Don't report changes for :substitute, there will be many of them. -" Don't change other windows; turn off scroll bind temporarily -let s:old_title = &title -let s:old_icon = &icon -let s:old_et = &l:et -let s:old_bind = &l:scrollbind -let s:old_report = &report -let s:old_search = @/ -let s:old_more = &more +# Set some options to make it work faster. +# Don't report changes for :substitute, there will be many of them. +# Don't change other windows; turn off scroll bind temporarily +var old_title = &title +var old_icon = &icon +var old_et = &l:et +var old_bind = &l:scrollbind +var old_report = &report +var old_search = @/ +var old_more = &more set notitle noicon setlocal et set nomore set report=1000000 setlocal noscrollbind -if exists(':ownsyntax') && exists('w:current_syntax') - let s:current_syntax = w:current_syntax +var current_syntax: string +if exists(':ownsyntax') == 2 && exists('w:current_syntax') + current_syntax = w:current_syntax elseif exists('b:current_syntax') - let s:current_syntax = b:current_syntax + current_syntax = b:current_syntax else - let s:current_syntax = 'none' + current_syntax = 'none' endif -if s:current_syntax == '' - let s:current_syntax = 'none' +if current_syntax == '' + current_syntax = 'none' endif -" If the user is sourcing this script directly then the plugin version isn't -" known because the main plugin script didn't load. In the usual case where the -" user still has the full Vim runtime installed, or has this full plugin -" installed in a package or something, then we can extract the version from the -" main plugin file at it's usual spot relative to this file. Otherwise the user -" is assembling their runtime piecemeal and we have no idea what versions of -" other files may be present so don't even try to make a guess or assume the -" presence of other specific files with specific meaning. -" -" We don't want to actually source the main plugin file here because the user -" may have a good reason not to (e.g. they define their own TOhtml command or -" something). -" -" If this seems way too complicated and convoluted, it is. Probably I should -" have put the version information in the autoload file from the start. But the -" version has been in the global variable for so long that changing it could -" break a lot of user scripts. +# If the user is sourcing this script directly then the plugin version isn't +# known because the main plugin script didn't load. In the usual case where the +# user still has the full Vim runtime installed, or has this full plugin +# installed in a package or something, then we can extract the version from the +# main plugin file at it's usual spot relative to this file. Otherwise the user +# is assembling their runtime piecemeal and we have no idea what versions of +# other files may be present so don't even try to make a guess or assume the +# presence of other specific files with specific meaning. +# +# We don't want to actually source the main plugin file here because the user +# may have a good reason not to (e.g. they define their own TOhtml command or +# something). +# +# If this seems way too complicated and convoluted, it is. Probably I should +# have put the version information in the autoload file from the start. But the +# version has been in the global variable for so long that changing it could +# break a lot of user scripts. +var pluginversion: string if exists("g:loaded_2html_plugin") - let s:pluginversion = g:loaded_2html_plugin + pluginversion = g:loaded_2html_plugin else if !exists("g:unloaded_tohtml_plugin") - let s:main_plugin_path = expand(":p:h:h").."/plugin/tohtml.vim" - if filereadable(s:main_plugin_path) - let s:lines = readfile(s:main_plugin_path, "", 20) - call filter(s:lines, 'v:val =~ "loaded_2html_plugin = "') - if empty(s:lines) - let g:unloaded_tohtml_plugin = "unknown" + var main_plugin_path = expand(":p:h:h") .. "/plugin/tohtml.vim" + if filereadable(main_plugin_path) + var plugin_lines = readfile(main_plugin_path, "", 20) + filter(plugin_lines, (_, val) => val =~ "loaded_2html_plugin = ") + if empty(plugin_lines) + g:unloaded_tohtml_plugin = "unknown" else - let g:unloaded_tohtml_plugin = substitute(s:lines[0], '.*loaded_2html_plugin = \([''"]\)\(\%(\1\@!.\)\+\)\1', '\2', '') + g:unloaded_tohtml_plugin = substitute(plugin_lines[0], '.*loaded_2html_plugin = \([''"]\)\(\%(\1\@!.\)\+\)\1', '\2', '') endif - unlet s:lines else - let g:unloaded_tohtml_plugin = "unknown" + g:unloaded_tohtml_plugin = "unknown" endif - unlet s:main_plugin_path endif - let s:pluginversion = g:unloaded_tohtml_plugin + pluginversion = g:unloaded_tohtml_plugin endif -" Split window to create a buffer with the HTML file. -let s:orgbufnr = winbufnr(0) -let s:origwin_stl = &l:stl +# Split window to create a buffer with the HTML file. +var orgbufnr = winbufnr(0) +var origwin_stl = &l:stl if expand("%") == "" if exists('g:html_diff_win_num') - exec 'new Untitled_win'..g:html_diff_win_num..'.'.(s:settings.use_xhtml ? 'xhtml' : 'html') + execute 'new Untitled_win' .. g:html_diff_win_num .. '.' .. (settings.use_xhtml ? 'xhtml' : 'html') else - exec 'new Untitled.'..(s:settings.use_xhtml ? 'xhtml' : 'html') + execute 'new Untitled.' .. (settings.use_xhtml ? 'xhtml' : 'html') endif else - exec 'new %.'..(s:settings.use_xhtml ? 'xhtml' : 'html') + execute 'new %.' .. (settings.use_xhtml ? 'xhtml' : 'html') endif -" Resize the new window to very small in order to make it draw faster -let s:old_winheight = winheight(0) -let s:old_winfixheight = &l:winfixheight -if s:old_winheight > 2 - resize 1 " leave enough room to view one line at a time +# Resize the new window to very small in order to make it draw faster +var old_winheight = winheight(0) +var old_winfixheight = &l:winfixheight +if old_winheight > 2 + resize 1 # leave enough room to view one line at a time norm! G norm! zt endif setlocal winfixheight -let s:newwin_stl = &l:stl +var newwin_stl = &l:stl -" on the new window, set the least time-consuming fold method -let s:old_fen = &foldenable +# on the new window, set the least time-consuming fold method +var old_fen = &foldenable setlocal foldmethod=manual setlocal nofoldenable -let s:newwin = winnr() -let s:orgwin = bufwinnr(s:orgbufnr) +var newwin = winnr() +var orgwin = bufwinnr(orgbufnr) setlocal modifiable -%d -let s:old_paste = &paste +:%d +var old_paste = &paste set paste -let s:old_magic = &magic +var old_magic = &magic set magic -" set the fileencoding to match the charset we'll be using -let &l:fileencoding=s:settings.vim_encoding +# set the fileencoding to match the charset we'll be using +&l:fileencoding = settings.vim_encoding -" According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte -" order mark is highly recommend on the web when using multibyte encodings. But, -" it is not a good idea to include it on UTF-8 files. Otherwise, let Vim -" determine when it is actually inserted. -if s:settings.vim_encoding == 'utf-8' +# According to http://www.w3.org/TR/html4/charset.html#doc-char-set, the byte +# order mark is highly recommend on the web when using multibyte encodings. But, +# it is not a good idea to include it on UTF-8 files. Otherwise, let Vim +# determine when it is actually inserted. +if settings.vim_encoding == 'utf-8' setlocal nobomb else setlocal bomb endif -let s:lines = [] +var lines = [] -if s:settings.use_xhtml - if s:settings.encoding != "" - call add(s:lines, "") +var tag_close: string +if settings.use_xhtml + if settings.encoding != "" + add(lines, "") else - call add(s:lines, "") + add(lines, "") endif - let s:tag_close = ' />' + tag_close = ' />' else - let s:tag_close = '>' + tag_close = '>' endif -let s:HtmlSpace = ' ' -let s:LeadingSpace = ' ' -let s:HtmlEndline = '' -if s:settings.no_pre - let s:HtmlEndline = '", - \ ""]) - " include encoding as close to the top as possible, but only if not already - " contained in XML information (to avoid haggling over content type) - if s:settings.encoding != "" && !s:settings.use_xhtml - if s:html5 - call add(s:lines, '", + ""]) + # include encoding as close to the top as possible, but only if not already + # contained in XML information (to avoid haggling over content type) + if settings.encoding != "" && !settings.use_xhtml + if html5 + add(lines, '"..expand("%:p:~")..""), - \ ("", - \ s:settings.use_xhtml ? "" : "", - \] + extend(lines, [ + ("" .. expand("%:p:~") .. ""), + ($'", + settings.use_xhtml ? "" : "", + ] else - " if we aren't doing hover_unfold, use CSS 1 only - call extend(s:lines, [ - \ ".FoldColumn { text-decoration: none; white-space: pre; }", - \ ".open-fold .fulltext { display: inline; }", - \ ".open-fold span.Folded { display: none; }", - \ ".open-fold .toggle-open { display: none; }", - \ ".open-fold .toggle-closed { display: inline; }", - \ "", - \ ".closed-fold .fulltext { display: none; }", - \ ".closed-fold span.Folded { display: inline; }", - \ ".closed-fold .toggle-open { display: inline; }", - \ ".closed-fold .toggle-closed { display: none; }", - \]) + # if we aren't doing hover_unfold, use CSS 1 only + extend(lines, [ + ".FoldColumn { text-decoration: none; white-space: pre; }", + ".open-fold .fulltext { display: inline; }", + ".open-fold span.Folded { display: none; }", + ".open-fold .toggle-open { display: none; }", + ".open-fold .toggle-closed { display: inline; }", + "", + ".closed-fold .fulltext { display: none; }", + ".closed-fold span.Folded { display: inline; }", + ".closed-fold .toggle-open { display: inline; }", + ".closed-fold .toggle-closed { display: none; }", + ]) endif endif - " else we aren't doing any dynamic folding, no need for any special rules - - call extend(s:lines, [ - \ s:settings.use_xhtml ? "" : '-->', - \ "", - \]) - call extend(s:lines, s:ieonly) - unlet s:ieonly + # else we aren't doing any dynamic folding, no need for any special rules + + extend(lines, [ + settings.use_xhtml ? "" : '-->', + "", + ]) + extend(lines, ieonly) endif - let s:uses_script = s:settings.dynamic_folds || s:settings.line_ids + var uses_script = settings.dynamic_folds || settings.line_ids - " insert script tag if needed - if s:uses_script - call extend(s:lines, [ - \ "", - \ "", - \ s:settings.use_xhtml ? '//", + settings.use_xhtml ? '//' : '-->', - \ "" - \ ]) + # insert script closing tag if needed + if uses_script + extend(lines, [ + '', + settings.use_xhtml ? '//]]>' : '-->', + "" + ]) endif - call extend(s:lines, ["", - \ ""]) + extend(lines, ["", + ""]) endif -if s:settings.no_pre - " if we're not using CSS we use a font tag which can't have a div inside - if s:settings.use_css - call extend(s:lines, ["
    "]) +if settings.no_pre + # if we're not using CSS we use a font tag which can't have a div inside + if settings.use_css + extend(lines, ["
    "]) endif else - call extend(s:lines, ["
    "])
    +  extend(lines, ["
    "])
     endif
     
    -exe s:orgwin .. "wincmd w"
    +execute $":{orgwin}wincmd w"
     
    -" caches of style data
    -" initialize to include line numbers if using them
    -if s:settings.number_lines
    -  let s:stylelist = { s:LINENR_ID : ".LineNr { " .. s:CSS1( s:LINENR_ID ) .. "}" }
    +# caches of style data
    +# initialize to include line numbers if using them
    +var stylelist: dict
    +if settings.number_lines
    +  stylelist = { [LINENR_ID]: ".LineNr { " .. CSS1(LINENR_ID) .. "}" }
     else
    -  let s:stylelist = {}
    +  stylelist = {}
     endif
    -let s:diffstylelist = {
    -      \   s:DIFF_A_ID : ".DiffAdd { " .. s:CSS1( s:DIFF_A_ID ) .. "}",
    -      \   s:DIFF_C_ID : ".DiffChange { " .. s:CSS1( s:DIFF_C_ID ) .. "}",
    -      \   s:DIFF_D_ID : ".DiffDelete { " .. s:CSS1( s:DIFF_D_ID ) .. "}",
    -      \   s:DIFF_T_ID : ".DiffText { " .. s:CSS1( s:DIFF_T_ID ) .. "}"
    -      \ }
    -
    -" set up progress bar in the status line
    -if !s:settings.no_progress
    -  " ProgressBar Indicator
    -  let s:progressbar={}
    -
    -  " Progressbar specific functions
    -
    -  func! s:SetProgbarColor()
    -    if hlID("TOhtmlProgress") != 0
    -      hi! link TOhtmlProgress_auto TOhtmlProgress
    -    elseif hlID("TOhtmlProgress_auto")==0 ||
    -       \ !exists("s:last_colors_name") || !exists("g:colors_name") ||
    -       \ g:colors_name != s:last_colors_name
    -      let s:last_colors_name = exists("g:colors_name") ? g:colors_name : "none"
    -
    -      let l:diffatr = synIDattr(hlID("DiffDelete")->synIDtrans(), "reverse", s:whatterm) ? "fg#" : "bg#"
    -      let l:stlatr = synIDattr(hlID("StatusLine")->synIDtrans(), "reverse", s:whatterm) ? "fg#" : "bg#"
    -
    -      let l:progbar_color = synIDattr(hlID("DiffDelete")->synIDtrans(), l:diffatr, s:whatterm)
    -      let l:stl_color = synIDattr(hlID("StatusLine")->synIDtrans(), l:stlatr, s:whatterm)
    -
    -      if "" == l:progbar_color
    -	let l:progbar_color = synIDattr(hlID("DiffDelete")->synIDtrans(), "reverse", s:whatterm) ? s:fgc : s:bgc
    -      endif
    -      if "" == l:stl_color
    -	let l:stl_color = synIDattr(hlID("StatusLine")->synIDtrans(), "reverse", s:whatterm) ? s:fgc : s:bgc
    -      endif
    +var diffstylelist: dict = {
    +  [DIFF_A_ID]: ".DiffAdd { " .. CSS1(DIFF_A_ID) .. "}",
    +  [DIFF_C_ID]: ".DiffChange { " .. CSS1(DIFF_C_ID) .. "}",
    +  [DIFF_D_ID]: ".DiffDelete { " .. CSS1(DIFF_D_ID) .. "}",
    +  [DIFF_T_ID]: ".DiffText { " .. CSS1(DIFF_T_ID) .. "}"
    +}
    +
    +var last_colors_name: string
    +# set up progress bar in the status line
    +# ProgressBar Indicator
    +# Progressbar specific functions
    +def SetProgbarColor()
    +  if hlID("TOhtmlProgress") != 0
    +    hi! link TOhtmlProgress_auto TOhtmlProgress
    +  elseif hlID("TOhtmlProgress_auto") == 0 ||
    +      last_colors_name == null_string || !exists("g:colors_name") ||
    +      g:colors_name != last_colors_name
    +    last_colors_name = exists("g:colors_name") ? g:colors_name : "none"
    +
    +    var diffatr = synIDattr(synIDtrans(hlID("DiffDelete")), "reverse", whatterm) == '1' ? "fg#" : "bg#"
    +    var stlatr = synIDattr(synIDtrans(hlID("StatusLine")), "reverse", whatterm) == '1' ? "fg#" : "bg#"
    +
    +    var progbar_color = synIDattr(synIDtrans(hlID("DiffDelete")), diffatr, whatterm)
    +    var stl_color = synIDattr(synIDtrans(hlID("StatusLine")), stlatr, whatterm)
    +
    +    if empty(progbar_color)
    +      progbar_color = synIDattr(synIDtrans(hlID("DiffDelete")), "reverse", whatterm) == '1' ? fgc : bgc
    +    endif
    +    if empty(stl_color)
    +      stl_color = synIDattr(synIDtrans(hlID("StatusLine")), "reverse", whatterm) == '1' ? fgc : bgc
    +    endif
     
    -      if l:progbar_color == l:stl_color
    -	if s:whatterm == 'cterm'
    -	  if l:progbar_color >= (&t_Co/2)
    -	    let l:progbar_color-=1
    -	  else
    -	    let l:progbar_color+=1
    -	  endif
    +    if progbar_color == stl_color
    +      if whatterm == 'cterm'
    +	var nr = str2nr(progbar_color)
    +	if nr >= (str2nr(&t_Co) / 2)
    +	  nr -= 1
     	else
    -	  let l:rgb = map(matchlist(l:progbar_color, '#\zs\x\x\ze\(\x\x\)\(\x\x\)')[:2], 'str2nr(v:val, 16)')
    -	  let l:avg = (l:rgb[0] + l:rgb[1] + l:rgb[2])/3
    -	  if l:avg >= 128
    -	    let l:avg_new = l:avg
    -	    while l:avg - l:avg_new < 0x15
    -	      let l:rgb = map(l:rgb, 'v:val * 3 / 4')
    -	      let l:avg_new = (l:rgb[0] + l:rgb[1] + l:rgb[2])/3
    -	    endwhile
    -	  else
    -	    let l:avg_new = l:avg
    -	    while l:avg_new - l:avg < 0x15
    -	      let l:rgb = map(l:rgb, 'min([max([v:val, 4]) * 5 / 4, 255])')
    -	      let l:avg_new = (l:rgb[0] + l:rgb[1] + l:rgb[2])/3
    -	    endwhile
    -	  endif
    -	  let l:progbar_color = printf("#%02x%02x%02x", l:rgb[0], l:rgb[1], l:rgb[2])
    +	  nr += 1
     	endif
    -	echomsg "diff detected progbar color set to" l:progbar_color
    +	progbar_color = string(nr)
    +      else
    +	var rgb = map(matchlist(progbar_color, '#\zs\x\x\ze\(\x\x\)\(\x\x\)')[: 2], (_, v) => str2nr(v, 16))
    +	var avg = (rgb[0] + rgb[1] + rgb[2]) / 3
    +	if avg >= 128
    +	  var avg_new = avg
    +	  while avg - avg_new < 0x15
    +	    rgb = map(rgb, (_, v) => v * 3 / 4)
    +	    avg_new = (rgb[0] + rgb[1] + rgb[2]) / 3
    +	  endwhile
    +	else
    +	  var avg_new = avg
    +	  while avg_new - avg < 0x15
    +	    rgb = map(rgb, (_, v) => min([max([v, 4]) * 5 / 4, 255]))
    +	    avg_new = (rgb[0] + rgb[1] + rgb[2]) / 3
    +	  endwhile
    +	endif
    +	progbar_color = printf("#%02x%02x%02x", rgb[0], rgb[1], rgb[2])
           endif
    -      exe "hi TOhtmlProgress_auto" s:whatterm.."bg="..l:progbar_color
    +      echomsg "diff detected progbar color set to" progbar_color
         endif
    -  endfun
    -
    -  func! s:ProgressBar(title, max_value, winnr)
    -    let pgb=copy(s:progressbar)
    -    let pgb.title = a:title..' '
    -    let pgb.max_value = a:max_value
    -    let pgb.winnr = a:winnr
    -    let pgb.cur_value = 0
    -
    -    let pgb.items = { 'title'   : { 'color' : 'Statusline' },
    -	  \'bar'     : { 'color' : 'Statusline' , 'fillcolor' : 'TOhtmlProgress_auto' , 'bg' : 'Statusline' } ,
    -	  \'counter' : { 'color' : 'Statusline' } }
    -    let pgb.last_value = 0
    -    let pgb.needs_redraw = 0
    -    " Note that you must use len(split) instead of len() if you want to use 
    -    " unicode in title.
    -    "
    -    " Subtract 3 for spacing around the title.
    -    " Subtract 4 for the percentage display.
    -    " Subtract 2 for spacing before this.
    -    " Subtract 2 more for the '|' on either side of the progress bar
    -    let pgb.subtractedlen=len(split(pgb.title, '\zs'))+3+4+2+2
    -    let pgb.max_len = 0
    +    execute "hi TOhtmlProgress_auto" whatterm .. "bg=" .. progbar_color
    +  endif
    +enddef
    +
    +class ProgressBar #{{{
    +  public var title: string
    +  public var max_value: number
    +  public var winnr: number
    +  public var cur_value: number = 0
    +  public var items: dict
    +  public var last_value: number = 0
    +  public var needs_redraw: number = 0
    +  public var pb_len: number = 0
    +  public var max_len: number = 0
    +  public var progress_ticks: list = []
    +  var subtractedlen: number
    +
    +  def new(title: string, max_value: number, winnr: number)
    +    this.title = title .. ' '
    +    this.max_value = max_value
    +    this.winnr = winnr
    +
    +    this.items = {
    +      'title': { 'color': 'Statusline' },
    +      'bar': { 'color': 'Statusline', 'fillcolor': 'TOhtmlProgress_auto', 'bg': 'Statusline' },
    +      'counter': { 'color': 'Statusline' }
    +    }
    +    # Note that you must use len(split) instead of len() if you want to use
    +    # unicode in title.
    +    #
    +    # Subtract 3 for spacing around the title.
    +    # Subtract 4 for the percentage display.
    +    # Subtract 2 for spacing before this.
    +    # Subtract 2 more for the '|' on either side of the progress bar
    +    this.subtractedlen = len(split(this.title, '\zs')) + 3 + 4 + 2 + 2
         set laststatus=2
    -    return pgb
    -  endfun
    +  enddef
     
    -  " Function: progressbar.calculate_ticks() {{{1
    -  func! s:progressbar.calculate_ticks(pb_len)
    -    if a:pb_len<=0
    -      let pb_len = 100
    -    else
    -      let pb_len = a:pb_len
    -    endif
    -    let self.progress_ticks = map(range(pb_len+1), "v:val * self.max_value / pb_len")
    -  endfun
    -
    -  "Function: progressbar.paint()
    -  func! s:progressbar.paint()
    -    " Recalculate widths.
    -    let max_len = winwidth(self.winnr)
    -    let pb_len = 0
    -    " always true on first call because of initial value of self.max_len
    -    if max_len != self.max_len
    -      let self.max_len = max_len
    -
    -      " Progressbar length
    -      let pb_len = max_len - self.subtractedlen
    -
    -      call self.calculate_ticks(pb_len)
    -
    -      let self.needs_redraw = 1
    -      let cur_value = 0
    -      let self.pb_len = pb_len
    +  def Paint()
    +    # Recalculate widths.
    +    var max_len = winwidth(this.winnr)
    +    var pb_len = 0
    +    var cur_value: number
    +
    +    def CalculateTicks(len: number)
    +      if len <= 0
    +	pb_len = 100
    +      else
    +	pb_len = len
    +      endif
    +      this.progress_ticks = range(pb_len + 1)->map((_, v) => v * this.max_value / pb_len)
    +    enddef
    +
    +    # always true on first call because of initial value of this.max_len
    +    if max_len != this.max_len
    +      this.max_len = max_len
    +
    +      # Progressbar length
    +      pb_len = max_len - this.subtractedlen
    +
    +      CalculateTicks(pb_len)
    +
    +      this.needs_redraw = 1
    +      cur_value = 0
    +      this.pb_len = pb_len
         else
    -      " start searching at the last found index to make the search for the
    -      " appropriate tick value normally take 0 or 1 comparisons
    -      let cur_value = self.last_value
    -      let pb_len = self.pb_len
    +      # start searching at the last found index to make the search for the
    +      # appropriate tick value normally take 0 or 1 comparisons
    +      cur_value = this.last_value
    +      pb_len = this.pb_len
         endif
     
    -    let cur_val_max = pb_len > 0 ? pb_len : 100
    +    var cur_val_max = pb_len > 0 ? pb_len : 100
     
    -    " find the current progress bar position based on precalculated thresholds
    -    while cur_value < cur_val_max && self.cur_value > self.progress_ticks[cur_value]
    -      let cur_value += 1
    +    # find the current progress bar position based on precalculated thresholds
    +    while cur_value < cur_val_max && this.cur_value > this.progress_ticks[cur_value]
    +      cur_value += 1
         endwhile
     
    -    " update progress bar
    -    if self.last_value != cur_value || self.needs_redraw || self.cur_value == self.max_value
    -      let self.needs_redraw = 1
    -      let self.last_value = cur_value
    -
    -      let t_color  = self.items.title.color
    -      let b_fcolor = self.items.bar.fillcolor
    -      let b_color  = self.items.bar.color
    -      let c_color  = self.items.counter.color
    -
    -      let stl =  "%#".t_color."#%-( ".self.title." %)".
    -	    \"%#".b_color."#".
    -	    \(pb_len>0 ?
    -	    \	('|%#'.b_fcolor."#%-(".repeat(" ",cur_value)."%)".
    -	    \	 '%#'.b_color."#".repeat(" ",pb_len-cur_value)."|"):
    -	    \	('')).
    -	    \"%=%#".c_color."#%( ".printf("%3.d ",100*self.cur_value/self.max_value)."%% %)"
    -      call setwinvar(self.winnr, '&stl', stl)
    +    # update progress bar
    +    if this.last_value != cur_value || this.needs_redraw || this.cur_value == this.max_value
    +      this.needs_redraw = 1
    +      this.last_value = cur_value
    +
    +      var t_color  = this.items.title.color
    +      var b_fcolor = this.items.bar.fillcolor
    +      var b_color  = this.items.bar.color
    +      var c_color  = this.items.counter.color
    +
    +      var stl =  $"%#{t_color}#%-( {this.title} %)%#{b_color}#" ..
    +	(pb_len > 0 ? $'|%#{b_fcolor}#%-({repeat(" ", cur_value)}%)%#{b_color}#{repeat(" ", pb_len - cur_value)}|' : (''))
    +	.. $"%=%#{c_color}#%( {printf("%3.d ", 100 * this.cur_value / this.max_value)}%% %)"
    +      setwinvar(this.winnr, '&stl', stl)
         endif
    -  endfun
    -
    -  func! s:progressbar.incr( ... )
    -    let self.cur_value += (a:0 ? a:1 : 1)
    -    " if we were making a general-purpose progress bar, we'd need to limit to a
    -    " lower limit as well, but since we always increment with a positive value
    -    " in this script, we only need limit the upper value
    -    let self.cur_value = (self.cur_value > self.max_value ? self.max_value : self.cur_value)
    -    call self.paint()
    -  endfun
    -  " }}}
    -  if s:settings.dynamic_folds
    -    " to process folds we make two passes through each line
    -    let s:pgb = s:ProgressBar("Processing folds:", line('$')*2, s:orgwin)
    +  enddef
    +
    +  def Incr(delta: number = 1)
    +    this.cur_value += delta
    +    # if we were making a general-purpose progress bar, we'd need to limit to a
    +    # lower limit as well, but since we always increment with a positive value
    +    # in this script, we only need limit the upper value
    +    this.cur_value = (this.cur_value > this.max_value ? this.max_value : this.cur_value)
    +    this.Paint()
    +  enddef
    +endclass #}}}
    +
    +var pgb: ProgressBar
    +if !settings.no_progress
    +  if settings.dynamic_folds
    +    # to process folds we make two passes through each line
    +    pgb = ProgressBar.new("Processing folds:", line('$') * 2, orgwin)
       endif
     
    -  call s:SetProgbarColor()
    +  SetProgbarColor()
     endif
     
    -let s:build_fun_lines = []
    -call add(s:build_fun_lines, [])
    -let s:build_fun_lines[-1] =<< trim ENDLET
    -    func! s:Add_diff_fill(lnum)
    -      let l:filler = diff_filler(a:lnum)
    -      if l:filler > 0
    -	let l:to_insert = l:filler
    -	while l:to_insert > 0
    -	  let l:new = repeat(s:difffillchar, 3)
    -
    -	  if l:to_insert > 2 && l:to_insert < l:filler && !s:settings.whole_filler
    -	    let l:new = l:new .. " " .. l:filler .. " inserted lines "
    -	    let l:to_insert = 2
    -	  endif
    +var build_fun_lines: list
    +build_fun_lines =<< trim ENDLET
    +  def Add_diff_fill(_lnum: number)
    +    var filler = diff_filler(_lnum)
    +    if filler > 0
    +      var to_insert = filler
    +      while to_insert > 0
    +	var new = repeat(difffillchar, 3)
    +
    +	if to_insert > 2 && to_insert < filler && !settings.whole_filler
    +	  new = new .. " " .. filler .. " inserted lines "
    +	  to_insert = 2
    +	endif
     ENDLET
    -call add(s:build_fun_lines, [])
    -if !s:settings.no_pre
    -  let s:build_fun_lines[-1] =<< trim ENDLET
    -	  " HTML line wrapping is off--go ahead and fill to the margin
    -	  " TODO: what about when CSS wrapping is turned on?
    -	  let l:new = l:new .. repeat(s:difffillchar, &columns - strlen(l:new) - s:margin)
    +if !settings.no_pre
    +  trim_tmp =<< trim ENDLET
    +	# HTML line wrapping is off--go ahead and fill to the margin
    +	# TODO: what about when CSS wrapping is turned on?
    +	new = new .. repeat(difffillchar, &columns - strlen(new) - margin)
       ENDLET
    +  build_fun_lines += trim_tmp
     else
    -  let s:build_fun_lines[-1] =<< trim ENDLET
    -	  let l:new = l:new .. repeat(s:difffillchar, 3)
    +  trim_tmp =<< trim ENDLET
    +	new = new .. repeat(difffillchar, 3)
       ENDLET
    +  build_fun_lines += trim_tmp
     endif
    -call add(s:build_fun_lines, [])
    -let s:build_fun_lines[-1] =<< trim ENDLET
    -	let l:new = s:HtmlFormat_d(l:new, s:DIFF_D_ID, 0)
    +trim_tmp =<< trim ENDLET
    +	new = HtmlFormat_d(new, DIFF_D_ID, 0)
     ENDLET
    -if s:settings.number_lines
    -  call add(s:build_fun_lines, [])
    -  let s:build_fun_lines[-1] =<< trim ENDLET
    -	  " Indent if line numbering is on. Indent gets style of line number
    -	  " column.
    -	  let l:new = s:HtmlFormat_n(repeat(' ', s:margin), s:LINENR_ID, 0, 0) .. l:new
    +build_fun_lines += trim_tmp
    +if settings.number_lines
    +  trim_tmp =<< trim ENDLET
    +	# Indent if line numbering is on. Indent gets style of line number
    +	# column.
    +	new = HtmlFormat_n(repeat(' ', margin), LINENR_ID, 0, 0) .. new
       ENDLET
    +  build_fun_lines += trim_tmp
     endif
    -if s:settings.dynamic_folds && !s:settings.no_foldcolumn 
    -  call add(s:build_fun_lines, [])
    -  let s:build_fun_lines[-1] =<< trim ENDLET
    -	  if s:foldcolumn > 0
    -	    " Indent for foldcolumn if there is one. Assume it's empty, there should
    -	    " not be a fold for deleted lines in diff mode.
    -	    let l:new = s:FoldColumn_fill() .. l:new
    -	  endif
    +if settings.dynamic_folds && !settings.no_foldcolumn
    +  trim_tmp =<< trim ENDLET
    +	if foldcolumn > 0
    +	  # Indent for foldcolumn if there is one. Assume it's empty, there should
    +	  # not be a fold for deleted lines in diff mode.
    +	  new = FoldColumn_fill() .. new
    +	endif
       ENDLET
    +  build_fun_lines += trim_tmp
     endif
    -" Ignore this comment, just bypassing a highlighting issue: if
    -call add(s:build_fun_lines, [])
    -let s:build_fun_lines[-1] =<< trim ENDLET
    -	call add(s:lines, l:new..s:HtmlEndline)
    -	let l:to_insert = l:to_insert - 1
    +# Ignore this comment, just bypassing a highlighting issue: if
    +trim_tmp =<< trim ENDLET
    +	add(lines, new .. HtmlEndline)
    +	to_insert -= 1
           endwhile
         endif
    -  endfun
    +  enddef
     ENDLET
    -exec join(flatten(s:build_fun_lines), "\n")
    -
    -" First do some preprocessing for dynamic folding. Do this for the entire file
    -" so we don't accidentally start within a closed fold or something.
    -let s:allfolds = []
    -
    -if s:settings.dynamic_folds
    -  let s:lnum = 1
    -  let s:end = line('$')
    -  " save the fold text and set it to the default so we can find fold levels
    -  let s:foldtext_save = &foldtext
    -  setlocal foldtext&
    -
    -  " we will set the foldcolumn in the html to the greater of the maximum fold
    -  " level and the current foldcolumn setting
    -  let s:foldcolumn = &foldcolumn
    -
    -  " get all info needed to describe currently closed folds
    -  while s:lnum <= s:end
    -    if foldclosed(s:lnum) == s:lnum
    -      " default fold text has '+-' and then a number of dashes equal to fold
    -      " level, so subtract 2 from index of first non-dash after the dashes
    -      " in order to get the fold level of the current fold
    -      let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
    -      " store fold info for later use
    -      let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
    -      call add(s:allfolds, s:newfold)
    -      " open the fold so we can find any contained folds
    -      execute s:lnum.."foldopen"
    -    else
    -      if !s:settings.no_progress
    -	call s:pgb.incr()
    -	if s:pgb.needs_redraw
    -	  redrawstatus
    -	  let s:pgb.needs_redraw = 0
    +build_fun_lines += trim_tmp
    +execute join(build_fun_lines, "\n")
    +
    +# First do some preprocessing for dynamic folding. Do this for the entire file
    +# so we don't accidentally start within a closed fold or something.
    +var allfolds: list = []
    +
    +var foldcolumn: number
    +if settings.dynamic_folds
    +  def ProcessFolds()
    +    lnum = 1
    +    end = line('$')
    +    # save the fold text and set it to the default so we can find fold levels
    +    var foldtext_save = &foldtext
    +    setlocal foldtext&
    +
    +    # we will set the foldcolumn in the html to the greater of the maximum fold
    +    # level and the current foldcolumn setting
    +    foldcolumn = &foldcolumn
    +
    +    # get all info needed to describe currently closed folds
    +    while lnum <= end
    +      if foldclosed(lnum) == lnum
    +	# default fold text has '+-' and then a number of dashes equal to fold
    +	# level, so subtract 2 from index of first non-dash after the dashes
    +	# in order to get the fold level of the current fold
    +	var level = match(foldtextresult(lnum), '+-*\zs[^-]') - 2
    +	# store fold info for later use
    +	var newfold = {firstline: lnum, lastline: foldclosedend(lnum), level: level, type: "closed-fold"}
    +	add(allfolds, newfold)
    +	# open the fold so we can find any contained folds
    +	execute $":{lnum}foldopen"
    +      else
    +	if !settings.no_progress
    +	  pgb.Incr()
    +	  if pgb.needs_redraw
    +	    redrawstatus
    +	    pgb.needs_redraw = 0
    +	  endif
     	endif
    +	lnum = lnum + 1
           endif
    -      let s:lnum = s:lnum + 1
    -    endif
    -  endwhile
    +    endwhile
     
    -  " close all folds to get info for originally open folds
    -  silent! %foldclose!
    -  let s:lnum = 1
    -
    -  " the originally open folds will be all folds we encounter that aren't
    -  " already in the list of closed folds
    -  while s:lnum <= s:end
    -    if foldclosed(s:lnum) == s:lnum
    -      " default fold text has '+-' and then a number of dashes equal to fold
    -      " level, so subtract 2 from index of first non-dash after the dashes
    -      " in order to get the fold level of the current fold
    -      let s:level = match(foldtextresult(s:lnum), '+-*\zs[^-]') - 2
    -      let s:newfold = {'firstline': s:lnum, 'lastline': foldclosedend(s:lnum), 'level': s:level,'type': "closed-fold"}
    -      " only add the fold if we don't already have it
    -      if empty(s:allfolds) || index(s:allfolds, s:newfold) == -1
    -	let s:newfold.type = "open-fold"
    -	call add(s:allfolds, s:newfold)
    -      endif
    -      " open the fold so we can find any contained folds
    -      execute s:lnum.."foldopen"
    -    else
    -      if !s:settings.no_progress
    -	call s:pgb.incr()
    -	if s:pgb.needs_redraw
    -	  redrawstatus
    -	  let s:pgb.needs_redraw = 0
    +    # close all folds to get info for originally open folds
    +    silent! :%foldclose!
    +    lnum = 1
    +
    +    # the originally open folds will be all folds we encounter that aren't
    +    # already in the list of closed folds
    +    while lnum <= end
    +      if foldclosed(lnum) == lnum
    +	# default fold text has '+-' and then a number of dashes equal to fold
    +	# level, so subtract 2 from index of first non-dash after the dashes
    +	# in order to get the fold level of the current fold
    +	var level = match(foldtextresult(lnum), '+-*\zs[^-]') - 2
    +	var newfold = {firstline: lnum, lastline: foldclosedend(lnum), level: level, type: "closed-fold"}
    +	# only add the fold if we don't already have it
    +	if empty(allfolds) || index(allfolds, newfold) == -1
    +	  newfold.type = "open-fold"
    +	  add(allfolds, newfold)
    +	endif
    +	# open the fold so we can find any contained folds
    +	execute $":{lnum}foldopen"
    +      else
    +	if !settings.no_progress
    +	  pgb.Incr()
    +	  if pgb.needs_redraw
    +	    redrawstatus
    +	    pgb.needs_redraw = 0
    +	  endif
     	endif
    +	lnum = lnum + 1
           endif
    -      let s:lnum = s:lnum + 1
    -    endif
    -  endwhile
    +    endwhile
     
    -  " sort the folds so that we only ever need to look at the first item in the
    -  " list of folds
    -  call sort(s:allfolds, "s:FoldCompare")
    -
    -  let &l:foldtext = s:foldtext_save
    -  unlet s:foldtext_save
    -
    -  " close all folds again so we can get the fold text as we go
    -  silent! %foldclose!
    -
    -  " Go through and remove folds we don't need to (or cannot) process in the
    -  " current conversion range
    -  "
    -  " If a fold is removed which contains other folds, which are included, we need
    -  " to adjust the level of the included folds as used by the conversion logic
    -  " (avoiding special cases is good)
    -  "
    -  " Note any time we remove a fold, either all of the included folds are in it,
    -  " or none of them, because we only remove a fold if neither its start nor its
    -  " end are within the conversion range.
    -  let leveladjust = 0
    -  for afold in s:allfolds
    -    let removed = 0
    -    if exists("g:html_start_line") && exists("g:html_end_line")
    -      if afold.firstline < g:html_start_line
    -	if afold.lastline <= g:html_end_line && afold.lastline >= g:html_start_line
    -	  " if a fold starts before the range to convert but stops within the
    -	  " range, we need to include it. Make it start on the first converted
    -	  " line.
    -	  let afold.firstline = g:html_start_line
    -	else
    -	  " if the fold lies outside the range or the start and stop enclose
    -	  " the entire range, don't bother parsing it
    -	  call remove(s:allfolds, index(s:allfolds, afold))
    -	  let removed = 1
    -	  if afold.lastline > g:html_end_line
    -	    let leveladjust += 1
    +    # sort the folds so that we only ever need to look at the first item in the
    +    # list of folds
    +    sort(allfolds, FoldCompare)
    +
    +    &l:foldtext = foldtext_save
    +
    +    # close all folds again so we can get the fold text as we go
    +    silent! :%foldclose!
    +
    +    # Go through and remove folds we don't need to (or cannot) process in the
    +    # current conversion range
    +    #
    +    # If a fold is removed which contains other folds, which are included, we need
    +    # to adjust the level of the included folds as used by the conversion logic
    +    # (avoiding special cases is good)
    +    #
    +    # Note any time we remove a fold, either all of the included folds are in it,
    +    # or none of them, because we only remove a fold if neither its start nor its
    +    # end are within the conversion range.
    +    var leveladjust = 0
    +    for afold in allfolds
    +      var removed = 0
    +      if exists("g:html_start_line") && exists("g:html_end_line")
    +	if afold.firstline < g:html_start_line
    +	  if afold.lastline <= g:html_end_line && afold.lastline >= g:html_start_line
    +	    # if a fold starts before the range to convert but stops within the
    +	    # range, we need to include it. Make it start on the first converted
    +	    # line.
    +	    afold.firstline = g:html_start_line
    +	  else
    +	    # if the fold lies outside the range or the start and stop enclose
    +	    # the entire range, don't bother parsing it
    +	    remove(allfolds, index(allfolds, afold))
    +	    removed = 1
    +	    if afold.lastline > g:html_end_line
    +	      leveladjust += 1
    +	    endif
     	  endif
    +	elseif afold.firstline > g:html_end_line
    +	  # If the entire fold lies outside the range we need to remove it.
    +	  remove(allfolds, index(allfolds, afold))
    +	  removed = 1
    +	endif
    +      elseif exists("g:html_start_line")
    +	if afold.firstline < g:html_start_line
    +	  # if there is no last line, but there is a first line, the end of the
    +	  # fold will always lie within the region of interest, so keep it
    +	  afold.firstline = g:html_start_line
    +	endif
    +      elseif exists("g:html_end_line")
    +	# if there is no first line we default to the first line in the buffer so
    +	# the fold start will always be included if the fold itself is included.
    +	# If however the entire fold lies outside the range we need to remove it.
    +	if afold.firstline > g:html_end_line
    +	  remove(allfolds, index(allfolds, afold))
    +	  removed = 1
     	endif
    -      elseif afold.firstline > g:html_end_line
    -	" If the entire fold lies outside the range we need to remove it.
    -	call remove(s:allfolds, index(s:allfolds, afold))
    -	let removed = 1
    -      endif
    -    elseif exists("g:html_start_line")
    -      if afold.firstline < g:html_start_line
    -	" if there is no last line, but there is a first line, the end of the
    -	" fold will always lie within the region of interest, so keep it
    -	let afold.firstline = g:html_start_line
    -      endif
    -    elseif exists("g:html_end_line")
    -      " if there is no first line we default to the first line in the buffer so
    -      " the fold start will always be included if the fold itself is included.
    -      " If however the entire fold lies outside the range we need to remove it.
    -      if afold.firstline > g:html_end_line
    -	call remove(s:allfolds, index(s:allfolds, afold))
    -	let removed = 1
           endif
    -    endif
    -    if !removed
    -      let afold.level -= leveladjust
    -      if afold.level+1 > s:foldcolumn
    -	let s:foldcolumn = afold.level+1
    +      if !removed
    +	afold.level -= leveladjust
    +	if afold.level + 1 > foldcolumn
    +	  foldcolumn = afold.level + 1
    +	endif
           endif
    -    endif
    -  endfor
    -
    -  " if we've removed folds containing the conversion range from processing,
    -  " getting foldtext as we go won't know to open the removed folds, so the
    -  " foldtext would be wrong; open them now.
    -  "
    -  " Note that only when a start and an end line is specified will a fold
    -  " containing the current range ever be removed.
    -  while leveladjust > 0
    -    exe g:html_start_line.."foldopen"
    -    let leveladjust -= 1
    -  endwhile
    +    endfor
    +
    +    # if we've removed folds containing the conversion range from processing,
    +    # getting foldtext as we go won't know to open the removed folds, so the
    +    # foldtext would be wrong; open them now.
    +    #
    +    # Note that only when a start and an end line is specified will a fold
    +    # containing the current range ever be removed.
    +    while leveladjust > 0
    +      execute $":{g:html_start_line}foldopen"
    +      leveladjust -= 1
    +    endwhile
    +  enddef
    +  ProcessFolds()
     endif
     
    -" Now loop over all lines in the original text to convert to html.
    -" Use html_start_line and html_end_line if they are set.
    +# Now loop over all lines in the original text to convert to html.
    +# Use html_start_line and html_end_line if they are set.
     if exists("g:html_start_line")
    -  let s:lnum = html_start_line
    -  if s:lnum < 1 || s:lnum > line("$")
    -    let s:lnum = 1
    +  lnum = g:html_start_line
    +  if lnum < 1 || lnum > line("$")
    +    lnum = 1
       endif
     else
    -  let s:lnum = 1
    +  lnum = 1
     endif
     if exists("g:html_end_line")
    -  let s:end = html_end_line
    -  if s:end < s:lnum || s:end > line("$")
    -    let s:end = line("$")
    +  end = g:html_end_line
    +  if end < lnum || end > line("$")
    +    end = line("$")
       endif
     else
    -  let s:end = line("$")
    +  end = line("$")
     endif
     
    -" stack to keep track of all the folds containing the current line
    -let s:foldstack = []
    +# stack to keep track of all the folds containing the current line
    +var foldstack: list = []
     
    -if !s:settings.no_progress
    -  let s:pgb = s:ProgressBar("Processing lines:", s:end - s:lnum + 1, s:orgwin)
    +if !settings.no_progress
    +  pgb = ProgressBar.new("Processing lines:", end - lnum + 1, orgwin)
     endif
     
    -if s:settings.number_lines
    -  let s:margin = strlen(s:end) + 1
    +var margin: number
    +if settings.number_lines
    +  margin = strlen(end) + 1
     else
    -  let s:margin = 0
    +  margin = 0
     endif
     
    -if has('folding') && !s:settings.ignore_folding
    -  let s:foldfillchar = &fillchars[matchend(&fillchars, 'fold:')]
    -  if s:foldfillchar == ''
    -    let s:foldfillchar = '-'
    +var foldfillchar: string
    +var charidx: number
    +if has('folding') && !settings.ignore_folding
    +  charidx = matchend(&fillchars, 'fold:')
    +  if charidx >= 0
    +    foldfillchar = &fillchars[charidx(&fillchars, charidx)]
    +  endif
    +  if foldfillchar->empty()
    +    foldfillchar = '-'
       endif
     endif
    -let s:difffillchar = &fillchars[matchend(&fillchars, 'diff:')]
    -if s:difffillchar == ''
    -  let s:difffillchar = '-'
    +var difffillchar: string
    +charidx = matchend(&fillchars, 'diff:')
    +if charidx >= 0
    +  difffillchar = &fillchars[charidx(&fillchars, charidx)]
    +endif
    +if difffillchar->empty()
    +  difffillchar = '-'
     endif
     
    -let s:foldId = 0
    -
    -if !s:settings.expand_tabs
    -  " If keeping tabs, add them to printable characters so we keep them when
    -  " formatting text (strtrans() doesn't replace printable chars)
    -  let s:old_isprint = &isprint
    +var foldId = 0
    +var old_isprint: string
    +if !settings.expand_tabs
    +  # If keeping tabs, add them to printable characters so we keep them when
    +  # formatting text (strtrans() doesn't replace printable chars)
    +  old_isprint = &isprint
       setlocal isprint+=9
     endif
     
    -while s:lnum <= s:end
    -
    -  " If there are filler lines for diff mode, show these above the line.
    -  call s:Add_diff_fill(s:lnum)
    +def ProcessLines()
    +  var all_lines = getline(lnum, end)
    +  var start_lnum = lnum
     
    -  " Start the line with the line number.
    -  if s:settings.number_lines
    -    let s:numcol = repeat(' ', s:margin - 1 - strlen(s:lnum)) .. s:lnum .. ' '
    -  endif
    +  while lnum <= end
     
    -  let s:new = ""
    +    # If there are filler lines for diff mode, show these above the line.
    +    Add_diff_fill(lnum)
     
    -  if has('folding') && !s:settings.ignore_folding && foldclosed(s:lnum) > -1 && !s:settings.dynamic_folds
    -    "
    -    " This is the beginning of a folded block (with no dynamic folding)
    -    let s:new = foldtextresult(s:lnum)
    -    if !s:settings.no_pre
    -      " HTML line wrapping is off--go ahead and fill to the margin
    -      let s:new = s:new .. repeat(s:foldfillchar, &columns - strlen(s:new))
    +    # Start the line with the line number.
    +    var numcol: string
    +    if settings.number_lines
    +      numcol = repeat(' ', margin - 1 - strlen(lnum)) .. lnum .. ' '
         endif
     
    -    " put numcol in a separate group for sake of unselectable text
    -    let s:new = (s:settings.number_lines ? s:HtmlFormat_n(s:numcol, s:FOLDED_ID, 0, s:lnum): "") .. s:HtmlFormat_t(s:new, s:FOLDED_ID, 0)
    -
    -    " Skip to the end of the fold
    -    let s:new_lnum = foldclosedend(s:lnum)
    +    var new: string
     
    -    if !s:settings.no_progress
    -      call s:pgb.incr(s:new_lnum - s:lnum)
    -    endif
    +    if has('folding') && !settings.ignore_folding && foldclosed(lnum) > -1 && !settings.dynamic_folds
    +      #
    +      # This is the beginning of a folded block (with no dynamic folding)
    +      new = foldtextresult(lnum)
    +      if !settings.no_pre
    +	# HTML line wrapping is off--go ahead and fill to the margin
    +	new ..= repeat(foldfillchar, &columns - strlen(new))
    +      endif
     
    -    let s:lnum = s:new_lnum
    +      # put numcol in a separate group for sake of unselectable text
    +      new = (settings.number_lines ? HtmlFormat_n(numcol, FOLDED_ID, 0, lnum) : "") .. HtmlFormat_t(new, FOLDED_ID, 0)
     
    -  else
    -    "
    -    " A line that is not folded, or doing dynamic folding.
    -    "
    -    let s:line = getline(s:lnum)
    -    let s:len = strlen(s:line)
    -
    -    if s:settings.dynamic_folds
    -      " First insert a closing for any open folds that end on this line
    -      while !empty(s:foldstack) && get(s:foldstack,0).lastline == s:lnum-1
    -	let s:new = s:new..""
    -	call remove(s:foldstack, 0)
    -      endwhile
    +      # Skip to the end of the fold
    +      var new_lnum = foldclosedend(lnum)
     
    -      " Now insert an opening for any new folds that start on this line
    -      let s:firstfold = 1
    -      while !empty(s:allfolds) && get(s:allfolds,0).firstline == s:lnum
    -	let s:foldId = s:foldId + 1
    -	let s:new ..= ""
    -
    -
    -	" Unless disabled, add a fold column for the opening line of a fold.
    -	"
    -	" Note that dynamic folds require using css so we just use css to take
    -	" care of the leading spaces rather than using   in the case of
    -	" html_no_pre to make it easier
    -	if !s:settings.no_foldcolumn
    -	  " add fold column that can open the new fold
    -	  if s:allfolds[0].level > 1 && s:firstfold
    -	    let s:new = s:new .. s:FoldColumn_build('|', s:allfolds[0].level - 1, 0, "",
    -		  \ 'toggle-open FoldColumn','javascript:toggleFold("fold'..s:foldstack[0].id..s:settings.id_suffix..'");')
    -	  endif
    -	  " add the filler spaces separately from the '+' char so that it can be
    -	  " shown/hidden separately during a hover unfold
    -	  let s:new = s:new .. s:FoldColumn_build("+", 1, 0, "",
    -		\ 'toggle-open FoldColumn', 'javascript:toggleFold("fold'..s:foldId..s:settings.id_suffix..'");')
    -	  " If this is not the last fold we're opening on this line, we need
    -	  " to keep the filler spaces hidden if the fold is opened by mouse
    -	  " hover. If it is the last fold to open in the line, we shouldn't hide
    -	  " them, so don't apply the toggle-filler class.
    -	  let s:new = s:new .. s:FoldColumn_build(" ", 1, s:foldcolumn - s:allfolds[0].level - 1, "",
    -		\ 'toggle-open FoldColumn'.. (get(s:allfolds, 1, {'firstline': 0}).firstline == s:lnum ?" toggle-filler" :""),
    -		\ 'javascript:toggleFold("fold'..s:foldId..s:settings.id_suffix..'");')
    -
    -	  " add fold column that can close the new fold
    -	  " only add extra blank space if we aren't opening another fold on the
    -	  " same line
    -	  if get(s:allfolds, 1, {'firstline': 0}).firstline != s:lnum
    -	    let s:extra_space = s:foldcolumn - s:allfolds[0].level
    -	  else
    -	    let s:extra_space = 0
    -	  endif
    -	  if s:firstfold
    -	    " the first fold in a line has '|' characters from folds opened in
    -	    " previous lines, before the '-' for this fold
    -	    let s:new ..= s:FoldColumn_build('|', s:allfolds[0].level - 1, s:extra_space, '-',
    -		  \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'..s:foldId..s:settings.id_suffix..'");')
    -	  else
    -	    " any subsequent folds in the line only add a single '-'
    -	    let s:new = s:new .. s:FoldColumn_build("-", 1, s:extra_space, "",
    -		  \ 'toggle-closed FoldColumn', 'javascript:toggleFold("fold'..s:foldId..s:settings.id_suffix..'");')
    -	  endif
    -	  let s:firstfold = 0
    -	endif
    +      if !settings.no_progress
    +	pgb.Incr(new_lnum - lnum)
    +      endif
     
    -	" Add fold text, moving the span ending to the next line so collapsing
    -	" of folds works correctly.
    -	" Put numcol in a separate group for sake of unselectable text.
    -	let s:new = s:new .. (s:settings.number_lines ? s:HtmlFormat_n(s:numcol, s:FOLDED_ID, 0, 0) : "") .. substitute(s:HtmlFormat_t(foldtextresult(s:lnum), s:FOLDED_ID, 0), '', s:HtmlEndline..'\n\0', '')
    -	let s:new = s:new .. ""
    -
    -	" open the fold now that we have the fold text to allow retrieval of
    -	" fold text for subsequent folds
    -	execute s:lnum.."foldopen"
    -	call insert(s:foldstack, remove(s:allfolds,0))
    -	let s:foldstack[0].id = s:foldId
    -      endwhile
    +      lnum = new_lnum
     
    -      " Unless disabled, add a fold column for other lines.
    -      "
    -      " Note that dynamic folds require using css so we just use css to take
    -      " care of the leading spaces rather than using   in the case of
    -      " html_no_pre to make it easier
    -      if !s:settings.no_foldcolumn
    -	if empty(s:foldstack)
    -	  " add the empty foldcolumn for unfolded lines if there is a fold
    -	  " column at all
    -	  if s:foldcolumn > 0
    -	    let s:new = s:new .. s:FoldColumn_fill()
    +    else
    +      #
    +      # A line that is not folded, or doing dynamic folding.
    +      #
    +      var line = all_lines[lnum - start_lnum]
    +      var len = strlen(line)
    +
    +      if settings.dynamic_folds
    +	# First insert a closing for any open folds that end on this line
    +	while !empty(foldstack) && get(foldstack, 0).lastline == lnum - 1
    +	  new ..= ""
    +	  remove(foldstack, 0)
    +	endwhile
    +
    +	# Now insert an opening for any new folds that start on this line
    +	var firstfold = 1
    +	while !empty(allfolds) && get(allfolds, 0).firstline == lnum
    +	  foldId = foldId + 1
    +	  new ..= ""
    +
    +
    +	  # Unless disabled, add a fold column for the opening line of a fold.
    +	  #
    +	  # Note that dynamic folds require using css so we just use css to take
    +	  # care of the leading spaces rather than using   in the case of
    +	  # html_no_pre to make it easier
    +	  if !settings.no_foldcolumn
    +	    # add fold column that can open the new fold
    +	    if allfolds[0].level > 1 && firstfold
    +	      new ..= FoldColumn_build('|', allfolds[0].level - 1, 0, "",
    +		'toggle-open FoldColumn', 'javascript:toggleFold("fold' .. foldstack[0].id .. settings.id_suffix .. '");')
    +	    endif
    +	    # add the filler spaces separately from the '+' char so that it can be
    +	    # shown/hidden separately during a hover unfold
    +	    new ..= FoldColumn_build("+", 1, 0, "",
    +	      'toggle-open FoldColumn', 'javascript:toggleFold("fold' .. foldId .. settings.id_suffix .. '");')
    +	    # If this is not the last fold we're opening on this line, we need
    +	    # to keep the filler spaces hidden if the fold is opened by mouse
    +	    # hover. If it is the last fold to open in the line, we shouldn't hide
    +	    # them, so don't apply the toggle-filler class.
    +	    new ..= FoldColumn_build(" ", 1, foldcolumn - allfolds[0].level - 1, "",
    +	      'toggle-open FoldColumn' .. (get(allfolds, 1, {firstline: 0}).firstline == lnum ? " toggle-filler" : ""),
    +	      'javascript:toggleFold("fold' .. foldId .. settings.id_suffix .. '");')
    +
    +	    # add fold column that can close the new fold
    +	    # only add extra blank space if we aren't opening another fold on the
    +	    # same line
    +	    var extra_space: number
    +	    if get(allfolds, 1, {firstline: 0}).firstline != lnum
    +	      extra_space = foldcolumn - allfolds[0].level
    +	    else
    +	      extra_space = 0
    +	    endif
    +	    if firstfold
    +	      # the first fold in a line has '|' characters from folds opened in
    +	      # previous lines, before the '-' for this fold
    +	      new ..= FoldColumn_build('|', allfolds[0].level - 1, extra_space, '-',
    +		'toggle-closed FoldColumn', 'javascript:toggleFold("fold' .. foldId .. settings.id_suffix .. '");')
    +	    else
    +	      # any subsequent folds in the line only add a single '-'
    +	      new ..= FoldColumn_build("-", 1, extra_space, "",
    +		'toggle-closed FoldColumn', 'javascript:toggleFold("fold' .. foldId .. settings.id_suffix .. '");')
    +	    endif
    +	    firstfold = 0
     	  endif
    -	else
    -	  " add the fold column for folds not on the opening line
    -	  if get(s:foldstack, 0).firstline < s:lnum
    -	    let s:new = s:new .. s:FoldColumn_build('|', s:foldstack[0].level, s:foldcolumn - s:foldstack[0].level, "",
    -		  \ 'FoldColumn', 'javascript:toggleFold("fold'..s:foldstack[0].id..s:settings.id_suffix..'");')
    +
    +	  # Add fold text, moving the span ending to the next line so collapsing
    +	  # of folds works correctly.
    +	  # Put numcol in a separate group for sake of unselectable text.
    +	  new ..= (settings.number_lines ? HtmlFormat_n(numcol, FOLDED_ID, 0, 0) : "") .. substitute(HtmlFormat_t(foldtextresult(lnum), FOLDED_ID, 0), '', HtmlEndline .. '\n\0', '')
    +	  new ..= ""
    +
    +	  # open the fold now that we have the fold text to allow retrieval of
    +	  # fold text for subsequent folds
    +	  execute $":{lnum}foldopen"
    +	  insert(foldstack, remove(allfolds, 0), 0)
    +	  foldstack[0].id = foldId
    +	endwhile
    +
    +	# Unless disabled, add a fold column for other lines.
    +	#
    +	# Note that dynamic folds require using css so we just use css to take
    +	# care of the leading spaces rather than using   in the case of
    +	# html_no_pre to make it easier
    +	if !settings.no_foldcolumn
    +	  if empty(foldstack)
    +	    # add the empty foldcolumn for unfolded lines if there is a fold
    +	    # column at all
    +	    if foldcolumn > 0
    +	      new = new .. FoldColumn_fill()
    +	    endif
    +	  else
    +	    # add the fold column for folds not on the opening line
    +	    if get(foldstack, 0).firstline < lnum
    +	      new = new .. FoldColumn_build('|', foldstack[0].level, foldcolumn - foldstack[0].level, "",
    +		'FoldColumn', 'javascript:toggleFold("fold' .. foldstack[0].id .. settings.id_suffix .. '");')
    +	    endif
     	  endif
     	endif
           endif
    -    endif
     
    -    " Now continue with the unfolded line text
    -    if s:settings.number_lines
    -      let s:new = s:new .. s:HtmlFormat_n(s:numcol, s:LINENR_ID, 0, s:lnum)
    -    elseif s:settings.line_ids
    -      let s:new = s:new .. s:HtmlFormat_n("", s:LINENR_ID, 0, s:lnum)
    -    endif
    +      # Now continue with the unfolded line text
    +      if settings.number_lines
    +	new ..= HtmlFormat_n(numcol, LINENR_ID, 0, lnum)
    +      elseif settings.line_ids
    +	new ..= HtmlFormat_n("", LINENR_ID, 0, lnum)
    +      endif
     
    -    " Get the diff attribute, if any.
    -    let s:diffattr = diff_hlID(s:lnum, 1)
    +      # Get the diff attribute, if any.
    +      var diffattr = diff_hlID(lnum, 1)
     
    -    " initialize conceal info to act like not concealed, just in case
    -    let s:concealinfo = [0, '']
    +      # initialize conceal info to act like not concealed, just in case
    +      var concealinfo = [0, '']
     
    -    " Loop over each character in the line
    -    let s:col = 1
    +      # Loop over each character in the line
    +      var col = 1
     
    -    " most of the time we won't use the diff_id, initialize to zero
    -    let s:diff_id = 0
    +      # most of the time we won't use the diff_id, initialize to zero
    +      var diff_id = 0
     
    -    while s:col <= s:len || (s:col == 1 && s:diffattr)
    -      let s:startcol = s:col " The start column for processing text
    -      if !s:settings.ignore_conceal && has('conceal')
    -	let s:concealinfo = synconcealed(s:lnum, s:col)
    -      endif
    -      if !s:settings.ignore_conceal && s:concealinfo[0]
    -	let s:col = s:col + 1
    -	" Speed loop (it's small - that's the trick)
    -	" Go along till we find a change in the match sequence number (ending
    -	" the specific concealed region) or until there are no more concealed
    -	" characters.
    -	while s:col <= s:len && s:concealinfo == synconcealed(s:lnum, s:col) | let s:col = s:col + 1 | endwhile
    -      elseif s:diffattr
    -	let s:diff_id = diff_hlID(s:lnum, s:col)
    -	let s:id = synID(s:lnum, s:col, 1)
    -	let s:col = s:col + 1
    -	" Speed loop (it's small - that's the trick)
    -	" Go along till we find a change in hlID
    -	while s:col <= s:len && s:id == synID(s:lnum, s:col, 1)
    -	      \   && s:diff_id == diff_hlID(s:lnum, s:col) |
    -	      \     let s:col = s:col + 1 |
    -	      \ endwhile
    -	if s:len < &columns && !s:settings.no_pre
    -	  " Add spaces at the end of the raw text line to extend the changed
    -	  " line to the full width.
    -	  let s:line = s:line .. repeat(' ', &columns - virtcol([s:lnum, s:len]) - s:margin)
    -	  let s:len = &columns
    +      while col <= len || (col == 1 && diffattr != 0)
    +	var id: number
    +	var startcol = col # The start column for processing text
    +	if !settings.ignore_conceal && has('conceal')
    +	  concealinfo = synconcealed(lnum, col)
     	endif
    -      else
    -	let s:id = synID(s:lnum, s:col, 1)
    -	let s:col = s:col + 1
    -	" Speed loop (it's small - that's the trick)
    -	" Go along till we find a change in synID
    -	while s:col <= s:len && s:id == synID(s:lnum, s:col, 1) | let s:col = s:col + 1 | endwhile
    -      endif
    -
    -      if s:settings.ignore_conceal || !s:concealinfo[0]
    -	" Expand tabs if needed
    -	let s:expandedtab = strpart(s:line, s:startcol - 1, s:col - s:startcol)
    -	if s:settings.expand_tabs
    -	  let s:offset = 0
    -	  let s:idx = stridx(s:expandedtab, "\t")
    -	  let s:tablist = split(&vts,',')
    -	  if empty(s:tablist)
    -	    let s:tablist = [ &ts ]
    +	if !settings.ignore_conceal && concealinfo[0]
    +	  col = col + 1
    +	  # Speed loop (it's small - that's the trick)
    +	  # Go along till we find a change in the match sequence number (ending
    +	  # the specific concealed region) or until there are no more concealed
    +	  # characters.
    +	  while col <= len && concealinfo == synconcealed(lnum, col) | col = col + 1 | endwhile
    +	elseif diffattr != 0
    +	  diff_id = diff_hlID(lnum, col)
    +	  id = synID(lnum, col, 1)
    +	  col = col + 1
    +	  # Speed loop (it's small - that's the trick)
    +	  # Go along till we find a change in hlID
    +	  while col <= len && id == synID(lnum, col, 1) && diff_id == diff_hlID(lnum, col)
    +	    col = col + 1
    +	  endwhile
    +	  if len < &columns && !settings.no_pre
    +	    # Add spaces at the end of the raw text line to extend the changed
    +	    # line to the full width.
    +	    line = line .. repeat(' ', &columns - virtcol([lnum, len]) - margin)
    +	    len = &columns
     	  endif
    -	  let s:tabidx = 0
    -	  let s:tabwidth = 0
    -	  while s:idx >= 0
    -	    if s:startcol + s:idx == 1
    -	      let s:i = s:tablist[0]
    -	    else
    -	      " Get the character, which could be multiple bytes, which falls
    -	      " immediately before the found tab. Extract it by matching a
    -	      " character just prior to the column where the tab matches.
    -	      " We'll use this to get the byte index of the character
    -	      " immediately preceding the tab, so we can then look up the
    -	      " virtual column that character appears in, to determine how
    -	      " much of the current tabstop has been used up.
    -	      if s:idx == 0
    -		" if the found tab is the first character in the text being
    -		" processed, we need to get the character prior to the text,
    -		" given by startcol.
    -		let s:prevc = matchstr(s:line, '.\%' .. (s:startcol + s:offset) .. 'c')
    +	else
    +	  id = synID(lnum, col, 1)
    +	  col = col + 1
    +	  # Speed loop (it's small - that's the trick)
    +	  # Go along till we find a change in synID
    +	  while col <= len && id == synID(lnum, col, 1) | col = col + 1 | endwhile
    +	endif
    +	var expandedtab: string
    +	if settings.ignore_conceal || !concealinfo[0]
    +	  # Expand tabs if needed
    +	  expandedtab = strpart(line, startcol - 1, col - startcol)
    +	  if settings.expand_tabs
    +	    var offset = 0
    +	    var idx = stridx(expandedtab, "\t")
    +	    var tablist = mapnew(split(&vts, ','), (_, v) => str2nr(v))
    +	    if empty(tablist)
    +	      tablist = [&ts]
    +	    endif
    +	    var tabidx = 0
    +	    var tabwidth = 0
    +	    var i: number
    +	    while idx >= 0
    +	      if startcol + idx == 1
    +		i = tablist[0]
     	      else
    -		" Otherwise, the byte index of the tab into s:expandedtab is
    -		" given by s:idx.
    -		let s:prevc = matchstr(s:expandedtab, '.\%' .. (s:idx + 1) .. 'c')
    -	      endif
    -	      let s:vcol = virtcol([s:lnum, s:startcol + s:idx + s:offset - len(s:prevc)])
    -
    -	      " find the tabstop interval to use for the tab we just found. Keep
    -	      " adding tabstops (which could be variable) until we would exceed
    -	      " the virtual screen position of the start of the found tab.
    -	      while s:vcol >= s:tabwidth + s:tablist[s:tabidx]
    -		let s:tabwidth += s:tablist[s:tabidx]
    -		if s:tabidx < len(s:tablist)-1
    -		  let s:tabidx = s:tabidx+1
    +		# Get the character, which could be multiple bytes, which falls
    +		# immediately before the found tab. Extract it by matching a
    +		# character just prior to the column where the tab matches.
    +		# We'll use this to get the byte index of the character
    +		# immediately preceding the tab, so we can then look up the
    +		# virtual column that character appears in, to determine how
    +		# much of the current tabstop has been used up.
    +		var prevc: string
    +		if idx == 0
    +		  # if the found tab is the first character in the text being
    +		  # processed, we need to get the character prior to the text,
    +		  # given by startcol.
    +		  prevc = matchstr(line, '.\%' .. (startcol + offset) .. 'c')
    +		else
    +		  # Otherwise, the byte index of the tab into expandedtab is
    +		  # given by idx.
    +		  prevc = matchstr(expandedtab, '.\%' .. (idx + 1) .. 'c')
     		endif
    -	      endwhile
    -	      let s:i = s:tablist[s:tabidx] - (s:vcol - s:tabwidth)
    -	    endif
    -	    " update offset to keep the index within the line corresponding to
    -	    " actual tab characters instead of replaced spaces; s:idx reflects
    -	    " replaced spaces in s:expandedtab, s:offset cancels out all but
    -	    " the tab character itself.
    -	    let s:offset -= s:i - 1
    -	    let s:expandedtab = substitute(s:expandedtab, '\t', repeat(' ', s:i), '')
    -	    let s:idx = stridx(s:expandedtab, "\t")
    -	  endwhile
    -	end
    -
    -	" get the highlight group name to use
    -	let s:id = synIDtrans(s:id)
    -      else
    -	" use Conceal highlighting for concealed text
    -	let s:id = s:CONCEAL_ID
    -	let s:expandedtab = s:concealinfo[1]
    -      endif
    -
    -      " Output the text with the same synID, with class set to the highlight ID
    -      " name, unless it has been concealed completely.
    -      if strlen(s:expandedtab) > 0
    -	let s:new = s:new .. s:HtmlFormat(s:expandedtab,  s:id, s:diff_id, "", 0)
    -      endif
    -    endwhile
    -  endif
    +		var vcol = virtcol([lnum, startcol + idx + offset - len(prevc)])
    +
    +		# find the tabstop interval to use for the tab we just found. Keep
    +		# adding tabstops (which could be variable) until we would exceed
    +		# the virtual screen position of the start of the found tab.
    +		while vcol >= tabwidth + tablist[tabidx]
    +		  tabwidth += tablist[tabidx]
    +		  if tabidx < len(tablist) - 1
    +		    tabidx = tabidx + 1
    +		  endif
    +		endwhile
    +		i = tablist[tabidx] - (vcol - tabwidth)
    +	      endif
    +	      # update offset to keep the index within the line corresponding to
    +	      # actual tab characters instead of replaced spaces; idx reflects
    +	      # replaced spaces in expandedtab, offset cancels out all but
    +	      # the tab character itself.
    +	      offset -= i - 1
    +	      expandedtab = substitute(expandedtab, '\t', repeat(' ', i), '')
    +	      idx = stridx(expandedtab, "\t")
    +	    endwhile
    +	  endif
     
    -  call extend(s:lines, split(s:new..s:HtmlEndline, '\n', 1))
    -  if !s:settings.no_progress && s:pgb.needs_redraw
    -    redrawstatus
    -    let s:pgb.needs_redraw = 0
    -  endif
    -  let s:lnum = s:lnum + 1
    +	  # get the highlight group name to use
    +	  id = synIDtrans(id)
    +	else
    +	  # use Conceal highlighting for concealed text
    +	  id = CONCEAL_ID
    +	  expandedtab = concealinfo[1]
    +	endif
     
    -  if !s:settings.no_progress
    -    call s:pgb.incr()
    -  endif
    -endwhile
    +	# Output the text with the same synID, with class set to the highlight ID
    +	# name, unless it has been concealed completely.
    +	if strlen(expandedtab) > 0
    +	  new = new .. HtmlFormat(expandedtab, id, diff_id, "", false)
    +	endif
    +      endwhile
    +    endif
     
    -" Diff filler is returned based on what needs inserting *before* the given line.
    -" So to get diff filler at the end of the buffer, we need to use last line + 1
    -call s:Add_diff_fill(s:end+1)
    +    extend(lines, split(new .. HtmlEndline, '\n', 1))
    +    if !settings.no_progress && pgb.needs_redraw
    +      redrawstatus
    +      pgb.needs_redraw = 0
    +    endif
    +    lnum = lnum + 1
     
    -if s:settings.dynamic_folds
    -  " finish off any open folds
    -  while !empty(s:foldstack)
    -    let s:lines[-1]..=""
    -    call remove(s:foldstack, 0)
    +    if !settings.no_progress
    +      pgb.Incr()
    +    endif
    +  endwhile
    +enddef
    +ProcessLines()
    +
    +# Diff filler is returned based on what needs inserting *before* the given line.
    +# So to get diff filler at the end of the buffer, we need to use last line + 1
    +Add_diff_fill(end + 1)
    +
    +if settings.dynamic_folds
    +  # finish off any open folds
    +  while !empty(foldstack)
    +    lines[-1] ..= ""
    +    remove(foldstack, 0)
       endwhile
     
    -  " add fold column to the style list if not already there
    -  let s:id = s:FOLD_C_ID
    -  if !has_key(s:stylelist, s:id)
    -    let s:stylelist[s:id] = '.FoldColumn { ' .. s:CSS1(s:id) .. '}'
    +  # add fold column to the style list if not already there
    +  var id = FOLD_C_ID
    +  if !has_key(stylelist, id)
    +    stylelist[id] = '.FoldColumn { ' .. CSS1(id) .. '}'
       endif
     endif
     
    -if s:settings.no_pre
    -  if !s:settings.use_css
    -    " Close off the font tag that encapsulates the whole 
    -    call extend(s:lines, [""])
    +if settings.no_pre
    +  if !settings.use_css
    +    # Close off the font tag that encapsulates the whole 
    +    extend(lines, [""])
       else
    -    call extend(s:lines, ["
    "]) + extend(lines, ["
    "]) endif else - call extend(s:lines, ["
    "]) + extend(lines, ["
    "]) endif -if !s:settings.no_doc - call extend(s:lines, ["", ""]) +if !settings.no_doc + extend(lines, ["", ""]) endif -exe s:newwin .. "wincmd w" -call setline(1, s:lines) -unlet s:lines - -" Mangle modelines so Vim doesn't try to use HTML text as a modeline if editing -" this file in the future; need to do this after generating all the text in case -" the modeline text has different highlight groups which all turn out to be -" stripped from the final output. -%s!\v(%(^|\s+)%([Vv]i%(m%([<=>]?\d+)?)?|ex)):!\1\:!ge - -" The generated HTML is admittedly ugly and takes a LONG time to fold. -" Make sure the user doesn't do syntax folding when loading a generated file, -" using a modeline. -if !s:settings.no_modeline - call append(line('$'), "") +execute $":{newwin}wincmd w" +setline(1, lines) + +# Mangle modelines so Vim doesn't try to use HTML text as a modeline if editing +# this file in the future; need to do this after generating all the text in case +# the modeline text has different highlight groups which all turn out to be +# stripped from the final output. +:%s!\v(%(^|\s+)%([Vv]i%(m%([<=>]?\d+)?)?|ex)):!\1\:!ge + +# The generated HTML is admittedly ugly and takes a LONG time to fold. +# Make sure the user doesn't do syntax folding when loading a generated file, +# using a modeline. +if !settings.no_modeline + append(line('$'), "") endif -" Now, when we finally know which, we define the colors and styles -if s:settings.use_css && !s:settings.no_doc - 1;//+1 +# Now, when we finally know which, we define the colors and styles +if settings.use_css && !settings.no_doc + :1;//+1 - " Normal/global attributes - if s:settings.no_pre - call append('.', "body { color: " .. s:fgc .. "; background-color: " .. s:bgc .. "; font-family: ".. s:htmlfont .."; }") - + + # Normal/global attributes + if settings.no_pre + append('.', "body { color: " .. fgc .. "; background-color: " .. bgc .. "; font-family: " .. htmlfont .. "; }") + :+ else - call append('.', "pre { " .. s:whitespace .. "font-family: ".. s:htmlfont .."; color: " .. s:fgc .. "; background-color: " .. s:bgc .. "; }") - + + append('.', "pre { " .. whitespace .. "font-family: " .. htmlfont .. "; color: " .. fgc .. "; background-color: " .. bgc .. "; }") + :+ yank put execute "normal! ^cwbody\e" - " body should not have the wrap formatting, only the pre section - if s:whitespace != '' - exec 's#'..s:whitespace + # body should not have the wrap formatting, only the pre section + if whitespace != '' + execute 's#' .. whitespace endif endif - " fix browser inconsistencies (sometimes within the same browser) of different - " default font size for different elements - call append('.', '* { font-size: 1em; }') - + - " use color scheme styles for links - " browser-default blue/purple colors for links don't look like the existing theme and are unreadable on dark backgrounds - call append('.', 'a { color: inherit; }') - + - " if we use any input elements for unselectable content, make sure they look - " like normal text - if !empty(s:settings.prevent_copy) - if s:settings.use_input_for_pc !=# "none" - call append('.', 'input { border: none; margin: 0; padding: 0; font-family: '..s:htmlfont..'; }') - + - " ch units for browsers which support them, em units for a somewhat - " reasonable fallback. + # fix browser inconsistencies (sometimes within the same browser) of different + # default font size for different elements + append('.', '* { font-size: 1em; }') + :+ + # use color scheme styles for links + # browser-default blue/purple colors for links don't look like the existing theme and are unreadable on dark backgrounds + append('.', 'a { color: inherit; }') + :+ + # if we use any input elements for unselectable content, make sure they look + # like normal text + if !empty(settings.prevent_copy) + if settings.use_input_for_pc !=# "none" + append('.', 'input { border: none; margin: 0; padding: 0; font-family: ' .. htmlfont .. '; }') + :+ + # ch units for browsers which support them, em units for a somewhat + # reasonable fallback. for w in range(1, 20, 1) - call append('.', [ - \ "input[size='"..w.."'] { width: "..w.."em; width: "..w.."ch; }" - \ ]) - + + append('.', [ + "input[size='" .. w .. "'] { width: " .. w .. "em; width: " .. w .. "ch; }" + ]) + :+ endfor endif - if s:settings.use_input_for_pc !=# 'all' - let s:unselectable_styles = [] - if s:settings.prevent_copy =~# 'f' - call add(s:unselectable_styles, 'FoldColumn') + if settings.use_input_for_pc !=# 'all' + var unselectable_styles: list = [] + if settings.prevent_copy =~# 'f' + add(unselectable_styles, 'FoldColumn') endif - if s:settings.prevent_copy =~# 'n' - call add(s:unselectable_styles, 'LineNr') + if settings.prevent_copy =~# 'n' + add(unselectable_styles, 'LineNr') endif - if s:settings.prevent_copy =~# 't' && !s:settings.ignore_folding - call add(s:unselectable_styles, 'Folded') + if settings.prevent_copy =~# 't' && !settings.ignore_folding + add(unselectable_styles, 'Folded') endif - if s:settings.prevent_copy =~# 'd' - call add(s:unselectable_styles, 'DiffDelete') + if settings.prevent_copy =~# 'd' + add(unselectable_styles, 'DiffDelete') endif - if s:settings.use_input_for_pc !=# 'none' - call append('.', [ - \ '/* Note: IE does not support @supports conditionals, but also does not fully support', - \ ' "content:" with custom content, so we *want* the check to fail */', - \ '@supports ( content: attr(data-custom-content) ) {' - \ ]) - +3 + if settings.use_input_for_pc !=# 'none' + append('.', [ + '/* Note: IE does not support @supports conditionals, but also does not fully support', + ' "content:" with custom content, so we *want* the check to fail */', + '@supports ( content: attr(data-custom-content) ) {' + ]) + :+3 endif - " The line number column inside the foldtext is styled just like the fold - " text in Vim, but it should use the prevent_copy settings of line number - " rather than fold text. Apply the prevent_copy styles to foldtext - " specifically for line numbers, which always come after the fold column, - " or at the beginning of the line. - if s:settings.prevent_copy =~# 'n' && !s:settings.ignore_folding - call append('.', [ - \ ' .FoldColumn + .Folded, .Folded:first-child { user-select: none; }', - \ ' .FoldColumn + [data-Folded-content]::before, [data-Folded-content]:first-child::before { content: attr(data-Folded-content); }', - \ ' .FoldColumn + [data-Folded-content]::before, [data-Folded-content]:first-child::before { padding-bottom: 1px; display: inline-block; /* match the 1-px padding of standard items with background */ }', - \ ' .FoldColumn + span[data-Folded-content]::before, [data-Folded-content]:first-child::before { cursor: default; }', - \ ]) - +4 + # The line number column inside the foldtext is styled just like the fold + # text in Vim, but it should use the prevent_copy settings of line number + # rather than fold text. Apply the prevent_copy styles to foldtext + # specifically for line numbers, which always come after the fold column, + # or at the beginning of the line. + if settings.prevent_copy =~# 'n' && !settings.ignore_folding + append('.', [ + ' .FoldColumn + .Folded, .Folded:first-child { user-select: none; }', + ' .FoldColumn + [data-Folded-content]::before, [data-Folded-content]:first-child::before { content: attr(data-Folded-content); }', + ' .FoldColumn + [data-Folded-content]::before, [data-Folded-content]:first-child::before { padding-bottom: 1px; display: inline-block; /* match the 1-px padding of standard items with background */ }', + ' .FoldColumn + span[data-Folded-content]::before, [data-Folded-content]:first-child::before { cursor: default; }', + ]) + :+4 endif - for s:style_name in s:unselectable_styles - call append('.', [ - \ ' .'..s:style_name..' { user-select: none; }', - \ ' [data-'..s:style_name..'-content]::before { content: attr(data-'..s:style_name..'-content); }', - \ ' [data-'..s:style_name..'-content]::before { padding-bottom: 1px; display: inline-block; /* match the 1-px padding of standard items with background */ }', - \ ' span[data-'..s:style_name..'-content]::before { cursor: default; }', - \ ]) - +4 + for style_name in unselectable_styles + append('.', [ + ' .' .. style_name .. ' { user-select: none; }', + ' [data-' .. style_name .. '-content]::before { content: attr(data-' .. style_name .. '-content); }', + ' [data-' .. style_name .. '-content]::before { padding-bottom: 1px; display: inline-block; /* match the 1-px padding of standard items with background */ }', + ' span[data-' .. style_name .. '-content]::before { cursor: default; }', + ]) + :+4 endfor - if s:settings.use_input_for_pc !=# 'none' - " Note, the extra '}' is to match the "@supports" above - call append('.', [ - \ ' input { display: none; }', - \ '}' - \ ]) - +2 + if settings.use_input_for_pc !=# 'none' + # Note, the extra '}' is to match the "@supports" above + append('.', [ + ' input { display: none; }', + '}' + ]) + :+2 endif - unlet s:unselectable_styles endif - " Fix mouse cursor shape for the fallback method of uncopyable text - if s:settings.use_input_for_pc !=# 'none' - if s:settings.prevent_copy =~# 'f' - " Make the cursor show active fold columns as active areas, and empty fold - " columns as not interactive. - call append('.', ['input.FoldColumn { cursor: pointer; }', - \ 'input.FoldColumn[value="'..repeat(' ', s:foldcolumn)..'"] { cursor: default; }' - \ ]) - +2 - if s:settings.use_input_for_pc !=# 'all' - call append('.', [ - \ 'a[data-FoldColumn-content="'..repeat(' ', s:foldcolumn)..'"] { cursor: default; }' - \ ]) - +1 - end + # Fix mouse cursor shape for the fallback method of uncopyable text + if settings.use_input_for_pc !=# 'none' + if settings.prevent_copy =~# 'f' + # Make the cursor show active fold columns as active areas, and empty fold + # columns as not interactive. + append('.', ['input.FoldColumn { cursor: pointer; }', + 'input.FoldColumn[value="' .. repeat(' ', foldcolumn) .. '"] { cursor: default; }' + ]) + :+2 + if settings.use_input_for_pc !=# 'all' + append('.', [ + 'a[data-FoldColumn-content="' .. repeat(' ', foldcolumn) .. '"] { cursor: default; }' + ]) + :+1 + endif endif - " make line number column show as non-interactive if not selectable - if s:settings.prevent_copy =~# 'n' - call append('.', 'input.LineNr { cursor: default; }') - + + # make line number column show as non-interactive if not selectable + if settings.prevent_copy =~# 'n' + append('.', 'input.LineNr { cursor: default; }') + :+ endif - " make fold text and line number column within fold text show as - " non-interactive if not selectable - if (s:settings.prevent_copy =~# 'n' || s:settings.prevent_copy =~# 't') && !s:settings.ignore_folding - call append('.', 'input.Folded { cursor: default; }') - + + # make fold text and line number column within fold text show as + # non-interactive if not selectable + if (settings.prevent_copy =~# 'n' || settings.prevent_copy =~# 't') && !settings.ignore_folding + append('.', 'input.Folded { cursor: default; }') + :+ endif - " make diff filler show as non-interactive if not selectable - if s:settings.prevent_copy =~# 'd' - call append('.', 'input.DiffDelete { cursor: default; }') - + + # make diff filler show as non-interactive if not selectable + if settings.prevent_copy =~# 'd' + append('.', 'input.DiffDelete { cursor: default; }') + :+ endif endif endif endif -if !s:settings.use_css && !s:settings.no_doc - " For Netscape 4, set attributes too, though, strictly speaking, it's - " incorrect. - execute '%s:]*\):\r attributes too, though, strictly speaking, it's + # incorrect. + execute $':%s/]*\)/\r diffstylelist[k]), (_, v) => v != "")) endif - if !empty(s:stylelist) - call append('.', filter(map(keys(s:stylelist), "s:stylelist[v:val]"), 'v:val != ""')) + if !empty(stylelist) + append('.', filter(map(keys(stylelist), (_, k) => stylelist[k]), (_, v) => v != "")) endif endif -" Add hyperlinks -if !s:settings.no_links - %s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|>\|<\|"\)+\1\2+ge +# Add hyperlinks +if !settings.no_links + :%s+\(https\=://\S\{-}\)\(\([.,;:}]\=\(\s\|$\)\)\|[\\"'<>]\|>\|<\|"\)+\1\2+ge endif -" The DTD -if !s:settings.no_doc - if s:settings.use_xhtml - exe "normal! gg$a\n" - elseif s:html5 - exe "normal! gg0i\n" +# The DTD +if !settings.no_doc + if settings.use_xhtml + execute "normal! gg$a\n" + elseif html5 + execute "normal! gg0i\n" else - exe "normal! gg0i\n" + execute "normal! gg0i\n" endif endif -if s:settings.use_xhtml && !s:settings.no_doc - exe "normal! gg/ /home/jiangyinzuo/vim/src/testdir/Test_tohtml_basic.c.html - + diff --git a/src/testdir/samples/Test_tohtml_basic_no_css.c.html b/src/testdir/samples/Test_tohtml_basic_no_css.c.html index 272b71248a..0cba690cf7 100644 --- a/src/testdir/samples/Test_tohtml_basic_no_css.c.html +++ b/src/testdir/samples/Test_tohtml_basic_no_css.c.html @@ -4,7 +4,7 @@ /home/jiangyinzuo/vim/src/testdir/Test_tohtml_basic_no_css.c.html - + From c83d2f888e6a0a399f4c1c30bec1fd9e5b9664b1 Mon Sep 17 00:00:00 2001 From: "Wu, Zhenyu" Date: Fri, 29 May 2026 18:10:39 +0000 Subject: [PATCH 348/545] patch 9.2.0558: filetype: Popcap Reanimation files are not recognized Problem: filetype: Popcap Reanimation files are not recognized Solution: Recognize *.reanim files as xml filetype (Wu, Zhenyu). Reference: https://github.com/wszqkzqk/PvZ-Portable closes: #20354 Signed-off-by: Wu, Zhenyu Signed-off-by: Christian Brabandt --- runtime/autoload/dist/ft.vim | 2 ++ src/testdir/test_filetype.vim | 2 +- src/version.c | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index 9419cf58a5..a75d31bb59 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -2637,6 +2637,8 @@ const ft_from_ext = { "qmd": "quarto", # QuickBms "bms": "quickbms", + # Popcap Reanimation files + "reanim": "xml", # Racket (formerly detected as "scheme") "rkt": "racket", "rktd": "racket", diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 5eb08f277d..e2350871be 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -986,7 +986,7 @@ def s:GetFilenameChecks(): dict> 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', 'any/etc/xdg/menus/file.menu', 'file.atom', 'file.rss', 'file.cdxml', 'file.psc1', 'file.mpd', 'fonts.conf', 'file.xcu', 'file.xlb', 'file.xlc', 'file.xba', 'file.xpr', - 'file.xpfm', 'file.spfm', 'file.bxml', 'file.mmi', 'file.slnx', 'Directory.Packages.props', 'Directory.Build.targets', 'Directory.Build.props'], + 'file.xpfm', 'file.spfm', 'file.bxml', 'file.mmi', 'file.slnx', 'Directory.Packages.props', 'Directory.Build.targets', 'Directory.Build.props', 'file.reanim'], xmodmap: ['anyXmodmap', 'Xmodmap', 'some-Xmodmap', 'some-xmodmap', 'some-xmodmap-file', 'xmodmap', 'xmodmap-file'], xpm: ['file.xpm'], xpm2: ['file.xpm2'], diff --git a/src/version.c b/src/version.c index 685887351a..23d96b9e47 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 558, /**/ 557, /**/ From f31407f9fa7e38ba63bc0c7abe532db98a21532b Mon Sep 17 00:00:00 2001 From: Patrick Meiser-Knosowski Date: Fri, 29 May 2026 18:19:39 +0000 Subject: [PATCH 349/545] runtime(karel): Add indent plugin for Karel closes: #20357 Signed-off-by: Patrick Meiser-Knosowski Signed-off-by: Christian Brabandt --- .github/MAINTAINERS | 1 + runtime/indent/karel.vim | 121 ++++++++++++++++++ runtime/indent/testdir/karel.in | 211 ++++++++++++++++++++++++++++++++ runtime/indent/testdir/karel.ok | 211 ++++++++++++++++++++++++++++++++ 4 files changed, 544 insertions(+) create mode 100644 runtime/indent/karel.vim create mode 100644 runtime/indent/testdir/karel.in create mode 100644 runtime/indent/testdir/karel.ok diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS index 4c4873507d..58a8f94562 100644 --- a/.github/MAINTAINERS +++ b/.github/MAINTAINERS @@ -405,6 +405,7 @@ runtime/indent/json.vim @elzr runtime/indent/jsonc.vim @izhakjakov runtime/indent/julia.vim @carlobaldassi runtime/indent/just.vim @pbnj +runtime/indent/karel.vim @KnoP-01 runtime/indent/kdl.vim @imsnif @jiangyinzuo runtime/indent/kotlin.vim @udalov runtime/indent/krl.vim @KnoP-01 diff --git a/runtime/indent/karel.vim b/runtime/indent/karel.vim new file mode 100644 index 0000000000..9913607570 --- /dev/null +++ b/runtime/indent/karel.vim @@ -0,0 +1,121 @@ +" Vim indent file +" Language: Fanuc Karel +" Maintainer: Patrick Meiser-Knosowski +" Version: 1.0.0 +" Last Change: 28. May 2026 + +" Only load this indent file when no other was loaded. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal nolisp +setlocal nocindent +setlocal nosmartindent +setlocal autoindent +setlocal indentexpr=GetKarelIndent() +setlocal indentkeys=!^F,o,O,=~end,0=~else,0=~case,0=~until,0=~const,0=~type,0=~var,0=~begin +let b:undo_indent = "setlocal lisp< cindent< smartindent< autoindent< indentexpr< indentkeys<" + +if get(g:,'karelSpaceIndent',1) + " Use spaces, not tabs, for indention, 2 is enough. + " More or even tabs would waste valuable space on the teach pendant. + setlocal softtabstop=2 + setlocal shiftwidth=2 + setlocal expandtab + setlocal shiftround + let b:undo_indent = b:undo_indent." softtabstop< shiftwidth< expandtab< shiftround<" +endif + +" Only define the function once. +if exists("*GetKarelIndent") + finish +endif +let s:keepcpo = &cpo +set cpo&vim + +function GetKarelIndent() abort + + let currentLine = getline(v:lnum) + if currentLine =~? '\v^--' && !get(g:, 'karelCommentIndent', 0) + " If current line has a -- in column 1, keep zero indent. + " This may be useful if code is commented out at the first column. + return 0 + endif + + " Find a non-blank line above the current line. + let preNoneBlankLineNum = s:karelPreNoneBlank(v:lnum - 1) + if preNoneBlankLineNum == 0 + " At the start of the file use zero indent. + return 0 + endif + + let preNoneBlankLine = getline(preNoneBlankLineNum) + let ind = indent(preNoneBlankLineNum) + + " Define add 'shiftwidth' pattern + let addShiftwidthPattern = '\v^\s*(' + let addShiftwidthPattern ..= 'if>|while>|for>|using>|condition>|.*' + let addShiftwidthPattern ..= '|else>' + let addShiftwidthPattern ..= '|case>' + let addShiftwidthPattern ..= '|repeat>' + let addShiftwidthPattern ..= '|const>' + let addShiftwidthPattern ..= '|type>' + let addShiftwidthPattern ..= '|var>' + let addShiftwidthPattern ..= '|begin>' + let addShiftwidthPattern ..= '|routine>' + if get(g:, 'karelIndentBetweenPrg', 1) + let addShiftwidthPattern ..= '|program>' + endif + let addShiftwidthPattern ..= ')' + + " Define Subtract 'shiftwidth' pattern + let subtractShiftwidthPattern = '\v^\s*(' + let subtractShiftwidthPattern ..= 'end(if|while|for|using|condition|structure)?>' + let subtractShiftwidthPattern ..= '|else>' + let subtractShiftwidthPattern ..= '|case>|endselect>' + let subtractShiftwidthPattern ..= '|until>' + let subtractShiftwidthPattern ..= '|const>' + let subtractShiftwidthPattern ..= '|type>' + let subtractShiftwidthPattern ..= '|var>' + let subtractShiftwidthPattern ..= '|begin>' + let subtractShiftwidthPattern ..= ')' + + " Add shiftwidth + if preNoneBlankLine =~? addShiftwidthPattern + let ind += &sw + endif + + " Subtract shiftwidth + if currentLine =~? subtractShiftwidthPattern + let ind = ind - &sw + endif + + " First case after a select gets the indent of the select. + if currentLine =~? '\v^\s*case>' + \&& preNoneBlankLine =~? '\v^\s*select>' + let ind = ind + &sw + endif + + return ind +endfunction + +" This function works almost like prevnonblank() but handles &-headers, +" comments and continue instructions like blank lines +function s:karelPreNoneBlank(lnum) abort + + let nPreNoneBlank = prevnonblank(a:lnum) + + while nPreNoneBlank > 0 && getline(nPreNoneBlank) =~? '\v^\s*(\&\w\+|--)' + " Previous none blank line irrelevant. Look further aback. + let nPreNoneBlank = prevnonblank(nPreNoneBlank - 1) + endwhile + + return nPreNoneBlank +endfunction + +let &cpo = s:keepcpo +unlet s:keepcpo + +" vim:sw=2 sts=2 et diff --git a/runtime/indent/testdir/karel.in b/runtime/indent/testdir/karel.in new file mode 100644 index 0000000000..edee2bdbbd --- /dev/null +++ b/runtime/indent/testdir/karel.in @@ -0,0 +1,211 @@ +-- vim: set ft=karel sw=2 : + +-- START_INDENT +-- upper case +-- test karelIndentBetweenPrg not existing +-- test karelCommentIndent not existing +PROGRAM UcKarelPrg +%CMOSVARS +TYPE +Array4OfInt = ARRAY[4] OF integer +CONST +Success = 0 +TYPE +sub_struct = STRUCTURE +subs_field_1: INTEGER +subs_field_2: BOOLEAN +ENDSTRUCTURE +MyStruct = STRUCTURE +subs_field_1: INTEGER +subs_field_2: sub_struct +ENDSTRUCTURE +VAR +i1 :integer +b1 :boolean +fhCh :file +ROUTINE prg1() +VAR +i :integer +BEGIN + -- +END prg1 +ROUTINE fcnt1( s :string ) :string +VAR +i :integer +BEGIN +RETURN ' ' +END fcnt1 +BEGIN +IF b1 THEN + -- +ENDIF +IF b1 THEN + -- +ELSE + -- +ENDIF +WHILE b1 DO + -- +ENDWHILE +FOR i1 = 1 TO 10 DO + -- +ENDFOR +FOR i1 = 10 DOWNTO 1 DO + -- +ENDFOR +USING sub_struct, MyStruct DO + -- +ENDUSING +CONDITION[1]: +WHEN DIN[1] DO DOUT[1] = TRUE +ENDCONDITION +SELECT i1 OF +CASE (1): + -- +CASE (2,3): +-- +ELSE: + -- +ENDSELECT +END UcKarelPrg +-- END_INDENT + +-- START_INDENT +-- lower case +-- INDENT_EXE let g:karelIndentBetweenPrg = 0 +-- INDENT_EXE let g:karelCommentIndent = 0 +program LcKarelPrg +%CMOSVARS +type +Array4OfInt = ARRAY[4] OF integer +const +Success = 0 +type +sub_struct = structure +subs_field_1: INTEGER +subs_field_2: BOOLEAN +endstructure +MyStruct = structure +subs_field_1: INTEGER +subs_field_2: sub_struct +endstructure +var +i1 :integer +b1 :boolean +fhCh :file +routine prg1() +var +i :integer +begin + -- +end prg1 +routine fcnt1( s :string ) :string +var +i :integer +begin +return ' ' +end fcnt1 +begin +if b1 then + -- +endif +if b1 then + -- +else + -- +endif +while b1 do + -- +endwhile +for i1 = 1 to 10 do + -- +endfor +for i1 = 10 downto 1 do + -- +endfor +using sub_struct, MyStruct do + -- +endusing +condition[1]: +when DIN[1] do DOUT[1] = TRUE +endcondition +select i1 of +case (1): + -- +case (2,3): +-- +else: + -- +endselect +end LcKarelPrg +-- END_INDENT + +-- START_INDENT +-- mixed case +-- INDENT_EXE let g:karelIndentBetweenPrg = 1 +-- INDENT_EXE let g:karelCommentIndent = 1 +Program McKarelPrg +%CMOSVARS +Type +Array4OfInt = ARRAY[4] OF integer +Const +Success = 0 +Type +sub_struct = Structure +subs_field_1: INTEGER +subs_field_2: BOOLEAN +EndStructure +MyStruct = Structure +subs_field_1: INTEGER +subs_field_2: sub_struct +EndStructure +Var +i1 :integer +b1 :boolean +fhCh :file +Routine prg1() +Var +i :integer +Begin + -- +End prg1 +Routine fcnt1( s :string ) :string +Var +i :integer +Begin +Return ' ' +End fcnt1 +Begin +If b1 Then + -- +EndIf +If b1 Then + -- +Else + -- +EndIf +While b1 Do + -- +EndWhile +For i1 = 1 To 10 Do + -- +EndFor +For i1 = 10 DownTo 1 Do + -- +EndFor +Using sub_struct, MyStruct Do + -- +EndUsing +Condition[1]: +When DIN[1] Do DOUT[1] = TRUE +EndCondition +Select i1 Of +Case (1): + -- +Case (2,3): +-- +Else: + -- +EndSelect +End McKarelPrg +-- END_INDENT diff --git a/runtime/indent/testdir/karel.ok b/runtime/indent/testdir/karel.ok new file mode 100644 index 0000000000..a7b5e9fa6d --- /dev/null +++ b/runtime/indent/testdir/karel.ok @@ -0,0 +1,211 @@ +-- vim: set ft=karel sw=2 : + +-- START_INDENT +-- upper case +-- test karelIndentBetweenPrg not existing +-- test karelCommentIndent not existing +PROGRAM UcKarelPrg + %CMOSVARS +TYPE + Array4OfInt = ARRAY[4] OF integer +CONST + Success = 0 +TYPE + sub_struct = STRUCTURE + subs_field_1: INTEGER + subs_field_2: BOOLEAN + ENDSTRUCTURE + MyStruct = STRUCTURE + subs_field_1: INTEGER + subs_field_2: sub_struct + ENDSTRUCTURE +VAR + i1 :integer + b1 :boolean + fhCh :file + ROUTINE prg1() + VAR + i :integer + BEGIN + -- + END prg1 + ROUTINE fcnt1( s :string ) :string + VAR + i :integer + BEGIN + RETURN ' ' + END fcnt1 +BEGIN + IF b1 THEN + -- + ENDIF + IF b1 THEN + -- + ELSE + -- + ENDIF + WHILE b1 DO + -- + ENDWHILE + FOR i1 = 1 TO 10 DO + -- + ENDFOR + FOR i1 = 10 DOWNTO 1 DO + -- + ENDFOR + USING sub_struct, MyStruct DO + -- + ENDUSING + CONDITION[1]: + WHEN DIN[1] DO DOUT[1] = TRUE + ENDCONDITION + SELECT i1 OF + CASE (1): + -- + CASE (2,3): +-- + ELSE: + -- + ENDSELECT +END UcKarelPrg +-- END_INDENT + +-- START_INDENT +-- lower case +-- INDENT_EXE let g:karelIndentBetweenPrg = 0 +-- INDENT_EXE let g:karelCommentIndent = 0 +program LcKarelPrg +%CMOSVARS +type + Array4OfInt = ARRAY[4] OF integer +const + Success = 0 +type + sub_struct = structure + subs_field_1: INTEGER + subs_field_2: BOOLEAN + endstructure + MyStruct = structure + subs_field_1: INTEGER + subs_field_2: sub_struct + endstructure +var + i1 :integer + b1 :boolean + fhCh :file + routine prg1() + var + i :integer + begin + -- + end prg1 + routine fcnt1( s :string ) :string + var + i :integer + begin + return ' ' + end fcnt1 +begin + if b1 then + -- + endif + if b1 then + -- + else + -- + endif + while b1 do + -- + endwhile + for i1 = 1 to 10 do + -- + endfor + for i1 = 10 downto 1 do + -- + endfor + using sub_struct, MyStruct do + -- + endusing + condition[1]: + when DIN[1] do DOUT[1] = TRUE + endcondition + select i1 of + case (1): + -- + case (2,3): +-- + else: + -- + endselect +end LcKarelPrg +-- END_INDENT + +-- START_INDENT +-- mixed case +-- INDENT_EXE let g:karelIndentBetweenPrg = 1 +-- INDENT_EXE let g:karelCommentIndent = 1 +Program McKarelPrg + %CMOSVARS +Type + Array4OfInt = ARRAY[4] OF integer +Const + Success = 0 +Type + sub_struct = Structure + subs_field_1: INTEGER + subs_field_2: BOOLEAN + EndStructure + MyStruct = Structure + subs_field_1: INTEGER + subs_field_2: sub_struct + EndStructure +Var + i1 :integer + b1 :boolean + fhCh :file + Routine prg1() + Var + i :integer + Begin + -- + End prg1 + Routine fcnt1( s :string ) :string + Var + i :integer + Begin + Return ' ' + End fcnt1 +Begin + If b1 Then + -- + EndIf + If b1 Then + -- + Else + -- + EndIf + While b1 Do + -- + EndWhile + For i1 = 1 To 10 Do + -- + EndFor + For i1 = 10 DownTo 1 Do + -- + EndFor + Using sub_struct, MyStruct Do + -- + EndUsing + Condition[1]: + When DIN[1] Do DOUT[1] = TRUE + EndCondition + Select i1 Of + Case (1): + -- + Case (2,3): + -- + Else: + -- + EndSelect +End McKarelPrg +-- END_INDENT From 339b5236578489a54846c37f9aea285c66f57815 Mon Sep 17 00:00:00 2001 From: Maxim Kim Date: Fri, 29 May 2026 18:22:32 +0000 Subject: [PATCH 350/545] runtime(colors): update colorschemes closes: #20363 Signed-off-by: Maxim Kim Signed-off-by: Christian Brabandt --- runtime/colors/blue.vim | 11 ++++-- runtime/colors/catppuccin.vim | 34 ++++++++--------- runtime/colors/darkblue.vim | 13 ++++--- runtime/colors/delek.vim | 11 ++++-- runtime/colors/desert.vim | 11 ++++-- runtime/colors/elflord.vim | 11 ++++-- runtime/colors/evening.vim | 7 ++-- runtime/colors/habamax.vim | 72 ++++++++++++++++++++++------------- runtime/colors/industry.vim | 11 ++++-- runtime/colors/koehler.vim | 11 ++++-- runtime/colors/lunaperche.vim | 30 +++++++++------ runtime/colors/morning.vim | 11 ++++-- runtime/colors/murphy.vim | 13 ++++--- runtime/colors/pablo.vim | 13 ++++--- runtime/colors/peachpuff.vim | 11 ++++-- runtime/colors/retrobox.vim | 30 +++++++++------ runtime/colors/shine.vim | 11 ++++-- runtime/colors/slate.vim | 11 ++++-- runtime/colors/torte.vim | 13 ++++--- runtime/colors/unokai.vim | 11 ++++-- runtime/colors/wildcharm.vim | 32 +++++++++------- runtime/colors/zellner.vim | 11 ++++-- 22 files changed, 237 insertions(+), 152 deletions(-) diff --git a/runtime/colors/blue.vim b/runtime/colors/blue.vim index 5179667f5a..07b4785ced 100644 --- a/runtime/colors/blue.vim +++ b/runtime/colors/blue.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer Steven Vertigan " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 14 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -124,7 +124,8 @@ hi ToolbarButton guifg=#ffffff guibg=#005faf guisp=NONE gui=NONE ctermfg=231 cte hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#ffa500 guibg=NONE guisp=NONE gui=bold ctermfg=214 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=NONE guibg=NONE guisp=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#008787 guibg=NONE guisp=NONE gui=NONE ctermfg=30 ctermbg=NONE cterm=NONE term=NONE +hi VertSplit guifg=#5fffff guibg=NONE guisp=NONE gui=NONE ctermfg=87 ctermbg=NONE cterm=NONE term=NONE +hi VertSplitNC guifg=#008787 guibg=NONE guisp=NONE gui=NONE ctermfg=30 ctermbg=NONE cterm=NONE term=NONE hi Visual guifg=#ffffff guibg=#008787 guisp=NONE gui=NONE ctermfg=231 ctermbg=30 cterm=NONE term=reverse hi VisualNOS guifg=#008787 guibg=#ffffff guisp=NONE gui=NONE ctermfg=30 ctermbg=231 cterm=NONE term=NONE hi WarningMsg guifg=#d787d7 guibg=NONE guisp=NONE gui=NONE ctermfg=176 ctermbg=NONE cterm=NONE term=standout @@ -197,7 +198,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkyellow ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkcyan ctermbg=NONE cterm=NONE + hi VertSplit ctermfg=cyan ctermbg=NONE cterm=NONE + hi VertSplitNC ctermfg=darkcyan ctermbg=NONE cterm=NONE hi Visual ctermfg=white ctermbg=darkcyan cterm=NONE hi VisualNOS ctermfg=darkcyan ctermbg=white cterm=NONE hi WarningMsg ctermfg=magenta ctermbg=NONE cterm=NONE @@ -268,6 +270,7 @@ if s:t_Co >= 8 hi Type ctermfg=red ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=cyan ctermbg=NONE cterm=NONE + hi VertSplitNC ctermfg=darkcyan ctermbg=NONE cterm=NONE hi Visual ctermfg=black ctermbg=cyan cterm=NONE hi VisualNOS ctermfg=cyan ctermbg=gray cterm=NONE hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE diff --git a/runtime/colors/catppuccin.vim b/runtime/colors/catppuccin.vim index 96e9c96350..b144332276 100644 --- a/runtime/colors/catppuccin.vim +++ b/runtime/colors/catppuccin.vim @@ -2,10 +2,10 @@ " Description: Soothing pastel theme for the high-spirited! " Author: The Catppuccin Community " Maintainer: Mao-Yining -" URL: https://www.github.com/vim/colorschemes -" Last Change: 2026 Jan 31 +" URL: https://github.com/vim/colorschemes +" Last Change: 2026 May 04 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 hi clear let g:colors_name = 'catppuccin' @@ -80,7 +80,7 @@ if &background == 'dark' hi PmenuExtra guifg=#6c7086 guibg=#181825 guisp=NONE gui=NONE ctermfg=59 ctermbg=232 cterm=NONE term=NONE hi PmenuExtraSel guifg=#6c7086 guibg=#313244 guisp=NONE gui=bold ctermfg=59 ctermbg=236 cterm=bold term=bold hi PmenuMatch guifg=#cdd6f4 guibg=NONE guisp=NONE gui=bold ctermfg=189 ctermbg=NONE cterm=bold term=bold - hi PmenuMatchSel guifg=NONE guibg=NONE guisp=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold term=bold + hi PmenuMatchSel guifg=#cdd6f4 guibg=NONE guisp=NONE gui=bold ctermfg=189 ctermbg=NONE cterm=bold term=bold hi PmenuSbar guifg=NONE guibg=#313244 guisp=NONE gui=NONE ctermfg=NONE ctermbg=236 cterm=NONE term=reverse hi PmenuSel guifg=NONE guibg=#313244 guisp=NONE gui=bold ctermfg=NONE ctermbg=236 cterm=bold term=bold hi PmenuThumb guifg=NONE guibg=#6c7086 guisp=NONE gui=NONE ctermfg=NONE ctermbg=59 cterm=NONE term=NONE @@ -98,8 +98,8 @@ if &background == 'dark' hi SpellLocal guifg=NONE guibg=NONE guisp=#89b4fa gui=undercurl ctermfg=111 ctermbg=NONE cterm=underline term=underline hi SpellRare guifg=NONE guibg=NONE guisp=#a6e3a1 gui=undercurl ctermfg=114 ctermbg=NONE cterm=underline term=underline hi Statement guifg=#cba6f7 guibg=NONE guisp=NONE gui=NONE ctermfg=183 ctermbg=NONE cterm=NONE term=NONE - hi StatusLine guifg=#cdd6f4 guibg=#11111b guisp=NONE gui=NONE ctermfg=189 ctermbg=16 cterm=NONE term=bold,reverse - hi StatusLineNC guifg=#45475a guibg=#181825 guisp=NONE gui=NONE ctermfg=240 ctermbg=232 cterm=NONE term=bold,underline + hi StatusLine guifg=#cdd6f4 guibg=#11111b guisp=NONE gui=bold ctermfg=189 ctermbg=16 cterm=bold term=bold,reverse + hi StatusLineNC guifg=#6c7086 guibg=#181825 guisp=NONE gui=NONE ctermfg=59 ctermbg=232 cterm=NONE term=bold,underline hi StorageClass guifg=#f9e2af guibg=NONE guisp=NONE gui=NONE ctermfg=222 ctermbg=NONE cterm=NONE term=NONE hi String guifg=#a6e3a1 guibg=NONE guisp=NONE gui=NONE ctermfg=114 ctermbg=NONE cterm=NONE term=NONE hi Structure guifg=#f9e2af guibg=NONE guisp=NONE gui=NONE ctermfg=222 ctermbg=NONE cterm=NONE term=NONE @@ -207,8 +207,8 @@ if &background == 'dark' hi PmenuExtra ctermfg=Grey ctermbg=DarkGrey cterm=NONE hi PmenuExtraSel ctermfg=Grey ctermbg=DarkYellow cterm=bold hi PmenuMatch ctermfg=White ctermbg=NONE cterm=bold - hi PmenuMatchSel ctermfg=NONE ctermbg=NONE cterm=bold - hi PmenuSbar ctermfg=NONE ctermbg=DarkYellow cterm=NONE + hi PmenuMatchSel ctermfg=White ctermbg=NONE cterm=bold + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE hi PmenuSel ctermfg=NONE ctermbg=DarkYellow cterm=bold hi PmenuThumb ctermfg=NONE ctermbg=Grey cterm=NONE hi PreInsert ctermfg=Grey ctermbg=NONE cterm=NONE @@ -328,8 +328,8 @@ if &background == 'dark' hi PmenuExtra ctermfg=Grey ctermbg=DarkGrey cterm=NONE hi PmenuExtraSel ctermfg=Grey ctermbg=DarkYellow cterm=bold hi PmenuMatch ctermfg=White ctermbg=NONE cterm=bold - hi PmenuMatchSel ctermfg=NONE ctermbg=NONE cterm=bold - hi PmenuSbar ctermfg=NONE ctermbg=DarkYellow cterm=NONE + hi PmenuMatchSel ctermfg=White ctermbg=NONE cterm=bold + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE hi PmenuSel ctermfg=NONE ctermbg=DarkYellow cterm=bold hi PmenuThumb ctermfg=NONE ctermbg=Grey cterm=NONE hi PreInsert ctermfg=Grey ctermbg=NONE cterm=NONE @@ -466,7 +466,7 @@ if &background == 'light' hi PmenuExtra guifg=#9ca0b0 guibg=#e6e9ef guisp=NONE gui=NONE ctermfg=247 ctermbg=255 cterm=NONE term=NONE hi PmenuExtraSel guifg=#9ca0b0 guibg=#ccd0da guisp=NONE gui=bold ctermfg=247 ctermbg=253 cterm=bold term=bold hi PmenuMatch guifg=#4c4f69 guibg=NONE guisp=NONE gui=bold ctermfg=236 ctermbg=NONE cterm=bold term=bold - hi PmenuMatchSel guifg=NONE guibg=NONE guisp=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold term=bold + hi PmenuMatchSel guifg=#4c4f69 guibg=NONE guisp=NONE gui=bold ctermfg=236 ctermbg=NONE cterm=bold term=bold hi PmenuSbar guifg=NONE guibg=#ccd0da guisp=NONE gui=NONE ctermfg=NONE ctermbg=253 cterm=NONE term=reverse hi PmenuSel guifg=NONE guibg=#ccd0da guisp=NONE gui=bold ctermfg=NONE ctermbg=253 cterm=bold term=bold hi PmenuThumb guifg=NONE guibg=#9ca0b0 guisp=NONE gui=NONE ctermfg=NONE ctermbg=247 cterm=NONE term=NONE @@ -484,8 +484,8 @@ if &background == 'light' hi SpellLocal guifg=NONE guibg=NONE guisp=#1e66f5 gui=undercurl ctermfg=27 ctermbg=NONE cterm=underline term=underline hi SpellRare guifg=NONE guibg=NONE guisp=#40a02b gui=undercurl ctermfg=34 ctermbg=NONE cterm=underline term=underline hi Statement guifg=#8839ef guibg=NONE guisp=NONE gui=NONE ctermfg=93 ctermbg=NONE cterm=NONE term=NONE - hi StatusLine guifg=#4c4f69 guibg=#dce0e8 guisp=NONE gui=NONE ctermfg=236 ctermbg=254 cterm=NONE term=bold,reverse - hi StatusLineNC guifg=#bcc0cc guibg=#e6e9ef guisp=NONE gui=NONE ctermfg=250 ctermbg=255 cterm=NONE term=bold,underline + hi StatusLine guifg=#4c4f69 guibg=#dce0e8 guisp=NONE gui=bold ctermfg=236 ctermbg=254 cterm=bold term=bold,reverse + hi StatusLineNC guifg=#9ca0b0 guibg=#e6e9ef guisp=NONE gui=NONE ctermfg=247 ctermbg=255 cterm=NONE term=bold,underline hi StorageClass guifg=#df8e1d guibg=NONE guisp=NONE gui=NONE ctermfg=172 ctermbg=NONE cterm=NONE term=NONE hi String guifg=#40a02b guibg=NONE guisp=NONE gui=NONE ctermfg=34 ctermbg=NONE cterm=NONE term=NONE hi Structure guifg=#df8e1d guibg=NONE guisp=NONE gui=NONE ctermfg=172 ctermbg=NONE cterm=NONE term=NONE @@ -593,8 +593,8 @@ if &background == 'light' hi PmenuExtra ctermfg=DarkGrey ctermbg=Grey cterm=NONE hi PmenuExtraSel ctermfg=DarkGrey ctermbg=Grey cterm=bold hi PmenuMatch ctermfg=Black ctermbg=NONE cterm=bold - hi PmenuMatchSel ctermfg=NONE ctermbg=NONE cterm=bold - hi PmenuSbar ctermfg=NONE ctermbg=Grey cterm=NONE + hi PmenuMatchSel ctermfg=Black ctermbg=NONE cterm=bold + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE hi PmenuSel ctermfg=NONE ctermbg=Grey cterm=bold hi PmenuThumb ctermfg=NONE ctermbg=DarkGrey cterm=NONE hi PreInsert ctermfg=Black ctermbg=NONE cterm=NONE @@ -714,8 +714,8 @@ if &background == 'light' hi PmenuExtra ctermfg=DarkGrey ctermbg=Grey cterm=NONE hi PmenuExtraSel ctermfg=DarkGrey ctermbg=Grey cterm=bold hi PmenuMatch ctermfg=Black ctermbg=NONE cterm=bold - hi PmenuMatchSel ctermfg=NONE ctermbg=NONE cterm=bold - hi PmenuSbar ctermfg=NONE ctermbg=Grey cterm=NONE + hi PmenuMatchSel ctermfg=Black ctermbg=NONE cterm=bold + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE hi PmenuSel ctermfg=NONE ctermbg=Grey cterm=bold hi PmenuThumb ctermfg=NONE ctermbg=DarkGrey cterm=NONE hi PreInsert ctermfg=Black ctermbg=NONE cterm=NONE diff --git a/runtime/colors/darkblue.vim b/runtime/colors/darkblue.vim index b490b0599a..b00a05a45a 100644 --- a/runtime/colors/darkblue.vim +++ b/runtime/colors/darkblue.vim @@ -3,9 +3,9 @@ " Author: Original author Bohdan Vlasyuk " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 14 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -126,7 +126,8 @@ hi ToolbarButton guifg=#ffffff guibg=#0030ff guisp=NONE gui=NONE ctermfg=231 cte hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#90f020 guibg=NONE guisp=NONE gui=NONE ctermfg=118 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=#80a0ff guibg=NONE guisp=NONE gui=underline ctermfg=111 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#000000 guibg=#808080 guisp=NONE gui=NONE ctermfg=16 ctermbg=102 cterm=NONE term=NONE +hi VertSplit guifg=#000040 guibg=#c0c0c0 guisp=NONE gui=NONE ctermfg=17 ctermbg=252 cterm=NONE term=NONE +hi VertSplitNC guifg=#000000 guibg=#808080 guisp=NONE gui=NONE ctermfg=16 ctermbg=102 cterm=NONE term=NONE hi Visual guifg=#8080ff guibg=#ffffff guisp=NONE gui=reverse ctermfg=105 ctermbg=231 cterm=reverse term=reverse hi VisualNOS guifg=#8080ff guibg=#c0c0c0 guisp=NONE gui=reverse,underline ctermfg=105 ctermbg=252 cterm=reverse,underline term=NONE hi WarningMsg guifg=#ff0000 guibg=NONE guisp=NONE gui=NONE ctermfg=196 ctermbg=NONE cterm=NONE term=standout @@ -196,7 +197,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=green ctermbg=NONE cterm=NONE hi Underlined ctermfg=blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=black ctermbg=darkgrey cterm=NONE + hi VertSplit ctermfg=black ctermbg=grey cterm=NONE + hi VertSplitNC ctermfg=black ctermbg=darkgrey cterm=NONE hi Visual ctermfg=blue ctermbg=white cterm=reverse hi VisualNOS ctermfg=blue ctermbg=grey cterm=reverse,underline hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE @@ -264,7 +266,8 @@ if s:t_Co >= 8 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=grey ctermbg=black cterm=reverse + hi VertSplit ctermfg=darkblue ctermbg=grey cterm=reverse + hi VertSplitNC ctermfg=grey ctermbg=black cterm=reverse hi Visual ctermfg=blue ctermbg=grey cterm=reverse hi VisualNOS ctermfg=blue ctermbg=grey cterm=reverse,underline hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE diff --git a/runtime/colors/delek.vim b/runtime/colors/delek.vim index 541b63ec0e..5a19cd26f5 100644 --- a/runtime/colors/delek.vim +++ b/runtime/colors/delek.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer David Schweikert " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=light @@ -91,7 +91,8 @@ hi ToolbarButton guifg=#ffffff guibg=#bcbcbc guisp=NONE gui=bold ctermfg=231 cte hi ToolbarLine guifg=NONE guibg=#e4e4e4 guisp=NONE gui=NONE ctermfg=NONE ctermbg=254 cterm=NONE term=reverse hi Type guifg=#0000ff guibg=NONE guisp=NONE gui=bold ctermfg=21 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#6a5acd guibg=NONE guisp=NONE gui=underline ctermfg=62 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#e4e4e4 guibg=#00008b guisp=NONE gui=NONE ctermfg=254 ctermbg=18 cterm=NONE term=NONE +hi VertSplit guifg=#ffff00 guibg=#00008b guisp=NONE gui=NONE ctermfg=226 ctermbg=18 cterm=NONE term=NONE +hi VertSplitNC guifg=#e4e4e4 guibg=#00008b guisp=NONE gui=NONE ctermfg=254 ctermbg=18 cterm=NONE term=NONE hi Visual guifg=#000000 guibg=#d0d0d0 guisp=NONE gui=NONE ctermfg=16 ctermbg=252 cterm=NONE term=reverse hi VisualNOS guifg=NONE guibg=#ee0000 guisp=NONE gui=NONE ctermfg=NONE ctermbg=196 cterm=NONE term=NONE hi WarningMsg guifg=#cd00cd guibg=#ffffff guisp=NONE gui=NONE ctermfg=164 ctermbg=231 cterm=NONE term=standout @@ -164,7 +165,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=grey cterm=NONE hi Type ctermfg=blue ctermbg=NONE cterm=bold hi Underlined ctermfg=blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=grey ctermbg=darkblue cterm=NONE + hi VertSplit ctermfg=yellow ctermbg=darkblue cterm=NONE + hi VertSplitNC ctermfg=grey ctermbg=darkblue cterm=NONE hi Visual ctermfg=white ctermbg=darkgrey cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=darkred cterm=NONE hi WarningMsg ctermfg=darkmagenta ctermbg=white cterm=NONE @@ -236,6 +238,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkblue ctermbg=NONE cterm=NONE hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline hi VertSplit ctermfg=darkblue ctermbg=darkyellow cterm=reverse + hi VertSplitNC ctermfg=darkblue ctermbg=darkyellow cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=NONE diff --git a/runtime/colors/desert.vim b/runtime/colors/desert.vim index 8ce22ea80a..02c6371b69 100644 --- a/runtime/colors/desert.vim +++ b/runtime/colors/desert.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer Hans Fugal " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -91,7 +91,8 @@ hi ToolbarButton guifg=#333333 guibg=#ffde9b guisp=NONE gui=bold ctermfg=236 cte hi ToolbarLine guifg=NONE guibg=#666666 guisp=NONE gui=NONE ctermfg=NONE ctermbg=241 cterm=NONE term=reverse hi Type guifg=#bdb76b guibg=NONE guisp=NONE gui=bold ctermfg=143 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#75a0ff guibg=NONE guisp=NONE gui=underline ctermfg=111 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#7f7f8c guibg=#c2bfa5 guisp=NONE gui=NONE ctermfg=242 ctermbg=144 cterm=NONE term=NONE +hi VertSplit guifg=#333333 guibg=#c2bfa5 guisp=NONE gui=NONE ctermfg=236 ctermbg=144 cterm=NONE term=NONE +hi VertSplitNC guifg=#7f7f8c guibg=#c2bfa5 guisp=NONE gui=NONE ctermfg=242 ctermbg=144 cterm=NONE term=NONE hi Visual guifg=#f0e68c guibg=#6b8e24 guisp=NONE gui=NONE ctermfg=186 ctermbg=64 cterm=NONE term=reverse hi VisualNOS guifg=#f0e68c guibg=#6dceeb guisp=NONE gui=NONE ctermfg=186 ctermbg=81 cterm=NONE term=NONE hi WarningMsg guifg=#cd5c5c guibg=NONE guisp=NONE gui=bold ctermfg=167 ctermbg=NONE cterm=bold term=standout @@ -167,7 +168,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkgrey ctermbg=grey cterm=NONE + hi VertSplit ctermfg=black ctermbg=grey cterm=NONE + hi VertSplitNC ctermfg=darkgrey ctermbg=grey cterm=NONE hi Visual ctermfg=white ctermbg=darkgreen cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=bold @@ -237,6 +239,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=grey ctermbg=black cterm=reverse + hi VertSplitNC ctermfg=grey ctermbg=black cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=bold diff --git a/runtime/colors/elflord.vim b/runtime/colors/elflord.vim index 1a8e224658..d9852a2316 100644 --- a/runtime/colors/elflord.vim +++ b/runtime/colors/elflord.vim @@ -3,9 +3,9 @@ " Maintainer: original maintainer Ron Aaron " URL: https://www.github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -114,7 +114,8 @@ hi ToolbarButton guifg=#000000 guibg=#e5e5e5 guisp=NONE gui=bold ctermfg=16 cter hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#60ff60 guibg=NONE guisp=NONE gui=bold ctermfg=83 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#80a0ff guibg=NONE guisp=NONE gui=underline ctermfg=111 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#000000 guibg=#00cdcd guisp=NONE gui=NONE ctermfg=16 ctermbg=44 cterm=NONE term=NONE +hi VertSplit guifg=#000000 guibg=#00ffff guisp=NONE gui=NONE ctermfg=16 ctermbg=51 cterm=NONE term=NONE +hi VertSplitNC guifg=#000000 guibg=#00cdcd guisp=NONE gui=NONE ctermfg=16 ctermbg=44 cterm=NONE term=NONE hi Visual guifg=#000000 guibg=#a9a9a9 guisp=NONE gui=NONE ctermfg=16 ctermbg=145 cterm=NONE term=reverse hi VisualNOS guifg=NONE guibg=#000000 guisp=NONE gui=bold,underline ctermfg=NONE ctermbg=16 cterm=underline term=NONE hi WarningMsg guifg=#ff0000 guibg=NONE guisp=NONE gui=NONE ctermfg=196 ctermbg=NONE cterm=NONE term=standout @@ -188,7 +189,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=green ctermbg=NONE cterm=bold hi Underlined ctermfg=blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=black ctermbg=darkcyan cterm=NONE + hi VertSplit ctermfg=black ctermbg=cyan cterm=NONE + hi VertSplitNC ctermfg=black ctermbg=darkcyan cterm=NONE hi Visual ctermfg=black ctermbg=darkgrey cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE @@ -263,6 +265,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline hi VertSplit ctermfg=black ctermbg=darkcyan cterm=NONE + hi VertSplitNC ctermfg=black ctermbg=darkcyan cterm=NONE hi Visual ctermfg=black ctermbg=grey cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE diff --git a/runtime/colors/evening.vim b/runtime/colors/evening.vim index 62a0a8620a..ddfb1f2854 100644 --- a/runtime/colors/evening.vim +++ b/runtime/colors/evening.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer Steven Vertigan " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -59,7 +59,8 @@ hi! link TabPanelFill EndOfBuffer hi! link Tag Special hi! link Terminal Normal hi! link Typedef Type -hi! link VertSplit StatusLineNC +hi! link VertSplit StatusLine +hi! link VertSplitNC StatusLineNC hi! link diffBDiffer WarningMsg hi! link diffCommon WarningMsg hi! link diffDiffer WarningMsg diff --git a/runtime/colors/habamax.vim b/runtime/colors/habamax.vim index ed9fba0651..4958ccdb7d 100644 --- a/runtime/colors/habamax.vim +++ b/runtime/colors/habamax.vim @@ -3,9 +3,9 @@ " Author: Maxim Kim " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 22 +" Last Change: 2026 May 28 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -37,17 +37,17 @@ hi! link TabPanelFill Normal hi! link Terminal Normal hi! link asciidoctorBlock Special hi! link asciidoctorCode String -hi! link asciidoctorH1Delimiter Statement -hi! link asciidoctorH2Delimiter Statement -hi! link asciidoctorH3Delimiter Statement -hi! link asciidoctorH4Delimiter Statement -hi! link asciidoctorH5Delimiter Statement -hi! link asciidoctorH6Delimiter Statement +hi! link asciidoctorH1 htmlH1 +hi! link asciidoctorH2 htmlH1 +hi! link asciidoctorH3 htmlH1 +hi! link asciidoctorH4 htmlH1 +hi! link asciidoctorH5 htmlH1 +hi! link asciidoctorH6 htmlH1 hi! link asciidoctorListMarker Constant hi! link asciidoctorMacro Special hi! link asciidoctorOption Special hi! link asciidoctorSetextHeaderDelimiter Statement -hi! link asciidoctorTitleDelimiter Statement +hi! link asciidoctorTitle htmlH1 hi! link javaScriptFunction Statement hi! link javaScriptIdentifier Statement hi! link lspDiagSignErrorText Removed @@ -58,11 +58,14 @@ hi! link lspDiagVirtualTextError Removed hi! link lspDiagVirtualTextHint Added hi! link lspDiagVirtualTextInfo Question hi! link lspDiagVirtualTextWarning Changed +hi! link markdownHeadingRule Special hi! link markdownUrl String +hi! link rstSection htmlH1 hi! link rubyDefine Statement hi! link rubyMacro Statement hi! link sqlKeyword Statement hi! link sqlSpecial Constant +hi! link typstMarkupHeading htmlH1 hi! link vimCommentString Comment hi! link vimOper Normal hi! link vimParenSep Normal @@ -107,12 +110,15 @@ hi PmenuExtra guifg=#767676 guibg=#3a3a3a guisp=NONE gui=NONE ctermfg=243 ctermb hi PmenuExtraSel guifg=#9e9e9e guibg=#585858 guisp=NONE gui=NONE ctermfg=247 ctermbg=240 cterm=NONE term=NONE hi PmenuKind guifg=#5f8787 guibg=#3a3a3a guisp=NONE gui=NONE ctermfg=66 ctermbg=237 cterm=NONE term=NONE hi PmenuKindSel guifg=#5f8787 guibg=#585858 guisp=NONE gui=NONE ctermfg=66 ctermbg=240 cterm=NONE term=NONE -hi PmenuMatch guifg=#ffaf5f guibg=#3a3a3a guisp=NONE gui=NONE ctermfg=215 ctermbg=237 cterm=NONE term=NONE -hi PmenuMatchSel guifg=#ffaf5f guibg=#585858 guisp=NONE gui=NONE ctermfg=215 ctermbg=240 cterm=NONE term=NONE -hi PmenuSbar guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse +hi PmenuMatch guifg=#ffaf5f guibg=NONE guisp=NONE gui=NONE ctermfg=215 ctermbg=NONE cterm=NONE term=NONE +hi PmenuMatchSel guifg=#ffaf5f guibg=NONE guisp=NONE gui=NONE ctermfg=215 ctermbg=NONE cterm=NONE term=NONE +hi PmenuSbar guifg=NONE guibg=#585858 guisp=NONE gui=NONE ctermfg=NONE ctermbg=240 cterm=NONE term=reverse hi PmenuSel guifg=NONE guibg=#585858 guisp=NONE gui=NONE ctermfg=NONE ctermbg=240 cterm=NONE term=bold hi PmenuShadow guifg=#767676 guibg=#121212 guisp=NONE gui=NONE ctermfg=243 ctermbg=233 cterm=NONE term=NONE hi PmenuThumb guifg=NONE guibg=#767676 guisp=NONE gui=NONE ctermfg=NONE ctermbg=243 cterm=NONE term=NONE +hi Popup guifg=NONE guibg=#3a3a3a guisp=NONE gui=NONE ctermfg=NONE ctermbg=237 cterm=NONE term=NONE +hi PopupBorder guifg=#767676 guibg=#3a3a3a guisp=NONE gui=NONE ctermfg=243 ctermbg=237 cterm=NONE term=NONE +hi PopupTitle guifg=#9e9e9e guibg=#3a3a3a guisp=NONE gui=bold ctermfg=247 ctermbg=237 cterm=bold term=bold hi PreProc guifg=#af875f guibg=NONE guisp=NONE gui=NONE ctermfg=137 ctermbg=NONE cterm=NONE term=NONE hi Question guifg=#d7af87 guibg=NONE guisp=NONE gui=NONE ctermfg=180 ctermbg=NONE cterm=NONE term=standout hi QuickFixLine guifg=NONE guibg=#4f2f4f guisp=NONE gui=NONE ctermfg=251 ctermbg=53 cterm=NONE term=NONE @@ -136,17 +142,19 @@ hi Title guifg=NONE guibg=NONE guisp=NONE gui=bold ctermfg=NONE ctermbg=NONE cte hi TitleBar guifg=#dadada guibg=#3c3c3c guisp=NONE gui=NONE ctermfg=253 ctermbg=237 cterm=NONE term=NONE hi TitleBarNC guifg=#767676 guibg=#2c2c2c guisp=NONE gui=NONE ctermfg=243 ctermbg=236 cterm=NONE term=NONE hi Todo guifg=#dadada guibg=NONE guisp=NONE gui=bold ctermfg=253 ctermbg=NONE cterm=bold term=bold,reverse -hi ToolbarButton guifg=#767676 guibg=#1c1c1c guisp=NONE gui=bold,reverse ctermfg=243 ctermbg=234 cterm=bold,reverse term=bold,reverse +hi ToolbarButton guifg=#5f8787 guibg=#1c1c1c guisp=NONE gui=bold,reverse ctermfg=66 ctermbg=234 cterm=bold,reverse term=bold,reverse hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#5f87af guibg=NONE guisp=NONE gui=NONE ctermfg=67 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=NONE guibg=NONE guisp=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#767676 guibg=#767676 guisp=NONE gui=NONE ctermfg=243 ctermbg=243 cterm=NONE term=NONE +hi VertSplit guifg=#9e9e9e guibg=#9e9e9e guisp=NONE gui=NONE ctermfg=247 ctermbg=247 cterm=NONE term=NONE +hi VertSplitNC guifg=#767676 guibg=#767676 guisp=NONE gui=NONE ctermfg=243 ctermbg=243 cterm=NONE term=NONE hi Visual guifg=NONE guibg=#1a3456 guisp=NONE gui=NONE ctermfg=109 ctermbg=234 cterm=reverse term=reverse hi VisualNOS guifg=#1c1c1c guibg=#5f8787 guisp=NONE gui=NONE ctermfg=234 ctermbg=66 cterm=NONE term=NONE hi WarningMsg guifg=#d75f87 guibg=NONE guisp=NONE gui=NONE ctermfg=168 ctermbg=NONE cterm=NONE term=standout hi WildMenu guifg=#1c1c1c guibg=#d7af87 guisp=NONE gui=bold ctermfg=234 ctermbg=180 cterm=bold term=bold hi debugBreakpoint guifg=#1c1c1c guibg=#d75f87 guisp=NONE gui=NONE ctermfg=234 ctermbg=168 cterm=NONE term=NONE hi debugPC guifg=#1c1c1c guibg=#5f87af guisp=NONE gui=NONE ctermfg=234 ctermbg=67 cterm=NONE term=NONE +hi htmlH1 guifg=#dadada guibg=NONE guisp=NONE gui=bold ctermfg=253 ctermbg=NONE cterm=bold term=bold hi lCursor guifg=#1c1c1c guibg=#5fff00 guisp=NONE gui=NONE ctermfg=234 ctermbg=82 cterm=NONE term=NONE if s:tgc || s:t_Co >= 256 @@ -202,12 +210,15 @@ if s:t_Co >= 16 hi PmenuExtraSel ctermfg=NONE ctermbg=NONE cterm=bold hi PmenuKind ctermfg=NONE ctermbg=NONE cterm=bold,reverse hi PmenuKindSel ctermfg=NONE ctermbg=NONE cterm=bold - hi PmenuMatch ctermfg=NONE ctermbg=NONE cterm=bold - hi PmenuMatchSel ctermfg=darkyellow ctermbg=NONE cterm=bold - hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE - hi PmenuSel ctermfg=darkyellow ctermbg=NONE cterm=reverse + hi PmenuMatch ctermfg=NONE ctermbg=darkred cterm=reverse + hi PmenuMatchSel ctermfg=darkred ctermbg=NONE cterm=bold + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSel ctermfg=NONE ctermbg=NONE cterm=bold hi PmenuShadow ctermfg=NONE ctermbg=NONE cterm=NONE - hi PmenuThumb ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuThumb ctermfg=NONE ctermbg=NONE cterm=NONE + hi Popup ctermfg=NONE ctermbg=NONE cterm=reverse + hi PopupBorder ctermfg=NONE ctermbg=NONE cterm=reverse + hi PopupTitle ctermfg=NONE ctermbg=NONE cterm=bold,reverse hi PreProc ctermfg=darkyellow ctermbg=NONE cterm=NONE hi Question ctermfg=yellow ctermbg=NONE cterm=NONE hi QuickFixLine ctermfg=black ctermbg=magenta cterm=NONE @@ -231,17 +242,19 @@ if s:t_Co >= 16 hi TitleBar ctermfg=white ctermbg=black cterm=NONE hi TitleBarNC ctermfg=darkgray ctermbg=black cterm=NONE hi Todo ctermfg=white ctermbg=NONE cterm=bold - hi ToolbarButton ctermfg=darkgray ctermbg=black cterm=bold,reverse + hi ToolbarButton ctermfg=NONE ctermbg=NONE cterm=reverse hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkblue ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkgray ctermbg=darkgray cterm=NONE + hi VertSplit ctermfg=gray ctermbg=gray cterm=NONE + hi VertSplitNC ctermfg=darkgray ctermbg=darkgray cterm=NONE hi Visual ctermfg=cyan ctermbg=black cterm=reverse hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE hi WildMenu ctermfg=black ctermbg=yellow cterm=bold hi debugBreakpoint ctermfg=black ctermbg=red cterm=NONE hi debugPC ctermfg=black ctermbg=darkblue cterm=NONE + hi htmlH1 ctermfg=white ctermbg=NONE cterm=bold hi lCursor ctermfg=black ctermbg=green cterm=NONE finish endif @@ -284,12 +297,15 @@ if s:t_Co >= 8 hi PmenuExtraSel ctermfg=NONE ctermbg=NONE cterm=bold hi PmenuKind ctermfg=NONE ctermbg=NONE cterm=bold,reverse hi PmenuKindSel ctermfg=NONE ctermbg=NONE cterm=bold - hi PmenuMatch ctermfg=NONE ctermbg=NONE cterm=bold - hi PmenuMatchSel ctermfg=darkyellow ctermbg=NONE cterm=bold - hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE - hi PmenuSel ctermfg=darkyellow ctermbg=NONE cterm=reverse + hi PmenuMatch ctermfg=NONE ctermbg=darkred cterm=reverse + hi PmenuMatchSel ctermfg=darkred ctermbg=NONE cterm=bold + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuSel ctermfg=NONE ctermbg=NONE cterm=bold hi PmenuShadow ctermfg=NONE ctermbg=NONE cterm=NONE - hi PmenuThumb ctermfg=NONE ctermbg=NONE cterm=reverse + hi PmenuThumb ctermfg=NONE ctermbg=NONE cterm=NONE + hi Popup ctermfg=NONE ctermbg=NONE cterm=reverse + hi PopupBorder ctermfg=NONE ctermbg=NONE cterm=reverse + hi PopupTitle ctermfg=NONE ctermbg=NONE cterm=bold,reverse hi PreProc ctermfg=darkyellow ctermbg=NONE cterm=NONE hi Question ctermfg=darkyellow ctermbg=NONE cterm=NONE hi QuickFixLine ctermfg=black ctermbg=magenta cterm=NONE @@ -313,17 +329,19 @@ if s:t_Co >= 8 hi TitleBar ctermfg=white ctermbg=black cterm=NONE hi TitleBarNC ctermfg=darkgray ctermbg=black cterm=NONE hi Todo ctermfg=white ctermbg=NONE cterm=bold - hi ToolbarButton ctermfg=gray ctermbg=black cterm=reverse + hi ToolbarButton ctermfg=NONE ctermbg=NONE cterm=reverse hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkblue ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=gray ctermbg=gray cterm=NONE + hi VertSplitNC ctermfg=gray ctermbg=gray cterm=NONE hi Visual ctermfg=darkcyan ctermbg=black cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=reverse hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE hi debugBreakpoint ctermfg=darkcyan ctermbg=NONE cterm=reverse hi debugPC ctermfg=darkblue ctermbg=NONE cterm=reverse + hi htmlH1 ctermfg=white ctermbg=NONE cterm=bold hi lCursor ctermfg=black ctermbg=green cterm=NONE finish endif diff --git a/runtime/colors/industry.vim b/runtime/colors/industry.vim index 5093293ae6..0bd318a620 100644 --- a/runtime/colors/industry.vim +++ b/runtime/colors/industry.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer Shian Lee. " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -92,7 +92,8 @@ hi Todo guifg=#005fff guibg=#ffff00 guisp=NONE gui=NONE ctermfg=27 ctermbg=226 c hi ToolbarLine guifg=NONE guibg=#303030 guisp=NONE gui=NONE ctermfg=NONE ctermbg=236 cterm=NONE term=reverse hi Type guifg=#00ff00 guibg=NONE guisp=NONE gui=bold ctermfg=46 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#87afff guibg=NONE guisp=NONE gui=underline ctermfg=111 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#000000 guibg=#6c6c6c guisp=NONE gui=NONE ctermfg=16 ctermbg=242 cterm=NONE term=NONE +hi VertSplit guifg=#000000 guibg=#dadada guisp=NONE gui=NONE ctermfg=16 ctermbg=253 cterm=NONE term=NONE +hi VertSplitNC guifg=#000000 guibg=#6c6c6c guisp=NONE gui=NONE ctermfg=16 ctermbg=242 cterm=NONE term=NONE hi Visual guifg=#dadada guibg=#6c6c6c guisp=NONE gui=NONE ctermfg=253 ctermbg=242 cterm=NONE term=reverse hi VisualNOS guifg=#dadada guibg=#6c6c6c guisp=NONE gui=NONE ctermfg=253 ctermbg=242 cterm=NONE term=NONE hi WarningMsg guifg=#ff0000 guibg=NONE guisp=NONE gui=bold ctermfg=196 ctermbg=NONE cterm=bold term=standout @@ -165,7 +166,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=white ctermbg=darkgrey cterm=NONE hi Type ctermfg=green ctermbg=NONE cterm=bold hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=black ctermbg=grey cterm=NONE + hi VertSplit ctermfg=black ctermbg=white cterm=NONE + hi VertSplitNC ctermfg=black ctermbg=grey cterm=NONE hi Visual ctermfg=black ctermbg=grey cterm=NONE hi VisualNOS ctermfg=white ctermbg=grey cterm=NONE hi WarningMsg ctermfg=red ctermbg=NONE cterm=bold @@ -238,6 +240,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=grey ctermbg=black cterm=reverse + hi VertSplitNC ctermfg=grey ctermbg=black cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=black ctermbg=grey cterm=NONE hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE diff --git a/runtime/colors/koehler.vim b/runtime/colors/koehler.vim index 8268f4e54c..8247692cea 100644 --- a/runtime/colors/koehler.vim +++ b/runtime/colors/koehler.vim @@ -3,9 +3,9 @@ " Maintainer: original maintainer Ron Aaron " URL: https://www.github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 27 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -114,7 +114,8 @@ hi ToolbarButton guifg=#000000 guibg=#e5e5e5 guisp=NONE gui=bold ctermfg=16 cter hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#60ff60 guibg=NONE guisp=NONE gui=bold ctermfg=83 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#add8e6 guibg=NONE guisp=NONE gui=bold,underline ctermfg=153 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#0000ff guibg=#e5e5e5 guisp=NONE gui=NONE ctermfg=21 ctermbg=254 cterm=NONE term=NONE +hi VertSplit guifg=#0000ff guibg=#ffffff guisp=NONE gui=NONE ctermfg=21 ctermbg=231 cterm=NONE term=NONE +hi VertSplitNC guifg=#0000ff guibg=#e5e5e5 guisp=NONE gui=NONE ctermfg=21 ctermbg=254 cterm=NONE term=NONE hi Visual guifg=NONE guibg=#666666 guisp=NONE gui=reverse ctermfg=NONE ctermbg=59 cterm=reverse term=reverse hi VisualNOS guifg=NONE guibg=#000000 guisp=NONE gui=bold,underline ctermfg=NONE ctermbg=16 cterm=underline term=NONE hi WarningMsg guifg=#ff0000 guibg=NONE guisp=NONE gui=NONE ctermfg=196 ctermbg=NONE cterm=NONE term=standout @@ -183,7 +184,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=green ctermbg=NONE cterm=bold hi Underlined ctermfg=blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkblue ctermbg=grey cterm=NONE + hi VertSplit ctermfg=darkblue ctermbg=white cterm=NONE + hi VertSplitNC ctermfg=darkblue ctermbg=grey cterm=NONE hi Visual ctermfg=NONE ctermbg=darkgrey cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE @@ -251,6 +253,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=darkblue ctermbg=grey cterm=NONE + hi VertSplitNC ctermfg=darkblue ctermbg=grey cterm=NONE hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE diff --git a/runtime/colors/lunaperche.vim b/runtime/colors/lunaperche.vim index af745e2cf2..15cd28c3ec 100644 --- a/runtime/colors/lunaperche.vim +++ b/runtime/colors/lunaperche.vim @@ -2,9 +2,9 @@ " Description: White(perchè il sole)/Black(la luna perchè?) background colorscheme. " Author: Maxim Kim " URL: https://www.github.com/vim/colorschemes -" Last Change: 2025 Oct 22 +" Last Change: 2026 Apr 30 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 hi clear let g:colors_name = 'lunaperche' @@ -138,8 +138,8 @@ if &background == 'dark' hi PmenuExtraSel guifg=#767676 guibg=#4e4e4e guisp=NONE gui=NONE ctermfg=243 ctermbg=239 cterm=NONE term=NONE hi PmenuKind guifg=#ff5f5f guibg=#303030 guisp=NONE gui=NONE ctermfg=203 ctermbg=236 cterm=NONE term=NONE hi PmenuKindSel guifg=#ff5f5f guibg=#4e4e4e guisp=NONE gui=NONE ctermfg=203 ctermbg=239 cterm=NONE term=NONE - hi PmenuMatch guifg=#d787d7 guibg=#303030 guisp=NONE gui=NONE ctermfg=176 ctermbg=236 cterm=NONE term=NONE - hi PmenuMatchSel guifg=#d787d7 guibg=#4e4e4e guisp=NONE gui=NONE ctermfg=176 ctermbg=239 cterm=NONE term=NONE + hi PmenuMatch guifg=#d787d7 guibg=NONE guisp=NONE gui=NONE ctermfg=176 ctermbg=NONE cterm=NONE term=NONE + hi PmenuMatchSel guifg=#d787d7 guibg=NONE guisp=NONE gui=NONE ctermfg=176 ctermbg=NONE cterm=NONE term=NONE hi PmenuSbar guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi PmenuSel guifg=NONE guibg=#4e4e4e guisp=NONE gui=NONE ctermfg=NONE ctermbg=239 cterm=NONE term=bold hi PmenuShadow guifg=#767676 guibg=#121212 guisp=NONE gui=NONE ctermfg=243 ctermbg=233 cterm=NONE term=NONE @@ -171,7 +171,8 @@ if &background == 'dark' hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#5fd75f guibg=NONE guisp=NONE gui=NONE ctermfg=77 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=NONE guibg=NONE guisp=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline term=underline - hi VertSplit guifg=#767676 guibg=#767676 guisp=NONE gui=NONE ctermfg=243 ctermbg=243 cterm=NONE term=NONE + hi VertSplit guifg=#c6c6c6 guibg=#c6c6c6 guisp=NONE gui=NONE ctermfg=251 ctermbg=251 cterm=NONE term=NONE + hi VertSplitNC guifg=#767676 guibg=#767676 guisp=NONE gui=NONE ctermfg=243 ctermbg=243 cterm=NONE term=NONE hi Visual guifg=NONE guibg=#104070 guisp=NONE gui=NONE ctermfg=32 ctermbg=16 cterm=reverse term=reverse hi VisualNOS guifg=#000000 guibg=#5fafaf guisp=NONE gui=NONE ctermfg=16 ctermbg=73 cterm=NONE term=NONE hi WarningMsg guifg=#ff5f5f guibg=NONE guisp=NONE gui=NONE ctermfg=203 ctermbg=NONE cterm=NONE term=standout @@ -266,7 +267,8 @@ if &background == 'dark' hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=green ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkgrey ctermbg=darkgrey cterm=NONE + hi VertSplit ctermfg=grey ctermbg=grey cterm=NONE + hi VertSplitNC ctermfg=darkgrey ctermbg=darkgrey cterm=NONE hi Visual ctermfg=darkblue ctermbg=black cterm=reverse hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE @@ -354,6 +356,7 @@ if &background == 'dark' hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=grey ctermbg=grey cterm=NONE + hi VertSplitNC ctermfg=grey ctermbg=grey cterm=NONE hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE @@ -426,8 +429,8 @@ if &background == 'light' hi PmenuExtraSel guifg=#767676 guibg=#c6c6c6 guisp=NONE gui=NONE ctermfg=243 ctermbg=251 cterm=NONE term=NONE hi PmenuKind guifg=#af0000 guibg=#e4e4e4 guisp=NONE gui=NONE ctermfg=124 ctermbg=254 cterm=NONE term=NONE hi PmenuKindSel guifg=#af0000 guibg=#c6c6c6 guisp=NONE gui=NONE ctermfg=124 ctermbg=251 cterm=NONE term=NONE - hi PmenuMatch guifg=#af00af guibg=#e4e4e4 guisp=NONE gui=NONE ctermfg=127 ctermbg=254 cterm=NONE term=NONE - hi PmenuMatchSel guifg=#af00af guibg=#c6c6c6 guisp=NONE gui=NONE ctermfg=127 ctermbg=251 cterm=NONE term=NONE + hi PmenuMatch guifg=#af00af guibg=NONE guisp=NONE gui=NONE ctermfg=127 ctermbg=NONE cterm=NONE term=NONE + hi PmenuMatchSel guifg=#af00af guibg=NONE guisp=NONE gui=NONE ctermfg=127 ctermbg=NONE cterm=NONE term=NONE hi PmenuSbar guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi PmenuSel guifg=NONE guibg=#c6c6c6 guisp=NONE gui=NONE ctermfg=NONE ctermbg=251 cterm=NONE term=bold hi PmenuShadow guifg=#767676 guibg=#303030 guisp=NONE gui=NONE ctermfg=243 ctermbg=236 cterm=NONE term=NONE @@ -459,7 +462,8 @@ if &background == 'light' hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#008700 guibg=NONE guisp=NONE gui=NONE ctermfg=28 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=NONE guibg=NONE guisp=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline term=underline - hi VertSplit guifg=#767676 guibg=#767676 guisp=NONE gui=NONE ctermfg=243 ctermbg=243 cterm=NONE term=NONE + hi VertSplit guifg=#000000 guibg=#000000 guisp=NONE gui=NONE ctermfg=16 ctermbg=16 cterm=NONE term=NONE + hi VertSplitNC guifg=#767676 guibg=#767676 guisp=NONE gui=NONE ctermfg=243 ctermbg=243 cterm=NONE term=NONE hi Visual guifg=NONE guibg=#bfdfff guisp=NONE gui=NONE ctermfg=67 ctermbg=231 cterm=reverse term=reverse hi VisualNOS guifg=#ffffff guibg=#008787 guisp=NONE gui=NONE ctermfg=231 ctermbg=30 cterm=NONE term=NONE hi WarningMsg guifg=#d70000 guibg=NONE guisp=NONE gui=bold ctermfg=160 ctermbg=NONE cterm=bold term=standout @@ -524,8 +528,8 @@ if &background == 'light' hi PmenuExtraSel ctermfg=black ctermbg=darkcyan cterm=NONE hi PmenuKind ctermfg=darkred ctermbg=grey cterm=NONE hi PmenuKindSel ctermfg=darkred ctermbg=darkcyan cterm=NONE - hi PmenuMatch ctermfg=darkmagenta ctermbg=grey cterm=NONE - hi PmenuMatchSel ctermfg=darkmagenta ctermbg=darkcyan cterm=NONE + hi PmenuMatch ctermfg=darkmagenta ctermbg=NONE cterm=NONE + hi PmenuMatchSel ctermfg=darkmagenta ctermbg=NONE cterm=NONE hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE hi PmenuSel ctermfg=black ctermbg=darkcyan cterm=NONE hi PmenuShadow ctermfg=NONE ctermbg=NONE cterm=NONE @@ -557,7 +561,8 @@ if &background == 'light' hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkgrey ctermbg=darkgrey cterm=NONE + hi VertSplit ctermfg=black ctermbg=black cterm=NONE + hi VertSplitNC ctermfg=darkgrey ctermbg=darkgrey cterm=NONE hi Visual ctermfg=white ctermbg=darkblue cterm=NONE hi VisualNOS ctermfg=black ctermbg=cyan cterm=NONE hi WarningMsg ctermfg=red ctermbg=NONE cterm=bold @@ -648,6 +653,7 @@ if &background == 'light' hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=black ctermbg=black cterm=NONE + hi VertSplitNC ctermfg=black ctermbg=black cterm=NONE hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE diff --git a/runtime/colors/morning.vim b/runtime/colors/morning.vim index aee04b0326..07b849ecbe 100644 --- a/runtime/colors/morning.vim +++ b/runtime/colors/morning.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer Bram Moolenaar " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=light @@ -92,7 +92,8 @@ hi ToolbarButton guifg=NONE guibg=#bcbcbc guisp=NONE gui=bold ctermfg=NONE cterm hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#2e8b57 guibg=NONE guisp=NONE gui=bold ctermfg=29 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#6a5acd guibg=NONE guisp=NONE gui=underline ctermfg=62 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#bcbcbc guibg=#000000 guisp=NONE gui=NONE ctermfg=250 ctermbg=16 cterm=NONE term=NONE +hi VertSplit guifg=#eeeeee guibg=#000000 guisp=NONE gui=NONE ctermfg=255 ctermbg=16 cterm=NONE term=NONE +hi VertSplitNC guifg=#bcbcbc guibg=#000000 guisp=NONE gui=NONE ctermfg=250 ctermbg=16 cterm=NONE term=NONE hi Visual guifg=NONE guibg=#d0d0d0 guisp=NONE gui=NONE ctermfg=NONE ctermbg=252 cterm=NONE term=reverse hi VisualNOS guifg=NONE guibg=#0000ff guisp=NONE gui=NONE ctermfg=NONE ctermbg=21 cterm=NONE term=NONE hi WarningMsg guifg=#6a0dad guibg=NONE guisp=NONE gui=bold ctermfg=55 ctermbg=NONE cterm=bold term=standout @@ -162,7 +163,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkgrey ctermbg=black cterm=NONE + hi VertSplit ctermfg=white ctermbg=black cterm=NONE + hi VertSplitNC ctermfg=darkgrey ctermbg=black cterm=NONE hi Visual ctermfg=NONE ctermbg=white cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=blue cterm=NONE hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=bold @@ -231,6 +233,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=black ctermbg=gray cterm=reverse + hi VertSplitNC ctermfg=black ctermbg=gray cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=NONE diff --git a/runtime/colors/murphy.vim b/runtime/colors/murphy.vim index a5610fc584..21c48e3032 100644 --- a/runtime/colors/murphy.vim +++ b/runtime/colors/murphy.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer Ron Aaron . " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -93,7 +93,8 @@ hi ToolbarButton guifg=#ffffff guibg=#444444 guisp=NONE gui=bold ctermfg=231 cte hi ToolbarLine guifg=NONE guibg=#303030 guisp=NONE gui=NONE ctermfg=NONE ctermbg=236 cterm=NONE term=reverse hi Type guifg=#bcbcbc guibg=NONE guisp=NONE gui=NONE ctermfg=250 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=#00afaf guibg=NONE guisp=NONE gui=underline ctermfg=37 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#ffffff guibg=#3a3a3a guisp=NONE gui=NONE ctermfg=231 ctermbg=237 cterm=NONE term=NONE +hi VertSplit guifg=#ffffff guibg=#00008b guisp=NONE gui=NONE ctermfg=231 ctermbg=18 cterm=NONE term=NONE +hi VertSplitNC guifg=#ffffff guibg=#3a3a3a guisp=NONE gui=NONE ctermfg=231 ctermbg=237 cterm=NONE term=NONE hi Visual guifg=#ffffff guibg=#005f00 guisp=NONE gui=NONE ctermfg=231 ctermbg=22 cterm=NONE term=reverse hi VisualNOS guifg=#ffffff guibg=#005f00 guisp=NONE gui=NONE ctermfg=231 ctermbg=22 cterm=NONE term=NONE hi WarningMsg guifg=#ffa700 guibg=NONE guisp=NONE gui=bold ctermfg=214 ctermbg=NONE cterm=bold term=standout @@ -163,7 +164,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE hi Type ctermfg=grey ctermbg=NONE cterm=NONE hi Underlined ctermfg=darkcyan ctermbg=NONE cterm=underline - hi VertSplit ctermfg=white ctermbg=darkgrey cterm=NONE + hi VertSplit ctermfg=white ctermbg=darkblue cterm=NONE + hi VertSplitNC ctermfg=white ctermbg=darkgrey cterm=NONE hi Visual ctermfg=white ctermbg=darkgreen cterm=NONE hi VisualNOS ctermfg=white ctermbg=darkgreen cterm=NONE hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=bold @@ -231,7 +233,8 @@ if s:t_Co >= 8 hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE hi Type ctermfg=grey ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=grey ctermbg=black cterm=reverse + hi VertSplit ctermfg=darkblue ctermbg=grey cterm=reverse + hi VertSplitNC ctermfg=grey ctermbg=black cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=reverse,underline hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=bold diff --git a/runtime/colors/pablo.vim b/runtime/colors/pablo.vim index 8829c0700d..4b85040417 100644 --- a/runtime/colors/pablo.vim +++ b/runtime/colors/pablo.vim @@ -3,9 +3,9 @@ " Maintainer: Original maintainerRon Aaron " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -92,7 +92,8 @@ hi ToolbarButton guifg=#000000 guibg=#e5e5e5 guisp=NONE gui=bold ctermfg=16 cter hi ToolbarLine guifg=NONE guibg=#000000 guisp=NONE gui=NONE ctermfg=NONE ctermbg=16 cterm=NONE term=reverse hi Type guifg=#00c000 guibg=NONE guisp=NONE gui=NONE ctermfg=34 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=#80a0ff guibg=NONE guisp=NONE gui=underline ctermfg=111 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#000000 guibg=#ffffff guisp=NONE gui=NONE ctermfg=16 ctermbg=231 cterm=NONE term=NONE +hi VertSplit guifg=#ffff00 guibg=#0000ee guisp=NONE gui=NONE ctermfg=226 ctermbg=20 cterm=NONE term=NONE +hi VertSplitNC guifg=#000000 guibg=#ffffff guisp=NONE gui=NONE ctermfg=16 ctermbg=231 cterm=NONE term=NONE hi Visual guifg=#00008b guibg=#a9a9a9 guisp=NONE gui=NONE ctermfg=20 ctermbg=248 cterm=NONE term=reverse hi VisualNOS guifg=NONE guibg=#000000 guisp=NONE gui=bold,underline ctermfg=NONE ctermbg=16 cterm=underline term=NONE hi WarningMsg guifg=#ff0000 guibg=NONE guisp=NONE gui=NONE ctermfg=196 ctermbg=NONE cterm=NONE term=standout @@ -163,7 +164,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=darkgreen ctermbg=NONE cterm=underline - hi VertSplit ctermfg=black ctermbg=white cterm=NONE + hi VertSplit ctermfg=yellow ctermbg=darkblue cterm=NONE + hi VertSplitNC ctermfg=black ctermbg=white cterm=NONE hi Visual ctermfg=darkblue ctermbg=grey cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE @@ -234,7 +236,8 @@ if s:t_Co >= 8 hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=grey ctermbg=black cterm=reverse + hi VertSplit ctermfg=darkblue ctermbg=grey cterm=reverse + hi VertSplitNC ctermfg=grey ctermbg=black cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=black ctermbg=grey cterm=NONE hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE diff --git a/runtime/colors/peachpuff.vim b/runtime/colors/peachpuff.vim index cff502b496..a433eee7ad 100644 --- a/runtime/colors/peachpuff.vim +++ b/runtime/colors/peachpuff.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer David Ne\v{c}as (Yeti) " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 14 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=light @@ -94,7 +94,8 @@ hi ToolbarButton guifg=#ffdab9 guibg=#737373 guisp=NONE gui=bold ctermfg=223 cte hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#2e8b57 guibg=NONE guisp=NONE gui=bold ctermfg=29 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#6a5acd guibg=NONE guisp=NONE gui=underline ctermfg=62 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#ffdab9 guibg=#737373 guisp=NONE gui=NONE ctermfg=223 ctermbg=243 cterm=NONE term=NONE +hi VertSplit guifg=#ffdab9 guibg=#000000 guisp=NONE gui=NONE ctermfg=223 ctermbg=16 cterm=NONE term=NONE +hi VertSplitNC guifg=#ffdab9 guibg=#737373 guisp=NONE gui=NONE ctermfg=223 ctermbg=243 cterm=NONE term=NONE hi Visual guifg=#000000 guibg=#cccccc guisp=NONE gui=NONE ctermfg=16 ctermbg=252 cterm=NONE term=reverse hi VisualNOS guifg=NONE guibg=#406090 guisp=NONE gui=NONE ctermfg=NONE ctermbg=25 cterm=NONE term=NONE hi WarningMsg guifg=#cd00cd guibg=#ffdab9 guisp=NONE gui=bold ctermfg=164 ctermbg=223 cterm=bold term=standout @@ -168,7 +169,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=white ctermbg=darkgrey cterm=NONE + hi VertSplit ctermfg=white ctermbg=black cterm=NONE + hi VertSplitNC ctermfg=white ctermbg=darkgrey cterm=NONE hi Visual ctermfg=black ctermbg=grey cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=darkgrey cterm=NONE hi WarningMsg ctermfg=darkmagenta ctermbg=white cterm=bold @@ -241,6 +243,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=black ctermbg=gray cterm=reverse + hi VertSplitNC ctermfg=black ctermbg=gray cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=bold diff --git a/runtime/colors/retrobox.vim b/runtime/colors/retrobox.vim index afe53ee5a5..59c06855a6 100644 --- a/runtime/colors/retrobox.vim +++ b/runtime/colors/retrobox.vim @@ -2,9 +2,9 @@ " Description: Retro groove color scheme similar to gruvbox originally designed by morhetz " Author: Maxim Kim , ported from gruvbox8 of Lifepillar " URL: https://www.github.com/vim/colorschemes -" Last Change: 2025 Oct 22 +" Last Change: 2026 Apr 30 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 hi clear let g:colors_name = 'retrobox' @@ -79,8 +79,8 @@ if &background == 'dark' hi PmenuExtraSel guifg=#a89984 guibg=#504945 guisp=NONE gui=NONE ctermfg=102 ctermbg=239 cterm=NONE term=NONE hi PmenuKind guifg=#fb5944 guibg=#3c3836 guisp=NONE gui=NONE ctermfg=203 ctermbg=237 cterm=NONE term=NONE hi PmenuKindSel guifg=#fb5944 guibg=#504945 guisp=NONE gui=NONE ctermfg=203 ctermbg=239 cterm=NONE term=NONE - hi PmenuMatch guifg=#d3869b guibg=#3c3836 guisp=NONE gui=NONE ctermfg=175 ctermbg=237 cterm=NONE term=NONE - hi PmenuMatchSel guifg=#d3869b guibg=#504945 guisp=NONE gui=NONE ctermfg=175 ctermbg=239 cterm=NONE term=NONE + hi PmenuMatch guifg=#d3869b guibg=NONE guisp=NONE gui=NONE ctermfg=175 ctermbg=NONE cterm=NONE term=NONE + hi PmenuMatchSel guifg=#d3869b guibg=NONE guisp=NONE gui=NONE ctermfg=175 ctermbg=NONE cterm=NONE term=NONE hi PmenuSbar guifg=NONE guibg=#3c3836 guisp=NONE gui=NONE ctermfg=NONE ctermbg=237 cterm=NONE term=reverse hi PmenuSel guifg=NONE guibg=#504945 guisp=NONE gui=NONE ctermfg=NONE ctermbg=239 cterm=NONE term=bold hi PmenuShadow guifg=#a89984 guibg=#121212 guisp=NONE gui=NONE ctermfg=102 ctermbg=233 cterm=NONE term=NONE @@ -118,7 +118,8 @@ if &background == 'dark' hi Type guifg=#fabd2f guibg=NONE guisp=NONE gui=NONE ctermfg=214 ctermbg=NONE cterm=NONE term=NONE hi Typedef guifg=#fabd2f guibg=NONE guisp=NONE gui=NONE ctermfg=214 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=#83a598 guibg=NONE guisp=NONE gui=underline ctermfg=109 ctermbg=NONE cterm=underline term=underline - hi VertSplit guifg=#303030 guibg=#1c1c1c guisp=NONE gui=NONE ctermfg=236 ctermbg=234 cterm=NONE term=NONE + hi VertSplit guifg=#504945 guibg=#1c1c1c guisp=NONE gui=NONE ctermfg=239 ctermbg=234 cterm=NONE term=NONE + hi VertSplitNC guifg=#303030 guibg=#1c1c1c guisp=NONE gui=NONE ctermfg=236 ctermbg=234 cterm=NONE term=NONE hi Visual guifg=NONE guibg=#2a405a guisp=NONE gui=NONE ctermfg=109 ctermbg=234 cterm=reverse term=reverse hi WarningMsg guifg=#fb5944 guibg=NONE guisp=NONE gui=bold ctermfg=203 ctermbg=NONE cterm=bold term=standout hi WildMenu guifg=#83a598 guibg=#504945 guisp=NONE gui=bold ctermfg=109 ctermbg=239 cterm=bold term=bold @@ -221,7 +222,8 @@ if &background == 'dark' hi Type ctermfg=Yellow ctermbg=NONE cterm=NONE hi Typedef ctermfg=Yellow ctermbg=NONE cterm=NONE hi Underlined ctermfg=Blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=Black ctermbg=gray cterm=NONE + hi VertSplit ctermfg=gray ctermbg=Black cterm=NONE + hi VertSplitNC ctermfg=gray ctermbg=Black cterm=NONE hi Visual ctermfg=Blue ctermbg=Black cterm=reverse hi WarningMsg ctermfg=Red ctermbg=NONE cterm=bold hi WildMenu ctermfg=White ctermbg=Black cterm=bold @@ -316,7 +318,8 @@ if &background == 'dark' hi Type ctermfg=Yellow ctermbg=NONE cterm=NONE hi Typedef ctermfg=Yellow ctermbg=NONE cterm=NONE hi Underlined ctermfg=Blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=Black ctermbg=gray cterm=NONE + hi VertSplit ctermfg=gray ctermbg=Black cterm=NONE + hi VertSplitNC ctermfg=gray ctermbg=Black cterm=NONE hi Visual ctermfg=Black ctermbg=Blue cterm=NONE hi WarningMsg ctermfg=Red ctermbg=NONE cterm=bold hi WildMenu ctermfg=Blue ctermbg=DarkGray cterm=bold @@ -389,8 +392,8 @@ if &background == 'light' hi PmenuExtraSel guifg=#3c3836 guibg=#bdae93 guisp=NONE gui=NONE ctermfg=237 ctermbg=144 cterm=NONE term=NONE hi PmenuKind guifg=#9d0006 guibg=#e5d4b1 guisp=NONE gui=NONE ctermfg=124 ctermbg=187 cterm=NONE term=NONE hi PmenuKindSel guifg=#9d0006 guibg=#bdae93 guisp=NONE gui=NONE ctermfg=124 ctermbg=144 cterm=NONE term=NONE - hi PmenuMatch guifg=#8f3f71 guibg=#e5d4b1 guisp=NONE gui=NONE ctermfg=126 ctermbg=187 cterm=NONE term=NONE - hi PmenuMatchSel guifg=#8f3f71 guibg=#bdae93 guisp=NONE gui=NONE ctermfg=126 ctermbg=144 cterm=NONE term=NONE + hi PmenuMatch guifg=#8f3f71 guibg=NONE guisp=NONE gui=NONE ctermfg=126 ctermbg=NONE cterm=NONE term=NONE + hi PmenuMatchSel guifg=#8f3f71 guibg=NONE guisp=NONE gui=NONE ctermfg=126 ctermbg=NONE cterm=NONE term=NONE hi PmenuSbar guifg=NONE guibg=#e5d4b1 guisp=NONE gui=NONE ctermfg=NONE ctermbg=187 cterm=NONE term=reverse hi PmenuSel guifg=NONE guibg=#bdae93 guisp=NONE gui=NONE ctermfg=NONE ctermbg=144 cterm=NONE term=bold hi PmenuShadow guifg=#7c6f64 guibg=#303030 guisp=NONE gui=NONE ctermfg=243 ctermbg=236 cterm=NONE term=NONE @@ -428,7 +431,8 @@ if &background == 'light' hi Type guifg=#b57614 guibg=NONE guisp=NONE gui=NONE ctermfg=172 ctermbg=NONE cterm=NONE term=NONE hi Typedef guifg=#b57614 guibg=NONE guisp=NONE gui=NONE ctermfg=172 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=#076678 guibg=NONE guisp=NONE gui=underline ctermfg=23 ctermbg=NONE cterm=underline term=underline - hi VertSplit guifg=#bdae93 guibg=#fbf1c7 guisp=NONE gui=NONE ctermfg=144 ctermbg=230 cterm=NONE term=NONE + hi VertSplit guifg=#7c6f64 guibg=#fbf1c7 guisp=NONE gui=NONE ctermfg=243 ctermbg=230 cterm=NONE term=NONE + hi VertSplitNC guifg=#bdae93 guibg=#fbf1c7 guisp=NONE gui=NONE ctermfg=144 ctermbg=230 cterm=NONE term=NONE hi Visual guifg=NONE guibg=#b0d0d0 guisp=NONE gui=NONE ctermfg=23 ctermbg=230 cterm=reverse term=reverse hi WarningMsg guifg=#9d0006 guibg=NONE guisp=NONE gui=bold ctermfg=124 ctermbg=NONE cterm=bold term=standout hi WildMenu guifg=#076678 guibg=#e5d4b1 guisp=NONE gui=bold ctermfg=23 ctermbg=187 cterm=bold term=bold @@ -531,7 +535,8 @@ if &background == 'light' hi Type ctermfg=Yellow ctermbg=NONE cterm=NONE hi Typedef ctermfg=Yellow ctermbg=NONE cterm=NONE hi Underlined ctermfg=Blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=DarkGray ctermbg=Grey cterm=NONE + hi VertSplit ctermfg=DarkGray ctermbg=White cterm=NONE + hi VertSplitNC ctermfg=DarkGray ctermbg=White cterm=NONE hi Visual ctermfg=Blue ctermbg=White cterm=reverse hi WarningMsg ctermfg=Red ctermbg=NONE cterm=bold hi WildMenu ctermfg=Black ctermbg=White cterm=bold @@ -626,7 +631,8 @@ if &background == 'light' hi Type ctermfg=Yellow ctermbg=NONE cterm=NONE hi Typedef ctermfg=Yellow ctermbg=NONE cterm=NONE hi Underlined ctermfg=Blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=White ctermbg=Black cterm=NONE + hi VertSplit ctermfg=Black ctermbg=White cterm=NONE + hi VertSplitNC ctermfg=Black ctermbg=White cterm=NONE hi Visual ctermfg=White ctermbg=Blue cterm=NONE hi WarningMsg ctermfg=Red ctermbg=NONE cterm=bold hi WildMenu ctermfg=Blue ctermbg=Grey cterm=bold diff --git a/runtime/colors/shine.vim b/runtime/colors/shine.vim index 4493bd4b2f..bbd3c9c27a 100644 --- a/runtime/colors/shine.vim +++ b/runtime/colors/shine.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer is Yasuhiro Matsumoto " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 27 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=light @@ -96,7 +96,8 @@ hi ToolbarButton guifg=NONE guibg=#a8a8a8 guisp=NONE gui=bold ctermfg=NONE cterm hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#2e8b57 guibg=NONE guisp=NONE gui=bold ctermfg=29 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#6a0dad guibg=NONE guisp=NONE gui=underline ctermfg=55 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#a8a8a8 guibg=#a8a8a8 guisp=NONE gui=NONE ctermfg=248 ctermbg=248 cterm=NONE term=NONE +hi VertSplit guifg=#000000 guibg=#000000 guisp=NONE gui=NONE ctermfg=16 ctermbg=16 cterm=NONE term=NONE +hi VertSplitNC guifg=#a8a8a8 guibg=#a8a8a8 guisp=NONE gui=NONE ctermfg=248 ctermbg=248 cterm=NONE term=NONE hi Visual guifg=#000000 guibg=#a8a8a8 guisp=NONE gui=NONE ctermfg=16 ctermbg=248 cterm=NONE term=reverse hi VisualNOS guifg=NONE guibg=NONE guisp=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=underline term=NONE hi WarningMsg guifg=#6a0dad guibg=#ffffff guisp=NONE gui=NONE ctermfg=55 ctermbg=231 cterm=NONE term=standout @@ -170,7 +171,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkgreen ctermbg=NONE cterm=bold hi Underlined ctermfg=darkmagenta ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkgrey ctermbg=darkgrey cterm=NONE + hi VertSplit ctermfg=black ctermbg=black cterm=NONE + hi VertSplitNC ctermfg=darkgrey ctermbg=darkgrey cterm=NONE hi Visual ctermfg=black ctermbg=darkgrey cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline hi WarningMsg ctermfg=darkmagenta ctermbg=white cterm=NONE @@ -243,6 +245,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=black ctermbg=grey cterm=reverse + hi VertSplitNC ctermfg=black ctermbg=grey cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline hi WarningMsg ctermfg=darkmagenta ctermbg=NONE cterm=NONE diff --git a/runtime/colors/slate.vim b/runtime/colors/slate.vim index 9795408bb6..ce9d26900c 100644 --- a/runtime/colors/slate.vim +++ b/runtime/colors/slate.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer Ralph Amissah " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 27 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -93,7 +93,8 @@ hi ToolbarButton guifg=#262626 guibg=#d7d787 guisp=NONE gui=NONE ctermfg=235 cte hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#5f87d7 guibg=NONE guisp=NONE gui=bold ctermfg=68 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#5f87d7 guibg=NONE guisp=NONE gui=underline ctermfg=68 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#666666 guibg=#afaf87 guisp=NONE gui=NONE ctermfg=241 ctermbg=144 cterm=NONE term=NONE +hi VertSplit guifg=#000000 guibg=#afaf87 guisp=NONE gui=NONE ctermfg=16 ctermbg=144 cterm=NONE term=NONE +hi VertSplitNC guifg=#666666 guibg=#afaf87 guisp=NONE gui=NONE ctermfg=241 ctermbg=144 cterm=NONE term=NONE hi Visual guifg=#d7d787 guibg=#5f8700 guisp=NONE gui=NONE ctermfg=186 ctermbg=64 cterm=NONE term=reverse hi VisualNOS guifg=#d7d787 guibg=#5f8700 guisp=NONE gui=NONE ctermfg=186 ctermbg=64 cterm=NONE term=NONE hi WarningMsg guifg=#ff8787 guibg=NONE guisp=NONE gui=NONE ctermfg=210 ctermbg=NONE cterm=NONE term=standout @@ -173,7 +174,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=blue ctermbg=NONE cterm=bold hi Underlined ctermfg=blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkgrey ctermbg=grey cterm=NONE + hi VertSplit ctermfg=white ctermbg=black cterm=reverse + hi VertSplitNC ctermfg=darkgrey ctermbg=grey cterm=NONE hi Visual ctermfg=yellow ctermbg=darkgreen cterm=NONE hi VisualNOS ctermfg=darkmagenta ctermbg=darkgreen cterm=NONE hi WarningMsg ctermfg=cyan ctermbg=NONE cterm=NONE @@ -247,6 +249,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkblue ctermbg=NONE cterm=bold hi Underlined ctermfg=blue ctermbg=NONE cterm=underline hi VertSplit ctermfg=grey ctermbg=black cterm=reverse + hi VertSplitNC ctermfg=grey ctermbg=black cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=darkmagenta ctermbg=darkgreen cterm=underline hi WarningMsg ctermfg=darkcyan ctermbg=NONE cterm=NONE diff --git a/runtime/colors/torte.vim b/runtime/colors/torte.vim index 6f53c7d958..e23324fb48 100644 --- a/runtime/colors/torte.vim +++ b/runtime/colors/torte.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer Thorsten Maerz " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 24 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -93,7 +93,8 @@ hi ToolbarButton guifg=#000000 guibg=#e5e5e5 guisp=NONE gui=bold ctermfg=16 cter hi ToolbarLine guifg=NONE guibg=#000000 guisp=NONE gui=NONE ctermfg=NONE ctermbg=16 cterm=NONE term=reverse hi Type guifg=#60ff60 guibg=NONE guisp=NONE gui=NONE ctermfg=83 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=#80a0ff guibg=NONE guisp=NONE gui=underline ctermfg=111 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#000000 guibg=#e5e5e5 guisp=NONE gui=NONE ctermfg=16 ctermbg=254 cterm=NONE term=NONE +hi VertSplit guifg=#000000 guibg=#0000ee guisp=NONE gui=NONE ctermfg=16 ctermbg=20 cterm=NONE term=NONE +hi VertSplitNC guifg=#000000 guibg=#e5e5e5 guisp=NONE gui=NONE ctermfg=16 ctermbg=254 cterm=NONE term=NONE hi Visual guifg=#000000 guibg=#a9a9a9 guisp=NONE gui=bold ctermfg=16 ctermbg=248 cterm=NONE term=reverse hi VisualNOS guifg=NONE guibg=#000000 guisp=NONE gui=bold,underline ctermfg=NONE ctermbg=16 cterm=underline term=NONE hi WarningMsg guifg=#ff0000 guibg=NONE guisp=NONE gui=NONE ctermfg=196 ctermbg=NONE cterm=NONE term=standout @@ -164,7 +165,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE hi Type ctermfg=green ctermbg=NONE cterm=NONE hi Underlined ctermfg=blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=black ctermbg=grey cterm=NONE + hi VertSplit ctermfg=black ctermbg=darkblue cterm=NONE + hi VertSplitNC ctermfg=black ctermbg=grey cterm=NONE hi Visual ctermfg=black ctermbg=grey cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE @@ -233,7 +235,8 @@ if s:t_Co >= 8 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkgreen ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=black ctermbg=grey cterm=NONE + hi VertSplit ctermfg=grey ctermbg=darkblue cterm=NONE + hi VertSplitNC ctermfg=black ctermbg=grey cterm=NONE hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=black cterm=underline hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE diff --git a/runtime/colors/unokai.vim b/runtime/colors/unokai.vim index 60cacf0b56..7c85cba568 100644 --- a/runtime/colors/unokai.vim +++ b/runtime/colors/unokai.vim @@ -2,9 +2,9 @@ " Description: Color scheme similar to Monokai originally created by Wimer Hazenberg for TextMate " Author: k-37 <60838818+k-37@users.noreply.github.com> " URL: https://github.com/vim/colorschemes -" Last Change: 2025 Oct 08 +" Last Change: 2026 May 07 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=dark @@ -108,7 +108,8 @@ hi ToolbarButton guifg=#74705d guibg=#f8f8f2 guisp=NONE gui=bold,reverse ctermfg hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#fd971f guibg=NONE guisp=NONE gui=bold ctermfg=208 ctermbg=NONE cterm=bold term=NONE hi Underlined guifg=#66d9ef guibg=NONE guisp=NONE gui=underline ctermfg=81 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#74705d guibg=#74705d guisp=NONE gui=NONE ctermfg=244 ctermbg=244 cterm=NONE term=NONE +hi VertSplit guifg=#bababa guibg=#bababa guisp=NONE gui=NONE ctermfg=250 ctermbg=250 cterm=NONE term=NONE +hi VertSplitNC guifg=#74705d guibg=#74705d guisp=NONE gui=NONE ctermfg=244 ctermbg=244 cterm=NONE term=NONE hi Visual guifg=#a1efe4 guibg=#282923 guisp=NONE gui=reverse ctermfg=116 ctermbg=235 cterm=reverse term=reverse hi VisualNOS guifg=#282923 guibg=#80beb5 guisp=NONE gui=NONE ctermfg=235 ctermbg=73 cterm=NONE term=NONE hi WarningMsg guifg=#f92672 guibg=NONE guisp=NONE gui=NONE ctermfg=197 ctermbg=NONE cterm=NONE term=standout @@ -206,7 +207,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold hi Underlined ctermfg=blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkgray ctermbg=darkgray cterm=NONE + hi VertSplit ctermfg=gray ctermbg=gray cterm=NONE + hi VertSplitNC ctermfg=darkgray ctermbg=darkgray cterm=NONE hi Visual ctermfg=cyan ctermbg=black cterm=reverse hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE @@ -298,6 +300,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline hi VertSplit ctermfg=gray ctermbg=gray cterm=NONE + hi VertSplitNC ctermfg=gray ctermbg=gray cterm=NONE hi Visual ctermfg=black ctermbg=darkcyan cterm=NONE hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE diff --git a/runtime/colors/wildcharm.vim b/runtime/colors/wildcharm.vim index fd52078dc4..4009437288 100644 --- a/runtime/colors/wildcharm.vim +++ b/runtime/colors/wildcharm.vim @@ -3,9 +3,9 @@ " Author: Maxim Kim " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 22 +" Last Change: 2026 Apr 30 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 hi clear let g:colors_name = 'wildcharm' @@ -67,8 +67,8 @@ if &background == 'dark' hi PmenuExtraSel guifg=#767676 guibg=#585858 guisp=NONE gui=NONE ctermfg=243 ctermbg=240 cterm=NONE term=NONE hi PmenuKind guifg=#ff5f87 guibg=#303030 guisp=NONE gui=NONE ctermfg=204 ctermbg=236 cterm=NONE term=NONE hi PmenuKindSel guifg=#ff5f87 guibg=#585858 guisp=NONE gui=NONE ctermfg=204 ctermbg=240 cterm=NONE term=NONE - hi PmenuMatch guifg=#d787d7 guibg=#303030 guisp=NONE gui=NONE ctermfg=176 ctermbg=236 cterm=NONE term=NONE - hi PmenuMatchSel guifg=#d787d7 guibg=#585858 guisp=NONE gui=NONE ctermfg=176 ctermbg=240 cterm=NONE term=NONE + hi PmenuMatch guifg=#d787d7 guibg=NONE guisp=NONE gui=NONE ctermfg=176 ctermbg=NONE cterm=NONE term=NONE + hi PmenuMatchSel guifg=#d787d7 guibg=NONE guisp=NONE gui=NONE ctermfg=176 ctermbg=NONE cterm=NONE term=NONE hi PmenuSbar guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi PmenuSel guifg=NONE guibg=#585858 guisp=NONE gui=NONE ctermfg=NONE ctermbg=240 cterm=NONE term=bold hi PmenuShadow guifg=#767676 guibg=#121212 guisp=NONE gui=NONE ctermfg=243 ctermbg=233 cterm=NONE term=NONE @@ -100,7 +100,8 @@ if &background == 'dark' hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#ffaf00 guibg=NONE guisp=NONE gui=NONE ctermfg=214 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=NONE guibg=NONE guisp=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline term=underline - hi VertSplit guifg=#767676 guibg=#767676 guisp=NONE gui=NONE ctermfg=243 ctermbg=243 cterm=NONE term=NONE + hi VertSplit guifg=#9e9e9e guibg=#9e9e9e guisp=NONE gui=NONE ctermfg=247 ctermbg=247 cterm=NONE term=NONE + hi VertSplitNC guifg=#767676 guibg=#767676 guisp=NONE gui=NONE ctermfg=243 ctermbg=243 cterm=NONE term=NONE hi Visual guifg=NONE guibg=#204060 guisp=NONE gui=NONE ctermfg=81 ctermbg=16 cterm=reverse term=reverse hi VisualNOS guifg=#000000 guibg=#00afff guisp=NONE gui=NONE ctermfg=16 ctermbg=39 cterm=NONE term=NONE hi WarningMsg guifg=#ffaf00 guibg=NONE guisp=NONE gui=NONE ctermfg=214 ctermbg=NONE cterm=NONE term=standout @@ -191,7 +192,8 @@ if &background == 'dark' hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=yellow ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=darkgrey ctermbg=darkgrey cterm=NONE + hi VertSplit ctermfg=grey ctermbg=grey cterm=NONE + hi VertSplitNC ctermfg=darkgrey ctermbg=darkgrey cterm=NONE hi Visual ctermfg=cyan ctermbg=black cterm=reverse hi VisualNOS ctermfg=black ctermbg=blue cterm=NONE hi WarningMsg ctermfg=yellow ctermbg=NONE cterm=NONE @@ -270,6 +272,7 @@ if &background == 'dark' hi Type ctermfg=darkyellow ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=grey ctermbg=grey cterm=NONE + hi VertSplitNC ctermfg=grey ctermbg=grey cterm=NONE hi Visual ctermfg=darkblue ctermbg=black cterm=reverse hi VisualNOS ctermfg=black ctermbg=darkblue cterm=NONE hi WarningMsg ctermfg=darkyellow ctermbg=NONE cterm=NONE @@ -331,13 +334,13 @@ if &background == 'light' hi Pmenu guifg=NONE guibg=#e4e4e4 guisp=NONE gui=NONE ctermfg=NONE ctermbg=254 cterm=NONE term=reverse hi PmenuBorder guifg=#808080 guibg=#e4e4e4 guisp=NONE gui=NONE ctermfg=240 ctermbg=254 cterm=NONE term=NONE hi PmenuExtra guifg=#808080 guibg=#e4e4e4 guisp=NONE gui=NONE ctermfg=240 ctermbg=254 cterm=NONE term=NONE - hi PmenuExtraSel guifg=#808080 guibg=#d0d0d0 guisp=NONE gui=NONE ctermfg=240 ctermbg=252 cterm=NONE term=NONE + hi PmenuExtraSel guifg=#808080 guibg=#c6c6c6 guisp=NONE gui=NONE ctermfg=240 ctermbg=251 cterm=NONE term=NONE hi PmenuKind guifg=#d70000 guibg=#e4e4e4 guisp=NONE gui=NONE ctermfg=160 ctermbg=254 cterm=NONE term=NONE - hi PmenuKindSel guifg=#d70000 guibg=#d0d0d0 guisp=NONE gui=NONE ctermfg=160 ctermbg=252 cterm=NONE term=NONE - hi PmenuMatch guifg=#870087 guibg=#e4e4e4 guisp=NONE gui=NONE ctermfg=90 ctermbg=254 cterm=NONE term=NONE - hi PmenuMatchSel guifg=#870087 guibg=#d0d0d0 guisp=NONE gui=NONE ctermfg=90 ctermbg=252 cterm=NONE term=NONE + hi PmenuKindSel guifg=#d70000 guibg=#c6c6c6 guisp=NONE gui=NONE ctermfg=160 ctermbg=251 cterm=NONE term=NONE + hi PmenuMatch guifg=#870087 guibg=NONE guisp=NONE gui=NONE ctermfg=90 ctermbg=NONE cterm=NONE term=NONE + hi PmenuMatchSel guifg=#870087 guibg=NONE guisp=NONE gui=NONE ctermfg=90 ctermbg=NONE cterm=NONE term=NONE hi PmenuSbar guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse - hi PmenuSel guifg=NONE guibg=#d0d0d0 guisp=NONE gui=NONE ctermfg=NONE ctermbg=252 cterm=NONE term=bold + hi PmenuSel guifg=NONE guibg=#c6c6c6 guisp=NONE gui=NONE ctermfg=NONE ctermbg=251 cterm=NONE term=bold hi PmenuShadow guifg=#808080 guibg=#303030 guisp=NONE gui=NONE ctermfg=240 ctermbg=236 cterm=NONE term=NONE hi PmenuThumb guifg=NONE guibg=#808080 guisp=NONE gui=NONE ctermfg=NONE ctermbg=240 cterm=NONE term=NONE hi PreProc guifg=#008787 guibg=NONE guisp=NONE gui=NONE ctermfg=30 ctermbg=NONE cterm=NONE term=NONE @@ -367,7 +370,8 @@ if &background == 'light' hi ToolbarLine guifg=NONE guibg=NONE guisp=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE term=reverse hi Type guifg=#af5f00 guibg=NONE guisp=NONE gui=NONE ctermfg=130 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=NONE guibg=NONE guisp=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline term=underline - hi VertSplit guifg=#d0d0d0 guibg=#d0d0d0 guisp=NONE gui=NONE ctermfg=252 ctermbg=252 cterm=NONE term=NONE + hi VertSplit guifg=#5f5f5f guibg=#5f5f5f guisp=NONE gui=NONE ctermfg=59 ctermbg=59 cterm=NONE term=NONE + hi VertSplitNC guifg=#d0d0d0 guibg=#d0d0d0 guisp=NONE gui=NONE ctermfg=252 ctermbg=252 cterm=NONE term=NONE hi Visual guifg=NONE guibg=#bfdfff guisp=NONE gui=NONE ctermfg=32 ctermbg=231 cterm=reverse term=reverse hi VisualNOS guifg=#ffffff guibg=#005faf guisp=NONE gui=NONE ctermfg=231 ctermbg=25 cterm=NONE term=NONE hi WarningMsg guifg=#af5f00 guibg=NONE guisp=NONE gui=NONE ctermfg=130 ctermbg=NONE cterm=NONE term=standout @@ -458,7 +462,8 @@ if &background == 'light' hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE hi Type ctermfg=darkyellow ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline - hi VertSplit ctermfg=lightgrey ctermbg=lightgrey cterm=NONE + hi VertSplit ctermfg=darkgrey ctermbg=darkgrey cterm=NONE + hi VertSplitNC ctermfg=lightgrey ctermbg=lightgrey cterm=NONE hi Visual ctermfg=blue ctermbg=white cterm=reverse hi VisualNOS ctermfg=white ctermbg=darkblue cterm=NONE hi WarningMsg ctermfg=darkyellow ctermbg=NONE cterm=NONE @@ -537,6 +542,7 @@ if &background == 'light' hi Type ctermfg=darkyellow ctermbg=NONE cterm=NONE hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=black ctermbg=black cterm=NONE + hi VertSplitNC ctermfg=black ctermbg=black cterm=NONE hi Visual ctermfg=darkblue ctermbg=grey cterm=reverse hi VisualNOS ctermfg=black ctermbg=darkblue cterm=NONE hi WarningMsg ctermfg=darkyellow ctermbg=NONE cterm=NONE diff --git a/runtime/colors/zellner.vim b/runtime/colors/zellner.vim index dc24f9b3a6..1ef6333055 100644 --- a/runtime/colors/zellner.vim +++ b/runtime/colors/zellner.vim @@ -4,9 +4,9 @@ " Maintainer: Original maintainer Ron Aaron " URL: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2025 Oct 08 +" Last Change: 2026 Apr 27 -" Generated by Colortemplate v3.0.0-beta10 +" Generated by Colortemplate v3.0.0-rc1 set background=light @@ -93,7 +93,8 @@ hi ToolbarButton guifg=NONE guibg=#a9a9a9 guisp=NONE gui=bold ctermfg=NONE cterm hi ToolbarLine guifg=NONE guibg=#d3d3d3 guisp=NONE gui=NONE ctermfg=NONE ctermbg=252 cterm=NONE term=reverse hi Type guifg=#0000ff guibg=NONE guisp=NONE gui=NONE ctermfg=21 ctermbg=NONE cterm=NONE term=NONE hi Underlined guifg=#6a5acd guibg=NONE guisp=NONE gui=underline ctermfg=62 ctermbg=NONE cterm=underline term=underline -hi VertSplit guifg=#ffffff guibg=#000000 guisp=NONE gui=NONE ctermfg=231 ctermbg=16 cterm=NONE term=NONE +hi VertSplit guifg=#ffff00 guibg=#a9a9a9 guisp=NONE gui=NONE ctermfg=226 ctermbg=248 cterm=NONE term=NONE +hi VertSplitNC guifg=#ffffff guibg=#000000 guisp=NONE gui=NONE ctermfg=231 ctermbg=16 cterm=NONE term=NONE hi Visual guifg=#000000 guibg=#ffff00 guisp=NONE gui=NONE ctermfg=16 ctermbg=226 cterm=NONE term=reverse hi VisualNOS guifg=NONE guibg=#ff0000 guisp=NONE gui=NONE ctermfg=NONE ctermbg=196 cterm=NONE term=NONE hi WarningMsg guifg=#a020f0 guibg=#ffffff guisp=NONE gui=NONE ctermfg=129 ctermbg=231 cterm=NONE term=standout @@ -166,7 +167,8 @@ if s:t_Co >= 16 hi ToolbarLine ctermfg=NONE ctermbg=black cterm=NONE hi Type ctermfg=blue ctermbg=NONE cterm=NONE hi Underlined ctermfg=blue ctermbg=NONE cterm=underline - hi VertSplit ctermfg=white ctermbg=black cterm=NONE + hi VertSplit ctermfg=yellow ctermbg=darkgrey cterm=NONE + hi VertSplitNC ctermfg=white ctermbg=black cterm=NONE hi Visual ctermfg=black ctermbg=yellow cterm=NONE hi VisualNOS ctermfg=NONE ctermbg=red cterm=NONE hi WarningMsg ctermfg=darkmagenta ctermbg=white cterm=NONE @@ -238,6 +240,7 @@ if s:t_Co >= 8 hi Type ctermfg=darkblue ctermbg=NONE cterm=bold hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi VertSplit ctermfg=black ctermbg=gray cterm=reverse + hi VertSplitNC ctermfg=black ctermbg=gray cterm=reverse hi Visual ctermfg=NONE ctermbg=NONE cterm=reverse hi VisualNOS ctermfg=NONE ctermbg=NONE cterm=underline hi WarningMsg ctermfg=darkmagenta ctermbg=black cterm=NONE From 73e619d699574f1fc96ce2f04c1e243748c4d7dc Mon Sep 17 00:00:00 2001 From: "Wu, Zhenyu" Date: Fri, 29 May 2026 18:25:25 +0000 Subject: [PATCH 351/545] patch 9.2.0559: filetype: Kaitai struct files are not recogonized Problem: filetype: Kaitai struct files are not recogonized Solution: Detect *.ksy files as yaml filetype (Wu, Zhenyu) Reference: https://doc.kaitai.io/ closes: #20353 Signed-off-by: Wu, Zhenyu Signed-off-by: Christian Brabandt --- runtime/autoload/dist/ft.vim | 2 ++ src/testdir/test_filetype.vim | 2 +- src/version.c | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index a75d31bb59..c6fbd07d35 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -2336,6 +2336,8 @@ const ft_from_ext = { "kts": "kotlin", # KDE script "ks": "kscript", + # Kaitai struct + "ksy": "yaml", # Kyaml "kyaml": "yaml", "kyml": "yaml", diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index e2350871be..3e6a538a82 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -996,7 +996,7 @@ def s:GetFilenameChecks(): dict> xslt: ['file.xsl', 'file.xslt'], yacc: ['file.yy', 'file.yxx', 'file.y++'], yaml: ['file.yaml', 'file.yml', 'file.eyaml', 'file.kyaml', 'file.kyml', 'any/.bundle/config', '.clangd', '.clang-format', '.clang-tidy', 'file.mplstyle', 'matplotlibrc', 'yarn.lock', - '/home/user/.kube/config', '/home/user/.kube/kuberc', '.condarc', 'condarc', '.mambarc', 'mambarc', 'pixi.lock', 'buf.lock'], + '/home/user/.kube/config', '/home/user/.kube/kuberc', '.condarc', 'condarc', '.mambarc', 'mambarc', 'pixi.lock', 'buf.lock', 'file.ksy'], yang: ['file.yang'], yara: ['file.yara', 'file.yar'], yuck: ['file.yuck'], diff --git a/src/version.c b/src/version.c index 23d96b9e47..f77ed07b9b 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 559, /**/ 558, /**/ From 69f402e97f9f8e5dbf17549c4813cf932c91a4ee Mon Sep 17 00:00:00 2001 From: Christoffer Aasted Date: Fri, 29 May 2026 18:31:35 +0000 Subject: [PATCH 352/545] patch 9.2.0560: filetype: busybox shebang lines are not recognized Problem: filetype: busybox shebang lines are not recognized Solution: Add filetype detection pattern for #!busybox sh, detect ash as shell in the shebang lines (Christoffer Aasted). closes: #20358 Signed-off-by: Christoffer Aasted Signed-off-by: Christian Brabandt --- runtime/autoload/dist/script.vim | 4 +++- src/testdir/test_filetype.vim | 5 ++++- src/version.c | 2 ++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/dist/script.vim b/runtime/autoload/dist/script.vim index 0106900f8f..3f9a5acfd0 100644 --- a/runtime/autoload/dist/script.vim +++ b/runtime/autoload/dist/script.vim @@ -45,6 +45,8 @@ def DetectFromHashBang(firstline: string) name = substitute(line1, '^#!.*\\s\+\(\i\+\).*', '\1', '') elseif line1 =~ '^#!\s*[^/\\ ]*\>\([^/\\]\|$\)' name = substitute(line1, '^#!\s*\([^/\\ ]*\>\).*', '\1', '') + elseif line1 =~ '^#!.*\' + name = substitute(line1, '^#!.*\\s\+\(\i\+\).*', '\1', '') else name = substitute(line1, '^#!\s*\S*[/\\]\(\f\+\).*', '\1', '') endif @@ -67,7 +69,7 @@ enddef # Returns an empty string when not recognized. export def Exe2filetype(name: string, line1: string): string # Bourne-like shell scripts: bash bash2 dash ksh ksh93 sh - if name =~ '^\(bash\d*\|dash\|ksh\d*\|sh\)\>' + if name =~ '^\(bash\d*\|d\?ash\|ksh\d*\|sh\)\>' return dist#ft#SetFileTypeSH(line1, false) # csh scripts diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 3e6a538a82..f4fd2002a1 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -1089,7 +1089,10 @@ def s:GetScriptChecks(): dict>> ['#!/path/bash2'], ['#!/path/dash'], ['#!/path/ksh'], - ['#!/path/ksh93']], + ['#!/path/ksh93'], + ['#!/path/ash'], + ['#!/path/busybox ash'], + ['#!/path/busybox sh']], csh: [['#!/path/csh']], tcsh: [['#!/path/tcsh']], zsh: [['#!/path/zsh']], diff --git a/src/version.c b/src/version.c index f77ed07b9b..cb845499e4 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 560, /**/ 559, /**/ From 4b850457e12e1a678dd209f2868154f7553cbf8d Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Fri, 29 May 2026 19:05:53 +0000 Subject: [PATCH 353/545] patch 9.2.0561: [security]: possible code execution with python3complete Problem: [security]: possible code execution with python3complete Solution: Disable execution of import/from statements Github Security Advisory: https://github.com/vim/vim/security/advisories/GHSA-52mc-rq6p-rc7c Signed-off-by: Christian Brabandt --- runtime/autoload/README.txt | 1 + runtime/autoload/python3complete.vim | 17 ++++++++++++++--- runtime/autoload/pythoncomplete.vim | 17 ++++++++++++++--- runtime/doc/filetype.txt | 15 ++++++++++++++- src/version.c | 2 ++ 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/runtime/autoload/README.txt b/runtime/autoload/README.txt index 3b18d3dde5..b22581963e 100644 --- a/runtime/autoload/README.txt +++ b/runtime/autoload/README.txt @@ -17,6 +17,7 @@ htmlcomplete.vim HTML javascriptcomplete.vim Javascript phpcomplete.vim PHP pythoncomplete.vim Python +python3complete.vim Python rubycomplete.vim Ruby syntaxcomplete.vim from syntax highlighting xmlcomplete.vim XML (uses files in the xml directory) diff --git a/runtime/autoload/python3complete.vim b/runtime/autoload/python3complete.vim index 3e54433f41..2b6a652525 100644 --- a/runtime/autoload/python3complete.vim +++ b/runtime/autoload/python3complete.vim @@ -14,6 +14,10 @@ " i.e. "import url" " Continue parsing on invalid line?? " +" v 0.10 by Vim project +" * disables importing local modules, unless the global Vim variable +" g:pythoncomplete_allow_import is set to non-zero +" " v 0.9 " * Fixed docstring parsing for classes and functions " * Fixed parsing of *args and **kwargs type arguments @@ -132,11 +136,20 @@ class Completer(object): def evalsource(self,text,line=0): sc = self.parser.parse(text,line) + try: allow_imports = int( + vim.eval("get(g:, 'pythoncomplete_allow_import', 0)")) + except Exception: + allow_imports = 0 src = sc.get_code() dbg("source: %s" % src) try: exec(src,self.compldict) except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1])) for l in sc.locals: + # Executing import/from statements harvested from the buffer runs + # arbitrary package code; only do so when the user opted in. + if not allow_imports and (l.startswith('import') + or l.startswith('from ')): + continue try: exec(l,self.compldict) except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)) @@ -300,13 +313,11 @@ class Scope(object): def get_code(self): str = "" if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n' - for l in self.locals: - if l.startswith('import'): str += l+'\n' str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' for sub in self.subscopes: str += sub.get_code() for l in self.locals: - if not l.startswith('import'): str += l+'\n' + if not l.startswith('import') and not l.startswith('from '): str += l+'\n' return str diff --git a/runtime/autoload/pythoncomplete.vim b/runtime/autoload/pythoncomplete.vim index aa28bb721f..10147767ef 100644 --- a/runtime/autoload/pythoncomplete.vim +++ b/runtime/autoload/pythoncomplete.vim @@ -12,6 +12,10 @@ " i.e. "import url" " Continue parsing on invalid line?? " +" v 0.10 by Vim project +" * disables importing local modules, unless the global Vim variable +" g:pythoncomplete_allow_import is set to non-zero +" " v 0.9 " * Fixed docstring parsing for classes and functions " * Fixed parsing of *args and **kwargs type arguments @@ -146,11 +150,20 @@ class Completer(object): def evalsource(self,text,line=0): sc = self.parser.parse(text,line) + try: allow_imports = int( + vim.eval("get(g:, 'pythoncomplete_allow_import', 0)")) + except Exception: + allow_imports = 0 src = sc.get_code() dbg("source: %s" % src) try: exec(src) in self.compldict except: dbg("parser: %s, %s" % (sys.exc_info()[0],sys.exc_info()[1])) for l in sc.locals: + # Executing import/from statements harvested from the buffer runs + # arbitrary package code; only do so when the user opted in. + if not allow_imports and (l.startswith('import') + or l.startswith('from ')): + continue try: exec(l) in self.compldict except: dbg("locals: %s, %s [%s]" % (sys.exc_info()[0],sys.exc_info()[1],l)) @@ -315,13 +328,11 @@ class Scope(object): def get_code(self): str = "" if len(self.docstr) > 0: str += '"""'+self.docstr+'"""\n' - for l in self.locals: - if l.startswith('import'): str += l+'\n' str += 'class _PyCmplNoType:\n def __getattr__(self,name):\n return None\n' for sub in self.subscopes: str += sub.get_code() for l in self.locals: - if not l.startswith('import'): str += l+'\n' + if not l.startswith('import') and not l.startswith('from '): str += l+'\n' return str diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 80c3bb810f..a9a0e92203 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -976,7 +976,20 @@ By default the following options are set, in accordance with PEP8: > To disable this behavior, set the following variable in your vimrc: > let g:python_recommended_style = 0 - +< +Python omni-completion |compl-omni| is provided by python3complete.vim (or +pythoncomplete.vim) for Vim builds with the |+python|/|+python3| interpreter. +By default it does not inspect the import / from statements found in the +buffer. This means completion of names defined in the buffer itself (classes, +functions, variables) works, but completion of members of imported modules is +not offered. + +To enable completion of imported module members, set: > + let g:pythoncomplete_allow_import = 1 +< +WARNING: enabling this causes omni-completion to execute the import statements +found in the buffer through Python's import machinery, which runs the imported +modules' top-level code. Only enable this for code you trust. QF QUICKFIX *qf.vim* *ft-qf-plugin* diff --git a/src/version.c b/src/version.c index cb845499e4..d7e0f590fc 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 561, /**/ 560, /**/ From 61ad44f4af293e1e245e3d006483ac8ab59ca96f Mon Sep 17 00:00:00 2001 From: Maxim Kim Date: Fri, 29 May 2026 19:19:38 +0000 Subject: [PATCH 354/545] runtime(odin): remove const and opaque keywords They don't exist in odin lang. closes: #20364 Signed-off-by: Maxim Kim Signed-off-by: Christian Brabandt --- runtime/syntax/odin.vim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime/syntax/odin.vim b/runtime/syntax/odin.vim index efa107b79f..42f3767a43 100644 --- a/runtime/syntax/odin.vim +++ b/runtime/syntax/odin.vim @@ -4,14 +4,14 @@ vim9script # Language: Odin # Maintainer: Maxim Kim # Website: https://github.com/habamax/vim-odin -# Last Change: 2026-02-02 +# Last Change: 2026-05-28 if exists("b:current_syntax") finish endif -syntax keyword odinKeyword using transmute cast auto_cast distinct opaque where dynamic -syntax keyword odinKeyword struct enum union const bit_field bit_set +syntax keyword odinKeyword using transmute cast auto_cast distinct where dynamic +syntax keyword odinKeyword struct enum union bit_field bit_set syntax keyword odinKeyword package proc map import export foreign syntax keyword odinKeyword size_of offset_of type_info_of typeid_of type_of align_of syntax keyword odinKeyword return defer From dda45ba88fef901ac524c158eaeb1ce799dde273 Mon Sep 17 00:00:00 2001 From: Borys Lykah Date: Fri, 29 May 2026 19:23:27 +0000 Subject: [PATCH 355/545] patch 9.2.0562: filetype: SGF files are not recognized Problem: filetype: SGF files are not recognized Solution: Detect *.sgf as sgf filetype (Borys Lykah) Reference: https://www.red-bean.com/sgf/index.html closes: #20349 Signed-off-by: Borys Lykah Signed-off-by: Christian Brabandt --- runtime/autoload/dist/ft.vim | 2 ++ src/testdir/test_filetype.vim | 1 + src/version.c | 2 ++ 3 files changed, 5 insertions(+) diff --git a/runtime/autoload/dist/ft.vim b/runtime/autoload/dist/ft.vim index c6fbd07d35..f59d95af49 100644 --- a/runtime/autoload/dist/ft.vim +++ b/runtime/autoload/dist/ft.vim @@ -2735,6 +2735,8 @@ const ft_from_ext = { "mill": "scala", # SBT - Scala Build Tool "sbt": "sbt", + # SGF, Smart Game Format + "sgf": "sgf", # Slang Shading Language "slang": "shaderslang", # Slint diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index f4fd2002a1..8ba27d5e1c 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -741,6 +741,7 @@ def s:GetFilenameChecks(): dict> services: ['/etc/services', 'any/etc/services'], setserial: ['/etc/serial.conf', 'any/etc/serial.conf'], sexplib: ['file.sexp'], + sgf: ['file.sgf'], sh: ['.bashrc', '.bash_profile', '.bash-profile', '.bash_logout', '.bash-logout', '.bash_aliases', '.bash-aliases', '.bash_history', '.bash-history', '/tmp/bash-fc-3Ozjlw', '/tmp/bash-fc.3Ozjlw', 'PKGBUILD', 'file.bash', '/usr/share/doc/bash-completion/filter.sh', '/etc/udev/cdsymlinks.conf', 'any/etc/udev/cdsymlinks.conf', 'file.bats', '.ash_history', 'any/etc/neofetch/config.conf', '.xprofile', diff --git a/src/version.c b/src/version.c index d7e0f590fc..179b90e700 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 562, /**/ 561, /**/ From 92510c09ed58911e019d09ac149e546dae6cdda9 Mon Sep 17 00:00:00 2001 From: Christoffer Aasted Date: Fri, 29 May 2026 19:39:04 +0000 Subject: [PATCH 356/545] patch 9.2.0563: GTK3/Wayland: crash with right mouse-button in tabline Problem: GTK3/Wayland: crash with right mouse-button in tabline Solution: Use gui.mainwin and get coordinates (Christoffer Aasted). GtkNotebook (tabline) is a windowless container widget causing a nullptr deref inside `gdk_window_get_effective_parent()` as Wayland lacks a surface to anchor to. fixes: #18864 closes: #20348 Signed-off-by: Christoffer Aasted Signed-off-by: Christian Brabandt --- src/gui_gtk_x11.c | 11 +++++++++++ src/testdir/test_codestyle.vim | 12 +++++++++++- src/version.c | 2 ++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c index 44b95345fe..8b57eef088 100644 --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -3435,6 +3435,17 @@ on_tabline_menu(GtkWidget *widget, GdkEvent *event) if (bevent->button == 3) { # if GTK_CHECK_VERSION(3,22,2) +# ifdef GDK_WINDOWING_WAYLAND + if (gui.is_wayland) + { + int x2, y2; + gui_gtk_get_pointer(gui.mainwin, &x2, &y2, NULL); + gtk_menu_popup_at_rect(GTK_MENU(widget), + gtk_widget_get_window(gui.mainwin), &(GdkRectangle){x2, y2, 1, 1}, + GDK_GRAVITY_SOUTH_WEST, GDK_GRAVITY_NORTH_WEST, NULL); + } + else +# endif gtk_menu_popup_at_pointer(GTK_MENU(widget), event); # else gtk_menu_popup(GTK_MENU(widget), NULL, NULL, NULL, NULL, diff --git a/src/testdir/test_codestyle.vim b/src/testdir/test_codestyle.vim index 1dcca61eab..91ca236177 100644 --- a/src/testdir/test_codestyle.vim +++ b/src/testdir/test_codestyle.vim @@ -50,7 +50,17 @@ def Test_source_files() continue endif - var skip = 'getline(".") =~ "condition) {" || getline(".") =~ "vimglob_func" || getline(".") =~ "{\"" || getline(".") =~ "{\\d" || getline(".") =~ "{{{"' + # ignore patterns: + # - condition) { + # - vimglob_func + # - struct initializer: {" + # - numeric initializer: {\d + # - fold marker {{{ + # - compound literals: (\w\+) *{ + + + var skip = 'getline(".") =~ "condition) {" || getline(".") =~ "vimglob_func" || getline(".") =~ "{\"" ||' + skip ..= ' getline(".") =~ "{\\d" || getline(".") =~ "{{{" || getline(".") =~ "(\\w\\+) *{"' PerformCheck(fname, ')\s*{', 'curly after closing paren', skip) # Examples in comments use double quotes. diff --git a/src/version.c b/src/version.c index 179b90e700..d3977ad1ff 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 563, /**/ 562, /**/ From 2c5b3838075061dea427ef304dd89c136d198221 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Fri, 29 May 2026 19:47:24 +0000 Subject: [PATCH 357/545] patch 9.2.0564: GTK4: tabline does not respond to mouse clicks Problem: GTK4: tabline does not respond to mouse clicks Solution: Connect on_select_tab() to "switch-page" to fire the tabline event, and on_tab_reordered() to "page-reordered" to call tabpage_move() with the new index (Foxe Chen). closes: #20362 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 2 files changed, 44 insertions(+) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index dace72ae4d..b5e134b8a4 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -278,6 +278,10 @@ static void focus_out_event(GtkEventControllerFocus *controller, gpointer data); #ifdef FEAT_DND static gboolean drop_cb(GtkDropTarget *target, const GValue *value, double x, double y, gpointer data); #endif +#ifdef FEAT_GUI_TABLINE +static void on_select_tab(GtkNotebook *notebook, gpointer *page, gint idx, gpointer data); +static void on_tab_reordered(GtkNotebook *notebook, gpointer *page, gint idx, gpointer data); +#endif static void mainwin_destroy_cb(GObject *object, gpointer data); static gboolean delete_event_cb(GtkWindow *window, gpointer data); static void mainwin_fullscreened_cb(GObject *obj, GParamSpec *pspec, gpointer user_data); @@ -488,6 +492,11 @@ gui_mch_init(void) gtk_notebook_set_scrollable(GTK_NOTEBOOK(gui.tabline), TRUE); gtk_widget_set_visible(gui.tabline, FALSE); gtk_box_append(GTK_BOX(vbox), gui.tabline); + + g_signal_connect(G_OBJECT(gui.tabline), "switch-page", + G_CALLBACK(on_select_tab), NULL); + g_signal_connect(G_OBJECT(gui.tabline), "page-reordered", + G_CALLBACK(on_tab_reordered), NULL); #endif // The form widget manages absolute positioning of scrollbars. @@ -2550,6 +2559,39 @@ gui_mch_set_curtab(int nr) if (gui.tabline != NULL) gtk_notebook_set_current_page(GTK_NOTEBOOK(gui.tabline), nr - 1); } + +/* + * Handle selecting one of the tabs. + */ + static void +on_select_tab( + GtkNotebook *notebook UNUSED, + gpointer *page UNUSED, + gint idx, + gpointer data UNUSED) +{ + if (!ignore_tabline_evt) + send_tabline_event(idx + 1); +} + +/* + * Handle reordering the tabs (using D&D). + */ + static void +on_tab_reordered( + GtkNotebook *notebook UNUSED, + gpointer *page UNUSED, + gint idx, + gpointer data UNUSED) +{ + if (ignore_tabline_evt) + return; + + if ((tabpage_index(curtab) - 1) < idx) + tabpage_move(idx + 1); + else + tabpage_move(idx); +} #endif /* diff --git a/src/version.c b/src/version.c index d3977ad1ff..31d5a23059 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 564, /**/ 563, /**/ From 63680c6d3d52477817b49cd1a66e7aabe8a7aa19 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sat, 30 May 2026 16:34:40 +0000 Subject: [PATCH 358/545] patch 9.2.0565: [security]: out-of-bounds read in update_snapshot() Problem: Out-of-bounds read in update_snapshot() when a terminal cell fills all VTERM_MAX_CHARS_PER_CELL slots (a base character plus five combining marks): the loop over cell.chars[] has no upper bound and libvterm leaves the array unterminated when full, so it reads past the array and appends out-of-bounds values to a buffer sized for only VTERM_MAX_CHARS_PER_CELL characters. Solution: Bound the loop with i < VTERM_MAX_CHARS_PER_CELL, mirroring the loop in handle_pushline() (Christian Brabandt). Signed-off-by: Christian Brabandt --- src/terminal.c | 3 +- src/testdir/samples/combining_chars.txt | 200 ++++++++++++++++++++++++ src/testdir/test_terminal3.vim | 15 ++ src/version.c | 2 + 4 files changed, 219 insertions(+), 1 deletion(-) create mode 100644 src/testdir/samples/combining_chars.txt diff --git a/src/terminal.c b/src/terminal.c index b843f22da3..b748ed35f1 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -2265,7 +2265,8 @@ update_snapshot(term_T *term) int i; int c; - for (i = 0; (c = cell.chars[i]) > 0 || i == 0; ++i) + for (i = 0; i < VTERM_MAX_CHARS_PER_CELL && + ((c = cell.chars[i]) > 0 || i == 0); ++i) ga.ga_len += utf_char2bytes(c == NUL ? ' ' : c, (char_u *)ga.ga_data + ga.ga_len); } diff --git a/src/testdir/samples/combining_chars.txt b/src/testdir/samples/combining_chars.txt new file mode 100644 index 0000000000..d9a3c171fb --- /dev/null +++ b/src/testdir/samples/combining_chars.txt @@ -0,0 +1,200 @@ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ +á́́́́ጁ +á́́́́ጁ +á́́́́ጁ +á́́́́ጁ +á́́́́ጁ +á́́́́ጁ +á́́́́ጁ +á́́́́ጁ +á́́́́ጁ +á́́́́ጁ diff --git a/src/testdir/test_terminal3.vim b/src/testdir/test_terminal3.vim index 04c7c925e3..738a4c6284 100644 --- a/src/testdir/test_terminal3.vim +++ b/src/testdir/test_terminal3.vim @@ -1241,4 +1241,19 @@ func Test_terminal_csi_args_overflow() call StopVimInTerminal(buf) endfunc +func Test_terminal_output_combining_chars() + CheckUnix + new + let cmd = "cat samples/combining_chars.txt" + let buf = term_start(cmd, {'curwin': 1, 'term_finish': 'open', 'term_rows': 10, 'term_cols': 30}) + call WaitForAssert({-> assert_match('finished', term_getstatus(buf))}) + call TermWait(buf) + let lines = getbufline(buf, 1, '$') + " get byte lengths to confirm combining chars present + let lens = map(copy(lines), 'len(v:val)') + let expected = repeat([11], 190) + repeat([14], 10) + call assert_equal(expected, lens) + bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index 31d5a23059..7c9c18a7db 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 565, /**/ 564, /**/ From 113e507cdd7be13607db81dcd6cd09aaedf93540 Mon Sep 17 00:00:00 2001 From: Yohei Kojima Date: Sat, 30 May 2026 18:07:21 +0000 Subject: [PATCH 359/545] patch 9.2.0566: f duplicates window if do_ecmd() is aborted Problem: If got_int is true when win_close() is called, it unexpectedly fails in the branch that detects failure in apply_autocmds(). This causes wingotofile in do_window() to duplicate current window when do_ecmd() is aborted with got_int. Solution: Fix do_window() to save the got_int value before trying to close the split window (Yohei Kojima). Steps to reproduce: 1. run `touch a && touch .a.swp && echo a > b && vim b` 2. Type `f` 3. In the warning dialogue, type `a` to abort 4. Current window is duplicated closes: #20382 Signed-off-by: Yohei Kojima Signed-off-by: Christian Brabandt --- src/testdir/test_window_cmd.vim | 32 ++++++++++++++++++++++++++++++++ src/version.c | 2 ++ src/window.c | 12 ++++++++++++ 3 files changed, 46 insertions(+) diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index b5ec9e5e75..e7f391ed9c 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -113,7 +113,39 @@ func Test_window_cmd_wincmd_gf() call assert_notequal(fname, bufname("%")) new | only! + au! test_window_cmd_wincmd_gf augroup! test_window_cmd_wincmd_gf + delfunc s:swap_exists + bw! +endfunc + +func Test_abort_in_wincmd_f() + let fname = 'test_f.txt' + let swp_fname = $'.{fname}.swp' + call writefile([], fname, 'D') + call writefile([], swp_fname, 'D') + " Remove the catch-all that runtest.vim adds + au! SwapExists + augroup test_window_cmd_wincmd_f + autocmd! + " (A)bort + autocmd SwapExists test_f.txt let v:swapchoice = 'a' + augroup END + + call setline(1, fname) + call assert_equal(1, winnr('$')) + try + wincmd f + catch /^Vim:Interrupt$/ + " expected interrupt by abort + endtry + call assert_equal(1, winnr('$')) + new | only! + + " See :h W19 for the background of this au!. Ideally other tests + " should also follow this. + au! test_window_cmd_wincmd_f + augroup! test_window_cmd_wincmd_f bw! endfunc diff --git a/src/version.c b/src/version.c index 7c9c18a7db..cf4cf4f2dc 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 566, /**/ 565, /**/ diff --git a/src/window.c b/src/window.c index 46302b848d..bd56885008 100644 --- a/src/window.c +++ b/src/window.c @@ -682,9 +682,21 @@ do_window( if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) { + /* + * Note: if FEAT_EVAL is defined and do_ecmd() is aborted resulting + * in got_int be true, win_close() unconditionally fails. In such + * case, the window split for do_ecmd() is left unclosed, i.e. the + * current window is just duplicated. To avoid this, save and load + * got_int value before and after closing the window. + */ + sig_atomic_t old_got_int = got_int; + got_int = FALSE; + // Failed to open the file, close the window // opened for it. win_close(curwin, FALSE); + got_int = got_int || old_got_int; + goto_tabpage_win(oldtab, oldwin); } else From 3b0a32d7d8bd8710d1b9fd94464a0903085f516e Mon Sep 17 00:00:00 2001 From: Shuo Wang Date: Sat, 30 May 2026 18:18:41 +0000 Subject: [PATCH 360/545] runtime(pilrc): fix typo country names in pilrcCountry syntax list Corrected "Indian" to "India" for accurate naming. closes: #20369 Signed-off-by: wangshuo Signed-off-by: Christian Brabandt --- runtime/syntax/pilrc.vim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runtime/syntax/pilrc.vim b/runtime/syntax/pilrc.vim index f0e5f9bbc7..afa527aeea 100644 --- a/runtime/syntax/pilrc.vim +++ b/runtime/syntax/pilrc.vim @@ -1,7 +1,8 @@ " Vim syntax file " Language: pilrc - a resource compiler for Palm OS development " Maintainer: Brian Schau -" Last change: 2003 May 11 +" Last Change: 2003 May 11 +" 2026 May 30 by Vim project: Fix typo #20369 " Available on: http://www.schau.com/pilrcvim/pilrc.vim " quit when a syntax file was already loaded @@ -72,7 +73,7 @@ syn keyword pilrcType WARNING WEEKSTARTDAY " Country syn keyword pilrcCountry Australia Austria Belgium Brazil Canada Denmark -syn keyword pilrcCountry Finland France Germany HongKong Iceland Indian +syn keyword pilrcCountry Finland France Germany HongKong Iceland India syn keyword pilrcCountry Indonesia Ireland Italy Japan Korea Luxembourg Malaysia syn keyword pilrcCountry Mexico Netherlands NewZealand Norway Philippines syn keyword pilrcCountry RepChina Singapore Spain Sweden Switzerland Thailand From 6de842c273afbb2b1a17ab42ff7fd50069dd243d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sat, 30 May 2026 18:20:47 +0000 Subject: [PATCH 361/545] runtime(autoload): consistently align with TABs in README.txt closes: #20378 Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- runtime/autoload/README.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/autoload/README.txt b/runtime/autoload/README.txt index b22581963e..2180a744bf 100644 --- a/runtime/autoload/README.txt +++ b/runtime/autoload/README.txt @@ -14,10 +14,10 @@ Omni completion files: ccomplete.vim C csscomplete.vim HTML / CSS htmlcomplete.vim HTML -javascriptcomplete.vim Javascript +javascriptcomplete.vim Javascript phpcomplete.vim PHP pythoncomplete.vim Python -python3complete.vim Python +python3complete.vim Python rubycomplete.vim Ruby syntaxcomplete.vim from syntax highlighting xmlcomplete.vim XML (uses files in the xml directory) From 0e8cf80ca1b0c0c4c921ca736b22ffc8c7c2c425 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 30 May 2026 18:24:24 +0000 Subject: [PATCH 362/545] CI: Bump github/codeql-action Bumps the github-actions group with 1 update in the / directory: [github/codeql-action](https://github.com/github/codeql-action). Updates `github/codeql-action` from 4.35.5 to 4.36.0 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/v4.35.5...v4.36.0) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 4.36.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: github-actions ... closes: #20377 Signed-off-by: dependabot[bot] Signed-off-by: Christian Brabandt --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index ae2a4a5315..24ef96eaf0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -48,7 +48,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v4.35.5 + uses: github/codeql-action/init@v4.36.0 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -59,7 +59,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v4.35.5 + uses: github/codeql-action/autobuild@v4.36.0 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -73,4 +73,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v4.35.5 + uses: github/codeql-action/analyze@v4.36.0 From 22a78b5de72099e6f063dfbc2ab1daebc25f3ae6 Mon Sep 17 00:00:00 2001 From: Mao-Yining Date: Sat, 30 May 2026 18:31:45 +0000 Subject: [PATCH 363/545] runtime(vim9): remove unnecessary temp variable closes: #20374 Signed-off-by: Mao-Yining Signed-off-by: Christian Brabandt --- runtime/autoload/dist/vim9.vim | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/runtime/autoload/dist/vim9.vim b/runtime/autoload/dist/vim9.vim index cd4022aba7..a8bb4f95d0 100644 --- a/runtime/autoload/dist/vim9.vim +++ b/runtime/autoload/dist/vim9.vim @@ -3,7 +3,7 @@ vim9script # Vim runtime support library # # Maintainer: The Vim Project -# Last Change: 2026 May 06 +# Last Change: 2026 May 30 export def IsSafeExecutable(filetype: string, executable: string): bool if empty(exepath(executable)) @@ -131,13 +131,12 @@ enddef export def Open(file: string) # disable shellslash for shellescape, required on Windows #17995 if exists('+shellslash') && &shellslash - &shellslash = false defer setbufvar('%', '&shellslash', true) + &shellslash = false endif if &shell == 'pwsh' || &shell == 'powershell' - const shell = &shell + defer setbufvar('%', '&shell', &shell) setlocal shell& - defer setbufvar('%', '&shell', shell) endif if has('unix') && !has('win32unix') && !exists('$WSL_DISTRO_NAME') Launch($"{Viewer()} {shellescape(file)}") From 2b2dfc4f5a6064efd192c19750b551a01f393e83 Mon Sep 17 00:00:00 2001 From: thinca Date: Sat, 30 May 2026 18:36:34 +0000 Subject: [PATCH 364/545] patch 9.2.0567: dict function name allocation failure not handled Problem: When defining a dictionary function, the function name string is allocated with vim_strnsave() but the result is not checked. On allocation failure the dict entry is left with type VAR_FUNC and a NULL name, and in the overwrite case the previous entry has already been freed before the NULL is stored. Solution: Allocate the name before modifying the dict entry and bail out on failure, freeing it on all error paths (thinca) closes: #20376 Co-Authored-by: Claude Signed-off-by: thinca Signed-off-by: Christian Brabandt --- src/userfunc.c | 11 ++++++++++- src/version.c | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/userfunc.c b/src/userfunc.c index 2615185386..bd4c0bbc32 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -5612,18 +5612,27 @@ define_function( if (fudi.fd_dict != NULL) { + char_u *func_name = vim_strnsave(name, namelen); + + if (func_name == NULL) + { + VIM_CLEAR(fp); + goto erret; + } if (fudi.fd_di == NULL) { // add new dict entry fudi.fd_di = dictitem_alloc(fudi.fd_newkey); if (fudi.fd_di == NULL) { + vim_free(func_name); VIM_CLEAR(fp); goto erret; } if (dict_add(fudi.fd_dict, fudi.fd_di) == FAIL) { vim_free(fudi.fd_di); + vim_free(func_name); VIM_CLEAR(fp); goto erret; } @@ -5632,7 +5641,7 @@ define_function( // overwrite existing dict entry clear_tv(&fudi.fd_di->di_tv); fudi.fd_di->di_tv.v_type = VAR_FUNC; - fudi.fd_di->di_tv.vval.v_string = vim_strnsave(name, namelen); + fudi.fd_di->di_tv.vval.v_string = func_name; // behave like "dict" was used flags |= FC_DICT; diff --git a/src/version.c b/src/version.c index cf4cf4f2dc..56ebd6f0c0 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 567, /**/ 566, /**/ From 868ad62cb8bf8038322eab2badd31bd98b02b9df Mon Sep 17 00:00:00 2001 From: thinca Date: Sun, 31 May 2026 12:33:07 +0000 Subject: [PATCH 365/545] patch 9.2.0568: pythoncomplete: g:pythoncomplete_allow_import had no effect Problem: The security patch 9.2.0561 added a vim.eval() call inside Completer.evalsource() to honor g:pythoncomplete_allow_import. But the 'vim' module is only imported inside the outer vimcomplete() / vimpy3complete() function, not at the script's top level, so referring to it from a Completer method raises NameError. The surrounding bare 'except' silently swallows the error and leaves allow_imports at 0, meaning the opt-in never takes effect -- 'import os' (and any other buffer-level import) is always skipped, no candidates are produced for 'os.<...>' and Test_popup_and_preview_autocommand() fails on the Windows CI matrix (Linux skips the test because Python 2 is absent). Solution: Re-import 'vim' at the top of evalsource() in both pythoncomplete.vim and python3complete.vim so the eval reads the global, and set g:pythoncomplete_allow_import = 1 in the test (it is the opt-in intended for callers that trust the buffer contents) (thinca). closes: #20386 Signed-off-by: thinca Signed-off-by: Christian Brabandt --- runtime/autoload/python3complete.vim | 3 +++ runtime/autoload/pythoncomplete.vim | 3 +++ src/testdir/test_popup.vim | 4 ++++ src/version.c | 2 ++ 4 files changed, 12 insertions(+) diff --git a/runtime/autoload/python3complete.vim b/runtime/autoload/python3complete.vim index 2b6a652525..0cbfc02ab9 100644 --- a/runtime/autoload/python3complete.vim +++ b/runtime/autoload/python3complete.vim @@ -135,6 +135,9 @@ class Completer(object): self.parser = PyParser() def evalsource(self,text,line=0): + # vim is imported locally in vimpy3complete(); re-import here so the + # vim.eval() below works (otherwise NameError, silently caught). + import vim sc = self.parser.parse(text,line) try: allow_imports = int( vim.eval("get(g:, 'pythoncomplete_allow_import', 0)")) diff --git a/runtime/autoload/pythoncomplete.vim b/runtime/autoload/pythoncomplete.vim index 10147767ef..b4340f7ae2 100644 --- a/runtime/autoload/pythoncomplete.vim +++ b/runtime/autoload/pythoncomplete.vim @@ -149,6 +149,9 @@ class Completer(object): self.parser = PyParser() def evalsource(self,text,line=0): + # vim is imported locally in vimcomplete(); re-import here so the + # vim.eval() below works (otherwise NameError, silently caught). + import vim sc = self.parser.parse(text,line) try: allow_imports = int( vim.eval("get(g:, 'pythoncomplete_allow_import', 0)")) diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim index adead6bac4..cf9cbe166e 100644 --- a/src/testdir/test_popup.vim +++ b/src/testdir/test_popup.vim @@ -723,6 +723,9 @@ func Test_popup_and_preview_autocommand() au! au BufAdd * nested tab sball augroup END + " Let pythoncomplete follow the buffer's 'import os' (off by default + " since v9.2.0561) so 'os.' can be completed. + let g:pythoncomplete_allow_import = 1 set omnifunc=pythoncomplete#Complete call setline(1, 'import os') " make the line long @@ -745,6 +748,7 @@ func Test_popup_and_preview_autocommand() augroup END augroup! MyBufAdd bw! + unlet g:pythoncomplete_allow_import endfunc func s:run_popup_and_previewwindow_dump(lines, dumpfile) diff --git a/src/version.c b/src/version.c index 56ebd6f0c0..d9815be705 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 568, /**/ 567, /**/ From 54ffef7eb83f2592833f422b89bf5f30de6b4bb0 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 31 May 2026 14:27:16 +0000 Subject: [PATCH 366/545] patch 9.2.0569: out-of-bounds access in libvterm CSI 8 t resize Problem: In the bundled libvterm the CSI 8 ; rows ; cols t sequence reaches on_resize() without validating its arguments. Missing, zero or negative dimensions cause a negative-size memmove() in resize_buffer() and out-of-bounds accesses in set_lineinfo() and DECALN, all reachable from output rendered in a terminal window (Yukihiro Nakamura). Solution: Reject missing, zero or negative dimensions before calling on_resize(). Also clamp a negative cell width in on_text() as hardening for the bundled libvterm. Signed-off-by: Christian Brabandt --- src/libvterm/src/state.c | 7 +++- src/testdir/test_terminal3.vim | 61 ++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 69 insertions(+), 1 deletion(-) diff --git a/src/libvterm/src/state.c b/src/libvterm/src/state.c index 34b37445c2..70bce0a236 100644 --- a/src/libvterm/src/state.c +++ b/src/libvterm/src/state.c @@ -420,6 +420,9 @@ static int on_text(const char bytes[], size_t len, void *user) width = this_width; // TODO: should be += ? } + if (width < 0) + width = 0; + while(i < npoints && vterm_unicode_is_combining(codepoints[i])) i++; @@ -1640,7 +1643,9 @@ static int on_csi(const char *leader, const long args[], int argcount, const cha case 0x74: switch(CSI_ARG(args[0])) { case 8: // CSI 8 ; rows ; cols t set size - if (argcount == 3) + if (argcount == 3 && + !CSI_ARG_IS_MISSING(args[1]) && !CSI_ARG_IS_MISSING(args[2]) && + CSI_ARG(args[1]) > 0 && CSI_ARG(args[2]) > 0) on_resize(CSI_ARG(args[1]), CSI_ARG(args[2]), state); break; default: diff --git a/src/testdir/test_terminal3.vim b/src/testdir/test_terminal3.vim index 738a4c6284..2e80f6fe44 100644 --- a/src/testdir/test_terminal3.vim +++ b/src/testdir/test_terminal3.vim @@ -1256,4 +1256,65 @@ func Test_terminal_output_combining_chars() bw! endfunc +" This caused a Crash +func Test_terminal_csi_resize_oob() + return + CheckUnix + CheckExecutable printf + + " CSI 8 ; rows ; cols t with missing, zero or negative dimensions reached + " on_resize()/resize_buffer() unvalidated, causing a negative-size memmove() + " and out-of-bounds set_lineinfo()/DECALN accesses in libvterm. Rendering + " these must not crash Vim. + + " Sequences: + " 1 resize_buffer negative-size memmove + " 2 set_lineinfo OOB after corrupt resize + " 3 DECALN putglyph OOB after corrupt resize + let seqs = ["\[8;0;t", + \ "\[8;;t\[J", + \ "\[8;;0t\#8"] + + for seq in seqs + let buf = term_start([&shell, &shellcmdflag, 'printf "%s" ' .. shellescape(seq)], + \ #{term_rows: 10, term_cols: 40}) + call TermWait(buf) + " Getting here without a crash (and no ASAN report) is the test. + call assert_true(bufexists(buf)) + exe 'bwipe! ' .. buf + endfor +endfunc + +" This caused a Crash, but Vim builds libvterm using -DWCWIDTH_FUNCTION=utf_uint2cells +" which wouldn't return -1 and therefore does not reproduce here +func Test_terminal_negative_col_oob() + CheckUnix + CheckExecutable printf + + " A wcwidth() == -1 codepoint (U+0087, \302\207 in UTF-8) drove + " state->pos.col negative, then used as an array index in the tabstop + " helpers and moverect_internal(). These are single-byte / single-bit + " out-of-bounds accesses that do not crash a normal build, so this test + " is only meaningful under a sanitizer build; otherwise it just confirms + " Vim does not crash. + + " Sequences: + " 1. clear_col_tabstop OOB read + " 2. is_col_tabstop OOB read + " 3. set_col_tabstop OOB write (HTS) + " 4. moverect_internal memmove (insert mode) + + let seqs = ["\302\207\[g", + \ "\302\207\302\207\t", + \ "\302\207\H", + \ "\[4h\302\2070"] + for seq in seqs + let buf = term_start([&shell, &shellcmdflag, 'printf "%s" ' .. shellescape(seq)], + \ #{term_rows: 10, term_cols: 40}) + call TermWait(buf) + call assert_true(bufexists(buf)) + exe 'bwipe! ' .. buf + endfor +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index d9815be705..cc7fdadc15 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 569, /**/ 568, /**/ From d0fffdab1628c17220425f90d5f341f462739bb1 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Sun, 31 May 2026 18:24:21 +0000 Subject: [PATCH 367/545] patch 9.2.0570: GTK4: mouse wheel scrolling does not work correctly Problem: GTK4: mouse wheel scrolling does not work correctly Solution: Use gui_mch_getmouse() to obtain the pointer position, and add GTK_EVENT_CONTROLLER_SCROLL_DISCRETE to the scroll controller flags (Foxe Chen). closes: #20389 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 11 +++++------ src/version.c | 2 ++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index b5e134b8a4..1596f19e5c 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -573,7 +573,8 @@ gui_mch_init(void) { GtkEventController *scroll = gtk_event_controller_scroll_new( - GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES); + GTK_EVENT_CONTROLLER_SCROLL_BOTH_AXES + | GTK_EVENT_CONTROLLER_SCROLL_DISCRETE); g_signal_connect(scroll, "scroll", G_CALLBACK(scroll_event), NULL); gtk_widget_add_controller(gui.drawarea, scroll); @@ -1860,6 +1861,7 @@ scroll_event(GtkEventControllerScroll *controller UNUSED, { int button; int_u vim_modifiers; + int x, y; GdkModifierType state; GdkEvent *event; @@ -1882,11 +1884,8 @@ scroll_event(GtkEventControllerScroll *controller UNUSED, vim_modifiers = modifiers_gdk2mouse(state); - { - double mx, my; - gdk_event_get_position(event, &mx, &my); - gui_send_mouse_event(button, (int)mx, (int)my, FALSE, vim_modifiers); - } + gui_mch_getmouse(&x, &y); + gui_send_mouse_event(button, x, y, FALSE, vim_modifiers); return TRUE; } diff --git a/src/version.c b/src/version.c index cc7fdadc15..15a4d34e9c 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 570, /**/ 569, /**/ From 37089793b8343548d75b1c856d0606e7bac62387 Mon Sep 17 00:00:00 2001 From: thinca Date: Sun, 31 May 2026 18:28:34 +0000 Subject: [PATCH 368/545] patch 9.2.0571: Vim9: memory leak in compile_nested_function() on failure Problem: compile_nested_function() calls define_function(), which registers the new ufunc in func_hashtab with uf_refcount == 1. For a local nested function the caller then reserves a local lvalue and generates a FUNCREF instruction; if either step fails, the code jumps to the theend label and leaves the ufunc behind with refcount 1 and no external reference, leaking it. This mirrors patch 8.2.3951, which fixed the same leak for the "text after :enddef" branch a few lines above. Solution: Call func_ptr_unref() on the ufunc before "goto theend" on both failure paths in the local-variable branch (thinca). closes: #20394 Co-Authored-by: Claude Signed-off-by: thinca Signed-off-by: Christian Brabandt --- src/version.c | 2 ++ src/vim9compile.c | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/src/version.c b/src/version.c index 15a4d34e9c..a90dc1cb9b 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 571, /**/ 570, /**/ diff --git a/src/vim9compile.c b/src/vim9compile.c index cbee429763..aa9384bbe9 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -1170,9 +1170,15 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free) lvar = reserve_local(cctx, func_name, name_end - name_start, ASSIGN_CONST, ufunc->uf_func_type); if (lvar == NULL) + { + func_ptr_unref(ufunc); goto theend; + } if (generate_FUNCREF(cctx, ufunc, NULL, FALSE, 0, &funcref_isn_idx) == FAIL) + { + func_ptr_unref(ufunc); goto theend; + } r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL); } From 09f7fc60d3d76504cf46871277e471d034776583 Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Sun, 31 May 2026 18:43:42 +0000 Subject: [PATCH 369/545] patch 9.2.0572: lines disappear with wrapping virtual text after a double-width char Problem: With 'nowrap', when a line ends with a double-width character exactly at the window width and has wrapping "after" virtual text, the lines below disappear and "@@@" is shown. Solution: Detect that the last character fills the rightmost column using its displayed width (win_chartabsize(), so a or double-width character is handled like a single-width one), and also when it overflows the last column. Also clarify in the help that "wrap" only takes effect with the 'wrap' option set. fixes: #20384 related: #12213 closes: #20395 Co-Authored-By: Claude Opus 4.8 (1M context) Signed-off-by: zeertzjq Signed-off-by: Christian Brabandt --- runtime/doc/textprop.txt | 6 ++- src/drawline.c | 6 ++- ...Test_prop_with_text_after_wide_char_1.dump | 8 +++ ...Test_prop_with_text_after_wide_char_2.dump | 8 +++ src/testdir/test_textprop.vim | 50 +++++++++++++++++++ src/testdir/test_vim9_cmd.vim | 21 ++++++++ src/userfunc.c | 4 +- src/version.c | 2 + 8 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 src/testdir/dumps/Test_prop_with_text_after_wide_char_1.dump create mode 100644 src/testdir/dumps/Test_prop_with_text_after_wide_char_2.dump diff --git a/runtime/doc/textprop.txt b/runtime/doc/textprop.txt index 9fd5ff6686..ce28677913 100644 --- a/runtime/doc/textprop.txt +++ b/runtime/doc/textprop.txt @@ -1,4 +1,4 @@ -*textprop.txt* For Vim version 9.2. Last change: 2026 Apr 07 +*textprop.txt* For Vim version 9.2. Last change: 2026 May 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -177,7 +177,9 @@ prop_add({lnum}, {col}, {props}) When omitted "truncate" is used. Note that this applies to the individual text property, the 'wrap' option sets the overall - behavior + behavior. "wrap" only takes effect when the + 'wrap' option is set; with 'nowrap' the text + is truncated at the right edge of the window. All fields except "type" are optional. It is an error when both "length" and "end_lnum" or "end_col" diff --git a/src/drawline.c b/src/drawline.c index 3ebb56a173..0b912e1eae 100644 --- a/src/drawline.c +++ b/src/drawline.c @@ -2479,7 +2479,11 @@ win_line( // displaying that character. // Or when not wrapping and at the rightmost column. - int only_below_follows = !wp->w_p_wrap && wlv.col == wp->w_width - 1; + // Use the displayed width so a double-width or last + // character filling the rightmost column is detected too. + int only_below_follows = !wp->w_p_wrap + && wlv.col + win_chartabsize(wp, ptr, wlv.vcol) + >= wp->w_width; int suffix_flags = text_prop_suffix_flags[text_prop_next]; text_prop_follows = (suffix_flags diff --git a/src/testdir/dumps/Test_prop_with_text_after_wide_char_1.dump b/src/testdir/dumps/Test_prop_with_text_after_wide_char_1.dump new file mode 100644 index 0000000000..36dfed3ff2 --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_after_wide_char_1.dump @@ -0,0 +1,8 @@ +>x+0&#ffffff0@42|口*& +|s+&|e|c|o|n|d| |l|i|n|e| @33 +|t|h|i|r|d| |l|i|n|e| @34 +|~+0#4040ff13&| @43 +|~| @43 +|~| @43 +|~| @43 +| +0#0000000&@26|1|,|1| @10|A|l@1| diff --git a/src/testdir/dumps/Test_prop_with_text_after_wide_char_2.dump b/src/testdir/dumps/Test_prop_with_text_after_wide_char_2.dump new file mode 100644 index 0000000000..50136517f4 --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_after_wide_char_2.dump @@ -0,0 +1,8 @@ +>x+0&#ffffff0@38|>+0#4040ff13& +|b+0#0000000&|e|t|w|e@1|n| |l|i|n|e| @27 +|x@31| @7 +|l|a|s|t| |l|i|n|e| @30 +|~+0#4040ff13&| @38 +|~| @38 +|~| @38 +| +0#0000000&@21|1|,|1| @10|A|l@1| diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index a57493bc81..7d868ec064 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -3574,6 +3574,56 @@ func Test_props_with_text_after_nowrap() call StopVimInTerminal(buf) endfunc +func Test_props_with_text_after_wide_char_at_end() + CheckScreendump + CheckRunVimInTerminal + + " The buffer line ends with a double-width character exactly at the window + " width and has wrapping "after" virtual text. This must not leave blank + " lines or "@@@", see issue #20384. + let lines =<< trim END + vim9script + set nowrap + setline(1, [repeat('x', 43) .. '口', 'second line', 'third line']) + prop_type_add('errtype', {highlight: 'WarningMsg', text_wrap: 'wrap'}) + prop_add(1, 0, {type: 'errtype', text_padding_left: 3, text: 'E>'}) + END + call writefile(lines, 'XscriptPropsAfterWideChar', 'D') + let buf = RunVimInTerminal('-S XscriptPropsAfterWideChar', #{rows: 8, cols: 45}) + call VerifyScreenDump(buf, 'Test_prop_with_text_after_wide_char_1', {}) + + call StopVimInTerminal(buf) +endfunc + +func Test_props_with_text_after_wide_char_overflow() + CheckScreendump + CheckRunVimInTerminal + + " Like above, but the last character reaches the rightmost column without + " starting on it: a double-width character that does not fit in the last + " column, and a that expands up to the window width. Both must be + " detected as filling the line so the wrapping "after" text does not cause + " blank lines, "@@@" or a spurious wrap with 'nowrap'. + let lines =<< trim END + vim9script + set nowrap tabstop=8 noexpandtab + setline(1, [ + repeat('x', 39) .. '口', + 'between line', + repeat('x', 32) .. "\t", + 'last line', + ]) + prop_type_add('errtype', {highlight: 'WarningMsg', text_wrap: 'wrap'}) + prop_add(1, 0, {type: 'errtype', text_padding_left: 3, text: 'E>'}) + prop_add(3, 0, {type: 'errtype', text_padding_left: 3, text: 'E>'}) + END + call writefile(lines, 'XscriptPropsAfterWideOverflow', 'D') + let buf = RunVimInTerminal('-S XscriptPropsAfterWideOverflow', #{rows: 8, cols: 40}) + call VerifyScreenDump(buf, 'Test_prop_with_text_after_wide_char_2', {}) + + call StopVimInTerminal(buf) +endfunc + func Test_prop_with_text_below_cul() CheckScreendump CheckRunVimInTerminal diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index f90d719715..a4c2d05d45 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -2157,4 +2157,25 @@ def Test_map_legacy_expr() v9.CheckDefAndScriptSuccess(lines) enddef +" :call on a funcref stored in a dict member used to fail with E1017 in Vim9 +" script because get_lval() treated the subscript as a re-declaration. +def Test_call_dict_funcref() + var lines =<< trim END + vim9script + var d: dict = {} + var marker = '' + def F() + marker = 'called' + enddef + d.key = F + d['k2'] = F + call d.key() + assert_equal('called', marker) + marker = '' + call d['k2']() + assert_equal('called', marker) + END + v9.CheckScriptSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/userfunc.c b/src/userfunc.c index bd4c0bbc32..ff5cf76a0e 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -6273,7 +6273,7 @@ ex_delfunction(exarg_T *eap) int is_global = FALSE; p = eap->arg; - name = trans_function_name_ext(&p, &is_global, eap->skip, 0, &fudi, + name = trans_function_name_ext(&p, &is_global, eap->skip, TFN_NO_DECL, &fudi, NULL, NULL, NULL); vim_free(fudi.fd_newkey); if (name == NULL) @@ -6823,7 +6823,7 @@ ex_call(exarg_T *eap) return; } - tofree = trans_function_name_ext(&arg, NULL, FALSE, TFN_INT, + tofree = trans_function_name_ext(&arg, NULL, FALSE, TFN_INT | TFN_NO_DECL, &fudi, &partial, vim9script ? &type : NULL, &ufunc); if (fudi.fd_newkey != NULL) { diff --git a/src/version.c b/src/version.c index a90dc1cb9b..ac58fbdf80 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 572, /**/ 571, /**/ From 905312e0e6a2f7982b9b0ff19f26cc19a7226abd Mon Sep 17 00:00:00 2001 From: "K.Takata" Date: Sun, 31 May 2026 18:46:40 +0000 Subject: [PATCH 370/545] NSIS: Fix 32-bit context menu The 32-bit context menu was not translated after 8ae45e420218f5ed913c6b99cbccce99245ea97c. NSIS requires a space after a `!`. closes: #20396 Signed-off-by: K.Takata Signed-off-by: Christian Brabandt --- nsis/gvim.nsi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nsis/gvim.nsi b/nsis/gvim.nsi index 3d48008863..02751ce90a 100644 --- a/nsis/gvim.nsi +++ b/nsis/gvim.nsi @@ -638,7 +638,7 @@ SectionGroupEnd !undef LIBRARY_X64 ${EndIf} - !if !${ARM64} + !if ! ${ARM64} # Install DLLs for 32-bit gvimext.dll into the GvimExt32 directory. SetOutPath $0\GvimExt32 ClearErrors From 38d9a16eba8cdf3377e8b867da805bf369454108 Mon Sep 17 00:00:00 2001 From: Peter Kenny Date: Sun, 31 May 2026 19:14:21 +0000 Subject: [PATCH 371/545] patch 9.2.0573: Vim9: missing EX_WHOLE on some block keywords Problem: Several Vim9 keywords lack EX_WHOLE and can be shortened in Vim9 script, inconsistent with endif/enddef/endfor/endwhile/ endtry which already have it. The error from :endd in a nested function also hardcodes "enddef" instead of reporting what the user typed. fullcommand("ho") returns "horizontal" even though :ho is below the documented 3-char minimum. Solution: Add EX_WHOLE to :class, :def, :endclass, :endinterface, :endenum, :public and :static. In get_function_body() pass the user-typed command to the error message. Force :ho to CMD_SIZE in find_ex_command() so fullcommand() reflects the modifier minimum. Extend tests and documentation accordingly (Peter Kenny). fixes: #20032 closes: #20191 Signed-off-by: Peter Kenny Signed-off-by: Christian Brabandt --- runtime/doc/builtin.txt | 24 ++- runtime/doc/change.txt | 32 ++-- runtime/doc/eval.txt | 6 +- runtime/doc/repeat.txt | 5 +- runtime/doc/tagsrch.txt | 7 +- runtime/doc/userfunc.txt | 22 ++- runtime/doc/usr_20.txt | 19 +- src/ex_cmds.h | 14 +- src/ex_docmd.c | 7 + src/testdir/test_cmdmods.vim | 21 +- src/testdir/test_marks.vim | 7 + src/testdir/test_vim9_class.vim | 25 +-- src/testdir/test_vim9_func.vim | 6 + src/testdir/test_vim9_interface.vim | 2 +- src/testdir/test_vim9_script.vim | 284 ++++++++++++++++++++-------- src/testdir/test_vimscript.vim | 27 +++ src/userfunc.c | 2 +- src/version.c | 2 + 18 files changed, 361 insertions(+), 151 deletions(-) diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 0268d158e7..b415fc039f 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1,4 +1,4 @@ -*builtin.txt* For Vim version 9.2. Last change: 2026 May 21 +*builtin.txt* For Vim version 9.2. Last change: 2026 May 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -3672,14 +3672,22 @@ fullcommand({name} [, {vim9}]) *fullcommand()* ambiguous (for user-defined commands) or cannot be shortened this way. |vim9-no-shorten| - Without the {vim9} argument uses the current script version. - If {vim9} is present and FALSE then legacy script rules are - used. When {vim9} is present and TRUE then Vim9 rules are - used, e.g. "en" is not a short form of "endif". - - For example `fullcommand('s')`, `fullcommand('sub')`, - `fullcommand(':%substitute')` all return "substitute". + Without the {vim9} argument, the current script version is + used. When {vim9} is present and FALSE, legacy script rules + are used. When {vim9} is present and TRUE, Vim9 rules are + used (e.g., "en" is not a short form of "endif"). + Note: Command validation is not performed. Results depend on + Vim's internal command-specific identification rules. + Examples: +>vim + echo [fullcommand('s')] |" ['substitute'] + echo [fullcommand('sub')] |" ['substitute'] + echo [fullcommand(': mark word')] |" ['mark'] + echo [fullcommand(': markword')] |" [''] + echo [fullcommand('en')] |" ['endif'] + echo [fullcommand('en', v:true)] |" [''] +< Can also be used as a |method|: > GetName()->fullcommand() < diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index d197ead5c8..b3448bf892 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1,4 +1,4 @@ -*change.txt* For Vim version 9.2. Last change: 2026 Mar 31 +*change.txt* For Vim version 9.2. Last change: 2026 May 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -75,18 +75,21 @@ For inserting text see |insert.txt|. *:d* *:de* *:del* *:delete* *:dl* *:dp* :[range]d[elete] [x] Delete [range] lines (default: current line) [into register x]. - Note these weird abbreviations: - :dl delete and list - :dell idem - :delel idem - :deletl idem - :deletel idem - :dp delete and print - :dep idem - :delp idem - :delep idem - :deletp idem - :deletep idem + Note these weird abbreviations applicable only to + legacy Vim script: + :dl delete and list + :dell idem + :delel idem + :deletl idem + :deletel idem + :dp delete and print + :dep idem + :delp idem + :delep idem + :deletp idem + :deletep idem + Warning: These give |E492| in |Vim9| script and `:dl` + executes as `:dlist`. :[range]d[elete] [x] {count} Delete {count} lines, starting with [range] @@ -798,7 +801,8 @@ out then. Example: > :%s/TESTING This deletes "TESTING" from all lines, but only one per line. *E1270* -For compatibility with Vi these two exceptions are allowed in legacy script: +For compatibility with Vi these two exceptions are allowed in legacy Vim +script: "\/{string}/" and "\?{string}?" do the same as "//{string}/r". "\&{string}&" does the same as "//{string}/". *pattern-delimiter* *E146* *E1241* *E1242* diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index a451c9f2fe..dffeb3859f 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 9.2. Last change: 2026 May 21 +*eval.txt* For Vim version 9.2. Last change: 2026 May 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -3384,7 +3384,7 @@ text... s: script-local variables l: local function variables v: Vim variables. - This does not work in Vim9 script. |vim9-declaration| + This does not work in Vim9 script. |vim9-declaration| :let List the values of all variables. The type of the variable is indicated before the value: @@ -3660,7 +3660,7 @@ text... all nested `:try`s inside the loop. The outermost `:endtry` then jumps back to the start of the loop. - In |Vim9| script `:cont` is the shortest form, to + In |Vim9| script `:continue` cannot be shortened, to improve script readability. *:break* *:brea* *E587* :brea[k] When used inside a `:while` or `:for` loop, skips to diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index ac7c78712a..95ccbb6687 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -1,4 +1,4 @@ -*repeat.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*repeat.txt* For Vim version 9.2. Last change: 2026 May 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -454,6 +454,9 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. nested ":try"s in the script. The outermost ":endtry" then stops sourcing the script. + In |Vim9| script `:finish` cannot be shortened, to + improve script readability. + All commands and command sequences can be repeated by putting them in a named register and then executing it. There are two ways to get the commands in the register: diff --git a/runtime/doc/tagsrch.txt b/runtime/doc/tagsrch.txt index d278383863..e381d5fc08 100644 --- a/runtime/doc/tagsrch.txt +++ b/runtime/doc/tagsrch.txt @@ -1,4 +1,4 @@ -*tagsrch.txt* For Vim version 9.2. Last change: 2026 May 17 +*tagsrch.txt* For Vim version 9.2. Last change: 2026 May 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -833,8 +833,9 @@ CTRL-W i Open a new window, with the cursor on the first line Like `[D` and `]D`, but search in [range] lines (default: whole file). See |:search-args| for [/] and [!]. - Note that `:dl` works like `:delete` with the "l" - flag, not `:dlist`. + Note: In legacy Vim script, `:dl` works like + `:delete` with the "l" flag, not `:dlist`, whereas in + |Vim9| script `:dl` does work like `:dlist`. *[_CTRL-D* [ CTRL-D Jump to the first macro definition that contains the diff --git a/runtime/doc/userfunc.txt b/runtime/doc/userfunc.txt index 6388d12e18..b0051fb27b 100644 --- a/runtime/doc/userfunc.txt +++ b/runtime/doc/userfunc.txt @@ -1,4 +1,4 @@ -*userfunc.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*userfunc.txt* For Vim version 9.2. Last change: 2026 May 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -27,13 +27,13 @@ make them script-local. If you do use a global function then avoid obvious, short names. A good habit is to start the function name with the name of the script, e.g., "HTMLcolor()". -In legacy script it is also possible to use curly braces, see +In legacy Vim script it is also possible to use curly braces, see |curly-braces-names|. The |autoload| facility is useful to define a function only when it's called. *local-function* -A function local to a legacy script must start with "s:". A local script +A function local to a legacy Vim script must start with "s:". A local script function can only be called from within the script and from functions, user commands and autocommands defined in the script. It is also possible to call the function from a mapping defined in the script, but then || must be @@ -195,9 +195,19 @@ See |:verbose-cmd| for more information. When a function ends without an explicit ":return", the number 0 is returned. - In a :def function *E1095* is given if unreachable - code follows after the `:return`. - In legacy script there is no check for unreachable + In |Vim9| script: + - `:return` cannot be shortened, and + - *E1095* is given if unreachable code follows after + the `:return`. For example: +>vim9 + vim9script + var L: func = (): bool => { + return false + echo 'no' # E1095: Unreachable code after :return + } + echo L() +< + In legacy Vim script there is no check for unreachable lines, thus there is no warning if commands follow `:return`. Also, there is no check if the following line contains a valid command. Forgetting the line diff --git a/runtime/doc/usr_20.txt b/runtime/doc/usr_20.txt index 2a28395411..f4cc5dd64c 100644 --- a/runtime/doc/usr_20.txt +++ b/runtime/doc/usr_20.txt @@ -1,9 +1,7 @@ -*usr_20.txt* For Vim version 9.2. Last change: 2026 Feb 14 - +*usr_20.txt* For Vim version 9.2. Last change: 2026 May 31 VIM USER MANUAL by Bram Moolenaar - Typing command-line commands quickly @@ -116,9 +114,18 @@ command. It's like deleting the ":" or "/" that the line starts with. *20.2* Command line abbreviations Some of the ":" commands are really long. We already mentioned that -":substitute" can be abbreviated to ":s". This is a generic mechanism, all -":" commands can be abbreviated. - +":substitute" can be abbreviated to ":s". This is a generic mechanism, and +most ":" commands can be abbreviated. However, in Vim9 script some commands +cannot be shortened to improve readability - see |vim9-no-shorten|. + +The builtin function |fullcommand()| can be used to return an abbreviated +command's full name. For example, the following commands echo "edit", "echo", +and "echowindow": +>vim + :echo fullcommand('e') + :echo fullcommand('ec') + :echo fullcommand('echow') +< How short can a command get? There are 26 letters, and many more commands. For example, ":set" also starts with ":s", but ":s" doesn't start a ":set" command. Instead ":set" can be abbreviated to ":se". diff --git a/src/ex_cmds.h b/src/ex_cmds.h index 1a3ff0985a..5dfb824764 100644 --- a/src/ex_cmds.h +++ b/src/ex_cmds.h @@ -362,7 +362,7 @@ EXCMD(CMD_clast, "clast", ex_cc, EX_RANGE|EX_COUNT|EX_TRLBAR|EX_BANG, ADDR_UNSIGNED), EXCMD(CMD_class, "class", ex_class, - EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT, + EX_EXTRA|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE|EX_EXPORT, ADDR_NONE), EXCMD(CMD_close, "close", ex_close, EX_BANG|EX_RANGE|EX_COUNT|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, @@ -470,7 +470,7 @@ EXCMD(CMD_debuggreedy, "debuggreedy", ex_debuggreedy, EX_RANGE|EX_ZEROR|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_OTHER), EXCMD(CMD_def, "def", ex_function, - EX_EXTRA|EX_BANG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_EXPORT, + EX_EXTRA|EX_BANG|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE|EX_EXPORT, ADDR_NONE), EXCMD(CMD_defcompile, "defcompile", ex_defcompile, EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_TRLBAR|EX_EXTRA, @@ -575,16 +575,16 @@ EXCMD(CMD_endif, "endif", ex_endif, EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE, ADDR_NONE), EXCMD(CMD_endinterface, "endinterface", ex_wrongmodifier, - EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE, ADDR_NONE), EXCMD(CMD_endclass, "endclass", ex_wrongmodifier, - EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE, ADDR_NONE), EXCMD(CMD_enddef, "enddef", ex_endfunction, EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE, ADDR_NONE), EXCMD(CMD_endenum, "endenum", ex_wrongmodifier, - EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE, ADDR_NONE), EXCMD(CMD_endfunction, "endfunction", ex_endfunction, EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, @@ -1229,7 +1229,7 @@ EXCMD(CMD_put, "put", ex_put, EX_RANGE|EX_WHOLEFOLD|EX_BANG|EX_REGSTR|EX_TRLBAR|EX_ZEROR|EX_CMDWIN|EX_LOCK_OK|EX_MODIFY, ADDR_LINES), EXCMD(CMD_public, "public", ex_wrongmodifier, - EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE, ADDR_NONE), EXCMD(CMD_pwd, "pwd", ex_pwd, EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, @@ -1508,7 +1508,7 @@ EXCMD(CMD_startreplace, "startreplace", ex_startinsert, EX_BANG|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, ADDR_NONE), EXCMD(CMD_static, "static", ex_wrongmodifier, - EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, + EX_EXTRA|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK|EX_WHOLE, ADDR_NONE), EXCMD(CMD_stopinsert, "stopinsert", ex_stopinsert, EX_BANG|EX_TRLBAR|EX_CMDWIN|EX_LOCK_OK, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 11ea59e3b0..e716613ec2 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -4025,6 +4025,13 @@ find_ex_command( if (eap->cmdidx == CMD_final && p - eap->cmd == 4 && !vim9) eap->cmdidx = CMD_finally; + // Force ":ho" to be unresolved. Without this, find_ex_command() + // matches it to CMD_horizontal (the only "ho*" entry), which makes + // fullcommand("ho") return "horizontal" even though ":ho" cannot be + // used as the modifier (cmdmods[] requires 3 chars, "hor"). + if (eap->cmdidx == CMD_horizontal && p - eap->cmd == 2) + eap->cmdidx = CMD_SIZE; + #ifdef FEAT_EVAL if (eap->cmdidx < CMD_SIZE && vim9 diff --git a/src/testdir/test_cmdmods.vim b/src/testdir/test_cmdmods.vim index 3b0deab7b5..7efe6e3111 100644 --- a/src/testdir/test_cmdmods.vim +++ b/src/testdir/test_cmdmods.vim @@ -50,12 +50,18 @@ def Test_cmdmods_array() enddef def Test_keep_cmdmods_names() - # :k only available in legacy script - legacy call assert_equal('k', fullcommand(':k')) - legacy call assert_equal('k', fullcommand(':ke')) - # single character commands not supported in Vim9 - assert_equal('', fullcommand(':k')) - assert_equal('keepmarks', fullcommand(':ke')) + # :k is only available in legacy Vim script + assert_equal('k', fullcommand(':k', false)) + # many single character commands are not supported in Vim9 script, incl. :k + assert_equal('', fullcommand(':k', true)) + # :k{a-zA-Z'} in legacy Vim script + assert_equal('k', fullcommand(':ka', false)) + assert_equal('', fullcommand(':ka', true)) + # :ke is an exception - it is 'keepmarks', not 'k', in Vim9 script + assert_equal('k', fullcommand(':ke', false)) + assert_equal('keepmarks', fullcommand(':ke', true)) + # :kee* shortenings + assert_equal('keepmarks', fullcommand(':kee', false)) assert_equal('keepmarks', fullcommand(':kee')) assert_equal('keepmarks', fullcommand(':keep')) assert_equal('keepmarks', fullcommand(':keepm')) @@ -63,14 +69,17 @@ def Test_keep_cmdmods_names() assert_equal('keepmarks', fullcommand(':keepmar')) assert_equal('keepmarks', fullcommand(':keepmark')) assert_equal('keepmarks', fullcommand(':keepmarks')) + assert_equal('keepalt', fullcommand(':keepa', false)) assert_equal('keepalt', fullcommand(':keepa')) assert_equal('keepalt', fullcommand(':keepal')) assert_equal('keepalt', fullcommand(':keepalt')) + assert_equal('keepjumps', fullcommand(':keepj', false)) assert_equal('keepjumps', fullcommand(':keepj')) assert_equal('keepjumps', fullcommand(':keepju')) assert_equal('keepjumps', fullcommand(':keepjum')) assert_equal('keepjumps', fullcommand(':keepjump')) assert_equal('keepjumps', fullcommand(':keepjumps')) + assert_equal('keeppatterns', fullcommand(':keepp', false)) assert_equal('keeppatterns', fullcommand(':keepp')) assert_equal('keeppatterns', fullcommand(':keeppa')) assert_equal('keeppatterns', fullcommand(':keeppat')) diff --git a/src/testdir/test_marks.vim b/src/testdir/test_marks.vim index ed09221449..61bfade627 100644 --- a/src/testdir/test_marks.vim +++ b/src/testdir/test_marks.vim @@ -254,7 +254,14 @@ func Test_marks_k_cmd() call setline(1, ['foo', 'bar', 'baz', 'qux']) 1,3kr call assert_equal([0, 3, 1, 0], getpos("'r")) + " whitespace before mark + 4k f + call assert_equal([0, 4, 1, 0], getpos("'f")) + :2 k g + call assert_equal([0, 2, 1, 0], getpos("'g")) bw! + call assert_fails(':kz7', 'E488: Trailing characters: z7') + call assert_fails(':execute ":k^"', 'E191: Argument must be a letter or forward/backward quote') endfunc " Test for file marks (A-Z) diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 9f060a9f47..7d725e3745 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -42,7 +42,8 @@ def Test_class_basic() END v9.CheckSourceFailure(lines, 'E475: Invalid argument: classy Something', 2) - # The complete "endclass" should be specified. + # Test for "endclass" cannot be shortened. Test_shortened_invalid_vim9() in + # test_vim9_script.vim has complete coverage (:endc to :endclas) lines =<< trim END vim9script class Something @@ -50,7 +51,7 @@ def Test_class_basic() END v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endcl', 3) - # "endclass" cannot be shortened (variant incl. whitespace and colon) + # "endclass" cannot be shortened (variant incl. colon-whitespace) lines =<< trim END vim9script class Something @@ -1361,13 +1362,14 @@ def Test_instance_variable_access() echo Foo.new() .Add(1).Add(2).x echo Foo.new() - .Add(1) + .Add(1) .Add(2) .x END v9.CheckSourceSuccess(lines) - # Test for "public" cannot be abbreviated + # Test for "public" cannot be shortened. Test_shortened_invalid_vim9() in + # test_vim9_script.vim has complete coverage (:pub to :publi) lines =<< trim END vim9script class Something @@ -1460,7 +1462,8 @@ enddef " Test for class variable access def Test_class_variable_access() - # Test for "static" cannot be abbreviated + # Test for "static" cannot be shortened. Test_shortened_invalid_vim9() in + # test_vim9_script.vim has complete coverage (:stat and :stati) var lines =<< trim END vim9script class Something @@ -2951,7 +2954,8 @@ def Test_abstract_class() END v9.CheckSourceFailure(lines, 'E1316: Class can only be defined in Vim9 script', 1) - # Test for "abstract" cannot be abbreviated + # Test for "abstract" cannot be shortened. Test_shortened_invalid_vim9() in + # test_vim9_script.vim has complete coverage (:abs to :abstrac) lines =<< trim END vim9script abs class A @@ -5580,15 +5584,6 @@ def Test_abstract_method() END v9.CheckSourceFailure(lines, 'E1404: Abstract cannot be used in an interface', 3) - # Abbreviate the "abstract" keyword - lines =<< trim END - vim9script - class A - abs def Foo() - endclass - END - v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()', 3) - # Use "abstract" with a member variable lines =<< trim END vim9script diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 549145f2f1..a2faec225d 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -389,6 +389,12 @@ def Test_endfunc_enddef() enddef there END v9.CheckScriptFailure(lines, 'E1173: Text found after enddef: there', 6) + + lines =<< trim END + def ShortEnddef() + endd + END + v9.CheckScriptFailure(lines, 'E1065: Command cannot be shortened: endd', 2) enddef def Test_missing_endfunc_enddef() diff --git a/src/testdir/test_vim9_interface.vim b/src/testdir/test_vim9_interface.vim index 0c99e0bc71..f576e28475 100644 --- a/src/testdir/test_vim9_interface.vim +++ b/src/testdir/test_vim9_interface.vim @@ -86,7 +86,7 @@ def Test_interface_basics() END v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: endin', 3) - # "endinterface" cannot be shortened (variant incl. whitespace and colon) + # "endinterface" cannot be shortened (variant incl. colon-whitespace) lines =<< trim END vim9script interface Short diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index fcb585f026..2f54f6da62 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -3,6 +3,7 @@ import './util/vim9.vim' as v9 source util/screendump.vim +" Test for has('vim9script') def Test_vim9script_feature() # example from the help, here the feature is always present var lines =<< trim END @@ -1216,7 +1217,7 @@ def Test_error_in_catch() v9.CheckDefExecFailure(lines, 'E684:', 4) enddef -" :while at the very start of a function that :continue jumps to +" Test for :while at the very start of a function that :continue jumps to def s:TryContinueFunc() while g:Count < 2 g:sequence ..= 't' @@ -1330,8 +1331,8 @@ def Test_nocatch_throw_silenced() source XthrowSilenced enddef -" g:DeletedFunc() is found when compiling Test_try_catch_throw() and then -" deleted, this should give a runtime error. +" Test for g:DeletedFunc() is found when compiling Test_try_catch_throw() and +" then deleted, this should give a runtime error. def DeletedFunc(): list return ['delete me'] enddef @@ -1522,7 +1523,7 @@ def Try_catch_skipped() endif enddef -" The skipped try/endtry was updating the wrong instruction. +" Test for when the skipped try/endtry was updating the wrong instruction. def Test_try_catch_skipped() var instr = execute('disassemble Try_catch_skipped') assert_match("NEWLIST size 0\n", instr) @@ -4376,62 +4377,144 @@ def Run_test_reject_declaration() g:StopVimInTerminal(buf) enddef -def Test_minimal_command_name_length() - var names = [ - 'cons', - 'brea', - 'cat', - 'catc', - 'con', - 'cont', - 'conti', - 'contin', - 'continu', - 'el', - 'els', - 'elsei', - 'endfo', - 'en', - 'end', - 'endi', - 'endw', - 'endt', - 'endtr', - 'exp', - 'expo', - 'expor', - 'fina', - 'finall', - 'fini', - 'finis', - 'imp', - 'impo', - 'impor', - 'retu', - 'retur', - 'th', - 'thr', - 'thro', - 'wh', - 'whi', - 'whil', - ] - for name in names - v9.CheckDefAndScriptFailure([name .. ' '], 'E1065:') +" Test shortened commands that are invalid in Vim9 script +def Test_shortened_invalid_vim9() + # Many Vim9 script commands cannot be shortened/abbreviated. + # SHORTENED is a list of dicts, each with a single key (the exact shortened + # command) and a list value with four items: + # [0] list Lines passed to the check function (without 'vim9script' + # for SourceFailure lines) + # [1] number Line number where the error is expected ('vimscript', + # which is not in the list, is line 1 in the + # 'SourceFailure' and 'DefFailure lines, so needs to be + # included in the count) + # [2] string 'DefAndScriptFailure', 'SourceFailure', or 'DefFailure' + # specifying the applicable 'Check' function to call + const SHORTENED: list>> = [ + # abstract + {abs: [['abs class A'], 1, 'DefAndScriptFailure']}, + {abst: [['abst class A'], 1, 'DefAndScriptFailure']}, + {abstr: [['abstr class A'], 1, 'DefAndScriptFailure']}, + {abstra: [['abstra class A'], 1, 'DefAndScriptFailure']}, + {abstrac: [['abstrac class A'], 1, 'DefAndScriptFailure']}, + # break + {brea: [['for k in range(0, 2)', 'brea', 'endfor'], 2, 'DefAndScriptFailure']}, + # catch + {cat: [['try', 'echo 0', 'cat'], 3, 'DefAndScriptFailure']}, + {catc: [['try', 'echo 0', 'catc'], 3, 'DefAndScriptFailure']}, + # class - n/a because :clas is :clast + # const + {cons: [['cons C = 0'], 1, 'DefAndScriptFailure']}, + # continue + {con: [['var n: number', 'while n < 9', '++n', 'con'], 4, 'DefAndScriptFailure']}, + {cont: [['var n: number', 'while n < 9', '++n', 'cont'], 4, 'DefAndScriptFailure']}, + {conti: [['var n: number', 'while n < 9', '++n', 'conti'], 4, 'DefAndScriptFailure']}, + {contin: [['var n: number', 'while n < 9', '++n', 'contin'], 4, 'DefAndScriptFailure']}, + {continu: [['var n: number', 'while n < 9', '++n', 'continu'], 4, 'DefAndScriptFailure']}, + # def has no applicable shortened form (:de is :delete) + # else + {els: [['if true', 'els'], 2, 'DefAndScriptFailure']}, + # elseif + {elsei: [['if true', 'elsei false'], 2, 'DefAndScriptFailure']}, + # endclass + {endc: [['class C', 'endc'], 3, 'SourceFailure']}, + {endcl: [['class C', 'endcl'], 3, 'SourceFailure']}, + {endcla: [['class C', 'endcla'], 3, 'SourceFailure']}, + {endclas: [['class C', 'endclas'], 3, 'SourceFailure']}, + # enddef + # (NB: The separate DefFailure check tests them nested - + # DefAndScriptFailure cannot be used for testing :endd[e]) + {endd: [['def D()', 'endd'], 3, 'SourceFailure']}, + {endde: [['def D()', 'endde'], 3, 'SourceFailure']}, + {endd: [['var R: func = (): bool => {', 'def D()', 'endd', '}'], 4, 'DefFailure']}, + {endde: [['var R: func = (): bool => {', 'def D()', 'endde', '}'], 4, 'DefFailure']}, + # endenum + {ende: [['enum E', 'ende'], 3, 'SourceFailure']}, + {enden: [['enum E', 'enden'], 3, 'SourceFailure']}, + {endenu: [['enum E', 'endenu'], 3, 'SourceFailure']}, + # endfor + {endfo: [['for n in range(0, 2)', 'endfo'], 2, 'DefAndScriptFailure']}, + # endif + {en: [['if true', 'en'], 2, 'DefAndScriptFailure']}, + {end: [['if true', 'end'], 2, 'DefAndScriptFailure']}, + {endi: [['if true', 'endi'], 2, 'DefAndScriptFailure']}, + # endinterface + {endin: [['interface I', 'endin'], 3, 'SourceFailure']}, + {endint: [['interface I', 'endint'], 3, 'SourceFailure']}, + {endinte: [['interface I', 'endinte'], 3, 'SourceFailure']}, + {endinter: [['interface I', 'endinter'], 3, 'SourceFailure']}, + {endinterf: [['interface I', 'endinterf'], 3, 'SourceFailure']}, + {endinterfa: [['interface I', 'endinterfa'], 3, 'SourceFailure']}, + {endinterfac: [['interface I', 'endinterfac'], 3, 'SourceFailure']}, + # endtry + {endt: [['try', 'echo 0', 'endt'], 3, 'DefAndScriptFailure']}, + {endtr: [['try', 'echo 0', 'endtr'], 3, 'DefAndScriptFailure']}, + # endwhile + {endw: [['var n = 9', 'while n > 0', '--n', 'endw'], 4, 'DefAndScriptFailure']}, + {endwh: [['var n = 9', 'while n > 0', '--n', 'endwh'], 4, 'DefAndScriptFailure']}, + {endwhi: [['var n = 9', 'while n > 0', '--n', 'endwhi'], 4, 'DefAndScriptFailure']}, + {endwhil: [['var n = 9', 'while n > 0', '--n', 'endwhil'], 4, 'DefAndScriptFailure']}, + # enum + {enu: [['enu E', 'endenum'], 2, 'SourceFailure']}, + # export + {exp: [['exp var b: bool'], 2, 'SourceFailure']}, + {expo: [['expo var b: bool'], 2, 'SourceFailure']}, + {expor: [['expor var b: bool'], 2, 'SourceFailure']}, + # final has no applicable shortened form (because :fina is short for :finally) + # finally + {fina: [['try', '# Do something', 'fina'], 3, 'DefAndScriptFailure']}, + # finish + {fini: [['fini'], 1, 'DefAndScriptFailure']}, + # import + {imp: [['imp $"{$VIMRUNTIME}/autoload/ccomplete.vim"'], 2, 'SourceFailure']}, + {impo: [['impo $"{$VIMRUNTIME}/autoload/ccomplete.vim"'], 2, 'SourceFailure']}, + {impor: [['impor $"{$VIMRUNTIME}/autoload/ccomplete.vim"'], 2, 'SourceFailure']}, + # interface + {inte: [['inte I', 'endinterface'], 2, 'SourceFailure']}, + {inter: [['inter I', 'endinterface'], 2, 'SourceFailure']}, + {interf: [['interf I', 'endinterface'], 2, 'SourceFailure']}, + {interfa: [['interfa I', 'endinterface'], 2, 'SourceFailure']}, + {interfac: [['interfac I', 'endinterface'], 2, 'SourceFailure']}, + # public + {pub: [['class P', 'pub var b: bool', 'endclass'], 3, 'SourceFailure']}, + {publ: [['class P', 'publ var b: bool', 'endclass'], 3, 'SourceFailure']}, + {publi: [['class P', 'publi var b: bool', 'endclass'], 3, 'SourceFailure']}, + # return (NB: line is 0 - for CheckDefAndScriptFailure the first line of the Vim9 script lambda function is considered 0) + {retu: [['var R: func = (): bool => {', 'retu false', '}'], 0, 'DefAndScriptFailure']}, + {retur: [['var R: func = (): bool => {', 'retur false', '}'], 0, 'DefAndScriptFailure']}, + # static + {stat: [['class S', 'stat var b: bool', 'endclass'], 3, 'SourceFailure']}, + {stati: [['class S', 'stati var b: bool', 'endclass'], 3, 'SourceFailure']}, + # this + {thi: [['thi'], 1, 'DefAndScriptFailure']}, + # throw + {th: [['try', 'th 9', 'catch 9', 'echo "Should give E1065"', 'thr'], 2, 'DefAndScriptFailure']}, + {thr: [['try', 'thr 9', 'catch 9', 'echo "Should give E1065"', 'thr'], 2, 'DefAndScriptFailure']}, + {thro: [['try', 'thro 9', 'catch 9', 'echo "Should give E1065"', 'thro'], 2, 'DefAndScriptFailure']}, + # type + {ty: [['ty ListOfBools = list'], 1, 'DefAndScriptFailure']}, + {typ: [['typ ListOfBools = list'], 1, 'DefAndScriptFailure']}, + # var + {va: [['va b: bool'], 1, 'DefAndScriptFailure']}, + # while + {wh: [['var n = 9', 'wh n > 0', '--n', 'endwhile'], 2, 'DefAndScriptFailure']}, + {whi: [['var n = 9', 'whi n > 0', '--n', 'endwhile'], 2, 'DefAndScriptFailure']}, + {whil: [['var n = 9', 'whil n > 0', '--n', 'endwhile'], 2, 'DefAndScriptFailure']}, + ] + for short in SHORTENED + const CMD: string = short->keys()[0] + const LINES: list = short[CMD][0] + const E1065: string = "E1065: Command cannot be shortened: " .. CMD + const LNUM: number = short[CMD][1] + const CHECK: string = short[CMD][2] + if CHECK == 'SourceFailure' + v9.CheckSourceFailure(['vim9script', LINES]->flattennew(), E1065, LNUM) + elseif CHECK == 'DefAndScriptFailure' + v9.CheckDefAndScriptFailure(LINES, E1065, LNUM) + elseif CHECK == 'DefFailure' + v9.CheckDefFailure(LINES, E1065, LNUM) + endif endfor - - var lines =<< trim END - vim9script - def SomeFunc() - endd - END - v9.CheckScriptFailure(lines, 'E1065:') - lines =<< trim END - vim9script - def SomeFunc() - endde - END - v9.CheckScriptFailure(lines, 'E1065:') enddef def Test_unset_any_variable() @@ -5792,27 +5875,6 @@ def Test_type_func_with_void() v9.CheckSourceFailure(lines, 'E1031: Cannot use void value', 4) enddef -" Keep this last, it messes up highlighting. -def Test_substitute_cmd() - new - setline(1, 'something') - :substitute(some(other( - assert_equal('otherthing', getline(1)) - bwipe! - - # also when the context is Vim9 script - var lines =<< trim END - vim9script - new - setline(1, 'something') - :substitute(some(other( - assert_equal('otherthing', getline(1)) - bwipe! - END - writefile(lines, 'Xvim9lines', 'D') - source Xvim9lines -enddef - def Test_call_stack_string() CheckScreendump var lines =<< trim END @@ -5994,4 +6056,66 @@ def Test_if_false_elseif_true_still_takes_elseif() v9.CheckScriptSuccess(lines) enddef +" Test for correct fullcommand() outputs: return the correct command (or '') +def Test_builtin_fullcommand() + # :hor is the minimum abbreviation of :horizontal; :ho is invalid + assert_equal('', fullcommand('ho', true)) + assert_equal('horizontal', fullcommand('hor', true)) + + # :k is an invalid one-letter command in Vim9 script + assert_equal('', fullcommand('k', true)) + assert_equal('', fullcommand(':k', true)) + assert_equal('', fullcommand('karrrrrgh!', true)) + assert_equal('k', fullcommand('k', false)) + assert_equal('k', fullcommand(':k', false)) + assert_equal('k', fullcommand('karrrrrgh!', false)) + + # :dl is "delete and list" in legacy Vim script but, because :dl itself is + # invalid in Vim9 script, :dl is 'dlist' in Vim9 script + assert_equal('delete', fullcommand('dl', v:false)) + assert_equal('dlist', fullcommand('dl', v:true)) + + # Substitute :s two and three letter commands in legacy Vim script are + # invalid in Vim9 script + assert_equal('', fullcommand('sIr', true)) + assert_equal('', fullcommand('sIrarrrrrgh!', true)) + assert_equal('substitute', fullcommand('sIr', false)) + assert_equal('substitute', fullcommand('sIrarrrrrgh!', false)) + + # Three :s? commands are exceptionss, returning different commands depending + # on whether the scope is legacy Vim script or Vim9 script + assert_equal('scriptnames', fullcommand('sc', true)) + assert_equal('simalt', fullcommand('si', true)) + assert_equal('srewind', fullcommand('sr', true)) + assert_equal('substitute', fullcommand('sc', false)) + assert_equal('substitute', fullcommand('si', false)) + assert_equal('substitute', fullcommand('sr', false)) + + # :finally cannot be shortened in Vim9 script but :final should return 'final' + assert_equal('', fullcommand('fina', true)) + assert_equal('final', fullcommand('final', true)) + assert_equal('', fullcommand('finall', true)) +enddef + +" Keep this last, it messes up highlighting. +def Test_substitute_cmd() + new + setline(1, 'something') + :substitute(some(other( + assert_equal('otherthing', getline(1)) + bwipe! + + # also when the context is Vim9 script + var lines =<< trim END + vim9script + new + setline(1, 'something') + :substitute(some(other( + assert_equal('otherthing', getline(1)) + bwipe! + END + writefile(lines, 'Xvim9lines', 'D') + source Xvim9lines +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim index 49f34a578c..a07ed6eadf 100644 --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -7702,6 +7702,33 @@ func Test_function_long_generic_name() delfunc TestFunc endfunc +" Test using fullcommand() {{{1 +func Test_builtin_fullcommand() + " :hor is the minimum abbreviation of :horizontal; :ho is invalid + call assert_equal('', fullcommand('ho')) + call assert_equal('horizontal', fullcommand('hor')) + + " :k takes one {a-zA-Z'} mark argument and optional whitespace + call assert_equal('k', fullcommand('k')) + call assert_equal('k', fullcommand(':k')) + call assert_equal('k', fullcommand('karrrrrgh!')) + + " :dl is "delete and list" in a legacy Vim script scope + call assert_equal('delete', fullcommand('dl')) + + " :s two and three letter commands + call assert_equal('substitute', fullcommand('sIr')) + call assert_equal('substitute', fullcommand('sIrarrrrrgh!')) + + " :finally + call assert_equal('finally', fullcommand('fina')) + " 'final' - returns 'final', a Vim9 script-exclusive keyword + " - is a valid shortening of :finally in legacy Vim script + call assert_equal('final', fullcommand('final')) + call assert_equal('finally', fullcommand('finall')) + +endfunc + "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/userfunc.c b/src/userfunc.c index ff5cf76a0e..66b55449e3 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1132,7 +1132,7 @@ get_function_body( { if (!nesting_inline[nesting] && nesting_def[nesting] && p < cmd + 6) - semsg(_(e_command_cannot_be_shortened_str), "enddef"); + semsg(_(e_command_cannot_be_shortened_str), cmd); if (nesting-- == 0) { char_u *nextcmd = NULL; diff --git a/src/version.c b/src/version.c index ac58fbdf80..53c557e432 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 573, /**/ 572, /**/ From b16a9ddc281dc11c0f7bd70de6a9d7041d9b2e54 Mon Sep 17 00:00:00 2001 From: thinca Date: Sun, 31 May 2026 19:43:42 +0000 Subject: [PATCH 372/545] patch 9.2.0574: tests: missing test for v9.2.0572 Problem: tests: missing test for v9.2.0572 (the patch accidentally included some additional unwanted changes from #20372) (thinca) Solution: Add additional test for delfunc (thinca) Patch 9.2.0572 fixed ":call d.key()" and ":delfunction d.key" failing with E1017 in Vim9 script, but only the :call form was covered by a regression test. Add Test_delfunction_dict_funcref to exercise both "delfunction d.key" and "delfunction d['k2']". closes: #20372 Signed-off-by: thinca Signed-off-by: Christian Brabandt --- src/testdir/test_vim9_cmd.vim | 19 +++++++++++++++++++ src/version.c | 2 ++ 2 files changed, 21 insertions(+) diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim index a4c2d05d45..cec81f3ed6 100644 --- a/src/testdir/test_vim9_cmd.vim +++ b/src/testdir/test_vim9_cmd.vim @@ -2178,4 +2178,23 @@ def Test_call_dict_funcref() v9.CheckScriptSuccess(lines) enddef +" :delfunction on a funcref stored in a dict member used to fail with E1017 in +" Vim9 script for the same reason as :call. +def Test_delfunction_dict_funcref() + var lines =<< trim END + vim9script + func g:LegacyFunc() + endfunc + var d: dict = {} + d.key = g:LegacyFunc + d['k2'] = g:LegacyFunc + delfunction d.key + assert_false(has_key(d, 'key')) + delfunction d['k2'] + assert_false(has_key(d, 'k2')) + delfunction g:LegacyFunc + END + v9.CheckScriptSuccess(lines) +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 53c557e432..361a0ca1f5 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 574, /**/ 573, /**/ From 0878792046316c454bb91273ab35b3d1f6fb439a Mon Sep 17 00:00:00 2001 From: Patrick Meiser-Knosowski Date: Sun, 31 May 2026 19:52:46 +0000 Subject: [PATCH 373/545] patch 9.2.0575: tests: filetype test for v9.2.0557 can be improved Problem: tests: filetype test for v9.2.0557 can be improved Solution: Use a correct Kawasaki robot AS test file (Patrick Meiser-Knosowski) related: #20370 closes: #20387 Signed-off-by: Patrick Meiser-Knosowski Signed-off-by: Christian Brabandt --- src/testdir/test_filetype.vim | 2 +- src/version.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 8ba27d5e1c..9dd8767fba 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -3552,7 +3552,7 @@ func Test_as_file() split Xfile.as call assert_equal('atlas', &filetype) bwipe! - call writefile(['', '.NETCONF'], 'Xfile.as', 'D') + call writefile(['', '.NETCONF 192.168.1.11,"TIMESYS-",255.255.255.0,192.168.0.1,0.0.0.0,0.0.0.0," "'], 'Xfile.as', 'D') split Xfile.as call assert_equal('kawasaki_as', &filetype) bwipe! diff --git a/src/version.c b/src/version.c index 361a0ca1f5..e1dd6429bf 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 575, /**/ 574, /**/ From 4f1cd5f78a6a7926f03b159fee8b63c5de558094 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 31 May 2026 20:00:14 +0000 Subject: [PATCH 374/545] patch 9.2.0576: popup_create() not blocked in secure/sandbox Problem: popup_create() is not gated by check_secure(), unlike the similar deferred-callback registrars timer_start() and feedkeys(). A popup created with a 'time' and 'callback' (or with close/filter callbacks) registers code that runs after the secure/sandbox context has been left, which is inconsistent with how timer_start() and feedkeys() handle the same situation. Solution: Call check_secure() at the top of popup_create(), matching the timer_start()/feedkeys() pattern. closes: #20400 Signed-off-by: Christian Brabandt --- src/popupwin.c | 3 +++ src/testdir/test_popup.vim | 4 ++++ src/version.c | 2 ++ 3 files changed, 9 insertions(+) diff --git a/src/popupwin.c b/src/popupwin.c index 624e20c613..87ea44de50 100644 --- a/src/popupwin.c +++ b/src/popupwin.c @@ -2864,6 +2864,9 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type) dict_T *d = NULL; int i; + if (check_secure()) + return NULL; + if (argvars != NULL) { if (in_vim9script() diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim index cf9cbe166e..32b2801888 100644 --- a/src/testdir/test_popup.vim +++ b/src/testdir/test_popup.vim @@ -2632,4 +2632,8 @@ func Test_popup_opacity_move_after_close() call StopVimInTerminal(buf) endfunc +func Test_popup_create_sandbox() + call assert_fails('sandbox call popup_create("hello", {})', 'E48:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index e1dd6429bf..dab821ad64 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 576, /**/ 575, /**/ From 568daf65b82173605aeecf799ebe46cec54d77a8 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Sun, 31 May 2026 20:09:52 +0000 Subject: [PATCH 375/545] patch 9.2.0577: GTK4: window resizing issues Problem: GTK4: window size does not account for client-side decorations Solution: Compute the client side decoration height from gui_resize_shell() (Foxe Chen) fixes: #20365 closes: #20388 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/gui.c | 3 ++ src/gui.h | 3 ++ src/gui_gtk4.c | 74 ++++++++++++++++++++++++++++++++++-------- src/proto/gui_gtk4.pro | 1 + src/version.c | 2 ++ 5 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/gui.c b/src/gui.c index 8cf4c8fa48..b0235eec28 100644 --- a/src/gui.c +++ b/src/gui.c @@ -1594,6 +1594,9 @@ gui_resize_shell(int pixel_width, int pixel_height) // Flush pending output before redrawing out_flush(); +#if defined(FEAT_GUI_GTK) && defined(USE_GTK4) + gui_gtk_init_decor_height(); +#endif gui.num_cols = (pixel_width - gui_get_base_width()) / gui.char_width; gui.num_rows = (pixel_height - gui_get_base_height()) / gui.char_height; diff --git a/src/gui.h b/src/gui.h index d1e5d2d5c0..609100a172 100644 --- a/src/gui.h +++ b/src/gui.h @@ -475,6 +475,9 @@ typedef struct Gui char *rsrc_input_method; char *rsrc_preedit_type_name; #endif +#if defined(FEAT_GUI_GTK) && defined(USE_GTK4) + int decor_height; +#endif } gui_T; extern gui_T gui; // this is defined in gui.c diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index 1596f19e5c..e7bbd9ad56 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -816,6 +816,28 @@ gui_mch_settitle(char_u *title, char_u *icon UNUSED) static int in_set_shellsize = FALSE; +/* + * Get height of window decorations, that we cannot determine directly. For + * example, the GtkHeaderBar widget. This is called in gui_resize_shell(), we + * cannot call it in gui_set_shellsize(), because that may be called before the + * drawarea/formwin is resized, which may cause the drawarea to be bigger than + * it actually is (while the window size is up to date), causing a negative + * "decor_height". + */ + void +gui_gtk_init_decor_height(void) +{ + int h = gtk_widget_get_height(gui.mainwin); + + if (h == 0) + return; + + h -= get_menu_tool_height(); + h -= gtk_widget_get_height(gui.formwin); + + gui.decor_height = h; +} + void gui_mch_set_shellsize(int width, int height, int min_width UNUSED, int min_height UNUSED, @@ -824,6 +846,11 @@ gui_mch_set_shellsize(int width, int height, { width += get_menu_tool_width(); height += get_menu_tool_height(); + + // GtkWindow default size also includes client side decorations, so must + // include it also. + height += gui.decor_height; + gtk_window_set_default_size(GTK_WINDOW(gui.mainwin), width, height); } @@ -3240,24 +3267,45 @@ get_menu_tool_width(void) int get_menu_tool_height(void) { - int height = 0; - + GtkWidget *widgets[] = { #ifdef FEAT_MENU - if (gui.menubar != NULL && gtk_widget_get_visible(gui.menubar)) - { - GtkRequisition req; - gtk_widget_get_preferred_size(gui.menubar, &req, NULL); - height += req.height; - } + gui.menubar, #endif #ifdef FEAT_TOOLBAR - if (gui.toolbar != NULL && gtk_widget_get_visible(gui.toolbar)) + gui.toolbar, +#endif +#ifdef FEAT_GUI_TABLINE + gui.tabline +#endif + }; + + int height = 0; + + for (int i = 0; i < ARRAY_LENGTH(widgets); i++) { - GtkRequisition req; - gtk_widget_get_preferred_size(gui.toolbar, &req, NULL); - height += req.height; + GtkRequisition min; + GtkRequisition nat; + int h; + + if (widgets[i] == NULL || !gtk_widget_get_visible(widgets[i])) + continue; + + h = gtk_widget_get_height(widgets[i]); + + if (h == 0) + { + // Allocation hasn't been updated yet (widget just became visible). + // Query the preferred height so the caller gets a valid value + // before the layout pass runs. Use the maximum of minimum and + // natural height: GTK may allocate min_h even when natural_h is + // smaller (e.g. GtkNotebook tab bar has min_h > natural_h due to + // CSS). + gtk_widget_get_preferred_size(widgets[i], &min, &nat); + height += MAX(min.height, nat.height); + } + else + height += h; } -#endif return height; } diff --git a/src/proto/gui_gtk4.pro b/src/proto/gui_gtk4.pro index 10f3ccc202..4d21e6a703 100644 --- a/src/proto/gui_gtk4.pro +++ b/src/proto/gui_gtk4.pro @@ -19,6 +19,7 @@ void gui_mch_unmaximize(void); void gui_mch_set_fullscreen(int flag); void gui_mch_newfont(void); void gui_mch_settitle(char_u *title, char_u *icon); +void gui_gtk_init_decor_height(void); void gui_mch_set_shellsize(int width, int height, int min_width, int min_height, int base_width, int base_height, int direction); void gui_mch_get_screen_dimensions(int *screen_w, int *screen_h); void gui_mch_enable_menu(int showit); diff --git a/src/version.c b/src/version.c index dab821ad64..b97066f50b 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 577, /**/ 576, /**/ From 63a9471e225ec8388f3d7f03ba6bc0a137533529 Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Sun, 31 May 2026 20:32:40 +0000 Subject: [PATCH 376/545] patch 9.2.0578: GTK4: :unmenu does not remove entries from the menubar Problem: GTK4: gui_mch_destroy_menu() never removed the entry from its parent GMenu, so :unmenu was effectively a no-op against the visible menubar model. After "Refresh menu" the Buffers menu items doubled because runtime/menu.vim BMShow() re-appended Refresh / Delete / Alternate / Next / Previous / -SEP- into a still-populated GMenu. The GAction registered for the item and the reference on its submenu GMenu were also leaked. Solution: Compute the entry's position in the parent GMenu by walking the vimmenu_T sibling list, call g_menu_remove(), remove the GAction we registered, and release our reference on the submenu GMenu (Yasuhiro Matsumoto) fixes: #20262 closes: #20314 Co-Authored-by: Claude Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 94 ++++++++++++++++++++++++++++++++++++++++++++------ src/version.c | 2 ++ 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index e7bbd9ad56..8d650397c4 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -3919,29 +3919,103 @@ gui_mch_menu_set_tip(vimmenu_T *menu UNUSED) { } +/* + * Return TRUE if "menu" has a corresponding entry in its parent's GMenu. + * Popup menus, toolbar children and orphaned submenus do not. + */ + static int +menu_has_gmenu_slot(vimmenu_T *menu) +{ + if (menu == NULL || menu->name == NULL) + return FALSE; + if (menu->name[0] == ']' || menu_is_popup(menu->name)) + return FALSE; + if (menu->parent != NULL) + { + if (menu_is_toolbar(menu->parent->name)) + return FALSE; + if (menu->parent->submenu_id == NULL) + return FALSE; + return TRUE; + } + return menu_is_menubar(menu->name); +} + +/* + * Find the parent GMenu containing the entry for "menu" and the position of + * that entry. Returns TRUE on success. + */ + static int +get_gmenu_pos_in_parent(vimmenu_T *menu, GMenu **parent_out, int *pos_out) +{ + GMenu *parent_gmenu; + vimmenu_T *first_sibling; + vimmenu_T *sib; + int pos = 0; + + if (!menu_has_gmenu_slot(menu)) + return FALSE; + + if (menu->parent != NULL) + { + parent_gmenu = (GMenu *)(gpointer)menu->parent->submenu_id; + first_sibling = menu->parent->children; + } + else + { + if (gui.menubar == NULL) + return FALSE; + parent_gmenu = (GMenu *)(gpointer)g_object_get_data( + G_OBJECT(gui.menubar), "vim-gmenu"); + first_sibling = root_menu; + } + if (parent_gmenu == NULL) + return FALSE; + + for (sib = first_sibling; sib != NULL && sib != menu; sib = sib->next) + if (menu_has_gmenu_slot(sib)) + pos++; + if (sib != menu) + return FALSE; + + *parent_out = parent_gmenu; + *pos_out = pos; + return TRUE; +} + void gui_mch_destroy_menu(vimmenu_T *menu) { - // For toolbar buttons, remove from toolbar + GMenu *parent_gmenu = NULL; + int pos = 0; + + // For toolbar buttons and separators, remove from the toolbar box. if (menu->id != NULL && menu->id != (GtkWidget *)1) { GtkWidget *parent_widget = gtk_widget_get_parent(menu->id); + if (parent_widget != NULL) gtk_box_remove(GTK_BOX(parent_widget), menu->id); - menu->id = NULL; } - else - menu->id = NULL; + menu->id = NULL; + + // Remove the entry from the parent GMenu so the visible menu updates. + if (get_gmenu_pos_in_parent(menu, &parent_gmenu, &pos)) + g_menu_remove(parent_gmenu, pos); - // Free stored action name - vim_free(menu->label); - menu->label = NULL; + // Remove the GAction created for this item and free its name. + if (menu->label != NULL) + { + if (menu_action_group != NULL) + g_action_map_remove_action(G_ACTION_MAP(menu_action_group), + (const char *)menu->label); + VIM_CLEAR(menu->label); + } - // GMenu items cannot be individually removed easily. - // The submenu GMenu is unreffed if present. + // Release our reference on the submenu GMenu (if any). if (menu->submenu_id != NULL) { - // Don't unref - GMenu may be referenced by the model + g_object_unref(menu->submenu_id); menu->submenu_id = NULL; } } diff --git a/src/version.c b/src/version.c index b97066f50b..80020f59fd 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 578, /**/ 577, /**/ From d69cf0dbcfdd721d94189ffa1558660c96129627 Mon Sep 17 00:00:00 2001 From: Miguel Barro Date: Sun, 31 May 2026 21:03:12 +0000 Subject: [PATCH 377/545] patch 9.2.0579: :mksession, :mkview and :mkvimrc emit legacy Vim script Problem: :mksession, :mkview and :mkvimrc emit legacy Vim script Solution: Generate vim9 script for those commands (Miguel Barro). fixes: #16549 fixes: #16688 fixes: #19005 closes: #20152 Co-authored-by: Christian Brabandt Signed-off-by: Miguel Barro Signed-off-by: Christian Brabandt --- runtime/doc/version9.txt | 4 +- src/fold.c | 8 +- src/map.c | 12 +- src/session.c | 156 ++++++++----- src/testdir/test_autocmd.vim | 2 +- src/testdir/test_mksession.vim | 332 +++++++++++++++++++++++++--- src/testdir/test_mksession_utf8.vim | 40 ++-- src/version.c | 2 + 8 files changed, 439 insertions(+), 117 deletions(-) diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 29882b4d55..2496d1b695 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.2. Last change: 2026 May 25 +*version9.txt* For Vim version 9.2. Last change: 2026 May 31 VIM REFERENCE MANUAL by Bram Moolenaar @@ -52644,6 +52644,8 @@ Other ~ attribute to handle completion of single arguments with spaces as expected. - Support %0{} in 'statusline' to insert the expression result verbatim and not drop leading spaces |stl-%0{|. +- Generated Session and View files are written in Vim9 script, see |:mksession|, + |:mkview| and |:mkvimrc| Platform specific ~ ----------------- diff --git a/src/fold.c b/src/fold.c index b9b54d8fc9..19c444c14c 100644 --- a/src/fold.c +++ b/src/fold.c @@ -3548,7 +3548,7 @@ put_folds(FILE *fd, win_T *wp) { if (put_line(fd, "silent! normal! zE") == FAIL || put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL - || put_line(fd, "let &fdl = &fdl") == FAIL) + || put_line(fd, "&fdl = &fdl") == FAIL) return FAIL; } @@ -3576,7 +3576,7 @@ put_folds_recurse(FILE *fd, garray_T *gap, linenr_T off) // Do nested folds first, they will be created closed. if (put_folds_recurse(fd, &fp->fd_nested, off + fp->fd_top) == FAIL) return FAIL; - if (fprintf(fd, "sil! %ld,%ldfold", fp->fd_top + off, + if (fprintf(fd, "sil! :%ld,%ldfold", fp->fd_top + off, fp->fd_top + off + fp->fd_len - 1) < 0 || put_eol(fd) == FAIL) return FAIL; @@ -3610,7 +3610,7 @@ put_foldopen_recurse( { // open nested folds while this fold is open // ignore errors - if (fprintf(fd, "%ld", fp->fd_top + off) < 0 + if (fprintf(fd, ":%ld", fp->fd_top + off) < 0 || put_eol(fd) == FAIL || put_line(fd, "sil! normal! zo") == FAIL) return FAIL; @@ -3651,7 +3651,7 @@ put_foldopen_recurse( static int put_fold_open_close(FILE *fd, fold_T *fp, linenr_T off) { - if (fprintf(fd, "%ld", fp->fd_top + off) < 0 + if (fprintf(fd, ":%ld", fp->fd_top + off) < 0 || put_eol(fd) == FAIL || fprintf(fd, "sil! normal! z%c", fp->fd_flags == FD_CLOSED ? 'c' : 'o') < 0 diff --git a/src/map.c b/src/map.c index 1c0d0ac7fd..34e465ec3f 100644 --- a/src/map.c +++ b/src/map.c @@ -2087,13 +2087,19 @@ makemap( did_cpo = TRUE; if (did_cpo) { - if (fprintf(fd, "let s:cpo_save=&cpo") < 0 + if (fprintf(fd, "cpo_save = &cpo") < 0 || put_eol(fd) < 0 || fprintf(fd, "set cpo&vim") < 0 || put_eol(fd) < 0) return FAIL; } } +#ifdef FEAT_EVAL + // If it is not vim9 use legacy + if (mp->m_expr && mp->m_script_ctx.sc_version < SCRIPT_VERSION_VIM9 + && fputs("legacy ", fd) < 0) + return FAIL; +#endif if (c1 && putc(c1, fd) < 0) return FAIL; if (mp->m_noremap != REMAP_YES && fprintf(fd, "nore") < 0) @@ -2128,9 +2134,7 @@ makemap( } if (did_cpo) - if (fprintf(fd, "let &cpo=s:cpo_save") < 0 - || put_eol(fd) < 0 - || fprintf(fd, "unlet s:cpo_save") < 0 + if (fprintf(fd, "&cpo = cpo_save") < 0 || put_eol(fd) < 0) return FAIL; return OK; diff --git a/src/session.c b/src/session.c index eed03269a0..090448937f 100644 --- a/src/session.c +++ b/src/session.c @@ -105,7 +105,7 @@ ses_arglist( if (fputs(cmd, fd) < 0 || put_eol(fd) == FAIL) return FAIL; - if (put_line(fd, "%argdel") == FAIL) + if (put_line(fd, ":%argdel") == FAIL) return FAIL; for (i = 0; i < gap->ga_len; ++i) { @@ -122,7 +122,7 @@ ses_arglist( s = buf; } } - if (fputs("$argadd ", fd) < 0 + if (fputs(":$argadd ", fd) < 0 || ses_put_fname(fd, s, flagp) == FAIL || put_eol(fd) == FAIL) { @@ -220,7 +220,7 @@ ses_win_rec(FILE *fd, frame_T *fr) // Go back to the first window. if (count > 0 && (fprintf(fd, fr->fr_layout == FR_COL - ? "%dwincmd k" : "%dwincmd h", count) < 0 + ? ":%dwincmd k" : ":%dwincmd h", count) < 0 || put_eol(fd) == FAIL)) return FAIL; @@ -259,14 +259,14 @@ ses_winsizes( // restore height when not full height if (wp->w_height + wp->w_status_height < topframe->fr_height && (fprintf(fd, - "exe '%dresize ' . ((&lines * %ld + %ld) / %ld)", + "exe ':%dresize ' .. ((&lines * %ld + %ld) / %ld)", n, (long)wp->w_height, Rows / 2, Rows) < 0 || put_eol(fd) == FAIL)) return FAIL; // restore width when not full width if (wp->w_width < Columns && (fprintf(fd, - "exe 'vert %dresize ' . ((&columns * %ld + %ld) / %ld)", + "exe 'vert :%dresize ' .. ((&columns * %ld + %ld) / %ld)", n, (long)wp->w_width, Columns / 2, Columns) < 0 || put_eol(fd) == FAIL)) return FAIL; @@ -339,7 +339,7 @@ put_view( if (wp->w_arg_idx != current_arg_idx && wp->w_arg_idx < WARGCOUNT(wp) && flagp == &ssop_flags) { - if (fprintf(fd, "%ldargu", (long)wp->w_arg_idx + 1) < 0 + if (fprintf(fd, ":%ldargu", (long)wp->w_arg_idx + 1) < 0 || put_eol(fd) == FAIL) return FAIL; did_next = TRUE; @@ -469,29 +469,32 @@ put_view( // Restore the cursor line in the file and relatively in the // window. Don't use "G", it changes the jumplist. + if (put_line(fd, "{") == FAIL) + return FAIL; + if (wp->w_height <= 0) { - if (fprintf(fd, "let s:l = %ld", (long)wp->w_cursor.lnum) < 0) + if (fprintf(fd, " var l: number = %ld", (long)wp->w_cursor.lnum) < 0) return FAIL; } else if (fprintf(fd, - "let s:l = %ld - ((%ld * winheight(0) + %ld) / %ld)", + " var l: number = %ld - ((%ld * winheight(0) + %ld) / %ld)", (long)wp->w_cursor.lnum, (long)(wp->w_cursor.lnum - wp->w_topline), (long)wp->w_height / 2, (long)wp->w_height) < 0) return FAIL; if (put_eol(fd) == FAIL - || put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL - || put_line(fd, "keepjumps exe s:l") == FAIL - || put_line(fd, "normal! zt") == FAIL - || fprintf(fd, "keepjumps %ld", (long)wp->w_cursor.lnum) < 0 + || put_line(fd, " if l < 1 | l = 1 | endif") == FAIL + || put_line(fd, " keepjumps exe \":\" .. l") == FAIL + || put_line(fd, " normal! zt") == FAIL + || fprintf(fd, " keepjumps :%ld", (long)wp->w_cursor.lnum) < 0 || put_eol(fd) == FAIL) return FAIL; // Restore the cursor column and left offset when not wrapping. if (wp->w_cursor.col == 0) { - if (put_line(fd, "normal! 0") == FAIL) + if (put_line(fd, " normal! 0") == FAIL) return FAIL; } else @@ -499,24 +502,27 @@ put_view( if (!wp->w_p_wrap && wp->w_leftcol > 0 && wp->w_width > 0) { if (fprintf(fd, - "let s:c = %ld - ((%ld * winwidth(0) + %ld) / %ld)", + " var c: number = %ld - ((%ld * winwidth(0) + %ld) / %ld)", (long)wp->w_virtcol + 1, (long)(wp->w_virtcol - wp->w_leftcol), (long)wp->w_width / 2, (long)wp->w_width) < 0 || put_eol(fd) == FAIL - || put_line(fd, "if s:c > 0") == FAIL + || put_line(fd, " if c > 0") == FAIL || fprintf(fd, - " exe 'normal! ' . s:c . '|zs' . %ld . '|'", + " exe 'normal! ' .. c .. '|zs' .. %ld .. '|'", (long)wp->w_virtcol + 1) < 0 || put_eol(fd) == FAIL - || put_line(fd, "else") == FAIL - || put_view_curpos(fd, wp, " ") == FAIL - || put_line(fd, "endif") == FAIL) + || put_line(fd, " else") == FAIL + || put_view_curpos(fd, wp, " ") == FAIL + || put_line(fd, " endif") == FAIL) return FAIL; } - else if (put_view_curpos(fd, wp, "") == FAIL) + else if (put_view_curpos(fd, wp, " ") == FAIL) return FAIL; } + + if (put_line(fd, "}") == FAIL) + return FAIL; } // Local directory, if the current flag is not view options or the "curdir" @@ -566,7 +572,7 @@ store_session_globals(FILE *fd) *t = 'n'; else if (*t == '\r') *t = 'r'; - if ((fprintf(fd, "let %s = %c%s%c", + if ((fprintf(fd, "g:%s = %c%s%c", this_var->di_key, (this_var->di_tv.v_type == VAR_STRING) ? '"' : ' ', @@ -591,7 +597,7 @@ store_session_globals(FILE *fd) f = -f; sign = '-'; } - if ((fprintf(fd, "let %s = %c%f", + if ((fprintf(fd, "g:%s = %c%f", this_var->di_key, sign, f) < 0) || put_eol(fd) == FAIL) return FAIL; @@ -638,12 +644,21 @@ makeopens( // Begin by setting the this_session variable, and then other // sessionable variables. # ifdef FEAT_EVAL - if (put_line(fd, "let v:this_session=expand(\":p\")") == FAIL) + + if (put_line(fd, "v:this_session = expand(\":p\")") == FAIL) goto fail; if (put_line(fd, "doautoall SessionLoadPre") == FAIL) goto fail; + if (put_line(fd, "var save_splitbelow: bool") == FAIL + || put_line(fd, "var save_splitright: bool") == FAIL + || put_line(fd, "var save_winminheight: number") == FAIL + || put_line(fd, "var save_winminwidth: number") == FAIL + || put_line(fd, "var wipebuf: number = -1") == FAIL + || put_line(fd, "var shortmess_save: string") == FAIL) + goto fail; + if (ssop_flags & SSOP_GLOBALS) if (store_session_globals(fd) == FAIL) goto fail; @@ -659,7 +674,7 @@ makeopens( // Now a :cd command to the session directory or the current directory if (ssop_flags & SSOP_SESDIR) { - if (put_line(fd, "exe \"cd \" . escape(expand(\":p:h\"), ' ')") + if (put_line(fd, "exe \"cd \" .. escape(expand(\":p:h\"), ' ')") == FAIL) goto fail; } @@ -681,14 +696,14 @@ makeopens( // Remember the buffer number. if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''") == FAIL) goto fail; - if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL) + if (put_line(fd, " wipebuf = bufnr('%')") == FAIL) goto fail; if (put_line(fd, "endif") == FAIL) goto fail; // Save 'shortmess' if not storing options. if ((ssop_flags & SSOP_OPTIONS) == 0 - && put_line(fd, "let s:shortmess_save = &shortmess") == FAIL) + && put_line(fd, "shortmess_save = &shortmess") == FAIL) goto fail; // Set 'shortmess' for the following. @@ -834,16 +849,16 @@ makeopens( if (tab_topframe->fr_layout != FR_LEAF) { // Save current window layout. - if (put_line(fd, "let s:save_splitbelow = &splitbelow") == FAIL - || put_line(fd, "let s:save_splitright = &splitright") + if (put_line(fd, "save_splitbelow = &splitbelow") == FAIL + || put_line(fd, "save_splitright = &splitright") == FAIL) goto fail; if (put_line(fd, "set splitbelow splitright") == FAIL) goto fail; if (ses_win_rec(fd, tab_topframe) == FAIL) goto fail; - if (put_line(fd, "let &splitbelow = s:save_splitbelow") == FAIL - || put_line(fd, "let &splitright = s:save_splitright") + if (put_line(fd, "&splitbelow = save_splitbelow") == FAIL + || put_line(fd, "&splitright = save_splitright") == FAIL) goto fail; } @@ -874,8 +889,8 @@ makeopens( // cursor can be set. This is done again below. // winminheight and winminwidth need to be set to avoid an error if // the user has set winheight or winwidth. - if (put_line(fd, "let s:save_winminheight = &winminheight") == FAIL - || put_line(fd, "let s:save_winminwidth = &winminwidth") + if (put_line(fd, "save_winminheight = &winminheight") == FAIL + || put_line(fd, "save_winminwidth = &winminwidth") == FAIL) goto fail; if (put_line(fd, "set winminheight=0") == FAIL @@ -925,7 +940,7 @@ makeopens( cur_arg_idx = next_arg_idx; // Restore cursor to the current window if it's not the first one. - if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0 + if (cnr > 1 && (fprintf(fd, ":%dwincmd w", cnr) < 0 || put_eol(fd) == FAIL)) goto fail; @@ -950,15 +965,13 @@ makeopens( goto fail; // Wipe out an empty unnamed buffer we started in. - if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0") + if (put_line(fd, "if wipebuf != -1 && len(win_findbuf(wipebuf)) == 0") == FAIL) goto fail; - if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL) + if (put_line(fd, " silent exe 'bwipe ' .. wipebuf") == FAIL) goto fail; if (put_line(fd, "endif") == FAIL) goto fail; - if (put_line(fd, "unlet! s:wipebuf") == FAIL) - goto fail; // Re-apply 'winheight' and 'winwidth'. if (fprintf(fd, "set winheight=%ld winwidth=%ld", @@ -973,22 +986,22 @@ makeopens( } else { - if (put_line(fd, "let &shortmess = s:shortmess_save") == FAIL) + if (put_line(fd, "&shortmess = shortmess_save") == FAIL) goto fail; } if (restore_height_width) { // Restore 'winminheight' and 'winminwidth'. - if (put_line(fd, "let &winminheight = s:save_winminheight") == FAIL - || put_line(fd, "let &winminwidth = s:save_winminwidth") == FAIL) + if (put_line(fd, "&winminheight = save_winminheight") == FAIL + || put_line(fd, "&winminwidth = save_winminwidth") == FAIL) goto fail; } // Lastly, execute the x.vim file if it exists. - if (put_line(fd, "let s:sx = expand(\":p:r\").\"x.vim\"") == FAIL - || put_line(fd, "if filereadable(s:sx)") == FAIL - || put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL + if (put_line(fd, "var sx: string = expand(\":p:r\") .. \"x.vim\"") == FAIL + || put_line(fd, "if filereadable(sx)") == FAIL + || put_line(fd, " exe \"source \" .. fnameescape(sx)") == FAIL || put_line(fd, "endif") == FAIL) goto fail; @@ -1137,9 +1150,9 @@ write_session_file(char_u *filename) fd = open_exfile(filename, TRUE, APPENDBIN); failed = (fd == NULL - || put_line(fd, "let v:this_session = Save_VV_this_session") + || put_line(fd, "v:this_session = g:Save_VV_this_session") == FAIL - || put_line(fd, "unlet Save_VV_this_session") == FAIL); + || put_line(fd, "unlet g:Save_VV_this_session") == FAIL); if (fd != NULL && fclose(fd) != 0) failed = TRUE; @@ -1177,6 +1190,10 @@ ex_mkrc(exarg_T *eap) char_u *viewFile = NULL; unsigned *flagp; #endif +#if defined(FEAT_EVAL) + int sid; + scriptitem_T *si = NULL; +#endif if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) { @@ -1258,6 +1275,10 @@ ex_mkrc(exarg_T *eap) mksession_nl = TRUE; #endif + // Enforce vim9script + if (put_line(fd, "vim9script") == FAIL) + failed = TRUE; + // Write the version command for :mkvimrc if (eap->cmdidx == CMD_mkvimrc) (void)put_line(fd, "version 6.0"); @@ -1265,7 +1286,7 @@ ex_mkrc(exarg_T *eap) #ifdef FEAT_SESSION if (eap->cmdidx == CMD_mksession) { - if (put_line(fd, "let SessionLoad = 1") == FAIL) + if (put_line(fd, "g:SessionLoad = 1") == FAIL) failed = TRUE; } @@ -1283,23 +1304,47 @@ ex_mkrc(exarg_T *eap) #ifdef FEAT_SESSION if (!view_session || (eap->cmdidx == CMD_mksession - && (*flagp & SSOP_OPTIONS))) + && (*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS)))) #endif { + bool do_mappings = true; int flags = OPT_GLOBAL; #ifdef FEAT_SESSION - if (eap->cmdidx == CMD_mksession && (*flagp & SSOP_SKIP_RTP)) - flags |= OPT_SKIPRTP; + failed |= put_line(fd, "var cpo_save: string") == FAIL; + + if (eap->cmdidx == CMD_mksession) + { + if (*flagp & SSOP_SKIP_RTP) + flags |= OPT_SKIPRTP; + + // SSOP_LOCALOPTIONS requires only local mappings + do_mappings = *flagp & SSOP_OPTIONS; + } #endif - failed |= (makemap(fd, NULL) == FAIL + + if (do_mappings) + failed |= (makemap(fd, NULL) == FAIL || makeset(fd, flags, FALSE) == FAIL); + +#if defined(FEAT_EVAL) + // Save delay load import modules. + // Either SSOP_LOCALOPTIONS or SSOP_OPTIONS require them + for (sid = 1; sid <= script_items.ga_len; ++sid) + { + si = SCRIPT_ITEM(sid); + if (si->sn_autoload_prefix && + (fprintf(fd, "import autoload '%s'", si->sn_name) < 0 || + put_eol(fd) == FAIL)) + failed = TRUE; + } +#endif } #ifdef FEAT_SESSION if (!failed && view_session) { - if (put_line(fd, "let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1") == FAIL) + if (put_line(fd, "const so_save: number = &g:so | const siso_save: number = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1") == FAIL) failed = TRUE; if (eap->cmdidx == CMD_mksession) { @@ -1341,11 +1386,11 @@ ex_mkrc(exarg_T *eap) } else { + failed |= put_line(fd, "var cpo_save: string") == FAIL; failed |= (put_view(fd, curwin, curtab, !using_vdir, flagp, -1, NULL) == FAIL); } - if (put_line(fd, "let &g:so = s:so_save | let &g:siso = s:siso_save") - == FAIL) + if (put_line(fd, "&g:so = so_save | &g:siso = siso_save") == FAIL) failed = TRUE; # ifdef FEAT_SEARCH_EXTRA if (no_hlsearch && put_line(fd, "nohlsearch") == FAIL) @@ -1355,12 +1400,13 @@ ex_mkrc(exarg_T *eap) failed = TRUE; if (eap->cmdidx == CMD_mksession) { - if (put_line(fd, "unlet SessionLoad") == FAIL) + if (put_line(fd, "unlet g:SessionLoad") == FAIL) failed = TRUE; } } #endif - if (put_line(fd, "\" vim: set ft=vim :") == FAIL) + + if (put_line(fd, "# vim: set ft=vim :") == FAIL) failed = TRUE; failed |= fclose(fd); diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index ecd5791c90..a1f282e06d 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -1199,10 +1199,10 @@ endfunc " Closing a window might cause an endless loop " E814 for older Vims func Test_autocmd_bufwipe_in_SessLoadPost() + set noswapfile edit Xtest tabnew file Xsomething - set noswapfile mksession! let content =<< trim [CODE] diff --git a/src/testdir/test_mksession.vim b/src/testdir/test_mksession.vim index 9dbd0094e2..9bf1b47ff5 100644 --- a/src/testdir/test_mksession.vim +++ b/src/testdir/test_mksession.vim @@ -75,9 +75,9 @@ func Test_mksession() \ ' four leadinG spaces', \ 'two consecutive tabs', \ 'two tabs in one line', - \ 'one ä multibyteCharacter', - \ 'aä Ä two multiByte characters', - \ 'Aäöü three mulTibyte characters', + \ 'one ä multibyteCharacter', + \ 'aä Ä two multiByte characters', + \ 'Aäöü three mulTibyte characters', \ 'short line', \ ]) let tmpfile = 'Xtemp' @@ -126,33 +126,33 @@ func Test_mksession() mksession! Xtest_mks.out let li = filter(readfile('Xtest_mks.out'), 'v:val =~# "\\(^ *normal! [0$]\\|^ *exe ''normal!\\)"') let expected = [ - \ 'normal! 016|', - \ 'normal! 016|', - \ 'normal! 016|', - \ 'normal! 08|', - \ 'normal! 08|', - \ 'normal! 016|', - \ 'normal! 016|', - \ 'normal! 016|', - \ 'normal! $', - \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", - \ " normal! 016|", - \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", - \ " normal! 016|", - \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", - \ " normal! 016|", - \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'", - \ " normal! 08|", - \ " exe 'normal! ' . s:c . '|zs' . 8 . '|'", - \ " normal! 08|", - \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", - \ " normal! 016|", - \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", - \ " normal! 016|", - \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", - \ " normal! 016|", - \ " exe 'normal! ' . s:c . '|zs' . 16 . '|'", - \ " normal! 016|" + \ ' normal! 016|', + \ ' normal! 016|', + \ ' normal! 016|', + \ ' normal! 08|', + \ ' normal! 08|', + \ ' normal! 016|', + \ ' normal! 016|', + \ ' normal! 016|', + \ ' normal! $', + \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'", + \ " normal! 016|", + \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'", + \ " normal! 016|", + \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'", + \ " normal! 016|", + \ " exe 'normal! ' .. c .. '|zs' .. 8 .. '|'", + \ " normal! 08|", + \ " exe 'normal! ' .. c .. '|zs' .. 8 .. '|'", + \ " normal! 08|", + \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'", + \ " normal! 016|", + \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'", + \ " normal! 016|", + \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'", + \ " normal! 016|", + \ " exe 'normal! ' .. c .. '|zs' .. 16 .. '|'", + \ " normal! 016|" \ ] call assert_equal(expected, li) tabclose! @@ -1066,7 +1066,7 @@ func Test_mksession_winminheight() let found_restore = 0 let lines = readfile('Xtest_mks.out') for line in lines - if line =~ '= s:save_winmin\(width\|height\)' + if line =~ '= save_winmin\(width\|height\)' let found_restore += 1 endif endfor @@ -1088,11 +1088,11 @@ func Test_mksession_shortmess() for line in lines let line = trim(line) - if line ==# 'let s:shortmess_save = &shortmess' + if line ==# 'shortmess_save = &shortmess' let found_save += 1 endif - if found_save !=# 0 && line ==# 'let &shortmess = s:shortmess_save' + if found_save !=# 0 && line ==# '&shortmess = shortmess_save' let found_restore += 1 endif endfor @@ -1109,7 +1109,7 @@ func Test_mksession_shortmess() let found_restore = 0 let lines = readfile('Xtest_mks.out') for line in lines - if line =~# 's:shortmess_save' + if line =~# '\(var \)\@ dummy-test dummy9.Test() .. "" + END + call writefile(plugin_sources, root . '/plugin/dummy9.vim') + + call mkdir(root . '/autoload', 'p') + let auto_sources =<< trim END + vim9script + const ref_txt = 'Hello from vim9 dummy plugin!' + export def Test(): string + writefile([ref_txt], 'XDummyOutput') + return has("gui_running") ? '' : $':echomsg "{ref_txt}"' + enddef + END + call writefile(auto_sources, root . '/autoload/dummy9.vim') + + " clean up later + defer delete(base, 'rf') + + " Load and check the plugin + const ref_txt = 'Hello from vim9 dummy plugin!' + let &packpath .= ',' . base + packadd dummy9 + messages clear + normal dummy-test + + if !has('gui_running') + call assert_match(ref_txt, execute('messages'), 'No vim9 plugin dummy.Test() execution') + endif + call assert_true(filereadable('XDummyOutput'), 'Output file was not created by Vim9 plugin') + call assert_equal([ref_txt], readfile('XDummyOutput')) + call delete('XDummyOutput') + + " Create a session file + mksession! XDummySession.vim + defer delete('XDummySession.vim') + call assert_true(filereadable('XDummySession.vim'), 'Session file was not created') + + " Check the session file mappings are operational + let test_sources =<< trim END + " load session + source XDummySession.vim + " execute vim9 expression mapping + normal dummy-test + " on my way + cq + END + call writefile(test_sources, 'XTest.vim', 'D') + " spawn a new Vim instance to load the session and execute the mapping + call system(GetVimCommand('XTest.vim')) + defer delete('XDummyOutput') + call assert_true(filereadable('XDummyOutput'), + \ 'Expected output file was not created by Vim9 plugin') + call assert_equal([ref_txt], readfile('XDummyOutput')) + +endfunc + +" Test legacy vimscript expression mappings +func Test_mksession_legacy_expr_mappings() + + CheckFeature packages + + " Create a dummy vim9 plugin + const base = getcwd() . '/rtdir' + const root = base . '/pack/test/opt/dummy' + call mkdir(root . '/plugin', 'p') + + " clean up later + defer delete(base, 'rf') + + let plugin_sources =<< trim END + nnoremap dummy-test dummy#Test() . "" + END + call writefile(plugin_sources, root . '/plugin/dummy.vim') + + call mkdir(root . '/autoload', 'p') + let auto_sources =<< trim END + const s:ref_txt = 'Hello from good old dummy plugin!' + func dummy#Test() + call writefile([s:ref_txt], 'XDummyOutput') + return has("gui_running") ? '' : $':echomsg "{s:ref_txt}"' + endfunc + END + call writefile(auto_sources, root . '/autoload/dummy.vim') + + " Load and check the plugin + const ref_txt = 'Hello from good old dummy plugin!' + let &packpath .= ',' . base + packadd dummy + messages clear + normal dummy-test + + if !has("gui_running") + call assert_match(ref_txt, execute('messages'), 'No vim9 plugin dummy.Test() execution') + endif + call assert_true(filereadable('XDummyOutput'), 'Output file was not created by legacy plugin') + call assert_equal([ref_txt], readfile('XDummyOutput')) + call delete('XDummyOutput') + + " Create a session file + mksession! XDummySession.vim + defer delete('XDummySession.vim') + call assert_true(filereadable('XDummySession.vim'), 'Session file was not created') + + " Check the session file mappings are operational + let test_sources =<< trim END + " load session + source XDummySession.vim + " execute legacy vimscript expression mapping + normal dummy-test + " on my way + cq + END + call writefile(test_sources, 'XTest.vim', 'D') + " spawn a new Vim instance to load the session and execute the mapping + call system(GetVimCommand('XTest.vim')) + defer delete('XDummyOutput') + call assert_true(filereadable('XDummyOutput'), + \ 'Expected output file was not created by legacy vim plugin') + call assert_equal([ref_txt], readfile('XDummyOutput')) + +endfunc + +" Test sessions cursor position management +func Test_mksession_cursor_position() + + " Set windows test scenario + let files = [] + for i in range(10) + let file = $'Xfile{i}' + exe $"{i ? 'split' : 'edit'} {file}" + call append(0, $"Session file cursor position testing {i}") + " Force cursor position restoring commands + setlocal nowrap + normal dd29zl + " Check expected position + call assert_equal([0, 1, 30, 0], getpos('.'), $"Fail to set cursor position for {file}") + write! + let files += [file] + endfor + + " Save session + mksession! Xtest_curpos + + " Test restoring session + %bwipe! + try + source Xtest_curpos + catch + call assert_report("Failure sourcing session file") + endtry + + " Check cursor position + for file in files + exe $"drop {file}" + call assert_equal([0, 1, 30, 0], getpos('.'), $"Cursor position not restored correctly for {file}") + endfor + + " Clean up + call delete('Xtest_curpos') + for file in files + call delete(file) + endfor +endfunc + +" Test sessions global and local mappings +func Test_mksession_localmappings() + + " Create sessions. Mapping execution is tested running a file + let valid_sessions = [] " keep map info + let invalid_sessions = [] " do not keep map info + " localoptions requires a buffer + setlocal noswapfile + silent write XDummy + defer delete('XDummy') + + for option in ["&", "=options", "=localoptions"] + for global in [0, 1] + + " select options + exe "set sessionoptions" .. option + + " mapping + exe "nnoremap" . (global ? " " : " ") + \ . "dummy-test silent write XDummyOutput" + let case = $"mapping_{global ? "global" : "local"}_{option}" + + " test mapping + normal dummy-test + call assert_true(filereadable("XDummyOutput"), $"Output file was not created by {case}") + call delete("XDummyOutput") + + " session + let sessionfile = "XSession_" . case + exe $"mksession {sessionfile}" + + if global && option =~ "localoptions" + let invalid_sessions += [sessionfile] + else + let valid_sessions += [sessionfile] + endif + + " clear mappings + nmapclear + nmapclear + + endfor + endfor + + " Check the session files are operational + for session in valid_sessions + + let test_sources =<< trim eval END + " load session + silent source {session} + " execute legacy vimscript expression mapping + normal dummy-test + " on my way + cq + END + + call writefile(test_sources, 'XTest.vim') + call system(GetVimCommand('XTest.vim')) + call assert_true(filereadable('XDummyOutput'), + \ $"Expected map not defined in session file {session}") + call delete('XDummyOutput') + call delete(session) + + endfor + + for session in invalid_sessions + + let test_sources =<< trim eval END + " load session + silent source {session} + " execute legacy vimscript expression mapping + normal dummy-test + " on my way + cq + END + + call writefile(test_sources, 'XTest.vim') + call system(GetVimCommand('XTest.vim')) + call assert_false(filereadable('XDummyOutput'), + \ $"Unexpected map defined in session file {session}") + if filereadable('XDummyOutput') + call delete('XDummyOutput') + endif + call delete(session) + endfor + +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_mksession_utf8.vim b/src/testdir/test_mksession_utf8.vim index d467dc20c6..90695f0c8d 100644 --- a/src/testdir/test_mksession_utf8.vim +++ b/src/testdir/test_mksession_utf8.vim @@ -64,34 +64,38 @@ func Test_mksession_utf8() mksession! test_mks.out let li = filter(readfile('test_mks.out'), 'v:val =~# "\\(^ *normal! 0\\|^ *exe ''normal!\\)"') let expected =<< trim [DATA] - normal! 016| - normal! 016| - normal! 016| - normal! 08| - normal! 08| - normal! 016| - normal! 016| - normal! 016| - exe 'normal! ' . s:c . '|zs' . 16 . '|' + | normal! 016| - exe 'normal! ' . s:c . '|zs' . 16 . '|' normal! 016| - exe 'normal! ' . s:c . '|zs' . 16 . '|' normal! 016| - exe 'normal! ' . s:c . '|zs' . 8 . '|' normal! 08| - exe 'normal! ' . s:c . '|zs' . 8 . '|' normal! 08| - exe 'normal! ' . s:c . '|zs' . 16 . '|' normal! 016| - exe 'normal! ' . s:c . '|zs' . 16 . '|' normal! 016| - exe 'normal! ' . s:c . '|zs' . 16 . '|' - normal! 016| - exe 'normal! ' . s:c . '|zs' . 16 . '|' normal! 016| + exe 'normal! ' .. c .. '|zs' .. 16 .. '|' + normal! 016| + exe 'normal! ' .. c .. '|zs' .. 16 .. '|' + normal! 016| + exe 'normal! ' .. c .. '|zs' .. 16 .. '|' + normal! 016| + exe 'normal! ' .. c .. '|zs' .. 8 .. '|' + normal! 08| + exe 'normal! ' .. c .. '|zs' .. 8 .. '|' + normal! 08| + exe 'normal! ' .. c .. '|zs' .. 16 .. '|' + normal! 016| + exe 'normal! ' .. c .. '|zs' .. 16 .. '|' + normal! 016| + exe 'normal! ' .. c .. '|zs' .. 16 .. '|' + normal! 016| + exe 'normal! ' .. c .. '|zs' .. 16 .. '|' + normal! 016| [DATA] + " remove indent marker + call remove(expected, 0) + call assert_equal(expected, li) tabclose! diff --git a/src/version.c b/src/version.c index 80020f59fd..95107bb286 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 579, /**/ 578, /**/ From f0cae9d5abba1ad97f8c78c8d3cbb0d1c46d5aff Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Sun, 31 May 2026 21:11:55 +0000 Subject: [PATCH 378/545] patch 9.2.0580: xxd: binary output is not colored with -R Problem: With xxd the -R option colors the hex output but leaves the binary output produced by -b uncolored (Boris Verkhovskiy) Solution: Color the binary (bits) output per byte with the same colors as the hex output, update the documentation and add a test (Hirohito Higashi). fixes: #20385 closes: #20401 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/xxd.1 | 6 +++--- runtime/doc/xxd.man | 11 ++++++----- src/testdir/test_xxd.vim | 16 ++++++++++++++++ src/version.c | 2 ++ src/xxd/xxd.c | 12 ++++++++++-- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/runtime/doc/xxd.1 b/runtime/doc/xxd.1 index 48166e03cc..d80c5e9c08 100644 --- a/runtime/doc/xxd.1 +++ b/runtime/doc/xxd.1 @@ -142,9 +142,9 @@ anywhere. Use the combination to read a bits dump instead of a hex dump. .TP .IR \-R " " when -In the output the hex-value and the value are both colored with the same color -depending on the hex-value. Mostly helping to differentiate printable and -non-printable characters. +In the output both the data column (hex, or bits with \-b) and the character +column are colored with the same color depending on the byte value. Mostly +helping to differentiate printable and non-printable characters. .I \fIwhen\fP is .BR never ", " always ", or " auto " (default: auto). diff --git a/runtime/doc/xxd.man b/runtime/doc/xxd.man index 8beba9c88a..17a9dd14ba 100644 --- a/runtime/doc/xxd.man +++ b/runtime/doc/xxd.man @@ -101,11 +101,12 @@ OPTIONS instead of a hex dump. -R when - In the output the hex-value and the value are both colored with - the same color depending on the hex-value. Mostly helping to - differentiate printable and non-printable characters. when is - never, always, or auto (default: auto). When the $NO_COLOR en‐ - vironment variable is set, colorization will be disabled. + In the output both the data column (hex, or bits with -b) and + the character column are colored with the same color depending + on the byte value. Mostly helping to differentiate printable and + non-printable characters. when is never, always, or auto (de‐ + fault: auto). When the $NO_COLOR environment variable is set, + colorization will be disabled. -seek offset When used after -r: revert with added to file positions diff --git a/src/testdir/test_xxd.vim b/src/testdir/test_xxd.vim index 430a07ccfb..990683b172 100644 --- a/src/testdir/test_xxd.vim +++ b/src/testdir/test_xxd.vim @@ -663,6 +663,22 @@ call writefile(data,'Xinput') endfunc +func Test_xxd_color_bits() + " Binary output (-b) should be colored per byte like the hex output, + " see issue #20385. Bytes cover the white/yellow/green/blue color groups. + let s:test = 1 + call writefile(0z000941FF, 'Xxxdbits') + + %d + exe '0r! ' . s:xxd_cmd . ' -b -R always -c 4 Xxxdbits' + $d + let expected = [ + \ "00000000: \e[1;37m00000000\e[0m \e[1;33m00001001\e[0m \e[1;32m01000001\e[0m \e[1;34m11111111\e[0m \e[1;37m.\e[0m\e[1;33m.\e[0m\e[1;32mA\e[0m\e[1;34m.\e[0m"] + call assert_equal(expected, getline(1, '$'), s:Mess(s:test)) + + call delete('Xxxdbits') +endfunc + func Test_xxd_color2() CheckScreendump CheckUnix diff --git a/src/version.c b/src/version.c index 95107bb286..e39f67aad7 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 580, /**/ 579, /**/ diff --git a/src/xxd/xxd.c b/src/xxd/xxd.c index fb45f6c284..323d5a3982 100644 --- a/src/xxd/xxd.c +++ b/src/xxd/xxd.c @@ -75,6 +75,7 @@ * 19.03.2026 Add -t option to end output with terminating null * 25.03.2026 Fix color output issues * 26.04.2026 Use unsigned long for printing offsets + * 31.05.2026 Colorize binary output * * (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com) * @@ -155,7 +156,7 @@ extern void perror __P((char *)); # endif #endif -char version[] = "xxd 2026-04-26 by Juergen Weigert et al."; +char version[] = "xxd 2026-05-31 by Juergen Weigert et al."; #ifdef WIN32 char osver[] = " (Win32)"; #else @@ -1194,8 +1195,15 @@ main(int argc, char *argv[]) } else /* hextype == HEX_BITS */ { + if (color) + cur_color = get_color_char(e, ebcdic); + for (i = 7; i >= 0; i--) - l[c++] = (e & (1 << i)) ? '1' : '0'; + { + if (color) + colors[c] = cur_color; + l[c++] = (e & (1 << i)) ? '1' : '0'; + } } if (e) nonzero++; From b7d07d3e8014d127118ce73056221972bb411555 Mon Sep 17 00:00:00 2001 From: Antonio Giovanni Colombo Date: Sun, 31 May 2026 21:24:16 +0000 Subject: [PATCH 379/545] translation(it): Update Italian xxd man page Signed-off-by: Antonio Giovanni Colombo Signed-off-by: Christian Brabandt --- runtime/doc/xxd-it.1 | 5 +++-- runtime/doc/xxd-it.UTF-8.1 | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/runtime/doc/xxd-it.1 b/runtime/doc/xxd-it.1 index c95cbc9524..99a9395867 100644 --- a/runtime/doc/xxd-it.1 +++ b/runtime/doc/xxd-it.1 @@ -143,8 +143,9 @@ possono essere presenti dappertutto [e vengono ignorati]. Usare la combinazione per leggere un dump binario, invece che un dump esadecimale. .TP .IR \-R " "[quando] -Nell'output i valori esadecimali e i caratteri corrispondenti hanno entrambi -lo stesso colore, a seconda del valore esadecimale. Utile soprattutto a +Nell'output sia le colonne con i dati (esadecimali, o binari - con \-b) +che quelle con i caratteri corrispondenti hanno entrambe +lo stesso colore, a seconda del valore del byte. Utile soprattutto a distinguere i caratteri stampabili da quelli non stampabili. .I quando può assumere i valori diff --git a/runtime/doc/xxd-it.UTF-8.1 b/runtime/doc/xxd-it.UTF-8.1 index b837d56968..57d4ab7b37 100644 --- a/runtime/doc/xxd-it.UTF-8.1 +++ b/runtime/doc/xxd-it.UTF-8.1 @@ -143,8 +143,9 @@ possono essere presenti dappertutto [e vengono ignorati]. Usare la combinazione per leggere un dump binario, invece che un dump esadecimale. .TP .IR \-R " "[quando] -Nell'output i valori esadecimali e i caratteri corrispondenti hanno entrambi -lo stesso colore, a seconda del valore esadecimale. Utile soprattutto a +Nell'output sia le colonne con i dati (esadecimali, o binari - con \-b) +che quelle con i caratteri corrispondenti hanno entrambe +lo stesso colore, a seconda del valore del byte. Utile soprattutto a distinguere i caratteri stampabili da quelli non stampabili. .I quando può assumere i valori From e9f8e0fcbe623ebf65bf1e8993d7ee6db57786d4 Mon Sep 17 00:00:00 2001 From: Christian Brabandt Date: Sun, 31 May 2026 21:34:51 +0000 Subject: [PATCH 380/545] runtime(matchit): Update matchit plugin Signed-off-by: Christian Brabandt --- runtime/pack/dist/opt/matchit/autoload/matchit.vim | 8 ++++---- runtime/pack/dist/opt/matchit/doc/matchit.txt | 10 +--------- runtime/pack/dist/opt/matchit/doc/tags | 3 --- runtime/pack/dist/opt/matchit/plugin/matchit.vim | 6 +++--- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/runtime/pack/dist/opt/matchit/autoload/matchit.vim b/runtime/pack/dist/opt/matchit/autoload/matchit.vim index 1e660ffd3e..76148c6e5a 100644 --- a/runtime/pack/dist/opt/matchit/autoload/matchit.vim +++ b/runtime/pack/dist/opt/matchit/autoload/matchit.vim @@ -1,6 +1,6 @@ " matchit.vim: (global plugin) Extended "%" matching " autload script of matchit plugin, see ../plugin/matchit.vim -" Last Change: Jan 09, 2026 +" Last Change: June 01, 2026 " Neovim does not support scriptversion if has("vimscript-4") @@ -124,7 +124,7 @@ function matchit#Match_wrapper(word, forward, mode) range let s:all = substitute(s:all, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g') " Just in case there are too many '\(...)' groups inside the pattern, make " sure to use \%(...) groups, so that error E872 can be avoided - let s:all = substitute(s:all, '\\(', '\\%(', 'g') + let s:all = substitute(s:all, s:notslash .. '\zs\\(', '\\%(', 'g') let s:all = '\%(' .. s:all .. '\)' if exists("b:match_debug") let b:match_pat = s:pat @@ -339,8 +339,8 @@ fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline) if table[d] != "-" let backref = substitute(a:matchline, a:prefix .. word .. a:suffix, \ '\' .. table[d], "") - " Are there any other characters that should be escaped? - let backref = escape(backref, '*,:') + " escape magic pattern metacharacters and matchit special characters [,:] + let backref = escape(backref, '\.*[^$~,:') execute s:Ref(ini, d, "start", "len") let ini = strpart(ini, 0, start) .. backref .. strpart(ini, start+len) let tailBR = substitute(tailBR, s:notslash .. '\zs\\' .. d, diff --git a/runtime/pack/dist/opt/matchit/doc/matchit.txt b/runtime/pack/dist/opt/matchit/doc/matchit.txt index e82bacdeb0..31fb466739 100644 --- a/runtime/pack/dist/opt/matchit/doc/matchit.txt +++ b/runtime/pack/dist/opt/matchit/doc/matchit.txt @@ -1,4 +1,4 @@ -*matchit.txt* Extended "%" matching Last change: 2026 Jan 06 +*matchit.txt* Extended "%" matching Last change: 2026 Jun 01 VIM REFERENCE MANUAL by Benji Fisher et al @@ -195,14 +195,6 @@ can be defined in the |filetype-plugin| or autocommand that defines The main variable is |b:match_words|. It is described in the section below on supporting a new language. - *MatchError* *matchit-hl* *matchit-highlight* -MatchError is the highlight group for error messages from the script. By -default, it is linked to WarningMsg. If you do not want to be bothered by -error messages, you can define this to be something invisible. For example, -if you use the GUI version of Vim and your command line is normally white, you -can do > - :hi MatchError guifg=white guibg=white -< *b:match_ignorecase* If you > :let b:match_ignorecase = 1 diff --git a/runtime/pack/dist/opt/matchit/doc/tags b/runtime/pack/dist/opt/matchit/doc/tags index 008c5686d1..f86a672196 100644 --- a/runtime/pack/dist/opt/matchit/doc/tags +++ b/runtime/pack/dist/opt/matchit/doc/tags @@ -1,7 +1,6 @@ :MatchDebug matchit.txt /*:MatchDebug* :MatchDisable matchit.txt /*:MatchDisable* :MatchEnable matchit.txt /*:MatchEnable* -MatchError matchit.txt /*MatchError* [% matchit.txt /*[%* ]% matchit.txt /*]%* b:match_col matchit.txt /*b:match_col* @@ -29,8 +28,6 @@ matchit-configure matchit.txt /*matchit-configure* matchit-debug matchit.txt /*matchit-debug* matchit-details matchit.txt /*matchit-details* matchit-disable matchit.txt /*matchit-disable* -matchit-highlight matchit.txt /*matchit-highlight* -matchit-hl matchit.txt /*matchit-hl* matchit-intro matchit.txt /*matchit-intro* matchit-languages matchit.txt /*matchit-languages* matchit-modes matchit.txt /*matchit-modes* diff --git a/runtime/pack/dist/opt/matchit/plugin/matchit.vim b/runtime/pack/dist/opt/matchit/plugin/matchit.vim index 947f530261..fef2a8ace0 100644 --- a/runtime/pack/dist/opt/matchit/plugin/matchit.vim +++ b/runtime/pack/dist/opt/matchit/plugin/matchit.vim @@ -1,7 +1,7 @@ " matchit.vim: (global plugin) Extended "%" matching " Maintainer: Christian Brabandt -" Version: 1.21 -" Last Change: 2024 May 20 +" Version: 1.22 +" Last Change: 2026 Jun 01 " Repository: https://github.com/chrisbra/matchit " Previous URL:http://www.vim.org/script.php?script_id=39 " Previous Maintainer: Benji Fisher PhD @@ -50,7 +50,7 @@ fun MatchEnable() nnoremap (MatchitNormalForward) :call matchit#Match_wrapper('',1,'n') nnoremap (MatchitNormalBackward) :call matchit#Match_wrapper('',0,'n') xnoremap (MatchitVisualForward) :call matchit#Match_wrapper('',1,'v') - \:if col("''") != col("$") \| exe ":normal! m'" \| endifgv`` + \:if line("''") != line(".") \|\| col("''") != col("$") \| exe ":normal! m'" \| endifgv`` xnoremap (MatchitVisualBackward) :call matchit#Match_wrapper('',0,'v')m'gv`` onoremap (MatchitOperationForward) :call matchit#Match_wrapper('',1,'o') onoremap (MatchitOperationBackward) :call matchit#Match_wrapper('',0,'o') From 07f055f5790cd57b83532928d8e74c1eabe45e5f Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sun, 31 May 2026 21:38:30 +0000 Subject: [PATCH 381/545] patch 9.2.0581: After maximizing and deleting the quickfix buffer, window height is wrong Problem: After maximizing and deleting the quickfix buffer, window height is wrong (tertium) Solution: Reset the winfixheight option when a quickfix buffer is deleted from a window (Yegappan Lakshmanan) fixes: #3378 closes: #20403 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- src/buffer.c | 5 +++++ src/testdir/test_quickfix.vim | 16 ++++++++++++++++ src/version.c | 2 ++ 3 files changed, 23 insertions(+) diff --git a/src/buffer.c b/src/buffer.c index 07176fbc43..d287183293 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -736,6 +736,11 @@ close_buffer( unblock_autocmds(); } + // When a quickfix buffer is deleted from a window, clear the + // 'winfixheight' option. + if (bt_quickfix(buf) && win_valid && win->w_buffer == buf) + win->w_p_wfh = FALSE; + // Remember if the buffer may be hidden soon, or is already hidden. hiding_buf = buf->b_nwindows <= 0 || ((win_valid || closed_popup) && win->w_buffer == buf && buf->b_nwindows == 1); diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index c656d205f0..b0c4475ccd 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -7090,4 +7090,20 @@ func Test_set_qftf_in_sandbox() call Xtest_set_qftf_in_sandbox('l') endfunc +" Test for the 'statusline' height remaining at one when the quickfix buffer +" is wiped out when quickfix window is the only window open. +func Test_qf_statusline_height() + %bw! + copen + wincmd p + let prev_wh = winheight('.') + wincmd p + only + bw! + copen + wincmd p + call assert_equal(prev_wh, winheight('.')) + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index e39f67aad7..44bc20abdc 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 581, /**/ 580, /**/ From 7895c9e6b8c6293b293656b48f2cd5f5a46a58b6 Mon Sep 17 00:00:00 2001 From: Doug Kearns Date: Mon, 1 Jun 2026 19:43:01 +0000 Subject: [PATCH 382/545] runtime(vim): Update ftplugin, add heredocs to b:match_words This depends on chrisbra/matchit#61 for full support of non-alphnum heredoc markers. closes: #20399 Signed-off-by: Doug Kearns Signed-off-by: Christian Brabandt --- runtime/ftplugin/vim.vim | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/runtime/ftplugin/vim.vim b/runtime/ftplugin/vim.vim index 72a87d1802..21ca040fa6 100644 --- a/runtime/ftplugin/vim.vim +++ b/runtime/ftplugin/vim.vim @@ -6,13 +6,7 @@ " @Konfekt " @tpope (s:Help()) " @lacygoill -" Last Change: 2025 Aug 07 -" 2025 Aug 06 by Vim Project (add gf maps #17881) -" 2025 Aug 08 by Vim Project (add Vim script complete function #17871) -" 2025 Aug 12 by Vim Project (improve vimgoto script #17970)) -" 2025 Aug 16 by Vim Project set com depending on Vim9 or legacy script -" 2026 Jan 26 by Vim Project set path to common Vim directories #19219 -" 2026 Feb 03 by Vim Project update s:Help to improve detecting functions #19320 +" Last Change: 2026 May 31 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -204,15 +198,17 @@ if exists("loaded_matchit") \ '\\)\@!\S:\,' .. \ '\:\,' .. \ '\:\,' .. - \ '\:\' + \ '\:\,' .. + "\ :let-heredoc + \ '\%(=<<\s\+\%(\%(trim\s\+eval\|eval\s\+trim\|trim\|eval\)\s\+\)\=\)\@16<=\(\l\@!\S\+\):\%(^\s*\)\@<=\1$' " Ignore syntax region commands and settings, any 'en*' would clobber " if-endif. " - set spl=de,en " - au! FileType javascript syntax region foldBraces start=/{/ end=/}/ … - " Also ignore here-doc and dictionary keys (vimVar). + " Also ignore heredoc content and dictionary keys (vimVar). let b:match_skip = 'synIDattr(synID(line("."), col("."), 1), "name") - \ =~? "comment\\|string\\|vimSynReg\\|vimSet\\|vimLetHereDoc\\|vimVar"' + \ =~? "comment\\|string\\|vimSynReg\\|vimSet\\|vimLetHeredoc$\\|vimVar"' endif let &cpo = s:cpo_save From e2d1c334ee88067e16351e091ece95f6c6f24af5 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Mon, 1 Jun 2026 19:46:46 +0000 Subject: [PATCH 383/545] patch 9.2.0582: GTK4: compile error when XFONTSET is defined Problem: GTK4: compile error when XFONTSET is defined (simozz) Solution: Undefine FEAT_XFONTSET when GTK4 UI is defined (Foxe Chen) fixes: #20404 closes: #20406 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/feature.h | 4 ++++ src/version.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/feature.h b/src/feature.h index 46508f74bb..92c4b1374b 100644 --- a/src/feature.h +++ b/src/feature.h @@ -444,6 +444,10 @@ # else // # define FEAT_XFONTSET # endif +#else +# if defined(USE_GTK4) +# undef FEAT_XFONTSET +# endif #endif /* diff --git a/src/version.c b/src/version.c index 44bc20abdc..02fc6a82f6 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 582, /**/ 581, /**/ From 9fa5f64135c1b018ca5bfc86d851ac8185f6472b Mon Sep 17 00:00:00 2001 From: glepnir Date: Mon, 1 Jun 2026 20:15:35 +0000 Subject: [PATCH 384/545] patch 9.2.0583: completion: indent not ignored for fuzzy line completion Problem: Indent is not stripped in whole-line completion (CTRL-X CTRL-L). Solution: Skip the matched line's indent for whole-line matches in search_for_fuzzy_match (glepnir). closes: #20405 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- src/fuzzy.c | 8 ++++++-- src/testdir/test_ins_complete.vim | 8 +++++++- src/version.c | 2 ++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/fuzzy.c b/src/fuzzy.c index b6d595a700..c8709c1079 100644 --- a/src/fuzzy.c +++ b/src/fuzzy.c @@ -865,11 +865,15 @@ search_for_fuzzy_match( } else { - if (fuzzy_match_str(*ptr, pattern) != FUZZY_SCORE_NONE) + char_u *line = *ptr; + char_u *p = skipwhite(line); + if (fuzzy_match_str(p, pattern) != FUZZY_SCORE_NONE) { found_new_match = TRUE; *pos = current_pos; - *len = (int)ml_get_buf_len(buf, current_pos.lnum); + *ptr = p; + *len = (int)ml_get_buf_len(buf, current_pos.lnum) - + (int)(p - line); break; } } diff --git a/src/testdir/test_ins_complete.vim b/src/testdir/test_ins_complete.vim index fbfb45e2f3..7eda457e1a 100644 --- a/src/testdir/test_ins_complete.vim +++ b/src/testdir/test_ins_complete.vim @@ -3696,10 +3696,16 @@ func Test_complete_opt_fuzzy() call feedkeys("Goa\\\", 'tx') call assert_equal('aaaa', getline('.')) + %d + set autoindent + set completeopt=menuone,fuzzy + call feedkeys("A\hello world\word is on fire\w\\\", 'tx') + call assert_equal("\thello world", getline('.')) + " clean up set omnifunc= bw! - set complete& completeopt& + set complete& completeopt& autoindent& autocmd! AAAAA_Group augroup! AAAAA_Group delfunc OnPumChange diff --git a/src/version.c b/src/version.c index 02fc6a82f6..6d7077fe9d 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 583, /**/ 582, /**/ From 8f508cdcfb2c354b93cbd20789301d03bfb0bcfc Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Mon, 1 Jun 2026 20:26:18 +0000 Subject: [PATCH 385/545] patch 9.2.0584: GTK4: missing UI features Problem: GTK4: :popup, menu item show/hide and menu bar state updates are not implemented. Solution: Implement gui_make_popup(), gui_mch_menu_hidden() and gui_mch_draw_menubar() (Foxe Chen). closes: #20393 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/gui_gtk4.c | 332 +++++++++++++++++++++++++++++-------------------- src/version.c | 2 + 2 files changed, 199 insertions(+), 135 deletions(-) diff --git a/src/gui_gtk4.c b/src/gui_gtk4.c index 8d650397c4..d6d32b7ccc 100644 --- a/src/gui_gtk4.c +++ b/src/gui_gtk4.c @@ -814,8 +814,6 @@ gui_mch_settitle(char_u *title, char_u *icon UNUSED) gtk_window_set_title(GTK_WINDOW(gui.mainwin), (const char *)title); } -static int in_set_shellsize = FALSE; - /* * Get height of window decorations, that we cannot determine directly. For * example, the GtkHeaderBar widget. This is called in gui_resize_shell(), we @@ -2480,17 +2478,41 @@ gui_mch_menu_grey(vimmenu_T *menu, int grey) } } +#if defined(FEAT_MENU) +/* + * Make menu item hidden or not hidden. + */ void -gui_mch_menu_hidden(vimmenu_T *menu UNUSED, int hidden UNUSED) +gui_mch_menu_hidden(vimmenu_T *menu, int hidden) { - // No-op: menu system not yet implemented for GTK4. + if (menu->id == 0) + return; + + if (hidden) + { + if (gtk_widget_get_visible(menu->id)) + { + gtk_widget_set_visible(menu->id, FALSE); + gui_mch_update(); + } + } + else + { + if (!gtk_widget_get_visible(menu->id)) + { + gtk_widget_set_visible(menu->id, TRUE); + gui_mch_update(); + } + } } void gui_mch_draw_menubar(void) { - // No-op: menu system not yet implemented for GTK4. + // Just make sure that the visual changes get effect immediately + gui_mch_update(); } +#endif /* * ============================================================ @@ -3244,19 +3266,181 @@ gui_get_x11_windis(Window *win UNUSED, Display **dis UNUSED) return FAIL; } +#if defined(FEAT_MENU) void gui_gtk_set_mnemonics(int enable UNUSED) { - // No-op: menu mnemonics depend on menu system, not yet implemented - // for GTK4. + // TODO: implement? +} + + static void +popupmenu_closed_cb(GtkPopover *popover, gpointer data UNUSED) +{ + gtk_widget_unparent(GTK_WIDGET(popover)); + if (gui.drawarea != NULL) + gtk_widget_queue_draw(gui.drawarea); +} + +typedef struct { + GtkPopover *popover; + vimmenu_T *menu; +} popup_item_data_T; + + static void +popup_item_clicked_cb(GtkButton *button UNUSED, gpointer data) +{ + popup_item_data_T *d = data; + + if (d->popover != NULL) + gtk_popover_popdown(d->popover); + if (d->menu != NULL) + { + gui_menu_cb(d->menu); + gui_mch_flush(); + } +} + + static void +popup_item_data_free(gpointer data, GClosure *closure UNUSED) +{ + g_free(data); +} + +/* + * Open a popup for the given menu at point (x, y). + */ + static void +gui_gtk_popup_at(vimmenu_T *menu, int x, int y) +{ + GtkWidget *popover; + GtkWidget *box; + GtkWidget *parent; + GdkRectangle rect; + vimmenu_T *child; + int mode; + int natural_width = 0; + + if (menu == NULL || menu->children == NULL) + return; + + // Attach the popover to drawarea's parent rather than to drawarea itself. + // GtkDrawingArea is a leaf widget whose snapshot does not iterate children, + // and parenting a popover to it has been observed to leave the drawing area + // blank while the popover is open. + parent = gtk_widget_get_parent(gui.drawarea); + if (parent == NULL) + parent = gui.drawarea; + + // Build the popover by hand instead of using gtk_popover_menu_new_from_model. + // GtkPopoverMenu relies on the "menu." action-group lookup walking up + // the parent chain, which has been observed to silently fail on some + // compositors when the popover is parented via gtk_widget_set_parent. Wiring + // each menu item to a plain "clicked" signal sidesteps that entirely. + popover = gtk_popover_new(); + gtk_widget_set_parent(popover, parent); + gtk_popover_set_has_arrow(GTK_POPOVER(popover), FALSE); + gtk_popover_set_position(GTK_POPOVER(popover), GTK_POS_BOTTOM); + gtk_widget_add_css_class(popover, "menu"); + + box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_popover_set_child(GTK_POPOVER(popover), box); + + mode = get_menu_mode_flag(); + + for (child = menu->children; child != NULL; child = child->next) + { + GtkWidget *item; + char_u *label; + popup_item_data_T *cb_data; + + if (menu_is_separator(child->name)) + { + item = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); + gtk_box_append(GTK_BOX(box), item); + continue; + } + + label = CONVERT_TO_UTF8(child->dname); + item = gtk_button_new_with_mnemonic( + label != NULL ? (const char *)label : ""); + CONVERT_TO_UTF8_FREE(label); + + gtk_widget_add_css_class(item, "flat"); + gtk_widget_add_css_class(item, "model"); + gtk_button_set_has_frame(GTK_BUTTON(item), FALSE); + gtk_widget_set_halign(item, GTK_ALIGN_FILL); + { + GtkWidget *btn_label = gtk_button_get_child(GTK_BUTTON(item)); + if (GTK_IS_LABEL(btn_label)) + gtk_label_set_xalign(GTK_LABEL(btn_label), 0.0); + } + + if (!(child->modes & child->enabled & mode)) + gtk_widget_set_sensitive(item, FALSE); + + cb_data = g_new0(popup_item_data_T, 1); + cb_data->popover = GTK_POPOVER(popover); + cb_data->menu = child; + g_signal_connect_data(item, "clicked", + G_CALLBACK(popup_item_clicked_cb), + cb_data, popup_item_data_free, 0); + + gtk_box_append(GTK_BOX(box), item); + } + + rect.x = x; + rect.y = y; + // GtkPopover with GTK_POS_BOTTOM centres horizontally on the pointing-to + // rectangle. Use the box's natural width so the popover's left edge ends + // up at the cursor (down-and-to-the-right of the pointer). + gtk_widget_measure(box, GTK_ORIENTATION_HORIZONTAL, -1, + NULL, &natural_width, NULL, NULL); + rect.width = natural_width > 0 ? natural_width : 1; + rect.height = 1; + gtk_popover_set_pointing_to(GTK_POPOVER(popover), &rect); + + g_signal_connect(popover, "closed", + G_CALLBACK(popupmenu_closed_cb), NULL); + gtk_popover_popup(GTK_POPOVER(popover)); } void -gui_make_popup(char_u *path_name UNUSED, int mouse_pos UNUSED) +gui_make_popup(char_u *path_name, int mouse_pos) { - // No-op: popup menus depend on menu system, not yet implemented - // for GTK4. + vimmenu_T *menu; + int x, y; + + menu = gui_find_menu(path_name); + if (menu == NULL || menu->submenu_id == NULL) + return; + + if (mouse_pos) + gui_mch_getmouse(&x, &y); + else + { + // Find the cursor position relative to parent of drawarea + GtkWidget *parent = gtk_widget_get_parent(gui.drawarea); + graphene_point_t point; + if (parent == NULL) + parent = gui.drawarea; + + if (!gtk_widget_compute_point(gui.drawarea, parent, + &GRAPHENE_POINT_INIT(0, 0), &point)) + x = y = 0; + else + { + x = point.x; + y = point.y; + } + + x += FILL_X(curwin->w_wincol + curwin->w_wcol + 1) + 1; + y += FILL_Y(W_WINROW(curwin) + curwin->w_wrow + 1) + 1; + } + + gui_gtk_popup_at(menu, x, y); + } +#endif // FEAT_MENU int get_menu_tool_width(void) @@ -4020,135 +4204,13 @@ gui_mch_destroy_menu(vimmenu_T *menu) } } - static void -popupmenu_closed_cb(GtkPopover *popover, gpointer data UNUSED) -{ - gtk_widget_unparent(GTK_WIDGET(popover)); - if (gui.drawarea != NULL) - gtk_widget_queue_draw(gui.drawarea); -} - -typedef struct { - GtkPopover *popover; - vimmenu_T *menu; -} popup_item_data_T; - - static void -popup_item_clicked_cb(GtkButton *button UNUSED, gpointer data) -{ - popup_item_data_T *d = data; - - if (d->popover != NULL) - gtk_popover_popdown(d->popover); - if (d->menu != NULL) - { - gui_menu_cb(d->menu); - gui_mch_flush(); - } -} - - static void -popup_item_data_free(gpointer data, GClosure *closure UNUSED) -{ - g_free(data); -} - void gui_mch_show_popupmenu(vimmenu_T *menu) { - GtkWidget *popover; - GtkWidget *box; - GtkWidget *parent; - GdkRectangle rect; - vimmenu_T *child; - int mode; - int natural_width = 0; - - if (menu == NULL || menu->children == NULL) - return; - - // Attach the popover to drawarea's parent (the GtkOverlay) rather than - // to drawarea itself. GtkDrawingArea is a leaf widget whose snapshot - // does not iterate children, and parenting a popover to it has been - // observed to leave the drawing area blank while the popover is open. - parent = gtk_widget_get_parent(gui.drawarea); - if (parent == NULL) - parent = gui.drawarea; - - // Build the popover by hand instead of using gtk_popover_menu_new_from_model. - // GtkPopoverMenu relies on the "menu." action-group lookup walking up - // the parent chain, which has been observed to silently fail on some - // compositors when the popover is parented via gtk_widget_set_parent. Wiring - // each menu item to a plain "clicked" signal sidesteps that entirely. - popover = gtk_popover_new(); - gtk_widget_set_parent(popover, parent); - gtk_popover_set_has_arrow(GTK_POPOVER(popover), FALSE); - gtk_popover_set_position(GTK_POPOVER(popover), GTK_POS_BOTTOM); - gtk_widget_add_css_class(popover, "menu"); - - box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); - gtk_popover_set_child(GTK_POPOVER(popover), box); - - mode = get_menu_mode_flag(); - - for (child = menu->children; child != NULL; child = child->next) - { - GtkWidget *item; - char_u *label; - popup_item_data_T *cb_data; - - if (menu_is_separator(child->name)) - { - item = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL); - gtk_box_append(GTK_BOX(box), item); - continue; - } - - label = CONVERT_TO_UTF8(child->dname); - item = gtk_button_new_with_mnemonic( - label != NULL ? (const char *)label : ""); - CONVERT_TO_UTF8_FREE(label); - - gtk_widget_add_css_class(item, "flat"); - gtk_widget_add_css_class(item, "model"); - gtk_button_set_has_frame(GTK_BUTTON(item), FALSE); - gtk_widget_set_halign(item, GTK_ALIGN_FILL); - { - GtkWidget *btn_label = gtk_button_get_child(GTK_BUTTON(item)); - if (GTK_IS_LABEL(btn_label)) - gtk_label_set_xalign(GTK_LABEL(btn_label), 0.0); - } - - if (!(child->modes & child->enabled & mode)) - gtk_widget_set_sensitive(item, FALSE); - - cb_data = g_new0(popup_item_data_T, 1); - cb_data->popover = GTK_POPOVER(popover); - cb_data->menu = child; - g_signal_connect_data(item, "clicked", - G_CALLBACK(popup_item_clicked_cb), - cb_data, popup_item_data_free, 0); - - gtk_box_append(GTK_BOX(box), item); - } - - if (!query_pointer_pos(&rect.x, &rect.y)) - { - rect.x = 0; - rect.y = 0; - } - // GtkPopover with GTK_POS_BOTTOM centres horizontally on the pointing-to - // rectangle. Use the box's natural width so the popover's left edge ends - // up at the cursor (down-and-to-the-right of the pointer). - gtk_widget_measure(box, GTK_ORIENTATION_HORIZONTAL, -1, - NULL, &natural_width, NULL, NULL); - rect.width = natural_width > 0 ? natural_width : 1; - rect.height = 1; - gtk_popover_set_pointing_to(GTK_POPOVER(popover), &rect); + int x, y; - g_signal_connect(popover, "closed", - G_CALLBACK(popupmenu_closed_cb), NULL); - gtk_popover_popup(GTK_POPOVER(popover)); + gui_mch_getmouse(&x, &y); + gui_gtk_popup_at(menu, x, y); } static void diff --git a/src/version.c b/src/version.c index 6d7077fe9d..208e7164d3 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 584, /**/ 583, /**/ From bcb8dacb6d945e5bd9b1ad42cc79c02fdea317ff Mon Sep 17 00:00:00 2001 From: Borys Lykah Date: Mon, 1 Jun 2026 20:36:45 +0000 Subject: [PATCH 386/545] runtime(sgf): Include sgf syntax script closes: #20380 Signed-off-by: Borys Lykah Signed-off-by: Doug Kearns Signed-off-by: Christian Brabandt --- .github/MAINTAINERS | 1 + runtime/syntax/sgf.vim | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 runtime/syntax/sgf.vim diff --git a/.github/MAINTAINERS b/.github/MAINTAINERS index 58a8f94562..fb7ebddf0c 100644 --- a/.github/MAINTAINERS +++ b/.github/MAINTAINERS @@ -678,6 +678,7 @@ runtime/syntax/scala.vim @derekwyatt runtime/syntax/scheme.vim @evhan runtime/syntax/scss.vim @tpope runtime/syntax/sed.vim @dkearns +runtime/syntax/sgf.vim @lykahb runtime/syntax/shaderslang.vim @mTvare6 runtime/syntax/shared/debversions.vim @jamessan runtime/syntax/shared/hgcommitDiff.vim @vegerot diff --git a/runtime/syntax/sgf.vim b/runtime/syntax/sgf.vim new file mode 100644 index 0000000000..48ee1ba89e --- /dev/null +++ b/runtime/syntax/sgf.vim @@ -0,0 +1,41 @@ +" Vim syntax file +" Language: Smart Game Format +" Maintainer: Borys Lykah +" Last Change: 2026 May 30 + +" Quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +syn match sgfDelimiter "[();]" + +syn keyword sgfMoveProp B W nextgroup=sgfValue skipwhite +syn keyword sgfSetupProp AB AE AW PL nextgroup=sgfValue skipwhite +syn keyword sgfMarkupProp AR CR DD DM FG GB GW HO LB LN MA SL SQ TR UC VW nextgroup=sgfValue skipwhite +syn keyword sgfRootProp AP CA FF GM ST SZ nextgroup=sgfValue skipwhite +syn keyword sgfGameInfoProp AN BR BT CP DT EV GC GN HA KM ON OT PB PC PW RE RO RU SO TM US WR WT nextgroup=sgfValue skipwhite +syn keyword sgfCommentProp C nextgroup=sgfCommentValue skipwhite + +syn match sgfProperty "\<[A-Za-z]\+\ze\s*\[" nextgroup=sgfValue skipwhite + +syn match sgfEscape "\\." contained +syn region sgfValue contained matchgroup=sgfBracket start="\[" end="\]" skip="\\\\\|\\\]" contains=sgfEscape keepend nextgroup=sgfValue skipwhite +syn region sgfCommentValue contained matchgroup=sgfBracket start="\[" end="\]" skip="\\\\\|\\\]" contains=sgfEscape,@Spell keepend nextgroup=sgfCommentValue skipwhite + +hi def link sgfDelimiter Delimiter +hi def link sgfMoveProp Statement +hi def link sgfSetupProp Type +hi def link sgfMarkupProp Identifier +hi def link sgfRootProp PreProc +hi def link sgfGameInfoProp Label +hi def link sgfCommentProp Comment +hi def link sgfProperty Identifier +hi def link sgfBracket Delimiter +hi def link sgfEscape SpecialChar +hi def link sgfValue String +hi def link sgfCommentValue Comment + +let b:current_syntax = "sgf" + +" vim: ts=8 From 64982d6010c3610e7f44190f4eb128d6f0f2b91a Mon Sep 17 00:00:00 2001 From: glepnir Date: Mon, 1 Jun 2026 20:43:56 +0000 Subject: [PATCH 387/545] patch 9.2.0585: line number wrong after undoing a deletion in quickfix buffer Problem: Deleting a quickfix line and undoing it leaves the entry pointing one line below where it should. Solution: Don't shift already cleared entries in qf_mark_adjust. closes: #20379 Signed-off-by: glepnir Signed-off-by: Christian Brabandt --- src/quickfix.c | 3 +++ src/testdir/test_quickfix.vim | 47 +++++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 52 insertions(+) diff --git a/src/quickfix.c b/src/quickfix.c index 2470902fd1..0c7a9c9a52 100644 --- a/src/quickfix.c +++ b/src/quickfix.c @@ -4434,6 +4434,9 @@ qf_mark_adjust( if (qfp->qf_fnum == curbuf->b_fnum) { found_one = TRUE; + if (qfp->qf_cleared) + continue; + if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2) { if (amount == MAXLNUM) diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim index b0c4475ccd..6ffc1828a9 100644 --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -2334,6 +2334,53 @@ func Test_adjust_lnum() call Xadjust_qflnum('l') endfunc +func Xqf_undo_after_delete(cchar) + call s:setup_commands(a:cchar) + + enew | only + + let fname = 'Xqfundofile' . a:cchar + call s:create_test_file(fname) + exe 'edit ' . fname + + Xgetexpr [fname . ':5:Line5', + \ fname . ':10:Line10', + \ fname . ':15:Line15'] + + " Delete the line of the second error and undo the deletion. + 10delete + let l = g:Xgetlist() + call assert_equal(5, l[0].lnum) + call assert_equal(10, l[1].lnum) + call assert_equal(14, l[2].lnum) + + undo + call assert_equal('Line10', getline(10)) + let l = g:Xgetlist() + call assert_equal(5, l[0].lnum) + call assert_equal(10, l[1].lnum) + call assert_equal(15, l[2].lnum) + + " Redo and undo again to make sure the line number does not drift. + redo + call assert_equal(14, g:Xgetlist()[2].lnum) + undo + let l = g:Xgetlist() + call assert_equal(10, l[1].lnum) + call assert_equal(15, l[2].lnum) + + call g:Xsetlist([], 'f') + enew! + call delete(fname) +endfunc + +func Test_qf_undo_after_delete() + call setloclist(0, []) + call Xqf_undo_after_delete('c') + call setqflist([]) + call Xqf_undo_after_delete('l') +endfunc + " Tests for the :grep/:lgrep and :grepadd/:lgrepadd commands func s:test_xgrep(cchar) call s:setup_commands(a:cchar) diff --git a/src/version.c b/src/version.c index 208e7164d3..be7bb521b4 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 585, /**/ 584, /**/ From 77b2376769a3f6b0dc21467a56986f3725fc270d Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 1 Jun 2026 21:04:08 +0000 Subject: [PATCH 388/545] runtime(c): classify type qualifiers, function specifiers and C23 attributes Move const, volatile, restrict and _Atomic to a new cTypeQualifier group and inline and _Noreturn to cFunctionSpec. Add the C23 standard attributes deprecated, fallthrough, maybe_unused, nodiscard, unsequenced and reproducible as cStandardAttribute, and reclassify the existing noreturn into the same group. The new groups link to cStorageClass, so the default highlighting and any existing cStorageClass override are unchanged, while allowing finer-grained customization. fixes: #19574 closes: #20368 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/syntax/c.vim | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim index 64bcd3e368..6838109520 100644 --- a/runtime/syntax/c.vim +++ b/runtime/syntax/c.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: C " Maintainer: The Vim Project -" Last Change: 2026 Jan 13 +" Last Change: 2026 Jun 01 " Former Maintainer: Bram Moolenaar " Quit when a (custom) syntax file was already loaded @@ -278,9 +278,9 @@ if exists("c_gnu") syn keyword cOperator __alignof__ syn keyword cOperator typeof __typeof__ syn keyword cOperator __real__ __imag__ - syn keyword cStorageClass __attribute__ __const__ __extension__ - syn keyword cStorageClass inline __inline __inline__ - syn keyword cStorageClass __restrict__ __volatile__ __noreturn__ + syn keyword cStorageClass __attribute__ __extension__ + syn keyword cTypeQualifier __const__ __restrict__ __volatile__ + syn keyword cFunctionSpec inline __inline __inline__ __noreturn__ endif syn keyword cType int long short char void syn keyword cType signed unsigned float double @@ -314,9 +314,11 @@ endif syn keyword cTypedef typedef syn keyword cStructure struct union enum -syn keyword cStorageClass static register auto volatile extern const +syn keyword cStorageClass static register auto extern +syn keyword cTypeQualifier const volatile if !exists("c_no_c99") && !s:in_cpp_family - syn keyword cStorageClass inline restrict + syn keyword cFunctionSpec inline + syn keyword cTypeQualifier restrict endif if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp11")) syn keyword cStorageClass constexpr @@ -324,11 +326,11 @@ endif if !exists("c_no_c11") syn keyword cStorageClass _Alignas alignas syn keyword cOperator _Alignof alignof - syn keyword cStorageClass _Atomic + syn keyword cTypeQualifier _Atomic syn keyword cOperator _Generic - syn keyword cStorageClass _Noreturn + syn keyword cFunctionSpec _Noreturn if !s:in_cpp_family - syn keyword cStorageClass noreturn + syn keyword cStandardAttribute noreturn endif syn keyword cOperator _Static_assert static_assert syn keyword cStorageClass _Thread_local thread_local @@ -352,6 +354,11 @@ if !exists("c_no_c11") syn keyword cType atomic_intmax_t atomic_uintmax_t endif +if !exists("c_no_c23") && !s:in_cpp_family + syn keyword cStandardAttribute deprecated fallthrough maybe_unused nodiscard + syn keyword cStandardAttribute unsequenced reproducible +endif + if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp20")) syn keyword cType char8_t endif @@ -606,6 +613,9 @@ hi def link cOperator Operator hi def link cStructure Structure hi def link cTypedef Structure hi def link cStorageClass StorageClass +hi def link cTypeQualifier cStorageClass +hi def link cFunctionSpec cStorageClass +hi def link cStandardAttribute cStorageClass hi def link cInclude Include hi def link cPreProc PreProc hi def link cDefine Macro From 2e7833bde99295be6da67d75c16d2185712ffc3a Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Mon, 1 Jun 2026 21:08:20 +0000 Subject: [PATCH 389/545] patch 9.2.0586: Crash with TextPut autocmd when pasting in terminal buffer Problem: Crash with TextPut autocmd when pasting in normal mode in a terminal buffer. Solution: Skip the TextPut autocmds when reg and insert are both NULL and regname is not '.' (Foxe Chen). closes: #20407 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/register.c | 6 +++++- src/testdir/test_autocmd.vim | 28 ++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/register.c b/src/register.c index 9daa7729e8..358fe6454f 100644 --- a/src/register.c +++ b/src/register.c @@ -1190,6 +1190,11 @@ put_do_autocmd( if (recursive || regname == '_') return; + if (regname != '.' && insert == NULL + && reg == NULL) + // Can happen when pasting text in normal mode in a terminal buffer + return; + v_event = get_v_event(&save_v_event); list = list_alloc(); @@ -1219,7 +1224,6 @@ put_do_autocmd( } else { - assert(reg != NULL); for (n = 0; n < reg->y_size; n++) list_append_string(list, reg->y_array[n].string, (int)reg->y_array[n].length); diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim index a1f282e06d..54116a5857 100644 --- a/src/testdir/test_autocmd.vim +++ b/src/testdir/test_autocmd.vim @@ -6094,7 +6094,35 @@ func Test_TextPutX() unlet g:post_event unlet g:pre_event bwipe! +endfunc + +" Test that attempting to put text in normal mode terminal buffer does not +" result in a crash. This only happens when terminal is only window in tabpage +" it seems. +func Test_TextPutPost_term_norm() + CheckFeature terminal + + tabnew + term ++curwin + + let bnr = bufnr('$') + call WaitForAssert({-> assert_equal('running', term_getstatus(bnr))}) + + call feedkeys("\\", 'xt') + call WaitForAssert({-> assert_equal('running,normal', term_getstatus(bnr))}) + let err = 0 + + try + call feedkeys("p", 'xt') + catch /^Vim\%((\S\+)\)\=:E21:/ + let err = 1 + endtry + + call assert_true(err) + + unlet err + bwipe! endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index be7bb521b4..e020aedc51 100644 --- a/src/version.c +++ b/src/version.c @@ -729,6 +729,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 586, /**/ 585, /**/ From fd30a736cc4acbefe3a1b05e6051f8df13aa6b2b Mon Sep 17 00:00:00 2001 From: Yasuhiro Matsumoto Date: Mon, 1 Jun 2026 21:15:41 +0000 Subject: [PATCH 390/545] runtime(cpp): recognize C++23 stdfloat types Add float16_t, float32_t, float64_t, float128_t and bfloat16_t from as cppType under a new cpp_no_cpp23 guard. fixes: #16498 closes: #20367 Signed-off-by: Yasuhiro Matsumoto Signed-off-by: Christian Brabandt --- runtime/doc/syntax.txt | 3 ++- runtime/syntax/cpp.vim | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index ca5daf1b73..e668de970b 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -1,4 +1,4 @@ -*syntax.txt* For Vim version 9.2. Last change: 2026 May 23 +*syntax.txt* For Vim version 9.2. Last change: 2026 Jun 01 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1260,6 +1260,7 @@ cpp_no_cpp11 don't highlight C++11 standard items cpp_no_cpp14 don't highlight C++14 standard items cpp_no_cpp17 don't highlight C++17 standard items cpp_no_cpp20 don't highlight C++20 standard items +cpp_no_cpp23 don't highlight C++23 standard items CSH *csh.vim* *ft-csh-syntax* diff --git a/runtime/syntax/cpp.vim b/runtime/syntax/cpp.vim index 5ea52ec502..1c725a4bfd 100644 --- a/runtime/syntax/cpp.vim +++ b/runtime/syntax/cpp.vim @@ -7,6 +7,7 @@ " 2024 May 04 by Vim Project fix digit separator in octals and floats " 2026 Jan 06 by Vim Project orphaning announcement " 2026 Jan 08 by Vim Project highlight capital letter prefixes for numbers +" 2026 May 29 by Vim Project add C++23 stdfloat types (#16498) " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -104,6 +105,11 @@ if !exists("cpp_no_cpp20") syn keyword cppModule import module export endif +" C++ 23 extensions +if !exists("cpp_no_cpp23") + syn keyword cppType float16_t float32_t float64_t float128_t bfloat16_t +endif + " The minimum and maximum operators in GNU C++ syn match cppMinMax "[<>]?" From de18ef628460b1cd3ae2aca9cd19b9b1c319e60c Mon Sep 17 00:00:00 2001 From: Hirohito Higashi Date: Mon, 1 Jun 2026 21:25:04 +0000 Subject: [PATCH 391/545] runtime(doc): Tweak documentation style closes: #20412 Signed-off-by: Hirohito Higashi Signed-off-by: Christian Brabandt --- runtime/doc/gui.txt | 6 ++--- runtime/doc/insert.txt | 27 ++++++++++++----------- runtime/doc/pi_netrw.txt | 2 +- runtime/doc/starting.txt | 4 ++-- runtime/doc/usr_20.txt | 4 +++- runtime/pack/dist/opt/netrw/doc/netrw.txt | 2 +- 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 3a9feac715..5edf1abcb1 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -1,4 +1,4 @@ -*gui.txt* For Vim version 9.2. Last change: 2026 May 25 +*gui.txt* For Vim version 9.2. Last change: 2026 Jun 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1260,8 +1260,8 @@ Full Screen *gui-fullscreen* Fullscreen mode is currently only supported in the Windows and GTK GUI versions of Vim. -To enable fullscreen mode in the GUI version of Vim, add the 's' flag -to the 'guioptions' setting. +To enable fullscreen mode in the GUI version of Vim, add the "s" flag to the +'guioptions' setting. For convenience, you can define a command or mapping to toggle fullscreen mode: diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 68a6c64766..13f2d89a2d 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1,4 +1,4 @@ -*insert.txt* For Vim version 9.2. Last change: 2026 Feb 14 +*insert.txt* For Vim version 9.2. Last change: 2026 Jun 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1813,14 +1813,15 @@ for the syntax items. For example, in the Scheme language completion should include the "-", call-with-output-file. Depending on your filetype, this may not provide the words you are expecting. Setting the g:omni_syntax_use_iskeyword option to 0 will force the syntax plugin to break -on word characters. This can be controlled adding the following to your +on word characters. This can be controlled adding the following to your vimrc: > let g:omni_syntax_use_iskeyword = 0 -For plugin developers, the plugin exposes a public function OmniSyntaxList. -This function can be used to request a List of syntax items. When editing a -SQL file (:e syntax.sql) you can use the ":syntax list" command to see the -various groups and syntax items. For example: > +For plugin developers, the plugin exposes a public function +syntaxcomplete#OmniSyntaxList. This function can be used to request a List of +syntax items. When editing a SQL file (:e syntax.sql) you can use the +":syntax list" command to see the various groups and syntax items. For +example: > syntax list Yields data similar to this: @@ -1834,22 +1835,22 @@ Yields data similar to this: image float integer timestamp real decimal ~ There are two syntax groups listed here: sqlOperator and sqlType. To retrieve -a List of syntax items you can call OmniSyntaxList a number of different -ways. To retrieve all syntax items regardless of syntax group: > - echo OmniSyntaxList( [] ) +a List of syntax items you can call syntaxcomplete#OmniSyntaxList a number of +different ways. To retrieve all syntax items regardless of syntax group: > + echo syntaxcomplete#OmniSyntaxList( [] ) To retrieve only the syntax items for the sqlOperator syntax group: > - echo OmniSyntaxList( ['sqlOperator'] ) + echo syntaxcomplete#OmniSyntaxList( ['sqlOperator'] ) To retrieve all syntax items for both the sqlOperator and sqlType groups: > - echo OmniSyntaxList( ['sqlOperator', 'sqlType'] ) + echo syntaxcomplete#OmniSyntaxList( ['sqlOperator', 'sqlType'] ) A regular expression can also be used: > - echo OmniSyntaxList( ['sql\w\+'] ) + echo syntaxcomplete#OmniSyntaxList( ['sql\w\+'] ) From within a plugin, you would typically assign the output to a List: > let myKeywords = [] - let myKeywords = OmniSyntaxList( ['sqlKeyword'] ) + let myKeywords = syntaxcomplete#OmniSyntaxList( ['sqlKeyword'] ) SQL *ft-sql-omni* diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index fd9339f1d5..e3c06852cc 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1368,7 +1368,7 @@ Associated setting variables: |g:netrw_chgperm| CHANGING TO A BOOKMARKED PATH *netrw-gb* {{{2 -To change to a bookmarked path, use +To change to a bookmarked path, use > [{cnt}]gb diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index db37cae188..840b065ed9 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1,4 +1,4 @@ -*starting.txt* For Vim version 9.2. Last change: 2026 May 24 +*starting.txt* For Vim version 9.2. Last change: 2026 Jun 02 VIM REFERENCE MANUAL by Bram Moolenaar @@ -646,7 +646,7 @@ a slash. Thus "-R" means recovery and "-/R" readonly. Run Vim on {display}, connecting to that X server instead of the one in $DISPLAY. The "--display" long form is accepted only by the GTK+ 2 or GTK+ 3 GUI, see also |-display|. - {requires the |+xterm_clipboard| feature} + {only available with the |+xterm_clipboard| feature} --role {role} *--role* GTK+ 2 GUI only. Set the role of the main window to {role}. diff --git a/runtime/doc/usr_20.txt b/runtime/doc/usr_20.txt index f4cc5dd64c..5b23ac89d5 100644 --- a/runtime/doc/usr_20.txt +++ b/runtime/doc/usr_20.txt @@ -1,7 +1,9 @@ -*usr_20.txt* For Vim version 9.2. Last change: 2026 May 31 +*usr_20.txt* For Vim version 9.2. Last change: 2026 Jun 02 + VIM USER MANUAL by Bram Moolenaar + Typing command-line commands quickly diff --git a/runtime/pack/dist/opt/netrw/doc/netrw.txt b/runtime/pack/dist/opt/netrw/doc/netrw.txt index 41e61cfad8..6fe5c5ebee 100644 --- a/runtime/pack/dist/opt/netrw/doc/netrw.txt +++ b/runtime/pack/dist/opt/netrw/doc/netrw.txt @@ -1368,7 +1368,7 @@ Associated setting variables: |g:netrw_chgperm| CHANGING TO A BOOKMARKED PATH *netrw-gb* {{{2 -To change to a bookmarked path, use +To change to a bookmarked path, use > [{cnt}]gb From 18c6b91ca94c94db018324cd0f8e768922d39af8 Mon Sep 17 00:00:00 2001 From: nyngwang Date: Tue, 2 Jun 2026 17:35:54 +0000 Subject: [PATCH 392/545] runtime(doc): fix a typo in :write-plugin closes: #20410 Signed-off-by: nyngwang Signed-off-by: Christian Brabandt --- runtime/doc/usr_51.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/doc/usr_51.txt b/runtime/doc/usr_51.txt index 1ed1b6b376..82bb0faf22 100644 --- a/runtime/doc/usr_51.txt +++ b/runtime/doc/usr_51.txt @@ -1,4 +1,4 @@ -*usr_51.txt* For Vim version 9.2. Last change: 2026 May 28 +*usr_51.txt* For Vim version 9.2. Last change: 2026 Jun 02 VIM USER MANUAL by Bram Moolenaar @@ -220,11 +220,11 @@ case only one item is used. When adding more items, creating a submenu is recommended. For example, "Plugin.CVS" could be used for a plugin that offers CVS operations "Plugin.CVS.checkin", "Plugin.CVS.checkout", etc. -Note that in line 28 ":noremap" is used to avoid that any other mappings cause -trouble. Someone may have remapped ":call", for example. In line 24 we also +Note that in line 26 ":noremap" is used to avoid that any other mappings cause +trouble. Someone may have remapped ":call", for example. In line 22 we also use ":noremap", but we do want "Add" to be remapped. This is why "