aboutsummaryrefslogtreecommitdiff
path: root/helix-term
diff options
context:
space:
mode:
authorMateusz S. Szczygieł2022-03-01 01:45:29 +0000
committerGitHub2022-03-01 01:45:29 +0000
commit14e2ced440a2ba0f1794644f5cc1295a5738ad36 (patch)
treea0ef9ea2b0c1e6ff8a6677550bcfba7d78038d57 /helix-term
parente83cdf3fd33a43e1ff78793995adbe23bd62ae49 (diff)
Make repeat operator work with completion edits (#1640)
* add basic completion replay * use transaction as the last completion * completion replay only on trigger position * cache changes in CompletionAction Co-authored-by: Blaž Hrastnik <blaz@mxxn.io>
Diffstat (limited to 'helix-term')
-rw-r--r--helix-term/src/ui/completion.rs33
-rw-r--r--helix-term/src/ui/editor.rs55
2 files changed, 75 insertions, 13 deletions
diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs
index a6e92a19..abed2846 100644
--- a/helix-term/src/ui/completion.rs
+++ b/helix-term/src/ui/completion.rs
@@ -1,10 +1,11 @@
use crate::compositor::{Component, Context, EventResult};
use crossterm::event::{Event, KeyCode, KeyEvent};
+use helix_view::editor::CompleteAction;
use tui::buffer::Buffer as Surface;
use std::borrow::Cow;
-use helix_core::Transaction;
+use helix_core::{Change, Transaction};
use helix_view::{graphics::Rect, Document, Editor};
use crate::commands;
@@ -92,13 +93,14 @@ impl Completion {
start_offset: usize,
trigger_offset: usize,
) -> Transaction {
- if let Some(edit) = &item.text_edit {
+ let transaction = if let Some(edit) = &item.text_edit {
let edit = match edit {
lsp::CompletionTextEdit::Edit(edit) => edit.clone(),
lsp::CompletionTextEdit::InsertAndReplace(item) => {
unimplemented!("completion: insert_and_replace {:?}", item)
}
};
+
util::generate_transaction_from_edits(
doc.text(),
vec![edit],
@@ -114,7 +116,16 @@ impl Completion {
doc.text(),
vec![(trigger_offset, trigger_offset, Some(text.into()))].into_iter(),
)
- }
+ };
+
+ transaction
+ }
+
+ fn completion_changes(transaction: &Transaction, trigger_offset: usize) -> Vec<Change> {
+ transaction
+ .changes_iter()
+ .filter(|(start, end, _)| (*start..=*end).contains(&trigger_offset))
+ .collect()
}
let (view, doc) = current!(editor);
@@ -123,7 +134,9 @@ impl Completion {
doc.restore(view.id);
match event {
- PromptEvent::Abort => {}
+ PromptEvent::Abort => {
+ editor.last_completion = None;
+ }
PromptEvent::Update => {
// always present here
let item = item.unwrap();
@@ -138,8 +151,12 @@ impl Completion {
// initialize a savepoint
doc.savepoint();
-
doc.apply(&transaction, view.id);
+
+ editor.last_completion = Some(CompleteAction {
+ trigger_offset,
+ changes: completion_changes(&transaction, trigger_offset),
+ });
}
PromptEvent::Validate => {
// always present here
@@ -152,8 +169,14 @@ impl Completion {
start_offset,
trigger_offset,
);
+
doc.apply(&transaction, view.id);
+ editor.last_completion = Some(CompleteAction {
+ trigger_offset,
+ changes: completion_changes(&transaction, trigger_offset),
+ });
+
// apply additional edits, mostly used to auto import unqualified types
let resolved_additional_text_edits = if item.additional_text_edits.is_some() {
None
diff --git a/helix-term/src/ui/editor.rs b/helix-term/src/ui/editor.rs
index b6aaf9e0..9ac72406 100644
--- a/helix-term/src/ui/editor.rs
+++ b/helix-term/src/ui/editor.rs
@@ -15,11 +15,11 @@ use helix_core::{
syntax::{self, HighlightEvent},
unicode::segmentation::UnicodeSegmentation,
unicode::width::UnicodeWidthStr,
- LineEnding, Position, Range, Selection,
+ LineEnding, Position, Range, Selection, Transaction,
};
use helix_view::{
document::{Mode, SCRATCH_BUFFER_NAME},
- editor::CursorShapeConfig,
+ editor::{CompleteAction, CursorShapeConfig},
graphics::{CursorKind, Modifier, Rect, Style},
input::KeyEvent,
keyboard::{KeyCode, KeyModifiers},
@@ -33,11 +33,18 @@ use tui::buffer::Buffer as Surface;
pub struct EditorView {
pub keymaps: Keymaps,
on_next_key: Option<Box<dyn FnOnce(&mut commands::Context, KeyEvent)>>,
- last_insert: (commands::MappableCommand, Vec<KeyEvent>),
+ last_insert: (commands::MappableCommand, Vec<InsertEvent>),
pub(crate) completion: Option<Completion>,
spinners: ProgressSpinners,
}
+#[derive(Debug, Clone)]
+pub enum InsertEvent {
+ Key(KeyEvent),
+ CompletionApply(CompleteAction),
+ TriggerCompletion,
+}
+
impl Default for EditorView {
fn default() -> Self {
Self::new(Keymaps::default())
@@ -766,8 +773,33 @@ impl EditorView {
// first execute whatever put us into insert mode
self.last_insert.0.execute(cxt);
// then replay the inputs
- for &key in &self.last_insert.1.clone() {
- self.insert_mode(cxt, key)
+ for key in self.last_insert.1.clone() {
+ match key {
+ InsertEvent::Key(key) => self.insert_mode(cxt, key),
+ InsertEvent::CompletionApply(compl) => {
+ let (view, doc) = current!(cxt.editor);
+
+ doc.restore(view.id);
+
+ let text = doc.text().slice(..);
+ let cursor = doc.selection(view.id).primary().cursor(text);
+
+ let shift_position =
+ |pos: usize| -> usize { pos + cursor - compl.trigger_offset };
+
+ let tx = Transaction::change(
+ doc.text(),
+ compl.changes.iter().cloned().map(|(start, end, t)| {
+ (shift_position(start), shift_position(end), t)
+ }),
+ );
+ doc.apply(&tx, view.id);
+ }
+ InsertEvent::TriggerCompletion => {
+ let (_, doc) = current!(cxt.editor);
+ doc.savepoint();
+ }
+ }
}
}
_ => {
@@ -808,6 +840,9 @@ impl EditorView {
// Immediately initialize a savepoint
doc_mut!(editor).savepoint();
+ editor.last_completion = None;
+ self.last_insert.1.push(InsertEvent::TriggerCompletion);
+
// TODO : propagate required size on resize to completion too
completion.required_size((size.width, size.height));
self.completion = Some(completion);
@@ -1067,9 +1102,6 @@ impl Component for EditorView {
} else {
match mode {
Mode::Insert => {
- // record last_insert key
- self.last_insert.1.push(key);
-
// let completion swallow the event if necessary
let mut consumed = false;
if let Some(completion) = &mut self.completion {
@@ -1093,8 +1125,15 @@ impl Component for EditorView {
// if completion didn't take the event, we pass it onto commands
if !consumed {
+ if let Some(compl) = cx.editor.last_completion.take() {
+ self.last_insert.1.push(InsertEvent::CompletionApply(compl));
+ }
+
self.insert_mode(&mut cx, key);
+ // record last_insert key
+ self.last_insert.1.push(InsertEvent::Key(key));
+
// lastly we recalculate completion
if let Some(completion) = &mut self.completion {
completion.update(&mut cx);