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 {
|
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| {
|
||||||
|
if this.focused {
|
||||||
this.completion_state = Some(CompletionState {
|
this.completion_state = Some(CompletionState {
|
||||||
|
initial_position: position,
|
||||||
completions: completions.into(),
|
completions: completions.into(),
|
||||||
selected_item: 0,
|
selected_item: 0,
|
||||||
list: Default::default(),
|
list: Default::default(),
|
||||||
});
|
});
|
||||||
cx.notify();
|
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 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(old_cursor_position) = old_cursor_position {
|
|
||||||
let new_cursor_position = selections
|
let new_cursor_position = selections
|
||||||
.iter()
|
.iter()
|
||||||
.max_by_key(|s| s.id)
|
.max_by_key(|s| s.id)
|
||||||
.map(|s| s.head().to_point(&buffer));
|
.map(|s| s.head().to_point(&buffer));
|
||||||
|
if let Some(old_cursor_position) = old_cursor_position {
|
||||||
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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
start.to_point(&map.buffer_snapshot).to_display_point(map)
|
||||||
..end.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)
|
||||||
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue