feat: real range/bang/visual handling for the deferred command gaps
Implements the four items previously left as "needs handler work" — all
fully wired, not cosmetic.
- ToggleListItem / IncrementListItem / DecrementListItem are now -range
(both clients). The client loops the per-line op over [<line1>,<line2>]
(toggle_list_item_range / list_cycle_symbol_range). The server's edits are
version-less single-line WorkspaceEdits, so each line applies independently
— no server protocol change. `:'<,'>VimwikiToggleListItem` toggles the whole
selection.
- VimwikiRebuildTags gains -bang: `:…RebuildTags!` passes { all: true } and
the server (tags_rebuild) re-indexes every configured wiki via
wikis_snapshot(), not just the current one.
- VimwikiNormalizeLink is -nargs=?: with `1` (upstream's visual flag; the
x-mode `+` mapping now passes it) it wraps the '< / '> selection as
[[selection]] (new wrap_visual_as_wikilink in both clients).
- VimwikiCheckLinks is -range: a ranged invocation filters the broken-link
report to the current buffer's selected lines (client-side filter in
results_to_qf / check_links).
Tests: cmd.toggle_list_item_range + cmd.normalize_link_visual_wraps_selection
(test-keymaps.lua), normalize.visual_wraps_selection (test-keymaps-vim.vim).
README + doc/nuwiki.txt + gap doc updated. fmt/clippy clean; nvim 274,
vim 266+18, all Rust tests + config-parity green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -529,6 +529,26 @@ endfunction
|
||||
function! nuwiki#commands#toggle_list_item() abort
|
||||
call s:exec_pos('nuwiki.list.toggleCheckbox')
|
||||
endfunction
|
||||
|
||||
" Apply a per-line action across [l1, l2] (visual-range commands). Each call
|
||||
" sends its own request for its line; the server's edits are version-less and
|
||||
" single-line, so applying them as they return never conflicts.
|
||||
function! s:over_range(l1, l2, Fn) abort
|
||||
let l:save = getcurpos()
|
||||
for l:ln in range(a:l1, a:l2)
|
||||
call cursor(l:ln, 1)
|
||||
call a:Fn()
|
||||
endfor
|
||||
call setpos('.', l:save)
|
||||
endfunction
|
||||
|
||||
function! nuwiki#commands#toggle_list_item_range(l1, l2) abort
|
||||
call s:over_range(a:l1, a:l2, function('nuwiki#commands#toggle_list_item'))
|
||||
endfunction
|
||||
|
||||
function! nuwiki#commands#list_cycle_symbol_range(direction, l1, l2) abort
|
||||
call s:over_range(a:l1, a:l2, { -> nuwiki#commands#list_cycle_symbol(a:direction) })
|
||||
endfunction
|
||||
function! nuwiki#commands#cycle_list_item() abort
|
||||
call s:exec_pos('nuwiki.list.cycleCheckbox')
|
||||
endfunction
|
||||
@@ -573,9 +593,12 @@ endfunction
|
||||
function! nuwiki#commands#links_generate() abort
|
||||
call s:exec('nuwiki.links.generate', [{'uri': s:buf_uri()}])
|
||||
endfunction
|
||||
function! nuwiki#commands#check_links() abort
|
||||
function! nuwiki#commands#check_links(...) abort
|
||||
" a:1 = range count; when >0, restrict the report to the current buffer's
|
||||
" [a:2, a:3] lines (`:'<,'>VimwikiCheckLinks`). No range → whole workspace.
|
||||
let l:rng = (a:0 >= 3 && a:1 > 0) ? [a:2, a:3] : []
|
||||
call s:exec('nuwiki.workspace.checkLinks', [{ 'uri': s:buf_uri() }],
|
||||
\ {n -> s:results_to_qf(n, 'broken links')})
|
||||
\ {n -> s:results_to_qf(n, 'broken links', l:rng)})
|
||||
endfunction
|
||||
|
||||
function! nuwiki#commands#find_orphans() abort
|
||||
@@ -583,7 +606,10 @@ function! nuwiki#commands#find_orphans() abort
|
||||
\ {n -> s:results_to_qf(n, 'orphans')})
|
||||
endfunction
|
||||
|
||||
function! s:results_to_qf(notification, title) abort
|
||||
function! s:results_to_qf(notification, title, ...) abort
|
||||
" Optional a:1 = [line1, line2]: keep only current-buffer rows in that range.
|
||||
let l:rng = a:0 >= 1 ? a:1 : []
|
||||
let l:cur = empty(l:rng) ? '' : resolve(expand('%:p'))
|
||||
let l:rows = get(get(a:notification, 'response', {}), 'result', [])
|
||||
if type(l:rows) != type([]) || empty(l:rows)
|
||||
echom 'nuwiki: no ' . a:title
|
||||
@@ -598,9 +624,18 @@ function! s:results_to_qf(notification, title) abort
|
||||
let l:lnum = l:row['range']['start']['line'] + 1
|
||||
let l:col = l:row['range']['start']['character'] + 1
|
||||
endif
|
||||
if !empty(l:rng)
|
||||
if resolve(l:path) !=# l:cur || l:lnum < l:rng[0] || l:lnum > l:rng[1]
|
||||
continue
|
||||
endif
|
||||
endif
|
||||
let l:text = get(l:row, 'message', get(l:row, 'name', ''))
|
||||
call add(l:items, { 'filename': l:path, 'lnum': l:lnum, 'col': l:col, 'text': l:text })
|
||||
endfor
|
||||
if empty(l:items)
|
||||
echom 'nuwiki: no ' . a:title
|
||||
return
|
||||
endif
|
||||
call setqflist([], ' ', { 'title': 'nuwiki ' . a:title, 'items': l:items })
|
||||
copen
|
||||
endfunction
|
||||
@@ -624,8 +659,11 @@ function! nuwiki#commands#tags_generate_links(tag) abort
|
||||
call s:exec('nuwiki.tags.generateLinks', [l:args])
|
||||
endfunction
|
||||
|
||||
function! nuwiki#commands#tags_rebuild() abort
|
||||
call s:exec('nuwiki.tags.rebuild', [{ 'uri': s:buf_uri() }])
|
||||
function! nuwiki#commands#tags_rebuild(...) abort
|
||||
" a:1 (the command's <bang>) → re-index every configured wiki.
|
||||
let l:all = a:0 >= 1 && a:1
|
||||
call s:exec('nuwiki.tags.rebuild',
|
||||
\ [{ 'uri': s:buf_uri(), 'all': l:all ? v:true : v:false }])
|
||||
endfunction
|
||||
|
||||
" ===== HTML export =====
|
||||
@@ -1205,15 +1243,46 @@ function! nuwiki#commands#smart_shift_tab() abort
|
||||
return "\<Cmd>call nuwiki#commands#_table_jump_to_cell(" . line('.') . ", " . l:target_idx . ")\<CR>"
|
||||
endfunction
|
||||
|
||||
" `:VimwikiNormalizeLink` — wrap the word at cursor as `[[word]]`
|
||||
" without following. Pure-VimL.
|
||||
function! nuwiki#commands#normalize_link() abort
|
||||
" `:VimwikiNormalizeLink [1]` — wrap text as `[[…]]` without following.
|
||||
" With a:1 (upstream's visual `1` arg, also the `x`-mode mapping) wrap the
|
||||
" `'<`/`'>` selection; otherwise the word under the cursor. Pure-VimL.
|
||||
function! nuwiki#commands#normalize_link(...) abort
|
||||
let l:visual = a:0 >= 1 && a:1
|
||||
if l:visual
|
||||
call s:wrap_visual_as_wikilink()
|
||||
return
|
||||
endif
|
||||
if s:cursor_inside_wikilink()
|
||||
return
|
||||
endif
|
||||
call s:wrap_cword_as_wikilink()
|
||||
endfunction
|
||||
|
||||
" Wrap the single-line `'<`/`'>` visual selection as `[[selection]]`.
|
||||
function! s:wrap_visual_as_wikilink() abort
|
||||
let l:sp = getpos("'<")
|
||||
let l:ep = getpos("'>")
|
||||
if l:sp[1] <= 0 || l:sp[1] != l:ep[1]
|
||||
return
|
||||
endif
|
||||
let l:line = getline(l:sp[1])
|
||||
let l:sc = l:sp[2]
|
||||
let l:ec = l:ep[2]
|
||||
if l:ec > strlen(l:line)
|
||||
let l:ec = strlen(l:line)
|
||||
endif
|
||||
if l:ec < l:sc
|
||||
return
|
||||
endif
|
||||
let l:sel = strpart(l:line, l:sc - 1, l:ec - l:sc + 1)
|
||||
" Don't double-wrap if the selection is already a bracketed link.
|
||||
if l:sel =~# '^\[\[.*\]\]$'
|
||||
return
|
||||
endif
|
||||
let l:new = strpart(l:line, 0, l:sc - 1) . '[[' . l:sel . ']]' . strpart(l:line, l:ec)
|
||||
call setline(l:sp[1], l:new)
|
||||
endfunction
|
||||
|
||||
function! s:echo_url_from(response, ...) abort
|
||||
let l:result = get(get(a:response, 'response', {}), 'result', '')
|
||||
if type(l:result) == type('') && l:result !=# ''
|
||||
|
||||
Reference in New Issue
Block a user