snippets: Fix tabstop completion choices (#31955)
I'm not sure when snippet tabstop choices broke, I checked the parent of #31872 and they also don't work before that. Release Notes: - N/A
This commit is contained in:
parent
ccc173ebb1
commit
d15d85830a
2 changed files with 56 additions and 33 deletions
|
@ -2712,7 +2712,9 @@ impl Editor {
|
||||||
.invalidate(&self.selections.disjoint_anchors(), buffer);
|
.invalidate(&self.selections.disjoint_anchors(), buffer);
|
||||||
self.take_rename(false, window, cx);
|
self.take_rename(false, window, cx);
|
||||||
|
|
||||||
let new_cursor_position = self.selections.newest_anchor().head();
|
let newest_selection = self.selections.newest_anchor();
|
||||||
|
let new_cursor_position = newest_selection.head();
|
||||||
|
let selection_start = newest_selection.start;
|
||||||
|
|
||||||
self.push_to_nav_history(
|
self.push_to_nav_history(
|
||||||
*old_cursor_position,
|
*old_cursor_position,
|
||||||
|
@ -2722,8 +2724,6 @@ impl Editor {
|
||||||
);
|
);
|
||||||
|
|
||||||
if local {
|
if local {
|
||||||
let new_cursor_position = self.selections.newest_anchor().head();
|
|
||||||
|
|
||||||
if let Some(buffer_id) = new_cursor_position.buffer_id {
|
if let Some(buffer_id) = new_cursor_position.buffer_id {
|
||||||
if !self.registered_buffers.contains_key(&buffer_id) {
|
if !self.registered_buffers.contains_key(&buffer_id) {
|
||||||
if let Some(project) = self.project.as_ref() {
|
if let Some(project) = self.project.as_ref() {
|
||||||
|
@ -2754,15 +2754,15 @@ impl Editor {
|
||||||
|
|
||||||
if should_update_completions {
|
if should_update_completions {
|
||||||
if let Some(completion_position) = completion_position {
|
if let Some(completion_position) = completion_position {
|
||||||
let new_cursor_offset = new_cursor_position.to_offset(buffer);
|
let start_offset = selection_start.to_offset(buffer);
|
||||||
let position_matches =
|
let position_matches = start_offset == completion_position.to_offset(buffer);
|
||||||
new_cursor_offset == completion_position.to_offset(buffer);
|
|
||||||
let continue_showing = if position_matches {
|
let continue_showing = if position_matches {
|
||||||
let (word_range, kind) = buffer.surrounding_word(new_cursor_offset, true);
|
if self.snippet_stack.is_empty() {
|
||||||
if let Some(CharKind::Word) = kind {
|
buffer.char_kind_before(start_offset, true) == Some(CharKind::Word)
|
||||||
word_range.start < new_cursor_offset
|
|
||||||
} else {
|
} else {
|
||||||
false
|
// Snippet choices can be shown even when the cursor is in whitespace.
|
||||||
|
// Dismissing the menu when actions like backspace
|
||||||
|
true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -5046,7 +5046,10 @@ impl Editor {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let position = self.selections.newest_anchor().head();
|
// Typically `start` == `end`, but with snippet tabstop choices the default choice is
|
||||||
|
// inserted and selected. To handle that case, the start of the selection is used so that
|
||||||
|
// the menu starts with all choices.
|
||||||
|
let position = self.selections.newest_anchor().start;
|
||||||
if position.diff_base_anchor.is_some() {
|
if position.diff_base_anchor.is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -8914,26 +8917,30 @@ impl Editor {
|
||||||
selection: Range<Anchor>,
|
selection: Range<Anchor>,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
if selection.start.buffer_id.is_none() {
|
let buffer_id = match (&selection.start.buffer_id, &selection.end.buffer_id) {
|
||||||
|
(Some(a), Some(b)) if a == b => a,
|
||||||
|
_ => {
|
||||||
|
log::error!("expected anchor range to have matching buffer IDs");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let multi_buffer = self.buffer().read(cx);
|
||||||
|
let Some(buffer) = multi_buffer.buffer(*buffer_id) else {
|
||||||
return;
|
return;
|
||||||
}
|
};
|
||||||
let buffer_id = selection.start.buffer_id.unwrap();
|
|
||||||
let buffer = self.buffer().read(cx).buffer(buffer_id);
|
|
||||||
let id = post_inc(&mut self.next_completion_id);
|
let id = post_inc(&mut self.next_completion_id);
|
||||||
let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
|
let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
|
||||||
|
*self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
|
||||||
if let Some(buffer) = buffer {
|
CompletionsMenu::new_snippet_choices(
|
||||||
*self.context_menu.borrow_mut() = Some(CodeContextMenu::Completions(
|
id,
|
||||||
CompletionsMenu::new_snippet_choices(
|
true,
|
||||||
id,
|
choices,
|
||||||
true,
|
selection,
|
||||||
choices,
|
buffer,
|
||||||
selection,
|
snippet_sort_order,
|
||||||
buffer,
|
),
|
||||||
snippet_sort_order,
|
));
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_snippet(
|
pub fn insert_snippet(
|
||||||
|
@ -8987,9 +8994,7 @@ impl Editor {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
// Sort in reverse order so that the first range is the newest created
|
tabstop_ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start, snapshot));
|
||||||
// selection. Completions will use it and autoscroll will prioritize it.
|
|
||||||
tabstop_ranges.sort_unstable_by(|a, b| b.start.cmp(&a.start, snapshot));
|
|
||||||
|
|
||||||
Tabstop {
|
Tabstop {
|
||||||
is_end_tabstop,
|
is_end_tabstop,
|
||||||
|
@ -9001,7 +9006,9 @@ impl Editor {
|
||||||
});
|
});
|
||||||
if let Some(tabstop) = tabstops.first() {
|
if let Some(tabstop) = tabstops.first() {
|
||||||
self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
||||||
s.select_ranges(tabstop.ranges.iter().cloned());
|
// Reverse order so that the first range is the newest created selection.
|
||||||
|
// Completions will use it and autoscroll will prioritize it.
|
||||||
|
s.select_ranges(tabstop.ranges.iter().rev().cloned());
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(choices) = &tabstop.choices {
|
if let Some(choices) = &tabstop.choices {
|
||||||
|
@ -9117,7 +9124,9 @@ impl Editor {
|
||||||
}
|
}
|
||||||
if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
|
if let Some(current_ranges) = snippet.ranges.get(snippet.active_index) {
|
||||||
self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
self.change_selections(Some(Autoscroll::fit()), window, cx, |s| {
|
||||||
s.select_ranges(current_ranges.iter().cloned())
|
// Reverse order so that the first range is the newest created selection.
|
||||||
|
// Completions will use it and autoscroll will prioritize it.
|
||||||
|
s.select_ranges(current_ranges.iter().rev().cloned())
|
||||||
});
|
});
|
||||||
|
|
||||||
if let Some(choices) = &snippet.choices[snippet.active_index] {
|
if let Some(choices) = &snippet.choices[snippet.active_index] {
|
||||||
|
|
|
@ -4182,6 +4182,20 @@ impl MultiBufferSnapshot {
|
||||||
(start..end, word_kind)
|
(start..end, word_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn char_kind_before<T: ToOffset>(
|
||||||
|
&self,
|
||||||
|
start: T,
|
||||||
|
for_completion: bool,
|
||||||
|
) -> Option<CharKind> {
|
||||||
|
let start = start.to_offset(self);
|
||||||
|
let classifier = self
|
||||||
|
.char_classifier_at(start)
|
||||||
|
.for_completion(for_completion);
|
||||||
|
self.reversed_chars_at(start)
|
||||||
|
.next()
|
||||||
|
.map(|ch| classifier.kind(ch))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_singleton(&self) -> bool {
|
pub fn is_singleton(&self) -> bool {
|
||||||
self.singleton
|
self.singleton
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue