Skip to main content

krait/commands/
rename.rs

1use std::path::Path;
2
3use anyhow::Context;
4use serde_json::{json, Value};
5
6use crate::commands::find::resolve_symbol_location;
7use crate::commands::workspace_edit::{apply_workspace_edit, count_workspace_edits};
8use crate::lsp::client::{path_to_uri, LspClient};
9use crate::lsp::files::FileTracker;
10
11/// Rename a symbol across all files using LSP `textDocument/rename`.
12///
13/// Resolves the symbol location, sends the rename request, and applies
14/// the returned `WorkspaceEdit` atomically to all affected files.
15///
16/// # Errors
17/// Returns an error if the symbol is not found, the LSP request fails,
18/// or any file cannot be written.
19pub async fn handle_rename(
20    name: &str,
21    new_name: &str,
22    client: &mut LspClient,
23    file_tracker: &mut FileTracker,
24    project_root: &Path,
25) -> anyhow::Result<Value> {
26    let (abs_path, line, character) = resolve_symbol_location(name, client, project_root).await?;
27
28    file_tracker
29        .ensure_open(&abs_path, client.transport_mut())
30        .await?;
31
32    let uri = path_to_uri(&abs_path)?;
33    let params = json!({
34        "textDocument": { "uri": uri.as_str() },
35        "position": { "line": line, "character": character },
36        "newName": new_name
37    });
38
39    let request_id = client
40        .transport_mut()
41        .send_request("textDocument/rename", params)
42        .await?;
43
44    let workspace_edit = client
45        .wait_for_response_public(request_id)
46        .await
47        .context("textDocument/rename request failed")?;
48
49    if workspace_edit.is_null() {
50        return Ok(json!({
51            "files_changed": 0,
52            "refs_changed": 0,
53        }));
54    }
55
56    let refs_changed = count_workspace_edits(&workspace_edit);
57    let modified = apply_workspace_edit(&workspace_edit, project_root)?;
58
59    Ok(json!({
60        "files_changed": modified.len(),
61        "refs_changed": refs_changed,
62    }))
63}
64
65#[cfg(test)]
66mod tests {
67    use serde_json::json;
68
69    #[test]
70    fn rename_response_shape() {
71        let data = json!({ "files_changed": 3, "refs_changed": 12 });
72        assert_eq!(data["files_changed"].as_u64(), Some(3));
73        assert_eq!(data["refs_changed"].as_u64(), Some(12));
74    }
75}