From 21bf614d5c1c68a6afcc98973ad7713f6ad461fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bla=C5=BE=20Hrastnik?= Date: Mon, 14 Jun 2021 10:52:21 +0900 Subject: [PATCH 01/17] wip: Code actions --- helix-lsp/src/client.rs | 27 +++++++++++++ helix-term/src/commands.rs | 79 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index 1c2a49b52e41..e50dac3f4d49 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -713,4 +713,31 @@ impl Client { self.call::(params) } + + // empty string to get all symbols + pub fn workspace_symbols(&self, query: String) -> impl Future> { + let params = lsp::WorkspaceSymbolParams { + query, + work_done_progress_params: lsp::WorkDoneProgressParams::default(), + partial_result_params: lsp::PartialResultParams::default(), + }; + + self.call::(params) + } + + pub fn code_actions( + &self, + text_document: lsp::TextDocumentIdentifier, + range: lsp::Range, + ) -> impl Future> { + let params = lsp::CodeActionParams { + text_document, + range, + context: lsp::CodeActionContext::default(), + work_done_progress_params: lsp::WorkDoneProgressParams::default(), + partial_result_params: lsp::PartialResultParams::default(), + }; + + self.call::(params) + } } diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 5fc96cd920c7..032498c5719e 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2091,6 +2091,85 @@ fn symbol_picker(cx: &mut Context) { ) } +pub fn workspace_symbol_picker(cx: &mut Context) { + let (view, doc) = cx.current(); + + let language_server = match doc.language_server() { + Some(language_server) => language_server, + None => return, + }; + let offset_encoding = language_server.offset_encoding(); + + let future = language_server.workspace_symbols("".to_string()); + + cx.callback( + future, + move |editor: &mut Editor, + compositor: &mut Compositor, + response: Option>| { + if let Some(symbols) = response { + let picker = Picker::new( + symbols, + |symbol| (&symbol.name).into(), + move |editor: &mut Editor, symbol, _action| { + push_jump(editor); + let (view, doc) = editor.current(); + + // if let Some(range) = + // lsp_range_to_range(doc.text(), symbol.location.range, offset_encoding) + // { + // doc.set_selection(view.id, Selection::single(range.to(), range.from())); + // align_view(doc, view, Align::Center); + // } + }, + ); + compositor.push(Box::new(picker)) + } + }, + ) +} + +pub fn code_action(cx: &mut Context) { + let (view, doc) = cx.current(); + + let language_server = match doc.language_server() { + Some(language_server) => language_server, + None => return, + }; + let offset_encoding = language_server.offset_encoding(); + + let range = range_to_lsp_range( + doc.text(), + doc.selection(view.id).primary(), + language_server.offset_encoding(), + ); + + let future = language_server.code_actions(doc.identifier(), range); + + cx.callback( + future, + move |editor: &mut Editor, + compositor: &mut Compositor, + response: Option| { + if let Some(actions) = response { + let picker = Picker::new( + actions, + |action| match action { + lsp::CodeActionOrCommand::CodeAction(action) => { + action.title.as_str().into() + } + lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(), + }, + move |editor: &mut Editor, code_action, _action| { + // + }, + ); + compositor.push(Box::new(picker)) + } + }, + ) +} + // I inserts at the first nonwhitespace character of each line with a selection fn prepend_to_line(cx: &mut Context) { goto_first_nonwhitespace(cx); From 3a7b92111a9b358f47460dc6d7594043b6632415 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Wed, 21 Jul 2021 16:38:57 +0200 Subject: [PATCH 02/17] fix(term): use current macro instead Context::context --- helix-term/src/commands.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 032498c5719e..b58e1020ddcd 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2092,7 +2092,7 @@ fn symbol_picker(cx: &mut Context) { } pub fn workspace_symbol_picker(cx: &mut Context) { - let (view, doc) = cx.current(); + let (view, doc) = current!(cx.editor); let language_server = match doc.language_server() { Some(language_server) => language_server, @@ -2113,7 +2113,7 @@ pub fn workspace_symbol_picker(cx: &mut Context) { |symbol| (&symbol.name).into(), move |editor: &mut Editor, symbol, _action| { push_jump(editor); - let (view, doc) = editor.current(); + let (view, doc) = current!(editor); // if let Some(range) = // lsp_range_to_range(doc.text(), symbol.location.range, offset_encoding) @@ -2130,7 +2130,7 @@ pub fn workspace_symbol_picker(cx: &mut Context) { } pub fn code_action(cx: &mut Context) { - let (view, doc) = cx.current(); + let (view, doc) = current!(cx.editor); let language_server = match doc.language_server() { Some(language_server) => language_server, From cdfb6f269b10412e65c1364181974190c8a6aec4 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Wed, 21 Jul 2021 18:09:11 +0200 Subject: [PATCH 03/17] feat(lsp): set code_action capabilities --- helix-lsp/src/client.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/helix-lsp/src/client.rs b/helix-lsp/src/client.rs index e50dac3f4d49..fd34f45d2afd 100644 --- a/helix-lsp/src/client.rs +++ b/helix-lsp/src/client.rs @@ -247,6 +247,26 @@ impl Client { content_format: Some(vec![lsp::MarkupKind::Markdown]), ..Default::default() }), + code_action: Some(lsp::CodeActionClientCapabilities { + code_action_literal_support: Some(lsp::CodeActionLiteralSupport { + code_action_kind: lsp::CodeActionKindLiteralSupport { + value_set: [ + lsp::CodeActionKind::EMPTY, + lsp::CodeActionKind::QUICKFIX, + lsp::CodeActionKind::REFACTOR, + lsp::CodeActionKind::REFACTOR_EXTRACT, + lsp::CodeActionKind::REFACTOR_INLINE, + lsp::CodeActionKind::REFACTOR_REWRITE, + lsp::CodeActionKind::SOURCE, + lsp::CodeActionKind::SOURCE_ORGANIZE_IMPORTS, + ] + .iter() + .map(|kind| kind.as_str().to_string()) + .collect(), + }, + }), + ..Default::default() + }), ..Default::default() }), window: Some(lsp::WindowClientCapabilities { From bc62f03ac25063667eae731fcb2698d09558187b Mon Sep 17 00:00:00 2001 From: gbaranski Date: Wed, 21 Jul 2021 18:09:50 +0200 Subject: [PATCH 04/17] feat(term): set SPC-a to code_action --- helix-term/src/commands.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index b58e1020ddcd..99b4a3a55548 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -215,6 +215,7 @@ impl Command { append_mode, command_mode, file_picker, + code_action, buffer_picker, symbol_picker, prepend_to_line, @@ -3846,6 +3847,8 @@ mode_info! { "P" => paste_clipboard_before, /// replace selections with clipboard "R" => replace_selections_with_clipboard, + /// show available code actions for current selection + "a" => code_action, /// keep primary selection "space" => keep_primary_selection, } From 5d253dffa190cf4d13dd919e79fcc8d74630cdc0 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Wed, 21 Jul 2021 20:45:41 +0200 Subject: [PATCH 05/17] feat(term): wip on applying code actions --- helix-term/src/commands.rs | 48 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 99b4a3a55548..020b70eeadee 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -9,8 +9,8 @@ use helix_core::{ object, pos_at_coords, regex::{self, Regex}, register::Register, - search, selection, surround, textobject, LineEnding, Position, Range, Rope, RopeGraphemes, - RopeSlice, Selection, SmallVec, Tendril, Transaction, + search, selection, surround, textobject, ChangeSet, LineEnding, Position, Range, Rope, + RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril, Transaction, }; use helix_view::{ @@ -2162,6 +2162,50 @@ pub fn code_action(cx: &mut Context) { lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(), }, move |editor: &mut Editor, code_action, _action| { + match code_action { + lsp::CodeActionOrCommand::Command(command) => { + log::debug!("command: {:?}", command); + } + lsp::CodeActionOrCommand::CodeAction(code_action) => { + log::debug!("code action: {:?}", code_action); + if let Some(ref edit) = code_action.edit { + if let Some(ref changes) = edit.document_changes { + match changes { + lsp::DocumentChanges::Edits(document_edits) => { + for document_edit in document_edits { + for edit in &document_edit.edits { + match edit { + lsp::OneOf::Left(text_edit) => { + let document = editor + .documents() + .find(|doc| { + doc.url().as_ref() + == Some( + &document_edit + .text_document + .uri, + ) + }) + .unwrap(); + + let transaction = Transaction { + changes: todo!(), + selection: todo!(), + }; + } + lsp::OneOf::Right( + annotated_text_edit, + ) => todo!(), + } + } + } + } + lsp::DocumentChanges::Operations(_) => todo!(), + } + } + } + } + } // }, ); From eeca3b5c16657f96856151dd0c7734e9630a81b5 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Wed, 21 Jul 2021 20:49:42 +0200 Subject: [PATCH 06/17] deps: `cargo update` --- Cargo.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d96b7008424..a1f0a798028c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -422,9 +422,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] @@ -460,9 +460,9 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +checksum = "bee0328b1209d157ef001c94dd85b4f8f64139adb0eac2659f4b08382b2f474d" dependencies = [ "cfg-if 1.0.0", ] @@ -494,9 +494,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.97" +version = "0.2.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" +checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790" [[package]] name = "libloading" @@ -662,9 +662,9 @@ checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" [[package]] name = "pin-project-lite" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" [[package]] name = "pin-utils" @@ -994,9 +994,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b5220f05bb7de7f3f53c7c065e1199b3172696fe2db9f9c4d8ad9b4ee74c342" +checksum = "4ac2e1d4bd0f75279cfd5a076e0d578bbf02c22b7c39e766c437dd49b3ec43e0" dependencies = [ "tinyvec_macros", ] @@ -1029,9 +1029,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37" +checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110" dependencies = [ "proc-macro2", "quote", From 70db2f140dbbc5f6b45511bbea3a66a57a271004 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Wed, 21 Jul 2021 23:34:43 +0200 Subject: [PATCH 07/17] feat(term): applying code actions edits --- helix-term/src/commands.rs | 114 +++++++++++++++++++++---------------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 020b70eeadee..2296ca1d7cec 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -9,7 +9,7 @@ use helix_core::{ object, pos_at_coords, regex::{self, Regex}, register::Register, - search, selection, surround, textobject, ChangeSet, LineEnding, Position, Range, Rope, + search, selection, surround, textobject, Change, LineEnding, Position, Range, Rope, RopeGraphemes, RopeSlice, Selection, SmallVec, Tendril, Transaction, }; @@ -2130,6 +2130,34 @@ pub fn workspace_symbol_picker(cx: &mut Context) { ) } +fn apply_edit( + editor: &mut Editor, + edited_document: &lsp::OptionalVersionedTextDocumentIdentifier, + offset_encoding: OffsetEncoding, + edit: &lsp::OneOf, +) { + let (view, doc) = current!(editor); + assert_eq!(doc.url().unwrap(), edited_document.uri); + + match edit { + lsp::OneOf::Left(text_edit) => { + let lsp_pos_to_pos = + |lsp_pos| lsp_pos_to_pos(doc.text(), lsp_pos, offset_encoding).unwrap(); + + // This clone probably could be optimized if Picker::new would give T instead of &T + let text_replacement = Tendril::from(text_edit.new_text.clone()); + let change: Change = ( + lsp_pos_to_pos(text_edit.range.start), + lsp_pos_to_pos(text_edit.range.end), + Some(text_replacement.clone().into()), + ); + let transaction = Transaction::change(doc.text(), std::iter::once(change)); + doc.apply(&transaction, view.id); + } + lsp::OneOf::Right(_annotated_text_edit) => todo!(), + } +} + pub fn code_action(cx: &mut Context) { let (view, doc) = current!(cx.editor); @@ -2137,7 +2165,6 @@ pub fn code_action(cx: &mut Context) { Some(language_server) => language_server, None => return, }; - let offset_encoding = language_server.offset_encoding(); let range = range_to_lsp_range( doc.text(), @@ -2146,10 +2173,11 @@ pub fn code_action(cx: &mut Context) { ); let future = language_server.code_actions(doc.identifier(), range); + let offset_encoding = language_server.offset_encoding().clone(); cx.callback( future, - move |editor: &mut Editor, + move |_editor: &mut Editor, compositor: &mut Compositor, response: Option| { if let Some(actions) = response { @@ -2162,51 +2190,7 @@ pub fn code_action(cx: &mut Context) { lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(), }, move |editor: &mut Editor, code_action, _action| { - match code_action { - lsp::CodeActionOrCommand::Command(command) => { - log::debug!("command: {:?}", command); - } - lsp::CodeActionOrCommand::CodeAction(code_action) => { - log::debug!("code action: {:?}", code_action); - if let Some(ref edit) = code_action.edit { - if let Some(ref changes) = edit.document_changes { - match changes { - lsp::DocumentChanges::Edits(document_edits) => { - for document_edit in document_edits { - for edit in &document_edit.edits { - match edit { - lsp::OneOf::Left(text_edit) => { - let document = editor - .documents() - .find(|doc| { - doc.url().as_ref() - == Some( - &document_edit - .text_document - .uri, - ) - }) - .unwrap(); - - let transaction = Transaction { - changes: todo!(), - selection: todo!(), - }; - } - lsp::OneOf::Right( - annotated_text_edit, - ) => todo!(), - } - } - } - } - lsp::DocumentChanges::Operations(_) => todo!(), - } - } - } - } - } - // + make_code_action_callback(editor, code_action, offset_encoding) }, ); compositor.push(Box::new(picker)) @@ -2215,6 +2199,40 @@ pub fn code_action(cx: &mut Context) { ) } +fn make_code_action_callback( + editor: &mut Editor, + code_action: &lsp::CodeActionOrCommand, + offset_encoding: OffsetEncoding, +) { + match code_action { + lsp::CodeActionOrCommand::Command(command) => { + log::debug!("command: {:?}", command); + } + lsp::CodeActionOrCommand::CodeAction(code_action) => { + log::debug!("code action: {:?}", code_action); + if let Some(ref edit) = code_action.edit { + if let Some(ref changes) = edit.document_changes { + match changes { + lsp::DocumentChanges::Edits(document_edits) => { + for document_edit in document_edits { + for edit in &document_edit.edits { + apply_edit( + editor, + &document_edit.text_document, + offset_encoding, + edit, + ); + } + } + } + lsp::DocumentChanges::Operations(_) => todo!(), + } + } + } + } + } +} + // I inserts at the first nonwhitespace character of each line with a selection fn prepend_to_line(cx: &mut Context) { goto_first_nonwhitespace(cx); From 9dcdb55e82af1913a0bb0401114369d18f277148 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Thu, 22 Jul 2021 10:57:32 +0200 Subject: [PATCH 08/17] fix(term): cleanup of apply_edit --- helix-term/src/commands.rs | 36 ++++++++++++++++++++++-------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 2296ca1d7cec..900c4b726bef 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2139,22 +2139,30 @@ fn apply_edit( let (view, doc) = current!(editor); assert_eq!(doc.url().unwrap(), edited_document.uri); + let mut apply_text_edit = |text_edit: &lsp::TextEdit| { + let lsp_pos_to_pos = + |lsp_pos| lsp_pos_to_pos(doc.text(), lsp_pos, offset_encoding).unwrap(); + + // This clone probably could be optimized if Picker::new would give T instead of &T + let text_replacement = Tendril::from(text_edit.new_text.clone()); + let change: Change = ( + lsp_pos_to_pos(text_edit.range.start), + lsp_pos_to_pos(text_edit.range.end), + Some(text_replacement.clone().into()), + ); + let transaction = Transaction::change(doc.text(), std::iter::once(change)); + doc.apply(&transaction, view.id); + }; + match edit { - lsp::OneOf::Left(text_edit) => { - let lsp_pos_to_pos = - |lsp_pos| lsp_pos_to_pos(doc.text(), lsp_pos, offset_encoding).unwrap(); - - // This clone probably could be optimized if Picker::new would give T instead of &T - let text_replacement = Tendril::from(text_edit.new_text.clone()); - let change: Change = ( - lsp_pos_to_pos(text_edit.range.start), - lsp_pos_to_pos(text_edit.range.end), - Some(text_replacement.clone().into()), - ); - let transaction = Transaction::change(doc.text(), std::iter::once(change)); - doc.apply(&transaction, view.id); + lsp::OneOf::Left(text_edit) => apply_text_edit(text_edit), + lsp::OneOf::Right(annotated_text_edit) => { + apply_text_edit(&annotated_text_edit.text_edit); + log::error!( + "annotation {} could not be handled", + annotated_text_edit.annotation_id + ); // TODO: Handle annotations } - lsp::OneOf::Right(_annotated_text_edit) => todo!(), } } From 32facf1b4d8b8f2d4d960568774124033e2bcc3a Mon Sep 17 00:00:00 2001 From: gbaranski Date: Thu, 22 Jul 2021 11:35:17 +0200 Subject: [PATCH 09/17] fix(term): applying edits as a whole thing instead one by one --- helix-term/src/commands.rs | 62 +++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 35 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 900c4b726bef..55720bef8c9d 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2130,40 +2130,34 @@ pub fn workspace_symbol_picker(cx: &mut Context) { ) } -fn apply_edit( +fn apply_edits( editor: &mut Editor, edited_document: &lsp::OptionalVersionedTextDocumentIdentifier, offset_encoding: OffsetEncoding, - edit: &lsp::OneOf, + edits: &Vec>, ) { let (view, doc) = current!(editor); assert_eq!(doc.url().unwrap(), edited_document.uri); + let lsp_pos_to_pos = |lsp_pos| lsp_pos_to_pos(doc.text(), lsp_pos, offset_encoding).unwrap(); - let mut apply_text_edit = |text_edit: &lsp::TextEdit| { - let lsp_pos_to_pos = - |lsp_pos| lsp_pos_to_pos(doc.text(), lsp_pos, offset_encoding).unwrap(); - - // This clone probably could be optimized if Picker::new would give T instead of &T - let text_replacement = Tendril::from(text_edit.new_text.clone()); - let change: Change = ( - lsp_pos_to_pos(text_edit.range.start), - lsp_pos_to_pos(text_edit.range.end), - Some(text_replacement.clone().into()), - ); - let transaction = Transaction::change(doc.text(), std::iter::once(change)); - doc.apply(&transaction, view.id); - }; - - match edit { - lsp::OneOf::Left(text_edit) => apply_text_edit(text_edit), - lsp::OneOf::Right(annotated_text_edit) => { - apply_text_edit(&annotated_text_edit.text_edit); - log::error!( - "annotation {} could not be handled", - annotated_text_edit.annotation_id - ); // TODO: Handle annotations - } - } + let changes = edits + .iter() + .map(|edit| match edit { + lsp::OneOf::Left(text_edit) => text_edit, + lsp::OneOf::Right(annotated_text_edit) => &annotated_text_edit.text_edit, // TODO: Handle annotations + }) + .map(|edit| -> Change { + log::debug!("text edit: {:?}", edit); + // This clone probably could be optimized if Picker::new would give T instead of &T + let text_replacement = Tendril::from(edit.new_text.clone()); + ( + lsp_pos_to_pos(edit.range.start), + lsp_pos_to_pos(edit.range.end), + Some(text_replacement.clone().into()), + ) + }); + let transaction = Transaction::change(doc.text(), changes); + doc.apply(&transaction, view.id); } pub fn code_action(cx: &mut Context) { @@ -2223,14 +2217,12 @@ fn make_code_action_callback( match changes { lsp::DocumentChanges::Edits(document_edits) => { for document_edit in document_edits { - for edit in &document_edit.edits { - apply_edit( - editor, - &document_edit.text_document, - offset_encoding, - edit, - ); - } + apply_edits( + editor, + &document_edit.text_document, + offset_encoding, + &document_edit.edits, + ); } } lsp::DocumentChanges::Operations(_) => todo!(), From 70dfc981f13e5d76916196167b604d7b0a3d6712 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Thu, 22 Jul 2021 11:44:13 +0200 Subject: [PATCH 10/17] refactor(term): move apply_edits below --- helix-term/src/commands.rs | 60 +++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 55720bef8c9d..b42092903609 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2130,36 +2130,6 @@ pub fn workspace_symbol_picker(cx: &mut Context) { ) } -fn apply_edits( - editor: &mut Editor, - edited_document: &lsp::OptionalVersionedTextDocumentIdentifier, - offset_encoding: OffsetEncoding, - edits: &Vec>, -) { - let (view, doc) = current!(editor); - assert_eq!(doc.url().unwrap(), edited_document.uri); - let lsp_pos_to_pos = |lsp_pos| lsp_pos_to_pos(doc.text(), lsp_pos, offset_encoding).unwrap(); - - let changes = edits - .iter() - .map(|edit| match edit { - lsp::OneOf::Left(text_edit) => text_edit, - lsp::OneOf::Right(annotated_text_edit) => &annotated_text_edit.text_edit, // TODO: Handle annotations - }) - .map(|edit| -> Change { - log::debug!("text edit: {:?}", edit); - // This clone probably could be optimized if Picker::new would give T instead of &T - let text_replacement = Tendril::from(edit.new_text.clone()); - ( - lsp_pos_to_pos(edit.range.start), - lsp_pos_to_pos(edit.range.end), - Some(text_replacement.clone().into()), - ) - }); - let transaction = Transaction::change(doc.text(), changes); - doc.apply(&transaction, view.id); -} - pub fn code_action(cx: &mut Context) { let (view, doc) = current!(cx.editor); @@ -2233,6 +2203,36 @@ fn make_code_action_callback( } } +fn apply_edits( + editor: &mut Editor, + edited_document: &lsp::OptionalVersionedTextDocumentIdentifier, + offset_encoding: OffsetEncoding, + edits: &Vec>, +) { + let (view, doc) = current!(editor); + assert_eq!(doc.url().unwrap(), edited_document.uri); + let lsp_pos_to_pos = |lsp_pos| lsp_pos_to_pos(doc.text(), lsp_pos, offset_encoding).unwrap(); + + let changes = edits + .iter() + .map(|edit| match edit { + lsp::OneOf::Left(text_edit) => text_edit, + lsp::OneOf::Right(annotated_text_edit) => &annotated_text_edit.text_edit, // TODO: Handle annotations + }) + .map(|edit| -> Change { + log::debug!("text edit: {:?}", edit); + // This clone probably could be optimized if Picker::new would give T instead of &T + let text_replacement = Tendril::from(edit.new_text.clone()); + ( + lsp_pos_to_pos(edit.range.start), + lsp_pos_to_pos(edit.range.end), + Some(text_replacement.clone().into()), + ) + }); + let transaction = Transaction::change(doc.text(), changes); + doc.apply(&transaction, view.id); +} + // I inserts at the first nonwhitespace character of each line with a selection fn prepend_to_line(cx: &mut Context) { goto_first_nonwhitespace(cx); From 1661bf032e795738563f31b5a60a7a603ba50bca Mon Sep 17 00:00:00 2001 From: gbaranski Date: Thu, 22 Jul 2021 11:48:39 +0200 Subject: [PATCH 11/17] fix(term): improve unimplemented messages for further investigation --- helix-term/src/commands.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index b42092903609..9888234b4369 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2161,7 +2161,7 @@ pub fn code_action(cx: &mut Context) { } lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(), }, - move |editor: &mut Editor, code_action, _action| { + move |editor, code_action, _action| { make_code_action_callback(editor, code_action, offset_encoding) }, ); @@ -2178,7 +2178,7 @@ fn make_code_action_callback( ) { match code_action { lsp::CodeActionOrCommand::Command(command) => { - log::debug!("command: {:?}", command); + todo!("command: {:?}", command); } lsp::CodeActionOrCommand::CodeAction(code_action) => { log::debug!("code action: {:?}", code_action); @@ -2195,7 +2195,9 @@ fn make_code_action_callback( ); } } - lsp::DocumentChanges::Operations(_) => todo!(), + lsp::DocumentChanges::Operations(operations) => { + todo!("operations: {:?}", operations) + } } } } From daeb8e52ca31d32ed8d9e1f5fe77c5ed7a98b5eb Mon Sep 17 00:00:00 2001 From: Grzegorz Baranski Date: Thu, 22 Jul 2021 11:51:52 +0200 Subject: [PATCH 12/17] fix(term): change code action command comment Co-authored-by: Ivan Tham --- helix-term/src/commands.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 9888234b4369..9ade80cf70c1 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -3911,7 +3911,7 @@ mode_info! { "P" => paste_clipboard_before, /// replace selections with clipboard "R" => replace_selections_with_clipboard, - /// show available code actions for current selection + /// perform code action "a" => code_action, /// keep primary selection "space" => keep_primary_selection, From b3f0d72268238c839b4d1881cdc0d6b3fef79160 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Thu, 22 Jul 2021 19:50:55 +0200 Subject: [PATCH 13/17] fix(term): add matching `}` --- helix-term/src/commands.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index c58c84c59846..30949f50d0b7 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2234,7 +2234,8 @@ fn apply_edits( }); let transaction = Transaction::change(doc.text(), changes); doc.apply(&transaction, view.id); - +} + fn last_picker(cx: &mut Context) { // TODO: last picker does not seemed to work well with buffer_picker cx.callback = Some(Box::new(|compositor: &mut Compositor| { From 36499a8cae99de922c83cbb614d88ef4841083fa Mon Sep 17 00:00:00 2001 From: gbaranski Date: Fri, 23 Jul 2021 11:32:17 +0200 Subject: [PATCH 14/17] fix(term): cleanup, todo!() on workspace edit --- helix-term/src/commands.rs | 109 +++++++++++++++++++------------------ 1 file changed, 57 insertions(+), 52 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 30949f50d0b7..87cd0078c0fd 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2162,8 +2162,16 @@ pub fn code_action(cx: &mut Context) { } lsp::CodeActionOrCommand::Command(command) => command.title.as_str().into(), }, - move |editor, code_action, _action| { - make_code_action_callback(editor, code_action, offset_encoding) + move |editor, code_action, _action| match code_action { + lsp::CodeActionOrCommand::Command(command) => { + todo!("command: {:?}", command); + } + lsp::CodeActionOrCommand::CodeAction(code_action) => { + log::debug!("code action: {:?}", code_action); + if let Some(ref workspace_edit) = code_action.edit { + apply_workspace_edit(editor, offset_encoding, workspace_edit) + } + } }, ); compositor.push(Box::new(picker)) @@ -2172,57 +2180,14 @@ pub fn code_action(cx: &mut Context) { ) } -fn make_code_action_callback( - editor: &mut Editor, - code_action: &lsp::CodeActionOrCommand, - offset_encoding: OffsetEncoding, -) { - match code_action { - lsp::CodeActionOrCommand::Command(command) => { - todo!("command: {:?}", command); - } - lsp::CodeActionOrCommand::CodeAction(code_action) => { - log::debug!("code action: {:?}", code_action); - if let Some(ref edit) = code_action.edit { - if let Some(ref changes) = edit.document_changes { - match changes { - lsp::DocumentChanges::Edits(document_edits) => { - for document_edit in document_edits { - apply_edits( - editor, - &document_edit.text_document, - offset_encoding, - &document_edit.edits, - ); - } - } - lsp::DocumentChanges::Operations(operations) => { - todo!("operations: {:?}", operations) - } - } - } - } - } - } -} - -fn apply_edits( +fn apply_workspace_edit( editor: &mut Editor, - edited_document: &lsp::OptionalVersionedTextDocumentIdentifier, offset_encoding: OffsetEncoding, - edits: &Vec>, + workspace_edit: &lsp::WorkspaceEdit, ) { - let (view, doc) = current!(editor); - assert_eq!(doc.url().unwrap(), edited_document.uri); - let lsp_pos_to_pos = |lsp_pos| lsp_pos_to_pos(doc.text(), lsp_pos, offset_encoding).unwrap(); - - let changes = edits - .iter() - .map(|edit| match edit { - lsp::OneOf::Left(text_edit) => text_edit, - lsp::OneOf::Right(annotated_text_edit) => &annotated_text_edit.text_edit, // TODO: Handle annotations - }) - .map(|edit| -> Change { + let edits_to_transaction = |doc: &Rope, edits: &Vec<&lsp::TextEdit>| { + let lsp_pos_to_pos = |lsp_pos| lsp_pos_to_pos(&doc, lsp_pos, offset_encoding).unwrap(); + let changes = edits.iter().map(|edit| -> Change { log::debug!("text edit: {:?}", edit); // This clone probably could be optimized if Picker::new would give T instead of &T let text_replacement = Tendril::from(edit.new_text.clone()); @@ -2232,8 +2197,48 @@ fn apply_edits( Some(text_replacement.clone().into()), ) }); - let transaction = Transaction::change(doc.text(), changes); - doc.apply(&transaction, view.id); + Transaction::change(doc, changes) + }; + + if let Some(ref changes) = workspace_edit.changes { + todo!("workspace changes: {:?}", changes); + // 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 + // for (url, edits) in changes.iter() { + // let file_path = url.origin().ascii_serialization(); + // let file_path = std::path::PathBuf::from(file_path); + // let file = std::fs::File::open(file_path).unwrap(); + // let mut text = Rope::from_reader(file).unwrap(); + // let transaction = edits_to_changes(&text, edits); + // transaction.apply(&mut text); + // } + } + + if let Some(ref document_changes) = workspace_edit.document_changes { + match document_changes { + lsp::DocumentChanges::Edits(document_edits) => { + for document_edit in document_edits { + let (view, doc) = current!(editor); + assert_eq!(doc.url().unwrap(), document_edit.text_document.uri); + 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 + } + }) + .collect(); + let transaction = edits_to_transaction(doc.text(), &edits); + doc.apply(&transaction, view.id); + } + } + lsp::DocumentChanges::Operations(operations) => { + todo!("operations: {:?}", operations) + } + } + } } fn last_picker(cx: &mut Context) { From 02c13b8d6a4816ee03be0364268a4023bcea4a90 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Fri, 23 Jul 2021 11:34:08 +0200 Subject: [PATCH 15/17] fix(term): remove unrelated workspace_symbol_picker --- helix-term/src/commands.rs | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 87cd0078c0fd..3c373089d991 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2093,44 +2093,6 @@ fn symbol_picker(cx: &mut Context) { ) } -pub fn workspace_symbol_picker(cx: &mut Context) { - 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 future = language_server.workspace_symbols("".to_string()); - - cx.callback( - future, - move |editor: &mut Editor, - compositor: &mut Compositor, - response: Option>| { - if let Some(symbols) = response { - let picker = Picker::new( - symbols, - |symbol| (&symbol.name).into(), - move |editor: &mut Editor, symbol, _action| { - push_jump(editor); - let (view, doc) = current!(editor); - - // if let Some(range) = - // lsp_range_to_range(doc.text(), symbol.location.range, offset_encoding) - // { - // doc.set_selection(view.id, Selection::single(range.to(), range.from())); - // align_view(doc, view, Align::Center); - // } - }, - ); - compositor.push(Box::new(picker)) - } - }, - ) -} - pub fn code_action(cx: &mut Context) { let (view, doc) = current!(cx.editor); From 6dd84e109a6d0d6e69fad07ddcfe3c49c2ce231c Mon Sep 17 00:00:00 2001 From: gbaranski Date: Fri, 23 Jul 2021 11:36:12 +0200 Subject: [PATCH 16/17] fix(term): apply cargo-clippy suggestions --- helix-term/src/commands.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 3c373089d991..284f82951209 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2108,7 +2108,7 @@ pub fn code_action(cx: &mut Context) { ); let future = language_server.code_actions(doc.identifier(), range); - let offset_encoding = language_server.offset_encoding().clone(); + let offset_encoding = language_server.offset_encoding(); cx.callback( future, @@ -2156,7 +2156,7 @@ fn apply_workspace_edit( ( lsp_pos_to_pos(edit.range.start), lsp_pos_to_pos(edit.range.end), - Some(text_replacement.clone().into()), + Some(text_replacement), ) }); Transaction::change(doc, changes) From 4907c63a1cd4e4882407366867307431add72e44 Mon Sep 17 00:00:00 2001 From: gbaranski Date: Fri, 23 Jul 2021 18:17:24 +0200 Subject: [PATCH 17/17] fix(term): replace todo!'s with editor.set_error --- helix-term/src/commands.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/helix-term/src/commands.rs b/helix-term/src/commands.rs index 284f82951209..24c53897c167 100644 --- a/helix-term/src/commands.rs +++ b/helix-term/src/commands.rs @@ -2126,7 +2126,8 @@ pub fn code_action(cx: &mut Context) { }, move |editor, code_action, _action| match code_action { lsp::CodeActionOrCommand::Command(command) => { - todo!("command: {:?}", command); + log::debug!("code action command: {:?}", command); + editor.set_error(String::from("Handling code action command is not implemented yet, see https://github.com/helix-editor/helix/issues/183")); } lsp::CodeActionOrCommand::CodeAction(code_action) => { log::debug!("code action: {:?}", code_action); @@ -2163,7 +2164,9 @@ fn apply_workspace_edit( }; if let Some(ref changes) = workspace_edit.changes { - todo!("workspace changes: {:?}", changes); + log::debug!("workspace changes: {:?}", changes); + editor.set_error(String::from("Handling workspace changesis not implemented yet, see https://github.com/helix-editor/helix/issues/183")); + 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 // for (url, edits) in changes.iter() { @@ -2197,7 +2200,8 @@ fn apply_workspace_edit( } } lsp::DocumentChanges::Operations(operations) => { - todo!("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")); } } }