From: J. Paulo Seibt Date: Tue, 5 May 2026 20:06:15 +0000 (+0000) Subject: patch 9.2.0446: runtime(netrw): off-by-one bug in s:NetrwUnMarkFile() X-Git-Tag: v9.2.0446^0 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7ccc273a4cb6012e5afbdb94d0f2597bc01fe2fd;p=thirdparty%2Fvim.git 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 --- 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, /**/