Skip to content

Commit

Permalink
add lsp rename symbol
Browse files Browse the repository at this point in the history
improve apply_workspace_edit
use space-r to goto_reference and gr for rename_symbol
  • Loading branch information
cossonleo committed Nov 8, 2021
1 parent 3074464 commit 601d8de
Show file tree
Hide file tree
Showing 4 changed files with 188 additions and 36 deletions.
27 changes: 27 additions & 0 deletions helix-lsp/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,12 @@ impl Client {
content_format: Some(vec![lsp::MarkupKind::Markdown]),
..Default::default()
}),
rename: Some(lsp::RenameClientCapabilities {
dynamic_registration: Some(false),
prepare_support: Some(false),
prepare_support_default_behavior: None,
honors_change_annotations: Some(false),
}),
code_action: Some(lsp::CodeActionClientCapabilities {
code_action_literal_support: Some(lsp::CodeActionLiteralSupport {
code_action_kind: lsp::CodeActionKindLiteralSupport {
Expand Down Expand Up @@ -773,4 +779,25 @@ impl Client {

self.call::<lsp::request::CodeActionRequest>(params)
}

pub async fn rename_symbol(
&self,
text_document: lsp::TextDocumentIdentifier,
position: lsp::Position,
new_name: String,
) -> anyhow::Result<lsp::WorkspaceEdit> {
let params = lsp::RenameParams {
text_document_position: lsp::TextDocumentPositionParams {
text_document,
position,
},
new_name,
work_done_progress_params: lsp::WorkDoneProgressParams {
work_done_token: None,
},
};

let response = self.request::<lsp::request::Rename>(params).await?;
Ok(response.unwrap_or_default())
}
}
188 changes: 153 additions & 35 deletions helix-term/src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use helix_view::{

use anyhow::{anyhow, bail, Context as _};
use helix_lsp::{
lsp,
block_on, lsp,
util::{lsp_pos_to_pos, lsp_range_to_range, pos_to_lsp_pos, range_to_lsp_range},
OffsetEncoding,
};
Expand Down Expand Up @@ -339,6 +339,7 @@ impl Command {
shell_append_output, "Append output of shell command after each selection",
shell_keep_pipe, "Filter selections with shell predicate",
suspend, "Suspend",
rename_symbol, "Rename symbol",
);
}

Expand Down Expand Up @@ -2749,14 +2750,104 @@ pub fn code_action(cx: &mut Context) {
)
}

pub fn apply_document_resource_op(op: &lsp::ResourceOp) -> std::io::Result<()> {
use lsp::ResourceOp;
use std::fs;
match op {
ResourceOp::Create(op) => {
let path = op.uri.to_file_path().unwrap();
let ignore_if_exists = if let Some(options) = &op.options {
!options.overwrite.unwrap_or(false) && options.ignore_if_exists.unwrap_or(false)
} else {
false
};
if ignore_if_exists && path.exists() {
Ok(())
} else {
fs::write(&path, [])
}
}
ResourceOp::Delete(op) => {
let path = op.uri.to_file_path().unwrap();
if path.is_dir() {
let recursive = if let Some(options) = &op.options {
options.recursive.unwrap_or(false)
} else {
false
};
if recursive {
fs::remove_dir_all(&path)
} else {
fs::remove_dir(&path)
}
} else if path.is_file() {
fs::remove_file(&path)
} else {
Ok(())
}
}
ResourceOp::Rename(op) => {
let from = op.old_uri.to_file_path().unwrap();
let to = op.new_uri.to_file_path().unwrap();
let ignore_if_exists = if let Some(options) = &op.options {
!options.overwrite.unwrap_or(false) && options.ignore_if_exists.unwrap_or(false)
} else {
false
};
if ignore_if_exists && to.exists() {
Ok(())
} else {
fs::rename(&from, &to)
}
}
}
}

fn apply_workspace_edit(
editor: &mut Editor,
offset_encoding: OffsetEncoding,
workspace_edit: &lsp::WorkspaceEdit,
) {
let mut apply_edits = |uri: &helix_lsp::Url, text_edits: Vec<lsp::TextEdit>| {
let path = uri
.to_file_path()
.expect("unable to convert URI to filepath");

let current_view_id = view!(editor).id;
let doc_id = editor.open(path, Action::Load).unwrap();
let doc = editor
.document_mut(doc_id)
.expect("Document for document_changes not found");

// Need to determine a view for apply/append_changes_to_history
let selections = doc.selections();
let view_id = if selections.contains_key(&current_view_id) {
// use current if possible
current_view_id
} else {
// Hack: we take the first available view_id
selections
.keys()
.next()
.copied()
.expect("No view_id available")
};

let transaction = helix_lsp::util::generate_transaction_from_edits(
doc.text(),
text_edits,
offset_encoding,
);
doc.apply(&transaction, view_id);
doc.append_changes_to_history(view_id);
};

if let Some(ref changes) = workspace_edit.changes {
log::debug!("workspace changes: {:?}", changes);
editor.set_error(String::from("Handling workspace_edit.changes is not implemented yet, see https://github.com/helix-editor/helix/issues/183"));
for (uri, text_edits) in changes {
let text_edits = text_edits.iter().cloned().collect();
apply_edits(uri, text_edits);
}
return;
// Not sure if it works properly, it'll be safer to just panic here to avoid breaking some parts of code on which code actions will be used
// TODO: find some example that uses workspace changes, and test it
Expand All @@ -2774,30 +2865,6 @@ fn apply_workspace_edit(
match document_changes {
lsp::DocumentChanges::Edits(document_edits) => {
for document_edit in document_edits {
let path = document_edit
.text_document
.uri
.to_file_path()
.expect("unable to convert URI to filepath");
let current_view_id = view!(editor).id;
let doc = editor
.document_by_path_mut(path)
.expect("Document for document_changes not found");

// Need to determine a view for apply/append_changes_to_history
let selections = doc.selections();
let view_id = if selections.contains_key(&current_view_id) {
// use current if possible
current_view_id
} else {
// Hack: we take the first available view_id
selections
.keys()
.next()
.copied()
.expect("No view_id available")
};

let edits = document_edit
.edits
.iter()
Expand All @@ -2809,19 +2876,33 @@ fn apply_workspace_edit(
})
.cloned()
.collect();

let transaction = helix_lsp::util::generate_transaction_from_edits(
doc.text(),
edits,
offset_encoding,
);
doc.apply(&transaction, view_id);
doc.append_changes_to_history(view_id);
apply_edits(&document_edit.text_document.uri, edits);
}
}
lsp::DocumentChanges::Operations(operations) => {
log::debug!("document changes - operations: {:?}", operations);
editor.set_error(String::from("Handling document operations is not implemented yet, see https://github.com/helix-editor/helix/issues/183"));
for operateion in operations {
match operateion {
lsp::DocumentChangeOperation::Op(op) => {
apply_document_resource_op(op).unwrap();
}

lsp::DocumentChangeOperation::Edit(document_edit) => {
let edits = document_edit
.edits
.iter()
.map(|edit| match edit {
lsp::OneOf::Left(text_edit) => text_edit,
lsp::OneOf::Right(annotated_text_edit) => {
&annotated_text_edit.text_edit
}
})
.cloned()
.collect();
apply_edits(&document_edit.text_document.uri, edits);
}
}
}
}
}
}
Expand Down Expand Up @@ -4982,3 +5063,40 @@ fn add_newline_impl(cx: &mut Context, open: Open) {
doc.apply(&transaction, view.id);
doc.append_changes_to_history(view.id);
}

fn rename_symbol(cx: &mut Context) {
let prompt = Prompt::new(
"Rename to: ".into(),
None,
|_input: &str| Vec::new(),
move |cx: &mut compositor::Context, input: &str, event: PromptEvent| {
if event != PromptEvent::Validate {
return;
}

log::debug!("renaming to: {:?}", input);

let (view, doc) = current!(cx.editor);
let language_server = match doc.language_server() {
Some(language_server) => language_server,
None => return,
};

let offset_encoding = language_server.offset_encoding();

let pos = pos_to_lsp_pos(
doc.text(),
doc.selection(view.id)
.primary()
.cursor(doc.text().slice(..)),
offset_encoding,
);

let task = language_server.rename_symbol(doc.identifier(), pos, input.to_string());
let edits = block_on(task).unwrap_or_default();
log::debug!("Edits from LSP: {:?}", edits);
apply_workspace_edit(&mut cx.editor, offset_encoding, &edits);
},
);
cx.push_layer(Box::new(prompt));
}
3 changes: 2 additions & 1 deletion helix-term/src/keymap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ impl Default for Keymaps {
"s" => goto_first_nonwhitespace,
"d" => goto_definition,
"y" => goto_type_definition,
"r" => goto_reference,
"r" => rename_symbol,
"i" => goto_implementation,
"t" => goto_window_top,
"m" => goto_window_middle,
Expand Down Expand Up @@ -584,6 +584,7 @@ impl Default for Keymaps {
"R" => replace_selections_with_clipboard,
"/" => global_search,
"k" => hover,
"r" => goto_reference,
},
"z" => { "View"
"z" | "c" => align_view_center,
Expand Down
6 changes: 6 additions & 0 deletions helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ impl Editor {
return;
}
Action::Load => {
let view_id = view!(self).id;
if let Some(doc) = self.document_mut(id) {
if doc.selections().is_empty() {
doc.selections.insert(view_id, Selection::point(0));
}
}
return;
}
Action::HorizontalSplit => {
Expand Down

0 comments on commit 601d8de

Please sign in to comment.