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
<C-S-CR> mappings (Vim + Lua) are repointed off the old tabnew cheat.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-31 07:54:20 -03:00
parent 00a671e486
commit f65d861f72
4 changed files with 86 additions and 5 deletions
+41
View File
@@ -188,6 +188,47 @@ function! s:badd_from_response(notification) abort
echom 'nuwiki: added ' . l:path echom 'nuwiki: added ' . l:path
endfunction 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 <CR>: follow or wrap-then-follow ===== " ===== Smart <CR>: follow or wrap-then-follow =====
function! s:cursor_inside_wikilink() abort function! s:cursor_inside_wikilink() abort
+13 -3
View File
@@ -47,6 +47,11 @@ if !has('nvim')
command! -buffer VimwikiDiaryPrevDay call nuwiki#commands#diary_prev() command! -buffer VimwikiDiaryPrevDay call nuwiki#commands#diary_prev()
command! -buffer VimwikiDiaryGenerateLinks call nuwiki#commands#diary_generate_index() command! -buffer VimwikiDiaryGenerateLinks call nuwiki#commands#diary_generate_index()
command! -buffer VimwikiFollowLink call nuwiki#commands#follow_link_or_create() 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! \<C-o>"
command! -buffer VimwikiBacklinks call nuwiki#commands#backlinks() command! -buffer VimwikiBacklinks call nuwiki#commands#backlinks()
command! -buffer VWB call nuwiki#commands#backlinks() command! -buffer VWB call nuwiki#commands#backlinks()
command! -buffer VimwikiNextLink call nuwiki#commands#link_next() 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 NuwikiDiaryNext call nuwiki#commands#diary_next()
command! -buffer NuwikiDiaryPrev call nuwiki#commands#diary_prev() command! -buffer NuwikiDiaryPrev call nuwiki#commands#diary_prev()
command! -buffer NuwikiFollowLink call nuwiki#commands#follow_link_or_create() 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! \<C-o>"
command! -buffer NuwikiBacklinks call nuwiki#commands#backlinks() command! -buffer NuwikiBacklinks call nuwiki#commands#backlinks()
command! -buffer NuwikiDeleteFile call nuwiki#commands#delete_file() command! -buffer NuwikiDeleteFile call nuwiki#commands#delete_file()
command! -buffer NuwikiRenameFile call nuwiki#commands#rename_file() command! -buffer NuwikiRenameFile call nuwiki#commands#rename_file()
@@ -196,7 +206,7 @@ if !has('nvim')
nnoremap <silent><buffer> <CR> :call nuwiki#commands#follow_link_or_create()<CR> nnoremap <silent><buffer> <CR> :call nuwiki#commands#follow_link_or_create()<CR>
nnoremap <silent><buffer> <S-CR> :split<CR>:call nuwiki#commands#follow_link_or_create()<CR> nnoremap <silent><buffer> <S-CR> :split<CR>:call nuwiki#commands#follow_link_or_create()<CR>
nnoremap <silent><buffer> <C-CR> :vsplit<CR>:call nuwiki#commands#follow_link_or_create()<CR> nnoremap <silent><buffer> <C-CR> :vsplit<CR>:call nuwiki#commands#follow_link_or_create()<CR>
nnoremap <silent><buffer> <C-S-CR> :tabnew<CR>:call nuwiki#commands#follow_link_or_create()<CR> nnoremap <silent><buffer> <C-S-CR> :call nuwiki#commands#follow_link_drop()<CR>
nnoremap <silent><buffer> <BS> <C-o> nnoremap <silent><buffer> <BS> <C-o>
nnoremap <silent><buffer> <Tab> /\[\[<CR>:nohlsearch<CR> nnoremap <silent><buffer> <Tab> /\[\[<CR>:nohlsearch<CR>
nnoremap <silent><buffer> <S-Tab> ?\[\[<CR>:nohlsearch<CR> nnoremap <silent><buffer> <S-Tab> ?\[\[<CR>:nohlsearch<CR>
@@ -357,7 +367,7 @@ command! -buffer VimwikiGoBackLink execute "normal! \<C-o>"
command! -buffer VimwikiSplitLink split | lua vim.lsp.buf.definition() command! -buffer VimwikiSplitLink split | lua vim.lsp.buf.definition()
command! -buffer VimwikiVSplitLink vsplit | 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 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 VimwikiNextLink lua require('nuwiki.commands').link_next()
command! -buffer VimwikiPrevLink lua require('nuwiki.commands').link_prev() command! -buffer VimwikiPrevLink lua require('nuwiki.commands').link_prev()
command! -buffer VimwikiBaddLink lua require('nuwiki.commands').badd_link() command! -buffer VimwikiBaddLink lua require('nuwiki.commands').badd_link()
@@ -465,7 +475,7 @@ command! -buffer NuwikiGoBackLink execute "normal! \<C-o>"
command! -buffer NuwikiSplitLink split | lua vim.lsp.buf.definition() command! -buffer NuwikiSplitLink split | lua vim.lsp.buf.definition()
command! -buffer NuwikiVSplitLink vsplit | 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 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 NuwikiRemoveDone lua require('nuwiki.commands').list_remove_done()
command! -buffer -range NuwikiRemoveCheckbox lua require('nuwiki.commands').list_remove_checkbox() command! -buffer -range NuwikiRemoveCheckbox lua require('nuwiki.commands').list_remove_checkbox()
command! -buffer NuwikiRemoveCheckboxInList lua require('nuwiki.commands').list_remove_checkbox_in_list() command! -buffer NuwikiRemoveCheckboxInList lua require('nuwiki.commands').list_remove_checkbox_in_list()
+30
View File
@@ -73,6 +73,10 @@ local function open_uri(uri, tab)
return return
end end
local path = vim.uri_to_fname(uri) local path = vim.uri_to_fname(uri)
if tab == 'tabdrop' then
vim.cmd('tab drop ' .. vim.fn.fnameescape(path))
return
end
local cmd local cmd
if tab == 'split' then if tab == 'split' then
cmd = 'split' cmd = 'split'
@@ -206,6 +210,32 @@ function M.badd_link()
end end
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() function M.follow_link_or_create()
if cursor_inside_wikilink() then if cursor_inside_wikilink() then
vim.lsp.buf.definition() vim.lsp.buf.definition()
+2 -2
View File
@@ -164,8 +164,8 @@ function M.attach(bufnr, mappings)
{ desc = 'nuwiki: follow link in split' }, bufnr) { desc = 'nuwiki: follow link in split' }, bufnr)
map('n', '<C-CR>', function() vim.cmd('vsplit'); cmd.follow_link_or_create() end, map('n', '<C-CR>', function() vim.cmd('vsplit'); cmd.follow_link_or_create() end,
{ desc = 'nuwiki: follow link in vsplit' }, bufnr) { desc = 'nuwiki: follow link in vsplit' }, bufnr)
map('n', '<C-S-CR>', function() vim.cmd('tabnew'); cmd.follow_link_or_create() end, map('n', '<C-S-CR>', cmd.follow_link_drop,
{ desc = 'nuwiki: follow link in new tab' }, bufnr) { desc = 'nuwiki: follow link in tab (reuse existing)' }, bufnr)
map('n', '<BS>', '<C-o>', { desc = 'nuwiki: go back', remap = true }, bufnr) map('n', '<BS>', '<C-o>', { desc = 'nuwiki: go back', remap = true }, bufnr)
map('n', '<Tab>', link.next, { desc = 'nuwiki: next wikilink' }, bufnr) map('n', '<Tab>', link.next, { desc = 'nuwiki: next wikilink' }, bufnr)
map('n', '<S-Tab>', link.prev, { desc = 'nuwiki: prev wikilink' }, bufnr) map('n', '<S-Tab>', link.prev, { desc = 'nuwiki: prev wikilink' }, bufnr)