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:
@@ -619,56 +619,77 @@ fn tags_generate_links(
|
||||
}
|
||||
|
||||
fn tags_rebuild(backend: &Backend, args: Vec<Value>) -> Result<Option<Value>, String> {
|
||||
// `:VimwikiRebuildTags!` passes `{ all: true }` to re-index every
|
||||
// configured wiki (upstream's bang variant); otherwise just the current.
|
||||
let all = args
|
||||
.first()
|
||||
.and_then(|v| v.get("all"))
|
||||
.and_then(Value::as_bool)
|
||||
.unwrap_or(false);
|
||||
let p = parse_optional_uri_arg(args)?;
|
||||
let Some(wiki) = resolve_diary_wiki(backend, p.uri.as_ref()) else {
|
||||
return Ok(None);
|
||||
};
|
||||
let registry = std::sync::Arc::clone(&backend.registry);
|
||||
let root = wiki.config.root.clone();
|
||||
if root.as_os_str().is_empty() {
|
||||
return Ok(Some(
|
||||
serde_json::json!({ "pages": 0, "reason": "no wiki root configured" }),
|
||||
));
|
||||
}
|
||||
let Some(plugin) = registry.get("vimwiki") else {
|
||||
return Err("vimwiki syntax plugin missing".to_string());
|
||||
};
|
||||
let files = crate::index::walk_wiki_files(&root);
|
||||
let total = files.len();
|
||||
let targets: Vec<crate::wiki::Wiki> = if all {
|
||||
backend.wikis_snapshot()
|
||||
} else {
|
||||
match resolve_diary_wiki(backend, p.uri.as_ref()) {
|
||||
Some(w) => vec![w],
|
||||
None => return Ok(None),
|
||||
}
|
||||
};
|
||||
|
||||
let mut indexed = 0usize;
|
||||
let mut idx = wiki
|
||||
.index
|
||||
.write()
|
||||
.map_err(|_| "index lock poisoned".to_string())?;
|
||||
// Drop pages whose files no longer exist. Collect first to avoid mutating
|
||||
// during iteration.
|
||||
let stale: Vec<Url> = idx
|
||||
.pages_by_uri
|
||||
.keys()
|
||||
.filter(|u| match u.to_file_path() {
|
||||
Ok(p) => !p.exists(),
|
||||
Err(_) => true,
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
for u in &stale {
|
||||
idx.remove(u);
|
||||
}
|
||||
for path in files {
|
||||
let Ok(text) = std::fs::read_to_string(&path) else {
|
||||
let mut total = 0usize;
|
||||
let mut stale_removed = 0usize;
|
||||
let mut wikis_done = 0usize;
|
||||
for wiki in &targets {
|
||||
let root = wiki.config.root.clone();
|
||||
if root.as_os_str().is_empty() {
|
||||
continue;
|
||||
};
|
||||
let ast = plugin.parse(&text);
|
||||
let Ok(uri) = Url::from_file_path(&path) else {
|
||||
continue;
|
||||
};
|
||||
idx.upsert(uri, &ast);
|
||||
indexed += 1;
|
||||
}
|
||||
let files = crate::index::walk_wiki_files(&root);
|
||||
total += files.len();
|
||||
let mut idx = wiki
|
||||
.index
|
||||
.write()
|
||||
.map_err(|_| "index lock poisoned".to_string())?;
|
||||
// Drop pages whose files no longer exist. Collect first to avoid
|
||||
// mutating during iteration.
|
||||
let stale: Vec<Url> = idx
|
||||
.pages_by_uri
|
||||
.keys()
|
||||
.filter(|u| match u.to_file_path() {
|
||||
Ok(p) => !p.exists(),
|
||||
Err(_) => true,
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
for u in &stale {
|
||||
idx.remove(u);
|
||||
}
|
||||
stale_removed += stale.len();
|
||||
for path in files {
|
||||
let Ok(text) = std::fs::read_to_string(&path) else {
|
||||
continue;
|
||||
};
|
||||
let ast = plugin.parse(&text);
|
||||
let Ok(uri) = Url::from_file_path(&path) else {
|
||||
continue;
|
||||
};
|
||||
idx.upsert(uri, &ast);
|
||||
indexed += 1;
|
||||
}
|
||||
wikis_done += 1;
|
||||
}
|
||||
|
||||
Ok(Some(serde_json::json!({
|
||||
"pages": indexed,
|
||||
"files_seen": total,
|
||||
"stale_removed": stale.len(),
|
||||
"stale_removed": stale_removed,
|
||||
"wikis": wikis_done,
|
||||
"all": all,
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user