From 7ffa81e1c69ce2dc3d8b2223df6fd08035a6746f Mon Sep 17 00:00:00 2001 From: Yomain Date: Sat, 21 Oct 2023 22:59:50 +0200 Subject: [PATCH] feat: add lsp support when moving files --- helix-lsp/src/client.rs | 50 ++++++++++++++++++++++++++++++++ helix-term/src/commands/typed.rs | 37 ++++++++++++++++++----- 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 341702c374ced..a30f91f18cf92 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -1341,6 +1341,56 @@ impl Client { Some(self.call::(params)) } + pub fn prepare_file_rename( + &self, + old_uri: String, + new_uri: String, + ) -> 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, new_uri }]; + let request = self.call::(lsp::RenameFilesParams { files }); + + Some(async move { + let json = request.await?; + let response: Option = serde_json::from_value(json)?; + Ok(response.unwrap_or_default()) + }) + } + + pub fn did_file_rename( + &self, + old_uri: String, + new_uri: String, + ) -> 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, new_uri }]; + Some(self.notify::(lsp::RenameFilesParams { files })) + } + pub fn prepare_rename( &self, text_document: lsp::TextDocumentIdentifier, diff --git a/helix-term/src/commands/typed.rs b/helix-term/src/commands/typed.rs index 8b5adb1327f76..d73f8e0ee1a1c 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2419,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 = args.first().unwrap().to_string(); + let doc = doc!(cx.editor); let old_path = doc .path() .ok_or_else(|| anyhow!("Scratch buffer cannot be moved. Use :write instead"))? - .clone(); + .to_string_lossy() + .to_string(); + + let edit = doc.language_servers().find_map(|lsp| { + if let Some(future) = lsp.prepare_file_rename(old_path.clone(), new_path.clone()) { + 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 edit.is_some() { + if let Err(e) = + apply_workspace_edit(cx.editor, helix_lsp::OffsetEncoding::Utf8, &edit.unwrap()) + { + log::error!(":move command failed to apply edits: {:?}", e); + }; + } + let doc = doc_mut!(cx.editor); + doc.set_path(Some(&PathBuf::from(&new_path))); if let Err(e) = std::fs::rename(&old_path, doc.path().unwrap()) { - doc.set_path(Some(old_path.as_path())); + doc.set_path(Some(&PathBuf::from(old_path))); bail!("Could not move file: {}", e); }; + doc.language_servers().for_each(|lsp| { + lsp.did_file_rename(old_path.clone(), new_path.clone()); + }); + Ok(()) }