Dismiss autocomplete when moving outside of a word

This commit is contained in:
Antonio Scandurra 2022-02-01 09:43:13 +01:00
parent 92f0491c0e
commit bcbd265de9
3 changed files with 112 additions and 37 deletions

View file

@ -428,6 +428,7 @@ struct BracketPairState {
} }
struct CompletionState { struct CompletionState {
initial_position: Anchor,
completions: Arc<[Completion<Anchor>]>, completions: Arc<[Completion<Anchor>]>,
selected_item: usize, selected_item: usize,
list: UniformListState, list: UniformListState,
@ -452,7 +453,7 @@ pub struct NavigationData {
offset: usize, offset: usize,
} }
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)] #[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub enum CharKind { pub enum CharKind {
Newline, Newline,
Punctuation, Punctuation,
@ -887,7 +888,7 @@ impl Editor {
mode = SelectMode::Character; mode = SelectMode::Character;
} }
2 => { 2 => {
let range = movement::surrounding_word(&display_map, position); let (range, _) = movement::surrounding_word(&display_map, position);
start = buffer.anchor_before(range.start.to_point(&display_map)); start = buffer.anchor_before(range.start.to_point(&display_map));
end = buffer.anchor_before(range.end.to_point(&display_map)); end = buffer.anchor_before(range.end.to_point(&display_map));
mode = SelectMode::Word(start.clone()..end.clone()); mode = SelectMode::Word(start.clone()..end.clone());
@ -990,7 +991,7 @@ impl Editor {
if movement::is_inside_word(&display_map, position) if movement::is_inside_word(&display_map, position)
|| original_display_range.contains(&position) || original_display_range.contains(&position)
{ {
let word_range = movement::surrounding_word(&display_map, position); let (word_range, _) = movement::surrounding_word(&display_map, position);
if word_range.start < original_display_range.start { if word_range.start < original_display_range.start {
head = word_range.start.to_point(&display_map); head = word_range.start.to_point(&display_map);
} else { } else {
@ -1397,6 +1398,9 @@ impl Editor {
.is_completion_trigger(selection.head(), text, cx) .is_completion_trigger(selection.head(), text, cx)
{ {
self.show_completions(&ShowCompletions, cx); self.show_completions(&ShowCompletions, cx);
} else {
self.completion_state.take();
cx.notify();
} }
} }
} }
@ -1528,25 +1532,30 @@ impl Editor {
} }
fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) { fn show_completions(&mut self, _: &ShowCompletions, cx: &mut ViewContext<Self>) {
let position = self let position = if let Some(selection) = self.newest_anchor_selection() {
.newest_selection::<usize>(&self.buffer.read(cx).read(cx)) selection.head()
.head(); } else {
return;
};
let completions = self let completions = self
.buffer .buffer
.update(cx, |buffer, cx| buffer.completions(position, cx)); .update(cx, |buffer, cx| buffer.completions(position.clone(), cx));
cx.spawn_weak(|this, mut cx| async move { cx.spawn_weak(|this, mut cx| async move {
let completions = completions.await?; let completions = completions.await?;
if !completions.is_empty() { if !completions.is_empty() {
if let Some(this) = cx.read(|cx| this.upgrade(cx)) { if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
this.update(&mut cx, |this, cx| { this.update(&mut cx, |this, cx| {
this.completion_state = Some(CompletionState { if this.focused {
completions: completions.into(), this.completion_state = Some(CompletionState {
selected_item: 0, initial_position: position,
list: Default::default(), completions: completions.into(),
}); selected_item: 0,
cx.notify(); list: Default::default(),
});
cx.notify();
}
}); });
} }
} }
@ -2905,7 +2914,7 @@ impl Editor {
} else if selections.len() == 1 { } else if selections.len() == 1 {
let selection = selections.last_mut().unwrap(); let selection = selections.last_mut().unwrap();
if selection.start == selection.end { if selection.start == selection.end {
let word_range = movement::surrounding_word( let (word_range, _) = movement::surrounding_word(
&display_map, &display_map,
selection.start.to_display_point(&display_map), selection.start.to_display_point(&display_map),
); );
@ -3525,7 +3534,8 @@ impl Editor {
) where ) where
T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug, T: ToOffset + ToPoint + Ord + std::marker::Copy + std::fmt::Debug,
{ {
let buffer = self.buffer.read(cx).snapshot(cx); let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
let buffer = &display_map.buffer_snapshot;
let old_cursor_position = self.newest_anchor_selection().map(|s| s.head()); let old_cursor_position = self.newest_anchor_selection().map(|s| s.head());
selections.sort_unstable_by_key(|s| s.start); selections.sort_unstable_by_key(|s| s.start);
@ -3570,16 +3580,32 @@ impl Editor {
} }
} }
let new_cursor_position = selections
.iter()
.max_by_key(|s| s.id)
.map(|s| s.head().to_point(&buffer));
if let Some(old_cursor_position) = old_cursor_position { if let Some(old_cursor_position) = old_cursor_position {
let new_cursor_position = selections
.iter()
.max_by_key(|s| s.id)
.map(|s| s.head().to_point(&buffer));
if new_cursor_position.is_some() { if new_cursor_position.is_some() {
self.push_to_nav_history(old_cursor_position, new_cursor_position, cx); self.push_to_nav_history(old_cursor_position, new_cursor_position, cx);
} }
} }
if let Some((completion_state, cursor_position)) =
self.completion_state.as_ref().zip(new_cursor_position)
{
let cursor_position = cursor_position.to_display_point(&display_map);
let initial_position = completion_state
.initial_position
.to_display_point(&display_map);
let (word_range, kind) = movement::surrounding_word(&display_map, initial_position);
if kind != Some(CharKind::Word) || !word_range.to_inclusive().contains(&cursor_position)
{
self.completion_state.take();
cx.notify();
}
}
if let Some(autoscroll) = autoscroll { if let Some(autoscroll) = autoscroll {
self.request_autoscroll(autoscroll, cx); self.request_autoscroll(autoscroll, cx);
} }
@ -4207,6 +4233,7 @@ impl View for Editor {
self.show_local_cursors = false; self.show_local_cursors = false;
self.buffer self.buffer
.update(cx, |buffer, cx| buffer.remove_active_selections(cx)); .update(cx, |buffer, cx| buffer.remove_active_selections(cx));
self.completion_state.take();
cx.emit(Event::Blurred); cx.emit(Event::Blurred);
cx.notify(); cx.notify();
} }

View file

@ -183,7 +183,10 @@ pub fn is_inside_word(map: &DisplaySnapshot, point: DisplayPoint) -> bool {
prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word)) prev_char_kind.zip(next_char_kind) == Some((CharKind::Word, CharKind::Word))
} }
pub fn surrounding_word(map: &DisplaySnapshot, point: DisplayPoint) -> Range<DisplayPoint> { pub fn surrounding_word(
map: &DisplaySnapshot,
point: DisplayPoint,
) -> (Range<DisplayPoint>, Option<CharKind>) {
let mut start = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left); let mut start = map.clip_point(point, Bias::Left).to_offset(map, Bias::Left);
let mut end = start; let mut end = start;
@ -211,8 +214,11 @@ pub fn surrounding_word(map: &DisplaySnapshot, point: DisplayPoint) -> Range<Dis
} }
} }
start.to_point(&map.buffer_snapshot).to_display_point(map) (
..end.to_point(&map.buffer_snapshot).to_display_point(map) start.to_point(&map.buffer_snapshot).to_display_point(map)
..end.to_point(&map.buffer_snapshot).to_display_point(map),
word_kind,
)
} }
#[cfg(test)] #[cfg(test)]
@ -406,59 +412,101 @@ mod tests {
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 0)), surrounding_word(&snapshot, DisplayPoint::new(0, 0)),
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 5) (
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 5),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 2)), surrounding_word(&snapshot, DisplayPoint::new(0, 2)),
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 5) (
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 5),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 5)), surrounding_word(&snapshot, DisplayPoint::new(0, 5)),
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 5) (
DisplayPoint::new(0, 0)..DisplayPoint::new(0, 5),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 6)), surrounding_word(&snapshot, DisplayPoint::new(0, 6)),
DisplayPoint::new(0, 6)..DisplayPoint::new(0, 11) (
DisplayPoint::new(0, 6)..DisplayPoint::new(0, 11),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 7)), surrounding_word(&snapshot, DisplayPoint::new(0, 7)),
DisplayPoint::new(0, 6)..DisplayPoint::new(0, 11) (
DisplayPoint::new(0, 6)..DisplayPoint::new(0, 11),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 11)), surrounding_word(&snapshot, DisplayPoint::new(0, 11)),
DisplayPoint::new(0, 6)..DisplayPoint::new(0, 11) (
DisplayPoint::new(0, 6)..DisplayPoint::new(0, 11),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 13)), surrounding_word(&snapshot, DisplayPoint::new(0, 13)),
DisplayPoint::new(0, 11)..DisplayPoint::new(0, 14) (
DisplayPoint::new(0, 11)..DisplayPoint::new(0, 14),
Some(CharKind::Whitespace)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 14)), surrounding_word(&snapshot, DisplayPoint::new(0, 14)),
DisplayPoint::new(0, 14)..DisplayPoint::new(0, 19) (
DisplayPoint::new(0, 14)..DisplayPoint::new(0, 19),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 17)), surrounding_word(&snapshot, DisplayPoint::new(0, 17)),
DisplayPoint::new(0, 14)..DisplayPoint::new(0, 19) (
DisplayPoint::new(0, 14)..DisplayPoint::new(0, 19),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(0, 19)), surrounding_word(&snapshot, DisplayPoint::new(0, 19)),
DisplayPoint::new(0, 14)..DisplayPoint::new(0, 19) (
DisplayPoint::new(0, 14)..DisplayPoint::new(0, 19),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(1, 0)), surrounding_word(&snapshot, DisplayPoint::new(1, 0)),
DisplayPoint::new(1, 0)..DisplayPoint::new(1, 4) (
DisplayPoint::new(1, 0)..DisplayPoint::new(1, 4),
Some(CharKind::Whitespace)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(1, 1)), surrounding_word(&snapshot, DisplayPoint::new(1, 1)),
DisplayPoint::new(1, 0)..DisplayPoint::new(1, 4) (
DisplayPoint::new(1, 0)..DisplayPoint::new(1, 4),
Some(CharKind::Whitespace)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(1, 6)), surrounding_word(&snapshot, DisplayPoint::new(1, 6)),
DisplayPoint::new(1, 4)..DisplayPoint::new(1, 7) (
DisplayPoint::new(1, 4)..DisplayPoint::new(1, 7),
Some(CharKind::Word)
)
); );
assert_eq!( assert_eq!(
surrounding_word(&snapshot, DisplayPoint::new(1, 7)), surrounding_word(&snapshot, DisplayPoint::new(1, 7)),
DisplayPoint::new(1, 4)..DisplayPoint::new(1, 7) (
DisplayPoint::new(1, 4)..DisplayPoint::new(1, 7),
Some(CharKind::Word)
)
); );
} }
} }

View file

@ -296,7 +296,7 @@ impl FindBar {
let mut text: String; let mut text: String;
if selection.start == selection.end { if selection.start == selection.end {
let point = selection.start.to_display_point(&display_map); let point = selection.start.to_display_point(&display_map);
let range = editor::movement::surrounding_word(&display_map, point); let (range, _) = editor::movement::surrounding_word(&display_map, point);
let range = range.start.to_offset(&display_map, Bias::Left) let range = range.start.to_offset(&display_map, Bias::Left)
..range.end.to_offset(&display_map, Bias::Right); ..range.end.to_offset(&display_map, Bias::Right);
text = display_map.buffer_snapshot.text_for_range(range).collect(); text = display_map.buffer_snapshot.text_for_range(range).collect();