Dismiss autocomplete when moving outside of a word
This commit is contained in:
parent
92f0491c0e
commit
bcbd265de9
3 changed files with 112 additions and 37 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue