on master help sign in
Make repeat operator work with completion edits (#1640)
14e2cedMateusz S. Szczygieł
helix-term/src/ui/completion.rs+28−5
@@ -1,10 +1,11 @@
1use crate::compositor::{Component, Context, EventResult};
1use crate::compositor::{Component, Context, EventResult};
2use crossterm::event::{Event, KeyCode, KeyEvent};
2use crossterm::event::{Event, KeyCode, KeyEvent};
3+use helix_view::editor::CompleteAction;
3use tui::buffer::Buffer as Surface;
4use tui::buffer::Buffer as Surface;
4
5
5use std::borrow::Cow;
6use std::borrow::Cow;
6
7
7−use helix_core::Transaction;
8+use helix_core::{Change, Transaction};
8use helix_view::{graphics::Rect, Document, Editor};
9use helix_view::{graphics::Rect, Document, Editor};
9
10
10use crate::commands;
11use crate::commands;
@@ -92,13 +93,14 @@ impl Completion {
92 start_offset: usize,
93 start_offset: usize,
93 trigger_offset: usize,
94 trigger_offset: usize,
94 ) -> Transaction {
95 ) -> Transaction {
95− if let Some(edit) = &item.text_edit {
96+ let transaction = if let Some(edit) = &item.text_edit {
96 let edit = match edit {
97 let edit = match edit {
97 lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
98 lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
98 lsp::CompletionTextEdit::InsertAndReplace(item) => {
99 lsp::CompletionTextEdit::InsertAndReplace(item) => {
99 unimplemented!("completion: insert_and_replace {:?}", item)
100 unimplemented!("completion: insert_and_replace {:?}", item)
100 }
101 }
101 };
102 };
103+
102 util::generate_transaction_from_edits(
104 util::generate_transaction_from_edits(
103 doc.text(),
105 doc.text(),
104 vec![edit],
106 vec![edit],
@@ -114,7 +116,16 @@ impl Completion {
114 doc.text(),
116 doc.text(),
115 vec![(trigger_offset, trigger_offset, Some(text.into()))].into_iter(),
117 vec![(trigger_offset, trigger_offset, Some(text.into()))].into_iter(),
116 )
118 )
117− }
119+ };
120+
121+ transaction
122+ }
123+
124+ fn completion_changes(transaction: &Transaction, trigger_offset: usize) -> Vec<Change> {
125+ transaction
126+ .changes_iter()
127+ .filter(|(start, end, _)| (*start..=*end).contains(&trigger_offset))
128+ .collect()
118 }
129 }
119
130
120 let (view, doc) = current!(editor);
131 let (view, doc) = current!(editor);
@@ -123,7 +134,9 @@ impl Completion {
123 doc.restore(view.id);
134 doc.restore(view.id);
124
135
125 match event {
136 match event {
126− PromptEvent::Abort => {}
137+ PromptEvent::Abort => {
138+ editor.last_completion = None;
139+ }
127 PromptEvent::Update => {
140 PromptEvent::Update => {
128 // always present here
141 // always present here
129 let item = item.unwrap();
142 let item = item.unwrap();
@@ -138,8 +151,12 @@ impl Completion {
138
151
139 // initialize a savepoint
152 // initialize a savepoint
140 doc.savepoint();
153 doc.savepoint();
141−
142 doc.apply(&transaction, view.id);
154 doc.apply(&transaction, view.id);
155+
156+ editor.last_completion = Some(CompleteAction {
157+ trigger_offset,
158+ changes: completion_changes(&transaction, trigger_offset),
159+ });
143 }
160 }
144 PromptEvent::Validate => {
161 PromptEvent::Validate => {
145 // always present here
162 // always present here
@@ -152,8 +169,14 @@ impl Completion {
152 start_offset,
169 start_offset,
153 trigger_offset,
170 trigger_offset,
154 );
171 );
172+
155 doc.apply(&transaction, view.id);
173 doc.apply(&transaction, view.id);
156
174
175+ editor.last_completion = Some(CompleteAction {
176+ trigger_offset,
177+ changes: completion_changes(&transaction, trigger_offset),
178+ });
179+
157 // apply additional edits, mostly used to auto import unqualified types
180 // apply additional edits, mostly used to auto import unqualified types
158 let resolved_additional_text_edits = if item.additional_text_edits.is_some() {
181 let resolved_additional_text_edits = if item.additional_text_edits.is_some() {
159 None
182 None
helix-term/src/ui/editor.rs+47−8
@@ -15,11 +15,11 @@ use helix_core::{
15 syntax::{self, HighlightEvent},
15 syntax::{self, HighlightEvent},
16 unicode::segmentation::UnicodeSegmentation,
16 unicode::segmentation::UnicodeSegmentation,
17 unicode::width::UnicodeWidthStr,
17 unicode::width::UnicodeWidthStr,
18− LineEnding, Position, Range, Selection,
18+ LineEnding, Position, Range, Selection, Transaction,
19};
19};
20use helix_view::{
20use helix_view::{
21 document::{Mode, SCRATCH_BUFFER_NAME},
21 document::{Mode, SCRATCH_BUFFER_NAME},
22− editor::CursorShapeConfig,
22+ editor::{CompleteAction, CursorShapeConfig},
23 graphics::{CursorKind, Modifier, Rect, Style},
23 graphics::{CursorKind, Modifier, Rect, Style},
24 input::KeyEvent,
24 input::KeyEvent,
25 keyboard::{KeyCode, KeyModifiers},
25 keyboard::{KeyCode, KeyModifiers},
@@ -33,11 +33,18 @@ use tui::buffer::Buffer as Surface;
33pub struct EditorView {
33pub struct EditorView {
34 pub keymaps: Keymaps,
34 pub keymaps: Keymaps,
35 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
35 on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
36− last_insert: (commands::MappableCommand, Vec<KeyEvent>),
36+ last_insert: (commands::MappableCommand, Vec<InsertEvent>),
37 pub(crate) completion: Option<Completion>,
37 pub(crate) completion: Option<Completion>,
38 spinners: ProgressSpinners,
38 spinners: ProgressSpinners,
39}
39}
40
40
41+#[derive(Debug, Clone)]
42+pub enum InsertEvent {
43+ Key(KeyEvent),
44+ CompletionApply(CompleteAction),
45+ TriggerCompletion,
46+}
47+
41impl Default for EditorView {
48impl Default for EditorView {
42 fn default() -> Self {
49 fn default() -> Self {
43 Self::new(Keymaps::default())
50 Self::new(Keymaps::default())
@@ -766,8 +773,33 @@ impl EditorView {
766 // first execute whatever put us into insert mode
773 // first execute whatever put us into insert mode
767 self.last_insert.0.execute(cxt);
774 self.last_insert.0.execute(cxt);
768 // then replay the inputs
775 // then replay the inputs
769− for &key in &self.last_insert.1.clone() {
776+ for key in self.last_insert.1.clone() {
777+ match key {
770− self.insert_mode(cxt, key)
778+ InsertEvent::Key(key) => self.insert_mode(cxt, key),
779+ InsertEvent::CompletionApply(compl) => {
780+ let (view, doc) = current!(cxt.editor);
781+
782+ doc.restore(view.id);
783+
784+ let text = doc.text().slice(..);
785+ let cursor = doc.selection(view.id).primary().cursor(text);
786+
787+ let shift_position =
788+ |pos: usize| -> usize { pos + cursor - compl.trigger_offset };
789+
790+ let tx = Transaction::change(
791+ doc.text(),
792+ compl.changes.iter().cloned().map(|(start, end, t)| {
793+ (shift_position(start), shift_position(end), t)
794+ }),
795+ );
796+ doc.apply(&tx, view.id);
797+ }
798+ InsertEvent::TriggerCompletion => {
799+ let (_, doc) = current!(cxt.editor);
800+ doc.savepoint();
801+ }
802+ }
771 }
803 }
772 }
804 }
773 _ => {
805 _ => {
@@ -808,6 +840,9 @@ impl EditorView {
808 // Immediately initialize a savepoint
840 // Immediately initialize a savepoint
809 doc_mut!(editor).savepoint();
841 doc_mut!(editor).savepoint();
810
842
843+ editor.last_completion = None;
844+ self.last_insert.1.push(InsertEvent::TriggerCompletion);
845+
811 // TODO : propagate required size on resize to completion too
846 // TODO : propagate required size on resize to completion too
812 completion.required_size((size.width, size.height));
847 completion.required_size((size.width, size.height));
813 self.completion = Some(completion);
848 self.completion = Some(completion);
@@ -1067,9 +1102,6 @@ impl Component for EditorView {
1067 } else {
1102 } else {
1068 match mode {
1103 match mode {
1069 Mode::Insert => {
1104 Mode::Insert => {
1070− // record last_insert key
1071− self.last_insert.1.push(key);
1072−
1073 // let completion swallow the event if necessary
1105 // let completion swallow the event if necessary
1074 let mut consumed = false;
1106 let mut consumed = false;
1075 if let Some(completion) = &mut self.completion {
1107 if let Some(completion) = &mut self.completion {
@@ -1093,8 +1125,15 @@ impl Component for EditorView {
1093
1125
1094 // if completion didn't take the event, we pass it onto commands
1126 // if completion didn't take the event, we pass it onto commands
1095 if !consumed {
1127 if !consumed {
1128+ if let Some(compl) = cx.editor.last_completion.take() {
1129+ self.last_insert.1.push(InsertEvent::CompletionApply(compl));
1130+ }
1131+
1096 self.insert_mode(&mut cx, key);
1132 self.insert_mode(&mut cx, key);
1097
1133
1134+ // record last_insert key
1135+ self.last_insert.1.push(InsertEvent::Key(key));
1136+
1098 // lastly we recalculate completion
1137 // lastly we recalculate completion
1099 if let Some(completion) = &mut self.completion {
1138 if let Some(completion) = &mut self.completion {
1100 completion.update(&mut cx);
1139 completion.update(&mut cx);
helix-view/src/editor.rs+10
@@ -33,6 +33,7 @@ pub use helix_core::register::Registers;
33use helix_core::{
33use helix_core::{
34 auto_pairs::AutoPairs,
34 auto_pairs::AutoPairs,
35 syntax::{self, AutoPairConfig},
35 syntax::{self, AutoPairConfig},
36+ Change,
36};
37};
37use helix_core::{Position, Selection};
38use helix_core::{Position, Selection};
38use helix_dap as dap;
39use helix_dap as dap;
@@ -301,9 +302,17 @@ pub struct Editor {
301 pub last_motion: Option<Motion>,
302 pub last_motion: Option<Motion>,
302 pub pseudo_pending: Option<String>,
303 pub pseudo_pending: Option<String>,
303
304
305+ pub last_completion: Option<CompleteAction>,
306+
304 pub exit_code: i32,
307 pub exit_code: i32,
305}
308}
306
309
310+#[derive(Debug, Clone)]
311+pub struct CompleteAction {
312+ pub trigger_offset: usize,
313+ pub changes: Vec<Change>,
314+}
315+
307#[derive(Debug, Copy, Clone)]
316#[derive(Debug, Copy, Clone)]
308pub enum Action {
317pub enum Action {
309 Load,
318 Load,
@@ -347,6 +356,7 @@ impl Editor {
347 autoinfo: None,
356 autoinfo: None,
348 idle_timer: Box::pin(sleep(config.idle_timeout)),
357 idle_timer: Box::pin(sleep(config.idle_timeout)),
349 last_motion: None,
358 last_motion: None,
359+ last_completion: None,
350 pseudo_pending: None,
360 pseudo_pending: None,
351 config,
361 config,
352 auto_pairs,
362 auto_pairs,