Commit: patch 9.2.0502: runtime(netrw): bookmark handling can be improved

1 view
Skip to first unread message

Christian Brabandt

unread,
May 19, 2026, 3:00:15 PM (11 hours ago) May 19
to vim...@googlegroups.com
patch 9.2.0502: runtime(netrw): bookmark handling can be improved

Commit: https://github.com/vim/vim/commit/35b767a090cc7b918dcafc9feb1a78cf0938d6bc
Author: J. Paulo Seibt <jps...@gmail.com>
Date: Tue May 19 18:51:14 2026 +0000

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 bookmarkhttps://github.com/vim/vim/issues/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 <jps...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt
index 3f744bc8f..6f20073d5 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 1fdbeeb8f..89db6e3ca 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: <mb>) bookmark current directory
-" 1: (user: <gb>) change to the bookmarked directory
+" 1: (user: <gb>) change to the bookmarked path
" 2: (user: <qb>) list bookmarks
" 3: (browsing) records current directory history
" 4: (user: <u>) 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 168deed34..2c5d6d822 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 7cd2aa89e..76fa41092 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 e6bc4886c..2e75367bf 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,
/**/
Reply all
Reply to author
Forward
0 new messages