diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 341702c374ced..b20cb4ab98fe6 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 file renaming. + 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 file renaming. + 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 }]; + 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..08703d2252e57 100644 --- a/helix-term/src/commands/typed.rs +++ b/helix-term/src/commands/typed.rs @@ -2419,22 +2419,48 @@ 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 new_path = args.first().unwrap().to_string(); let (_, doc) = current!(cx.editor); let old_path = doc .path() .ok_or_else(|| anyhow!("Scratch buffer cannot be moved. Use :write instead"))? + .to_string_lossy() + .to_string() .clone(); - doc.set_path(Some(new_path.as_path())); + let edit = doc.language_servers().find_map(|lsp| { + let future = lsp.prepare_file_rename(old_path.clone(), new_path.clone()); + if future.is_none() { + return None; + } + match helix_lsp::block_on(future.unwrap()) { + Ok(edit) => return Some(edit), + Err(e) => { + log::error!("LSP willRename request failed: {:?}", e); + return None; + } + } + }); + 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()); + }); + + 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); + }; + } + Ok(()) }