fix(follow-link): resolve+open definitions ourselves on the Vim clients
The Vim follow-link path delegated to the LSP client's jump UI
(:LspDefinition / coc jumpDefinition), which broke two ways for wiki
link-following:
1. **No create-on-follow (vim-lsp + coc).** vim-lsp's location handler
readfile()s the target to build a quickfix entry; for a not-yet-created
page that throws E484 and aborts the jump, so <CR> on [[NewPage]] did
nothing. The server was correct throughout — textDocument/definition
returns a *synthesised* location for missing pages (verified over
JSON-RPC).
2. **Wrong window placement (coc).** Without an explicit open command coc
fell back to coc.preferences.jumpCommand (e.g. a tab command).
Stop delegating: nuwiki#commands#follow_link_or_create() /
follow_link_drop() now resolve textDocument/definition directly and open
the URI themselves via a shared s:open_definition(open_cmd) helper — :edit
for <CR>, `tab drop` for <C-S-CR>. :edit opens a buffer for a missing path
(save creates it, matching vimwiki) and gives exact current-window
placement regardless of the user's coc jumpCommand. Removes
s:jump_definition / s:drop_from_response.
Also fix the coc action name: CocAction('getDefinition') does not exist
(E605 "Action getDefinition does not exist") — the registered action is
'definitions'. Fixed in s:open_definition and badd_link.
coc config delivery: the setup guidance shipped without
initializationOptions, so coc users never sent wiki_root and the server
resolved links against its default root (existing pages flagged broken,
follow-to-create landed in the cwd). The printed snippet
(autoload/nuwiki/lsp.vim) now emits initializationOptions populated with
the resolved settings, and the README coc snippet documents it as required.
Add development/start-vim-coc.sh — a coc.nvim dev launcher (sibling of
start-vim.sh) that wires coc's prebuilt release branch + a generated
coc-settings.json (with initializationOptions), deliberately omitting
vim-lsp so the coc path is exercised; doubles as a reproducer.
Tests: new cr.follow_opens_missing_page_in_current_window case in
test-keymaps-vim.vim (stubs CocAction to a missing page, asserts
current-window :edit). Keymap suite 254+18 green; config-parity (Vim +
Neovim) green. Neovim path unchanged and re-confirmed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -155,7 +155,12 @@ on your behalf, so add the entry once yourself (open it with
|
|||||||
"languageserver": {
|
"languageserver": {
|
||||||
"nuwiki": {
|
"nuwiki": {
|
||||||
"command": "~/.vim/pack/gffranco/start/nuwiki/bin/nuwiki-ls",
|
"command": "~/.vim/pack/gffranco/start/nuwiki/bin/nuwiki-ls",
|
||||||
"filetypes": ["vimwiki"]
|
"filetypes": ["vimwiki"],
|
||||||
|
"initializationOptions": {
|
||||||
|
"wiki_root": "~/vimwiki",
|
||||||
|
"file_extension": ".wiki",
|
||||||
|
"syntax": "vimwiki"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,9 +168,18 @@ on your behalf, so add the entry once yourself (open it with
|
|||||||
|
|
||||||
Point `command` at the installed binary — by default it lives in the
|
Point `command` at the installed binary — by default it lives in the
|
||||||
plugin's `bin/` directory (adjust the path for your plugin manager), or
|
plugin's `bin/` directory (adjust the path for your plugin manager), or
|
||||||
set `g:nuwiki_binary_path` and reuse that value. With coc you can also
|
set `g:nuwiki_binary_path` and reuse that value.
|
||||||
invoke server commands directly via `:CocCommand nuwiki.<command>`
|
|
||||||
instead of the `:Vimwiki*` / `:Nuwiki*` aliases.
|
`initializationOptions` is **required** — it carries `wiki_root` (and the
|
||||||
|
other [config keys](#configuration)) to the server, exactly as the vim-lsp
|
||||||
|
registration does automatically. Without it the server resolves links
|
||||||
|
against its default root, so existing pages show up as broken links and
|
||||||
|
following a link to a new page creates it in the wrong directory. (Run
|
||||||
|
`:NuwikiInstall`-time guidance, or `:call nuwiki#lsp#start()` once, to have
|
||||||
|
nuwiki print this snippet with your configured paths already filled in.)
|
||||||
|
With coc you can also invoke server commands directly via
|
||||||
|
`:CocCommand nuwiki.<command>` instead of the `:Vimwiki*` / `:Nuwiki*`
|
||||||
|
aliases.
|
||||||
|
|
||||||
### Try it without touching your config
|
### Try it without touching your config
|
||||||
|
|
||||||
|
|||||||
@@ -69,16 +69,66 @@ function! s:exec(command, arguments, ...) abort
|
|||||||
echohl None
|
echohl None
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Shared jump-to-definition helper used by follow_link* and the ftplugin.
|
" Resolve the wikilink under the cursor via textDocument/definition and open
|
||||||
function! s:jump_definition() abort
|
" the target with `a:open_cmd` ('edit', 'split', 'vsplit', or 'tab drop').
|
||||||
if exists(':LspDefinition') == 2
|
"
|
||||||
LspDefinition
|
" We resolve + open the URI ourselves rather than delegating to
|
||||||
elseif s:has_coc()
|
" `:LspDefinition` / coc's `jumpDefinition`. Those drive a jump UI that first
|
||||||
call CocActionAsync('jumpDefinition')
|
" fetches the target's text — vim-lsp `readfile()`s it for the quickfix entry
|
||||||
else
|
" (autoload/lsp/utils/location.vim), which throws E484 for a page that does
|
||||||
echohl WarningMsg
|
" not exist yet and aborts the whole jump. That silently broke "follow link
|
||||||
echom 'nuwiki: no LSP client available for jump-to-definition'
|
" to create the page". `:edit` on a missing path just opens an empty buffer
|
||||||
echohl None
|
" (saving creates the file), matching vimwiki, and gives us exact control over
|
||||||
|
" window placement regardless of the user's coc.preferences.jumpCommand.
|
||||||
|
function! s:open_definition(open_cmd) abort
|
||||||
|
if s:has_vimlsp()
|
||||||
|
call lsp#send_request(s:server, {
|
||||||
|
\ 'method': 'textDocument/definition',
|
||||||
|
\ 'params': s:cursor_position(),
|
||||||
|
\ 'on_notification': function('s:open_from_response', [a:open_cmd]),
|
||||||
|
\ })
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
if s:has_coc()
|
||||||
|
let l:locs = CocAction('definitions')
|
||||||
|
if type(l:locs) == type([]) && !empty(l:locs)
|
||||||
|
call s:open_location(a:open_cmd,
|
||||||
|
\ get(l:locs[0], 'uri', get(l:locs[0], 'targetUri', '')),
|
||||||
|
\ get(l:locs[0], 'range', get(l:locs[0], 'targetSelectionRange', {})))
|
||||||
|
endif
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
echohl WarningMsg
|
||||||
|
echom 'nuwiki: no LSP client available for jump-to-definition'
|
||||||
|
echohl None
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" vim-lsp on_notification callback: pull the URI + range out of the
|
||||||
|
" textDocument/definition response (Location or Location[]) and open it.
|
||||||
|
function! s:open_from_response(open_cmd, notification) abort
|
||||||
|
let l:result = get(get(a:notification, 'response', {}), 'result', {})
|
||||||
|
let l:uri = ''
|
||||||
|
let l:range = {}
|
||||||
|
if type(l:result) == type({})
|
||||||
|
let l:uri = get(l:result, 'uri', get(l:result, 'targetUri', ''))
|
||||||
|
let l:range = get(l:result, 'range', get(l:result, 'targetSelectionRange', {}))
|
||||||
|
elseif type(l:result) == type([]) && !empty(l:result)
|
||||||
|
let l:uri = get(l:result[0], 'uri', get(l:result[0], 'targetUri', ''))
|
||||||
|
let l:range = get(l:result[0], 'range', get(l:result[0], 'targetSelectionRange', {}))
|
||||||
|
endif
|
||||||
|
call s:open_location(a:open_cmd, l:uri, l:range)
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
" Open a resolved definition URI with `a:open_cmd`, then place the cursor at
|
||||||
|
" the response range start (when present).
|
||||||
|
function! s:open_location(open_cmd, uri, range) abort
|
||||||
|
if type(a:uri) != type('') || a:uri ==# ''
|
||||||
|
return
|
||||||
|
endif
|
||||||
|
let l:path = substitute(a:uri, '^file://', '', '')
|
||||||
|
execute a:open_cmd . ' ' . fnameescape(l:path)
|
||||||
|
if type(a:range) == type({}) && has_key(a:range, 'start')
|
||||||
|
call cursor(a:range.start.line + 1, a:range.start.character + 1)
|
||||||
endif
|
endif
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
@@ -158,7 +208,7 @@ function! nuwiki#commands#badd_link() abort
|
|||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
if s:has_coc()
|
if s:has_coc()
|
||||||
let l:locs = CocAction('getDefinition')
|
let l:locs = CocAction('definitions')
|
||||||
if type(l:locs) == type([]) && !empty(l:locs)
|
if type(l:locs) == type([]) && !empty(l:locs)
|
||||||
let l:uri = get(l:locs[0], 'uri', get(l:locs[0], 'targetUri', ''))
|
let l:uri = get(l:locs[0], 'uri', get(l:locs[0], 'targetUri', ''))
|
||||||
if !empty(l:uri)
|
if !empty(l:uri)
|
||||||
@@ -189,44 +239,9 @@ function! s:badd_from_response(notification) abort
|
|||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" `:VimwikiTabDropLink` — open the link target in a tab, reusing an
|
" `:VimwikiTabDropLink` — open the link target in a tab, reusing an
|
||||||
" existing tab/window if the file is already shown (`:tab drop`). Routes
|
" existing tab/window if the file is already shown (`:tab drop`).
|
||||||
" through vim-lsp's textDocument/definition like `badd_link`.
|
|
||||||
function! nuwiki#commands#follow_link_drop() abort
|
function! nuwiki#commands#follow_link_drop() abort
|
||||||
if s:has_vimlsp()
|
call s:open_definition('tab drop')
|
||||||
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
|
endfunction
|
||||||
|
|
||||||
" ===== Smart <CR>: follow or wrap-then-follow =====
|
" ===== Smart <CR>: follow or wrap-then-follow =====
|
||||||
@@ -285,7 +300,7 @@ endfunction
|
|||||||
" 3. Press inside an existing `[[…]]` follows immediately.
|
" 3. Press inside an existing `[[…]]` follows immediately.
|
||||||
function! nuwiki#commands#follow_link_or_create() abort
|
function! nuwiki#commands#follow_link_or_create() abort
|
||||||
if s:cursor_inside_wikilink()
|
if s:cursor_inside_wikilink()
|
||||||
call s:jump_definition()
|
call s:open_definition('edit')
|
||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
if s:wrap_cword_as_wikilink()
|
if s:wrap_cword_as_wikilink()
|
||||||
@@ -293,7 +308,7 @@ function! nuwiki#commands#follow_link_or_create() abort
|
|||||||
return
|
return
|
||||||
endif
|
endif
|
||||||
" Nothing to wrap — fall through to plain definition.
|
" Nothing to wrap — fall through to plain definition.
|
||||||
call s:jump_definition()
|
call s:open_definition('edit')
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
" Pure-VimL bullet-continuation for `o` / `O` — mirrors the Lua side
|
" Pure-VimL bullet-continuation for `o` / `O` — mirrors the Lua side
|
||||||
|
|||||||
@@ -47,13 +47,17 @@ function! nuwiki#lsp#start() abort
|
|||||||
if exists(':CocConfig') == 2 || exists('*coc#config')
|
if exists(':CocConfig') == 2 || exists('*coc#config')
|
||||||
" coc.nvim — coc reads its server list from coc-settings.json.
|
" coc.nvim — coc reads its server list from coc-settings.json.
|
||||||
" We can't inject it dynamically without owning the file, so just point
|
" We can't inject it dynamically without owning the file, so just point
|
||||||
" the user at the snippet.
|
" the user at the snippet. `initializationOptions` carries wiki_root etc.
|
||||||
|
" to the server exactly like the vim-lsp registration above — without it
|
||||||
|
" the server resolves links against its default root, so existing pages
|
||||||
|
" look broken and follow-to-create lands in the wrong directory.
|
||||||
echohl WarningMsg
|
echohl WarningMsg
|
||||||
echom 'nuwiki: coc.nvim detected. Add this to your coc-settings.json:'
|
echom 'nuwiki: coc.nvim detected. Add this to your coc-settings.json:'
|
||||||
echom ' "languageserver": {'
|
echom ' "languageserver": {'
|
||||||
echom ' "nuwiki": {'
|
echom ' "nuwiki": {'
|
||||||
echom ' "command": "' . l:bin . '",'
|
echom ' "command": "' . l:bin . '",'
|
||||||
echom ' "filetypes": ["vimwiki"]'
|
echom ' "filetypes": ["vimwiki"],'
|
||||||
|
echom ' "initializationOptions": ' . json_encode(s:settings())
|
||||||
echom ' }'
|
echom ' }'
|
||||||
echom ' }'
|
echom ' }'
|
||||||
echohl None
|
echohl None
|
||||||
|
|||||||
Executable
+178
@@ -0,0 +1,178 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
#
|
||||||
|
# start-vim-coc.sh — launch Vim with a minimal config that loads nuwiki
|
||||||
|
# through coc.nvim (instead of vim-lsp).
|
||||||
|
#
|
||||||
|
# The plugin's Vim path (autoload/nuwiki/lsp.vim) prefers `vim-lsp` and falls
|
||||||
|
# back to `coc.nvim`. start-vim.sh exercises the vim-lsp path; this script
|
||||||
|
# exercises the coc.nvim path. vim-lsp is deliberately kept OFF the runtimepath
|
||||||
|
# so `:LspDefinition` doesn't exist and nuwiki's coc branch is the one that runs
|
||||||
|
# (s:jump_definition() → CocActionAsync('jumpDefinition', 'edit')).
|
||||||
|
#
|
||||||
|
# This doubles as a reproducer for the "<CR> opens a new tab" regression:
|
||||||
|
# coc-settings.json sets coc.preferences.jumpCommand to `tabe`, so without the
|
||||||
|
# fix `<CR>` on a link opens a new tab. With the fix nuwiki passes 'edit'
|
||||||
|
# explicitly and the target opens in the *current* window regardless.
|
||||||
|
#
|
||||||
|
# It clones coc.nvim's prebuilt `release` branch into the dev cache on first
|
||||||
|
# run (no yarn/build needed — it just needs Node.js at runtime).
|
||||||
|
#
|
||||||
|
# Usage: ./development/start-vim-coc.sh [extra args...]
|
||||||
|
# ./development/start-vim-coc.sh # open the scratch wiki's index
|
||||||
|
# ./development/start-vim-coc.sh path/to/note.wiki # open a specific file
|
||||||
|
# NUWIKI_DEV_WIKI=/tmp/foo ./development/start-vim-coc.sh
|
||||||
|
# NUWIKI_DEV_NO_SEED=1 ./development/start-vim-coc.sh # use WIKI_DIR as-is (no scratch seeding)
|
||||||
|
# NUWIKI_DEV_SKIP_BUILD=1 ./development/start-vim-coc.sh # reuse the cached binary
|
||||||
|
# NUWIKI_DEV_COC_JUMP=edit ./development/start-vim-coc.sh # change coc.preferences.jumpCommand
|
||||||
|
#
|
||||||
|
# The release binary is rebuilt on every launch so source changes always reach
|
||||||
|
# the running LSP. `cargo build --release` is incremental, so this is a fast
|
||||||
|
# no-op when nothing changed. Set NUWIKI_DEV_SKIP_BUILD=1 to skip the build.
|
||||||
|
#
|
||||||
|
# Tested with Vim 9.1+ and Node 18+. State (viminfo/sessions, coc data) lives
|
||||||
|
# under $XDG_CACHE_HOME/nuwiki-dev/ so your real Vim install stays untouched.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Shared paths + helpers (log, ensure_binary, write_sample_wiki, seed_wiki).
|
||||||
|
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_common.sh"
|
||||||
|
|
||||||
|
VIMRC="$DEV_DIR/vimrc-coc"
|
||||||
|
VIM_STATE="$DEV_DIR/vim-coc-state"
|
||||||
|
COC_DIR="$DEV_DIR/coc.nvim"
|
||||||
|
COC_DATA="$DEV_DIR/coc-data"
|
||||||
|
COC_SETTINGS="$DEV_DIR/coc-settings.json"
|
||||||
|
COC_JUMP="${NUWIKI_DEV_COC_JUMP:-tabe}"
|
||||||
|
|
||||||
|
ensure_node() {
|
||||||
|
if ! command -v node >/dev/null 2>&1; then
|
||||||
|
log 'node not found — coc.nvim needs Node.js at runtime; skipping'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_coc() {
|
||||||
|
mkdir -p "$DEV_DIR"
|
||||||
|
if [[ -d "$COC_DIR/.git" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
if ! command -v git >/dev/null 2>&1; then
|
||||||
|
log 'git not found — cannot clone coc.nvim; skipping'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
log "cloning coc.nvim (release branch) → $COC_DIR"
|
||||||
|
git clone --depth 1 --branch release \
|
||||||
|
https://github.com/neoclide/coc.nvim.git "$COC_DIR" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
write_coc_settings() {
|
||||||
|
mkdir -p "$DEV_DIR"
|
||||||
|
# Mirror what the vim-lsp client sends (autoload/nuwiki/lsp.vim s:settings()):
|
||||||
|
# both `initializationOptions` (sent on initialize) and `settings.nuwiki`
|
||||||
|
# (served via workspace/configuration). Without these the server falls back
|
||||||
|
# to its own default wiki_root (~/vimwiki) instead of $WIKI_DIR, so it can't
|
||||||
|
# resolve links (e.g. Notes shows as broken) or create-on-follow correctly.
|
||||||
|
cat > "$COC_SETTINGS" <<EOF
|
||||||
|
{
|
||||||
|
"coc.preferences.jumpCommand": "$COC_JUMP",
|
||||||
|
"languageserver": {
|
||||||
|
"nuwiki": {
|
||||||
|
"command": "$REPO_ROOT/bin/nuwiki-ls",
|
||||||
|
"filetypes": ["vimwiki"],
|
||||||
|
"initializationOptions": {
|
||||||
|
"wiki_root": "$WIKI_DIR",
|
||||||
|
"file_extension": ".wiki",
|
||||||
|
"syntax": "vimwiki",
|
||||||
|
"log_level": "info",
|
||||||
|
"diagnostic": { "link_severity": "warn" }
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"nuwiki": {
|
||||||
|
"wiki_root": "$WIKI_DIR",
|
||||||
|
"file_extension": ".wiki",
|
||||||
|
"syntax": "vimwiki",
|
||||||
|
"log_level": "info",
|
||||||
|
"diagnostic": { "link_severity": "warn" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
write_vimrc() {
|
||||||
|
mkdir -p "$DEV_DIR"
|
||||||
|
cat > "$VIMRC" <<EOF
|
||||||
|
" start-vim-coc.sh: generated minimal config for nuwiki development (coc.nvim).
|
||||||
|
" Regenerated on every launch; edit start-vim-coc.sh, not this file.
|
||||||
|
|
||||||
|
" Confine state to the dev cache so we never touch the user's real Vim
|
||||||
|
" install.
|
||||||
|
set viminfo='100,n$VIM_STATE/viminfo
|
||||||
|
let &directory = '$VIM_STATE/swap//'
|
||||||
|
let &backupdir = '$VIM_STATE/backup//'
|
||||||
|
let &undodir = '$VIM_STATE/undo//'
|
||||||
|
|
||||||
|
" Make Vim look for runtime paths in the dev cache. vim-lsp is intentionally
|
||||||
|
" absent so nuwiki dispatches jump-to-definition through coc.
|
||||||
|
set nocompatible
|
||||||
|
let &runtimepath = '$REPO_ROOT' . ',' . &runtimepath
|
||||||
|
let &runtimepath .= ',$COC_DIR'
|
||||||
|
|
||||||
|
" Point coc at the generated settings + a private data dir.
|
||||||
|
let g:coc_config_home = '$DEV_DIR'
|
||||||
|
let g:coc_data_home = '$COC_DATA'
|
||||||
|
|
||||||
|
let g:nuwiki_binary_path = '$REPO_ROOT/bin/nuwiki-ls'
|
||||||
|
let g:nuwiki_wiki_root = '$WIKI_DIR'
|
||||||
|
let g:nuwiki_file_extension = '.wiki'
|
||||||
|
let g:nuwiki_syntax = 'vimwiki'
|
||||||
|
let g:nuwiki_log_level = 'info'
|
||||||
|
|
||||||
|
filetype plugin indent on
|
||||||
|
syntax enable
|
||||||
|
set hidden
|
||||||
|
set termguicolors
|
||||||
|
set number
|
||||||
|
set signcolumn=yes
|
||||||
|
set noswapfile
|
||||||
|
set nobackup
|
||||||
|
set nowritebackup
|
||||||
|
let mapleader = ' '
|
||||||
|
|
||||||
|
" Auto-detect filetype for .wiki files (the plugin ships ftdetect, but
|
||||||
|
" --clean disables those — copy the rule here).
|
||||||
|
augroup NuwikiFtdetect
|
||||||
|
autocmd!
|
||||||
|
autocmd BufRead,BufNewFile *.wiki setfiletype vimwiki
|
||||||
|
augroup END
|
||||||
|
|
||||||
|
" coc starts the server automatically from coc-settings.json once a vimwiki
|
||||||
|
" buffer loads — no explicit nuwiki#lsp#start() needed.
|
||||||
|
|
||||||
|
echomsg '[nuwiki-dev] ready (coc.nvim) — wiki root: $WIKI_DIR'
|
||||||
|
echomsg '[nuwiki-dev] coc.preferences.jumpCommand = $COC_JUMP'
|
||||||
|
echomsg '[nuwiki-dev] <CR> on a [[link]] should open in the CURRENT window'
|
||||||
|
echomsg '[nuwiki-dev] :CocList services for status, :messages for log'
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
ensure_node
|
||||||
|
ensure_binary
|
||||||
|
ensure_coc
|
||||||
|
write_coc_settings
|
||||||
|
seed_wiki
|
||||||
|
write_vimrc
|
||||||
|
mkdir -p "$VIM_STATE/swap" "$VIM_STATE/backup" "$VIM_STATE/undo" "$COC_DATA"
|
||||||
|
|
||||||
|
log "launching: vim --clean -u $VIMRC"
|
||||||
|
if [[ $# -gt 0 ]]; then
|
||||||
|
exec vim --clean -u "$VIMRC" "$@"
|
||||||
|
else
|
||||||
|
exec vim --clean -u "$VIMRC" "$WIKI_DIR/index.wiki"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@@ -477,6 +477,50 @@ call s:record(
|
|||||||
\ 'ext=' . get(get(s:wl, 1, {}), 'ext', ''))
|
\ 'ext=' . get(get(s:wl, 1, {}), 'ext', ''))
|
||||||
unlet g:nuwiki_wikis
|
unlet g:nuwiki_wikis
|
||||||
|
|
||||||
|
" ===== Follow-link resolves + opens in the current window (regression) =====
|
||||||
|
" follow_link_or_create() must resolve the definition itself and `:edit` the
|
||||||
|
" target in the CURRENT window — including a page that does not exist yet
|
||||||
|
" (create-on-follow). We open the URI ourselves rather than delegating to
|
||||||
|
" :LspDefinition / coc's jumpDefinition because those fetch the target text
|
||||||
|
" (vim-lsp readfile()s it, throwing E484 for a missing page and aborting the
|
||||||
|
" jump). This harness loads only nuwiki, so :LspDefinition is absent and the
|
||||||
|
" coc branch of s:open_definition() runs once CocAction* are stubbed.
|
||||||
|
" The stubbed getDefinition points at a NON-EXISTENT file, so a green result
|
||||||
|
" proves both current-window placement and create-on-follow.
|
||||||
|
let g:_nuwiki_target = tempname() . '-newpage.wiki'
|
||||||
|
function! CocActionAsync(...) abort
|
||||||
|
endfunction
|
||||||
|
function! CocAction(action, ...) abort
|
||||||
|
if a:action ==# 'definitions'
|
||||||
|
return [{ 'uri': 'file://' . g:_nuwiki_target,
|
||||||
|
\ 'range': { 'start': { 'line': 0, 'character': 0 },
|
||||||
|
\ 'end': { 'line': 0, 'character': 0 } } }]
|
||||||
|
endif
|
||||||
|
return []
|
||||||
|
endfunction
|
||||||
|
" `:edit` must be able to abandon the (modified) scratch buffer.
|
||||||
|
let s:had_hidden = &hidden
|
||||||
|
set hidden
|
||||||
|
let s:seed = expand('%:p')
|
||||||
|
call s:set_buf(['[[NewPage]] rest'])
|
||||||
|
call cursor(1, 4)
|
||||||
|
call nuwiki#commands#follow_link_or_create()
|
||||||
|
let s:now = expand('%:p')
|
||||||
|
call s:record(
|
||||||
|
\ (s:now =~# 'newpage\.wiki$' && s:now !=# s:seed) ? 1 : 0,
|
||||||
|
\ 'cr.follow_opens_missing_page_in_current_window',
|
||||||
|
\ 'buf=' . s:now)
|
||||||
|
let &hidden = s:had_hidden
|
||||||
|
delfunction CocAction
|
||||||
|
delfunction CocActionAsync
|
||||||
|
unlet g:_nuwiki_target
|
||||||
|
" NOTE: the vim-lsp branch of s:open_definition() can't be driven headlessly —
|
||||||
|
" it gates on `exists('*lsp#send_request')`, which returns 0 for an inline stub
|
||||||
|
" (Vim only resolves real autoload files). It shares the same s:open_location()
|
||||||
|
" open logic the coc case above exercises, and mirrors the shipped
|
||||||
|
" follow_link_drop() request mechanism; its end-to-end coverage is the manual
|
||||||
|
" development/start-vim.sh check.
|
||||||
|
|
||||||
" ===== Wrap up =====
|
" ===== Wrap up =====
|
||||||
|
|
||||||
call add(s:results, '')
|
call add(s:results, '')
|
||||||
|
|||||||
+34
-23
@@ -44,29 +44,40 @@ fix site.
|
|||||||
|
|
||||||
## Regressions (reported by users — verify, then fix)
|
## Regressions (reported by users — verify, then fix)
|
||||||
|
|
||||||
- [ ] **`<CR>` link-follow opens a new tab instead of reusing the current
|
- [x] **`<CR>` link-follow: wrong window placement + no create-on-follow on the
|
||||||
window** — pressing `<CR>` on a link used to open the target in the *same*
|
Vim clients** — two related Vim-branch bugs (Neovim path was already correct):
|
||||||
tab/window the user was browsing in (upstream behavior). It now opens a
|
(a) on coc.nvim, `<CR>` opened the target in a **new tab** instead of the
|
||||||
**new tab and switches to it**. Reported on Vim 9.x + Dein.
|
current window; (b) on **both** vim-lsp and coc, `<CR>` on a link to a
|
||||||
_Investigation so far:_ the nuwiki `<CR>` path is **unchanged** by the recent
|
not-yet-created page **failed to open/create it**.
|
||||||
P1 #1/#2/#3 work — `git blame` shows the `<CR>` map (`ftplugin/vimwiki.vim`
|
_Root cause:_ the Vim follow path delegated to the LSP client's jump UI
|
||||||
Vim branch `:206`, Neovim branch `lua/nuwiki/keymaps.lua:161`) and the
|
(`:LspDefinition` / `CocActionAsync('jumpDefinition')`). Those fetch the
|
||||||
`follow_link_or_create` → jump-definition chain were not touched; only
|
target's text before jumping — vim-lsp `readfile()`s it for the quickfix entry
|
||||||
`<C-S-CR>` was repointed to `follow_link_drop()` in `f65d861`. Both client
|
(`autoload/lsp/utils/location.vim`), which throws `E484` for a missing file
|
||||||
jump paths open in the current window by default
|
and aborts the jump (→ no create-on-follow); coc, lacking an explicit open
|
||||||
(`vim.lsp.buf.definition()` with no opts; Vim `s:jump_definition()` →
|
command, fell back to the user's `coc.preferences.jumpCommand` (→ new tab).
|
||||||
`LspDefinition`, `autoload/nuwiki/commands.vim:73`), so the new-tab behavior
|
The server itself was correct throughout — verified over JSON-RPC that
|
||||||
is **not** coming from nuwiki's own placement logic as written.
|
`textDocument/definition` returns a *synthesised* location for missing pages
|
||||||
_Rule out first (env, per memory):_ a **stale dein copy** of the plugin
|
and resolves existing links.
|
||||||
(`bin/nuwiki-ls` + copied ftplugin) and a **double `rtp` entry** where the
|
_Fix:_ stop delegating. `nuwiki#commands#follow_link_or_create()` /
|
||||||
upstream vimwiki bundle is still loaded alongside nuwiki — either can shadow
|
`follow_link_drop()` now resolve `textDocument/definition` directly and open
|
||||||
the `<CR>` map or pull in upstream/`LspDefinition --tab`-style behavior.
|
the URI ourselves via a shared `s:open_definition(open_cmd)` helper
|
||||||
Recache dein + rebuild + restart LSP and confirm only nuwiki's ftplugin is
|
(`autoload/nuwiki/commands.vim`) — `:edit` for `<CR>`, `tab drop` for
|
||||||
active before treating this as a nuwiki code bug.
|
`<C-S-CR>`. `:edit` opens a buffer for a missing path (save creates it,
|
||||||
_Suspected sites if it reproduces clean:_ vim-lsp's definition-open default
|
matching vimwiki) and gives exact current-window placement regardless of the
|
||||||
(Vim) and the `vim.lsp.buf.definition()` handler (Neovim) — pin which `<CR>`
|
user's coc jumpCommand. Covered by
|
||||||
press (bare-word wrap vs. existing-link follow) triggers the tab and whether
|
`cr.follow_opens_missing_page_in_current_window` in
|
||||||
the server's definition response shape steers placement.
|
`development/tests/test-keymaps-vim.vim`; manually reproducible via
|
||||||
|
`development/start-vim.sh` (vim-lsp) and `development/start-vim-coc.sh` (coc).
|
||||||
|
- [x] **coc.nvim reported valid links as broken / couldn't resolve** — only via
|
||||||
|
the new `development/start-vim-coc.sh` launcher: its generated
|
||||||
|
`coc-settings.json` registered the server with `command`/`filetypes` but no
|
||||||
|
`initializationOptions`, so `wiki_root` never reached the server and it
|
||||||
|
resolved links against its default (`~/vimwiki`). _Fix:_ the launcher now
|
||||||
|
emits `initializationOptions` **and** `settings.nuwiki` mirroring the vim-lsp
|
||||||
|
client (`autoload/nuwiki/lsp.vim` `s:settings()`). Verified with a headless
|
||||||
|
coc.nvim run: `[[Notes]]` resolves, only genuinely-missing links warn. (Shipped
|
||||||
|
plugin only — no production code change; coc users configure their own
|
||||||
|
`coc-settings.json` per the README snippet.)
|
||||||
|
|
||||||
## P2 — Moderate (real users hit these)
|
## P2 — Moderate (real users hit these)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user