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 {
initial_position: Anchor,
completions: Arc<[Completion<Anchor>]>,
selected_item: usize,
list: UniformListState,
@ -452,7 +453,7 @@ pub struct NavigationData {
offset: usize,
}
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord)]
#[derive(Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Debug)]
pub enum CharKind {
Newline,
Punctuation,
@ -887,7 +888,7 @@ impl Editor {
mode = SelectMode::Character;
}
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));
end = buffer.anchor_before(range.end.to_point(&display_map));
mode = SelectMode::Word(start.clone()..end.clone());
@ -990,7 +991,7 @@ impl Editor {
if movement::is_inside_word(&display_map, 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 {
head = word_range.start.to_point(&display_map);
} else {
@ -1397,6 +1398,9 @@ impl Editor {
.is_completion_trigger(selection.head(), text, 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>) {
let position = self
.newest_selection::<usize>(&self.buffer.read(cx).read(cx))
.head();
let position = if let Some(selection) = self.newest_anchor_selection() {
selection.head()
} else {
return;
};
let completions = self
.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 {
let completions = completions.await?;
if !completions.is_empty() {
if let Some(this) = cx.read(|cx| this.upgrade(cx)) {
this.update(&mut cx, |this, cx| {
this.completion_state = Some(CompletionState {
completions: completions.into(),
selected_item: 0,
list: Default::default(),
});
cx.notify();
if this.focused {
this.completion_state = Some(CompletionState {
initial_position: position,
completions: completions.into(),
selected_item: 0,
list: Default::default(),
});
cx.notify();
}
});
}
}
@ -2905,7 +2914,7 @@ impl Editor {
} else if selections.len() == 1 {
let selection = selections.last_mut().unwrap();
if selection.start == selection.end {
let word_range = movement::surrounding_word(
let (word_range, _) = movement::surrounding_word(
&display_map,
selection.start.to_display_point(&display_map),
);
@ -3525,7 +3534,8 @@ impl Editor {
) where
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());
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 {
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() {
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 {
self.request_autoscroll(autoscroll, cx);
}
@ -4207,6 +4233,7 @@ impl View for Editor {
self.show_local_cursors = false;
self.buffer
.update(cx, |buffer, cx| buffer.remove_active_selections(cx));
self.completion_state.take();
cx.emit(Event::Blurred);
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))
}
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 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)]
@ -406,59 +412,101 @@ mod tests {
assert_eq!(
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!(
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!(
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!(
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!(
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!(
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!(
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!(
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!(
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!(
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!(
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!(
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!(
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!(
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;
if selection.start == selection.end {
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)
..range.end.to_offset(&display_map, Bias::Right);
text = display_map.buffer_snapshot.text_for_range(range).collect();