phase 13: workspace/rename + executeCommand router + nuwiki.file.delete
Server-side rename with cross-document link rewriting (P11's
read_or_open delivers on its promise) and the first executeCommand
entry. Plus the routing infrastructure that Phases 14-17 will hang
their own commands off of.
commands.rs:
- commands::execute(backend, name, args) dispatches by command name
and returns the WorkspaceEdit to apply.
- COMMANDS const slice is the source of truth for what's advertised
in ExecuteCommandOptions.commands.
- First entry: nuwiki.file.delete with args { uri: <Url> } returning
a WorkspaceEdit with a DeleteFile op.
rename.rs:
- compute_rename(backend, target_uri, new_name, utf8):
1. Resolve the wiki via wiki_for_uri.
2. Build new URI from wiki.root + new_name + extension.
3. Snapshot the index's backlinks for the old page name before
releasing the read lock so we don't hold it across awaits.
4. For each backlink, read_or_open the source (live state or
disk + reparse) and validate the cached span against current
text.
5. Rewrite the [[...]] segment with rewrite_wikilink_target,
preserving anchor and description.
6. Add the RenameFile op so the builder emits it before the text
edits in documentChanges ordering.
- build_new_uri handles subdirectory paths and avoids double-
appending the file extension.
- rewrite_wikilink_target is pub so other tooling can call it.
lib.rs:
- Capabilities: rename_provider + execute_command_provider listing
commands::COMMANDS.
- New rename handler: cursor-on-wikilink renames the linked page,
else the buffer's file. Delegates to rename::compute_rename.
- New execute_command handler dispatches via commands::execute, then
pushes the resulting WorkspaceEdit through client.apply_edit. On
unknown command or bad args, logs and returns null.
- read_or_open loses its allow(dead_code) attribute now that Phase
13 calls it.
- rename_target_uri helper shares index-resolution with goto_def.
Tests (11 new in phase13_rename_commands.rs):
- rewrite_wikilink_target: plain, with description, with anchor,
with both, subdirectory path, non-wikilink-text rejection.
- build_new_uri: under root, with subdirectory, extension dedup,
empty path segments skipped.
- nuwiki.file.delete builder smoke test (DocumentChanges + DeleteFile).
All 211 v1.0+Phase 11+12 tests still pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
//! `workspace/executeCommand` dispatcher.
|
||||
//!
|
||||
//! Phase 13 lands the router + the first command. Phases 14–17 register
|
||||
//! their own commands here. Each handler returns the `WorkspaceEdit` it
|
||||
//! wants applied; the `Backend` calls `client.apply_edit` after the call.
|
||||
//!
|
||||
//! Phase 13 surface:
|
||||
//! - `nuwiki.file.delete` — args: `{ "uri": <Url> }`
|
||||
|
||||
use serde::Deserialize;
|
||||
use tower_lsp::lsp_types::{Url, WorkspaceEdit};
|
||||
|
||||
use crate::edits::{op_delete, WorkspaceEditBuilder};
|
||||
use crate::Backend;
|
||||
|
||||
/// All commands the server advertises in `ExecuteCommandOptions.commands`.
|
||||
/// Keep this in lock-step with the match arms in `execute` below — adding
|
||||
/// here without a handler will fail at runtime; adding a handler without
|
||||
/// listing here means the client won't see it as available.
|
||||
pub const COMMANDS: &[&str] = &["nuwiki.file.delete"];
|
||||
|
||||
/// Dispatch an `executeCommand` request. Returns `Ok(None)` for commands
|
||||
/// that don't produce a `WorkspaceEdit` (e.g. read-only queries); returns
|
||||
/// `Err` with a human-readable message for unknown commands or bad args
|
||||
/// — the LSP backend logs it and returns `null` to the client.
|
||||
pub(crate) async fn execute(
|
||||
_backend: &Backend,
|
||||
command: &str,
|
||||
args: Vec<serde_json::Value>,
|
||||
) -> Result<Option<WorkspaceEdit>, String> {
|
||||
match command {
|
||||
"nuwiki.file.delete" => file_delete(args),
|
||||
other => Err(format!("unknown nuwiki command: {other}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn file_delete(args: Vec<serde_json::Value>) -> Result<Option<WorkspaceEdit>, String> {
|
||||
#[derive(Deserialize)]
|
||||
struct Args {
|
||||
uri: Url,
|
||||
}
|
||||
let raw = args
|
||||
.into_iter()
|
||||
.next()
|
||||
.ok_or_else(|| "nuwiki.file.delete: missing arguments".to_string())?;
|
||||
let parsed: Args = serde_json::from_value(raw)
|
||||
.map_err(|e| format!("nuwiki.file.delete: invalid args — {e}"))?;
|
||||
let mut builder = WorkspaceEditBuilder::new();
|
||||
builder.file_op(op_delete(parsed.uri));
|
||||
Ok(Some(builder.build()))
|
||||
}
|
||||
Reference in New Issue
Block a user