diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 9c679a35434ef..00c04817ffbec 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -705,23 +705,60 @@ impl Client { }) } - pub fn will_rename_files( + pub fn prepare_file_rename( &self, - params: &Vec, - ) -> impl Future>> { - let files = params.to_owned(); + old_uri: &lsp::Url, + new_uri: &lsp::Url, + ) -> Option>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support willRename feature + match &capabilities.workspace { + Some(workspace) => match &workspace.file_operations { + Some(op) => { + op.will_rename.as_ref()?; + } + _ => return None, + }, + _ => return None, + } + + let files = vec![lsp::FileRename { + old_uri: old_uri.to_string(), + new_uri: new_uri.to_string(), + }]; let request = self.call::(lsp::RenameFilesParams { files }); - async move { + Some(async move { let json = request.await?; let response: Option = serde_json::from_value(json)?; - Ok(response) - } + Ok(response.unwrap_or_default()) + }) } - pub fn did_rename_files(&self, params: &[lsp::FileRename]) -> impl Future> { - let files = params.to_owned(); - self.notify::(lsp::RenameFilesParams { files }) + pub fn did_file_rename( + &self, + old_uri: &lsp::Url, + new_uri: &lsp::Url, + ) -> Option>> { + let capabilities = self.capabilities.get().unwrap(); + + // Return early if the server does not support DidRename feature + match &capabilities.workspace { + Some(workspace) => match &workspace.file_operations { + Some(op) => { + op.did_rename.as_ref()?; + } + _ => return None, + }, + _ => return None, + } + + let files = vec![lsp::FileRename { + old_uri: old_uri.to_string(), + new_uri: new_uri.to_string(), + }]; + Some(self.notify::(lsp::RenameFilesParams { files })) } // ------------------------------------------------------------------------------------------- diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 8bc173be4412f..375e04f148537 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -1,7 +1,5 @@ use std::fmt::Write; -use std::fmt::Write; use std::ops::Deref; -use std::{ffi::OsStr, ops::Deref}; use crate::job::Job; @@ -9,7 +7,6 @@ use super::*; use helix_core::fuzzy::fuzzy_match; use helix_core::{encoding, line_ending, shellwords::Shellwords}; -use helix_lsp::{lsp, Url}; use helix_view::document::DEFAULT_LANGUAGE_NAME; use helix_view::editor::{Action, CloseError, ConfigEvent}; use serde_json::Value; @@ -2422,22 +2419,45 @@ fn move_buffer( ensure!(args.len() == 1, format!(":move takes one argument")); - let new_path = - helix_core::path::get_normalized_path(&PathBuf::from(args.first().unwrap().as_ref())); - let (_, doc) = current!(cx.editor); + let new_path = match args.first().unwrap().parse() { + Ok(path) => path, + Err(e) => bail!("Invalid path: {}", e), + }; + let doc = doc!(cx.editor); let old_path = doc - .path() - .ok_or_else(|| anyhow!("Scratch buffer cannot be moved. Use :write instead"))? - .clone(); + .url() + .ok_or_else(|| anyhow!("Scratch buffer cannot be moved. Use :write instead"))?; + + let lsp_reply = doc.language_servers().find_map(|lsp| { + if let Some(future) = lsp.prepare_file_rename(&old_path, &new_path) { + match helix_lsp::block_on(future) { + Ok(edit) => return Some(edit), + Err(e) => { + log::error!("LSP willRename request failed: {:?}", e); + } + } + }; + None + }); - doc.set_path(Some(new_path.as_path())); + if let Some(edit) = lsp_reply { + if let Err(e) = apply_workspace_edit(cx.editor, helix_lsp::OffsetEncoding::Utf8, &edit) { + log::error!(":move command failed to apply edits: {:?}", e); + }; + } - if let Err(e) = std::fs::rename(&old_path, doc.path().unwrap()) { - doc.set_path(Some(old_path.as_path())); + let doc = doc_mut!(cx.editor); + doc.set_path(Some(&PathBuf::from(&new_path.as_str()))); + if let Err(e) = std::fs::rename(&PathBuf::from(&old_path.as_str()), doc.path().unwrap()) { + doc.set_path(Some(&PathBuf::from(old_path.as_str()))); bail!("Could not move file: {}", e); }; + doc.language_servers().for_each(|lsp| { + lsp.did_file_rename(&old_path, &new_path); + }); + Ok(()) }