From f65d861f72f93b9414426320adb55865b04cac78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20Fr=C3=B3es=20Franco?= Date: Sun, 31 May 2026 07:54:20 -0300 Subject: [PATCH] feat(client): add Vim split/tab/back link-follow commands + true tab-drop The plain-Vim client only defined VimwikiFollowLink; the split/vsplit/ tabnew/tabdrop/back command surface existed only in the Neovim branch. Add :Vimwiki{Split,VSplit,Tabnew,TabDrop,GoBack}Link (+ :Nuwiki* aliases) to the Vim branch, backed by a new nuwiki#commands#follow_link_drop() helper that runs `:tab drop` to reuse an already-open tab. Both clients now implement real tab-drop: open_uri gains a 'tabdrop' case and M.follow_link_drop(); the Neovim TabDropLink commands and the mappings (Vim + Lua) are repointed off the old tabnew cheat. Co-Authored-By: Claude Opus 4.7 --- autoload/nuwiki/commands.vim | 41 ++++++++++++++++++++++++++++++++++++ ftplugin/vimwiki.vim | 16 +++++++++++--- lua/nuwiki/commands.lua | 30 ++++++++++++++++++++++++++ lua/nuwiki/keymaps.lua | 4 ++-- 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/autoload/nuwiki/commands.vim b/autoload/nuwiki/commands.vim index 70473c4..4122acf 100644 --- a/autoload/nuwiki/commands.vim +++ b/autoload/nuwiki/commands.vim @@ -188,6 +188,47 @@ function! s:badd_from_response(notification) abort echom 'nuwiki: added ' . l:path endfunction +" `:VimwikiTabDropLink` — open the link target in a tab, reusing an +" existing tab/window if the file is already shown (`:tab drop`). Routes +" through vim-lsp's textDocument/definition like `badd_link`. +function! nuwiki#commands#follow_link_drop() abort + if s:has_vimlsp() + call lsp#send_request(s:server, { + \ 'method': 'textDocument/definition', + \ 'params': s:cursor_position(), + \ 'on_notification': function('s:drop_from_response'), + \ }) + return + endif + if s:has_coc() + let l:locs = CocAction('getDefinition') + if type(l:locs) == type([]) && !empty(l:locs) + let l:uri = get(l:locs[0], 'uri', get(l:locs[0], 'targetUri', '')) + if !empty(l:uri) + let l:path = substitute(l:uri, '^file://', '', '') + execute 'tab drop ' . fnameescape(l:path) + endif + endif + return + endif + echohl ErrorMsg | echom 'nuwiki: no supported LSP client for follow_link_drop' | echohl None +endfunction + +function! s:drop_from_response(notification) abort + let l:result = get(get(a:notification, 'response', {}), 'result', {}) + let l:uri = '' + if type(l:result) == type({}) + let l:uri = get(l:result, 'uri', get(l:result, 'targetUri', '')) + elseif type(l:result) == type([]) && !empty(l:result) + let l:uri = get(l:result[0], 'uri', get(l:result[0], 'targetUri', '')) + endif + if l:uri ==# '' + return + endif + let l:path = substitute(l:uri, '^file://', '', '') + execute 'tab drop ' . fnameescape(l:path) +endfunction + " ===== Smart : follow or wrap-then-follow ===== function! s:cursor_inside_wikilink() abort diff --git a/ftplugin/vimwiki.vim b/ftplugin/vimwiki.vim index dd33141..836243f 100644 --- a/ftplugin/vimwiki.vim +++ b/ftplugin/vimwiki.vim @@ -47,6 +47,11 @@ if !has('nvim') command! -buffer VimwikiDiaryPrevDay call nuwiki#commands#diary_prev() command! -buffer VimwikiDiaryGenerateLinks call nuwiki#commands#diary_generate_index() command! -buffer VimwikiFollowLink call nuwiki#commands#follow_link_or_create() + command! -buffer VimwikiSplitLink split | call nuwiki#commands#follow_link_or_create() + command! -buffer VimwikiVSplitLink vsplit | call nuwiki#commands#follow_link_or_create() + command! -buffer VimwikiTabnewLink tabnew | call nuwiki#commands#follow_link_or_create() + command! -buffer VimwikiTabDropLink call nuwiki#commands#follow_link_drop() + command! -buffer VimwikiGoBackLink execute "normal! \" command! -buffer VimwikiBacklinks call nuwiki#commands#backlinks() command! -buffer VWB call nuwiki#commands#backlinks() command! -buffer VimwikiNextLink call nuwiki#commands#link_next() @@ -113,6 +118,11 @@ if !has('nvim') command! -buffer NuwikiDiaryNext call nuwiki#commands#diary_next() command! -buffer NuwikiDiaryPrev call nuwiki#commands#diary_prev() command! -buffer NuwikiFollowLink call nuwiki#commands#follow_link_or_create() + command! -buffer NuwikiSplitLink split | call nuwiki#commands#follow_link_or_create() + command! -buffer NuwikiVSplitLink vsplit | call nuwiki#commands#follow_link_or_create() + command! -buffer NuwikiTabnewLink tabnew | call nuwiki#commands#follow_link_or_create() + command! -buffer NuwikiTabDropLink call nuwiki#commands#follow_link_drop() + command! -buffer NuwikiGoBackLink execute "normal! \" command! -buffer NuwikiBacklinks call nuwiki#commands#backlinks() command! -buffer NuwikiDeleteFile call nuwiki#commands#delete_file() command! -buffer NuwikiRenameFile call nuwiki#commands#rename_file() @@ -196,7 +206,7 @@ if !has('nvim') nnoremap :call nuwiki#commands#follow_link_or_create() nnoremap :split:call nuwiki#commands#follow_link_or_create() nnoremap :vsplit:call nuwiki#commands#follow_link_or_create() - nnoremap :tabnew:call nuwiki#commands#follow_link_or_create() + nnoremap :call nuwiki#commands#follow_link_drop() nnoremap nnoremap /\[\[:nohlsearch nnoremap ?\[\[:nohlsearch @@ -357,7 +367,7 @@ command! -buffer VimwikiGoBackLink execute "normal! \" command! -buffer VimwikiSplitLink split | lua vim.lsp.buf.definition() command! -buffer VimwikiVSplitLink vsplit | lua vim.lsp.buf.definition() command! -buffer VimwikiTabnewLink tabnew | lua vim.lsp.buf.definition() -command! -buffer VimwikiTabDropLink tabnew | lua vim.lsp.buf.definition() +command! -buffer VimwikiTabDropLink lua require('nuwiki.commands').follow_link_drop() command! -buffer VimwikiNextLink lua require('nuwiki.commands').link_next() command! -buffer VimwikiPrevLink lua require('nuwiki.commands').link_prev() command! -buffer VimwikiBaddLink lua require('nuwiki.commands').badd_link() @@ -465,7 +475,7 @@ command! -buffer NuwikiGoBackLink execute "normal! \" command! -buffer NuwikiSplitLink split | lua vim.lsp.buf.definition() command! -buffer NuwikiVSplitLink vsplit | lua vim.lsp.buf.definition() command! -buffer NuwikiTabnewLink tabnew | lua vim.lsp.buf.definition() -command! -buffer NuwikiTabDropLink tabnew | lua vim.lsp.buf.definition() +command! -buffer NuwikiTabDropLink lua require('nuwiki.commands').follow_link_drop() command! -buffer NuwikiRemoveDone lua require('nuwiki.commands').list_remove_done() command! -buffer -range NuwikiRemoveCheckbox lua require('nuwiki.commands').list_remove_checkbox() command! -buffer NuwikiRemoveCheckboxInList lua require('nuwiki.commands').list_remove_checkbox_in_list() diff --git a/lua/nuwiki/commands.lua b/lua/nuwiki/commands.lua index df101a1..607ca66 100644 --- a/lua/nuwiki/commands.lua +++ b/lua/nuwiki/commands.lua @@ -73,6 +73,10 @@ local function open_uri(uri, tab) return end local path = vim.uri_to_fname(uri) + if tab == 'tabdrop' then + vim.cmd('tab drop ' .. vim.fn.fnameescape(path)) + return + end local cmd if tab == 'split' then cmd = 'split' @@ -206,6 +210,32 @@ function M.badd_link() end end +--- `:VimwikiTabDropLink` — open the link target in a tab, reusing an +--- existing tab/window if the file is already shown (`:tab drop`). Uses +--- the LSP definition response to resolve the target file. +function M.follow_link_drop() + local client = find_client() + if not client then + vim.notify('nuwiki: language server not attached', vim.log.levels.ERROR) + return + end + local params = position_params(client) + params.textDocument = { uri = buf_uri() } + local req = client.request + local handler = function(_, result) + local loc + if type(result) == 'table' then + loc = result[1] or result + end + local uri = type(loc) == 'table' and (loc.uri or loc.targetUri) or nil + open_uri(uri, 'tabdrop') + end + local bufnr = vim.api.nvim_get_current_buf() + if type(req) == 'function' then + client:request('textDocument/definition', params, handler, bufnr) + end +end + function M.follow_link_or_create() if cursor_inside_wikilink() then vim.lsp.buf.definition() diff --git a/lua/nuwiki/keymaps.lua b/lua/nuwiki/keymaps.lua index 1b1c8fb..431688b 100644 --- a/lua/nuwiki/keymaps.lua +++ b/lua/nuwiki/keymaps.lua @@ -164,8 +164,8 @@ function M.attach(bufnr, mappings) { desc = 'nuwiki: follow link in split' }, bufnr) map('n', '', function() vim.cmd('vsplit'); cmd.follow_link_or_create() end, { desc = 'nuwiki: follow link in vsplit' }, bufnr) - map('n', '', function() vim.cmd('tabnew'); cmd.follow_link_or_create() end, - { desc = 'nuwiki: follow link in new tab' }, bufnr) + map('n', '', cmd.follow_link_drop, + { desc = 'nuwiki: follow link in tab (reuse existing)' }, bufnr) map('n', '', '', { desc = 'nuwiki: go back', remap = true }, bufnr) map('n', '', link.next, { desc = 'nuwiki: next wikilink' }, bufnr) map('n', '', link.prev, { desc = 'nuwiki: prev wikilink' }, bufnr)