From 3bc21c09034d763972d23969d5486053b0baae86 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Wed, 20 May 2026 09:12:12 +0800 Subject: [PATCH] vim-patch:9.2.0502: runtime(netrw): bookmark handling can be improved (#39900) 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: vim/vim#20211 https://github.com/vim/vim/commit/35b767a090cc7b918dcafc9feb1a78cf0938d6bc Co-authored-by: J. Paulo Seibt --- .../pack/dist/opt/netrw/autoload/netrw.vim | 68 +++++++++++++++---- runtime/pack/dist/opt/netrw/doc/netrw.txt | 19 ++++-- test/old/testdir/test_plugin_netrw.vim | 44 +++++++++++- 3 files changed, 111 insertions(+), 20 deletions(-) diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim index e82e695e18..abfa167a5d 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 b69dc926f0..117be4c8a8 100644 --- a/runtime/pack/dist/opt/netrw/doc/netrw.txt +++ b/runtime/pack/dist/opt/netrw/doc/netrw.txt @@ -1046,7 +1046,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| @@ -1055,11 +1055,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| @@ -1364,13 +1365,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|. @@ -1433,10 +1437,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/test/old/testdir/test_plugin_netrw.vim b/test/old/testdir/test_plugin_netrw.vim index 0d206b55cc..0a3fb33ba3 100644 --- a/test/old/testdir/test_plugin_netrw.vim +++ b/test/old/testdir/test_plugin_netrw.vim @@ -337,7 +337,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 @@ -724,7 +726,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 @@ -787,4 +826,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