Make completions menu stay open after after it's manually requested (#32015)
Also includes a clarity refactoring to remove `ignore_completion_provider`. Closes #15549 Release Notes: - Fixed completions menu closing on typing after being requested while `show_completions_on_input: false`.
This commit is contained in:
parent
522abe8e59
commit
8c46a4f594
7 changed files with 70 additions and 38 deletions
|
@ -926,8 +926,9 @@ impl CompletionProvider for ContextPickerCompletionProvider {
|
||||||
&self,
|
&self,
|
||||||
buffer: &Entity<language::Buffer>,
|
buffer: &Entity<language::Buffer>,
|
||||||
position: language::Anchor,
|
position: language::Anchor,
|
||||||
_: &str,
|
_text: &str,
|
||||||
_: bool,
|
_trigger_in_words: bool,
|
||||||
|
_menu_is_open: bool,
|
||||||
cx: &mut Context<Editor>,
|
cx: &mut Context<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let buffer = buffer.read(cx);
|
let buffer = buffer.read(cx);
|
||||||
|
|
|
@ -342,6 +342,7 @@ impl CompletionProvider for SlashCommandCompletionProvider {
|
||||||
position: language::Anchor,
|
position: language::Anchor,
|
||||||
_text: &str,
|
_text: &str,
|
||||||
_trigger_in_words: bool,
|
_trigger_in_words: bool,
|
||||||
|
_menu_is_open: bool,
|
||||||
cx: &mut Context<Editor>,
|
cx: &mut Context<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let buffer = buffer.read(cx);
|
let buffer = buffer.read(cx);
|
||||||
|
|
|
@ -89,6 +89,7 @@ impl CompletionProvider for MessageEditorCompletionProvider {
|
||||||
_position: language::Anchor,
|
_position: language::Anchor,
|
||||||
text: &str,
|
text: &str,
|
||||||
_trigger_in_words: bool,
|
_trigger_in_words: bool,
|
||||||
|
_menu_is_open: bool,
|
||||||
_cx: &mut Context<Editor>,
|
_cx: &mut Context<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
text == "@"
|
text == "@"
|
||||||
|
|
|
@ -309,6 +309,7 @@ impl CompletionProvider for ConsoleQueryBarCompletionProvider {
|
||||||
_position: language::Anchor,
|
_position: language::Anchor,
|
||||||
_text: &str,
|
_text: &str,
|
||||||
_trigger_in_words: bool,
|
_trigger_in_words: bool,
|
||||||
|
_menu_is_open: bool,
|
||||||
_cx: &mut Context<Editor>,
|
_cx: &mut Context<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
true
|
true
|
||||||
|
|
|
@ -194,6 +194,7 @@ pub enum ContextMenuOrigin {
|
||||||
|
|
||||||
pub struct CompletionsMenu {
|
pub struct CompletionsMenu {
|
||||||
pub id: CompletionId,
|
pub id: CompletionId,
|
||||||
|
pub source: CompletionsMenuSource,
|
||||||
sort_completions: bool,
|
sort_completions: bool,
|
||||||
pub initial_position: Anchor,
|
pub initial_position: Anchor,
|
||||||
pub initial_query: Option<Arc<String>>,
|
pub initial_query: Option<Arc<String>>,
|
||||||
|
@ -208,7 +209,6 @@ pub struct CompletionsMenu {
|
||||||
scroll_handle: UniformListScrollHandle,
|
scroll_handle: UniformListScrollHandle,
|
||||||
resolve_completions: bool,
|
resolve_completions: bool,
|
||||||
show_completion_documentation: bool,
|
show_completion_documentation: bool,
|
||||||
pub(super) ignore_completion_provider: bool,
|
|
||||||
last_rendered_range: Rc<RefCell<Option<Range<usize>>>>,
|
last_rendered_range: Rc<RefCell<Option<Range<usize>>>>,
|
||||||
markdown_cache: Rc<RefCell<VecDeque<(MarkdownCacheKey, Entity<Markdown>)>>>,
|
markdown_cache: Rc<RefCell<VecDeque<(MarkdownCacheKey, Entity<Markdown>)>>>,
|
||||||
language_registry: Option<Arc<LanguageRegistry>>,
|
language_registry: Option<Arc<LanguageRegistry>>,
|
||||||
|
@ -227,6 +227,13 @@ enum MarkdownCacheKey {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum CompletionsMenuSource {
|
||||||
|
Normal,
|
||||||
|
SnippetChoices,
|
||||||
|
Words,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: There should really be a wrapper around fuzzy match tasks that does this.
|
// TODO: There should really be a wrapper around fuzzy match tasks that does this.
|
||||||
impl Drop for CompletionsMenu {
|
impl Drop for CompletionsMenu {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
@ -237,9 +244,9 @@ impl Drop for CompletionsMenu {
|
||||||
impl CompletionsMenu {
|
impl CompletionsMenu {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
id: CompletionId,
|
id: CompletionId,
|
||||||
|
source: CompletionsMenuSource,
|
||||||
sort_completions: bool,
|
sort_completions: bool,
|
||||||
show_completion_documentation: bool,
|
show_completion_documentation: bool,
|
||||||
ignore_completion_provider: bool,
|
|
||||||
initial_position: Anchor,
|
initial_position: Anchor,
|
||||||
initial_query: Option<Arc<String>>,
|
initial_query: Option<Arc<String>>,
|
||||||
is_incomplete: bool,
|
is_incomplete: bool,
|
||||||
|
@ -258,13 +265,13 @@ impl CompletionsMenu {
|
||||||
|
|
||||||
let completions_menu = Self {
|
let completions_menu = Self {
|
||||||
id,
|
id,
|
||||||
|
source,
|
||||||
sort_completions,
|
sort_completions,
|
||||||
initial_position,
|
initial_position,
|
||||||
initial_query,
|
initial_query,
|
||||||
is_incomplete,
|
is_incomplete,
|
||||||
buffer,
|
buffer,
|
||||||
show_completion_documentation,
|
show_completion_documentation,
|
||||||
ignore_completion_provider,
|
|
||||||
completions: RefCell::new(completions).into(),
|
completions: RefCell::new(completions).into(),
|
||||||
match_candidates,
|
match_candidates,
|
||||||
entries: Rc::new(RefCell::new(Box::new([]))),
|
entries: Rc::new(RefCell::new(Box::new([]))),
|
||||||
|
@ -328,6 +335,7 @@ impl CompletionsMenu {
|
||||||
.collect();
|
.collect();
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
|
source: CompletionsMenuSource::SnippetChoices,
|
||||||
sort_completions,
|
sort_completions,
|
||||||
initial_position: selection.start,
|
initial_position: selection.start,
|
||||||
initial_query: None,
|
initial_query: None,
|
||||||
|
@ -342,7 +350,6 @@ impl CompletionsMenu {
|
||||||
scroll_handle: UniformListScrollHandle::new(),
|
scroll_handle: UniformListScrollHandle::new(),
|
||||||
resolve_completions: false,
|
resolve_completions: false,
|
||||||
show_completion_documentation: false,
|
show_completion_documentation: false,
|
||||||
ignore_completion_provider: false,
|
|
||||||
last_rendered_range: RefCell::new(None).into(),
|
last_rendered_range: RefCell::new(None).into(),
|
||||||
markdown_cache: RefCell::new(VecDeque::new()).into(),
|
markdown_cache: RefCell::new(VecDeque::new()).into(),
|
||||||
language_registry: None,
|
language_registry: None,
|
||||||
|
|
|
@ -211,8 +211,11 @@ use workspace::{
|
||||||
searchable::SearchEvent,
|
searchable::SearchEvent,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::hover_links::{find_url, find_url_from_range};
|
|
||||||
use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
|
use crate::signature_help::{SignatureHelpHiddenBy, SignatureHelpState};
|
||||||
|
use crate::{
|
||||||
|
code_context_menus::CompletionsMenuSource,
|
||||||
|
hover_links::{find_url, find_url_from_range},
|
||||||
|
};
|
||||||
|
|
||||||
pub const FILE_HEADER_HEIGHT: u32 = 2;
|
pub const FILE_HEADER_HEIGHT: u32 = 2;
|
||||||
pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
|
pub const MULTI_BUFFER_EXCERPT_HEADER_HEIGHT: u32 = 1;
|
||||||
|
@ -4510,30 +4513,40 @@ impl Editor {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let ignore_completion_provider = self
|
let completions_source = self
|
||||||
.context_menu
|
.context_menu
|
||||||
.borrow()
|
.borrow()
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|menu| match menu {
|
.and_then(|menu| match menu {
|
||||||
CodeContextMenu::Completions(completions_menu) => {
|
CodeContextMenu::Completions(completions_menu) => Some(completions_menu.source),
|
||||||
completions_menu.ignore_completion_provider
|
CodeContextMenu::CodeActions(_) => None,
|
||||||
}
|
});
|
||||||
CodeContextMenu::CodeActions(_) => false,
|
|
||||||
})
|
|
||||||
.unwrap_or(false);
|
|
||||||
|
|
||||||
if ignore_completion_provider {
|
match completions_source {
|
||||||
self.show_word_completions(&ShowWordCompletions, window, cx);
|
Some(CompletionsMenuSource::Words) => {
|
||||||
} else if self.is_completion_trigger(text, trigger_in_words, cx) {
|
self.show_word_completions(&ShowWordCompletions, window, cx)
|
||||||
self.show_completions(
|
}
|
||||||
&ShowCompletions {
|
Some(CompletionsMenuSource::Normal)
|
||||||
trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
|
| Some(CompletionsMenuSource::SnippetChoices)
|
||||||
},
|
| None
|
||||||
window,
|
if self.is_completion_trigger(
|
||||||
cx,
|
text,
|
||||||
);
|
trigger_in_words,
|
||||||
} else {
|
completions_source.is_some(),
|
||||||
self.hide_context_menu(window, cx);
|
cx,
|
||||||
|
) =>
|
||||||
|
{
|
||||||
|
self.show_completions(
|
||||||
|
&ShowCompletions {
|
||||||
|
trigger: Some(text.to_owned()).filter(|x| !x.is_empty()),
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.hide_context_menu(window, cx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4541,6 +4554,7 @@ impl Editor {
|
||||||
&self,
|
&self,
|
||||||
text: &str,
|
text: &str,
|
||||||
trigger_in_words: bool,
|
trigger_in_words: bool,
|
||||||
|
menu_is_open: bool,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let position = self.selections.newest_anchor().head();
|
let position = self.selections.newest_anchor().head();
|
||||||
|
@ -4558,6 +4572,7 @@ impl Editor {
|
||||||
position.text_anchor,
|
position.text_anchor,
|
||||||
text,
|
text,
|
||||||
trigger_in_words,
|
trigger_in_words,
|
||||||
|
menu_is_open,
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
@ -5008,7 +5023,7 @@ impl Editor {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
self.open_or_update_completions_menu(true, None, window, cx);
|
self.open_or_update_completions_menu(Some(CompletionsMenuSource::Words), None, window, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_completions(
|
pub fn show_completions(
|
||||||
|
@ -5017,12 +5032,12 @@ impl Editor {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
self.open_or_update_completions_menu(false, options.trigger.as_deref(), window, cx);
|
self.open_or_update_completions_menu(None, options.trigger.as_deref(), window, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_or_update_completions_menu(
|
fn open_or_update_completions_menu(
|
||||||
&mut self,
|
&mut self,
|
||||||
ignore_completion_provider: bool,
|
requested_source: Option<CompletionsMenuSource>,
|
||||||
trigger: Option<&str>,
|
trigger: Option<&str>,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
|
@ -5047,10 +5062,13 @@ impl Editor {
|
||||||
Self::completion_query(&self.buffer.read(cx).read(cx), position)
|
Self::completion_query(&self.buffer.read(cx).read(cx), position)
|
||||||
.map(|query| query.into());
|
.map(|query| query.into());
|
||||||
|
|
||||||
let provider = if ignore_completion_provider {
|
let provider = match requested_source {
|
||||||
None
|
Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
|
||||||
} else {
|
Some(CompletionsMenuSource::Words) => None,
|
||||||
self.completion_provider.clone()
|
Some(CompletionsMenuSource::SnippetChoices) => {
|
||||||
|
log::error!("bug: SnippetChoices requested_source is not handled");
|
||||||
|
None
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let sort_completions = provider
|
let sort_completions = provider
|
||||||
|
@ -5246,9 +5264,9 @@ impl Editor {
|
||||||
.map(|workspace| workspace.read(cx).app_state().languages.clone());
|
.map(|workspace| workspace.read(cx).app_state().languages.clone());
|
||||||
let menu = CompletionsMenu::new(
|
let menu = CompletionsMenu::new(
|
||||||
id,
|
id,
|
||||||
|
requested_source.unwrap_or(CompletionsMenuSource::Normal),
|
||||||
sort_completions,
|
sort_completions,
|
||||||
show_completion_documentation,
|
show_completion_documentation,
|
||||||
ignore_completion_provider,
|
|
||||||
position,
|
position,
|
||||||
query.clone(),
|
query.clone(),
|
||||||
is_incomplete,
|
is_incomplete,
|
||||||
|
@ -20295,6 +20313,7 @@ pub trait CompletionProvider {
|
||||||
position: language::Anchor,
|
position: language::Anchor,
|
||||||
text: &str,
|
text: &str,
|
||||||
trigger_in_words: bool,
|
trigger_in_words: bool,
|
||||||
|
menu_is_open: bool,
|
||||||
cx: &mut Context<Editor>,
|
cx: &mut Context<Editor>,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
|
|
||||||
|
@ -20612,6 +20631,7 @@ impl CompletionProvider for Entity<Project> {
|
||||||
position: language::Anchor,
|
position: language::Anchor,
|
||||||
text: &str,
|
text: &str,
|
||||||
trigger_in_words: bool,
|
trigger_in_words: bool,
|
||||||
|
menu_is_open: bool,
|
||||||
cx: &mut Context<Editor>,
|
cx: &mut Context<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut chars = text.chars();
|
let mut chars = text.chars();
|
||||||
|
@ -20626,7 +20646,7 @@ impl CompletionProvider for Entity<Project> {
|
||||||
|
|
||||||
let buffer = buffer.read(cx);
|
let buffer = buffer.read(cx);
|
||||||
let snapshot = buffer.snapshot();
|
let snapshot = buffer.snapshot();
|
||||||
if !snapshot.settings_at(position, cx).show_completions_on_input {
|
if !menu_is_open && !snapshot.settings_at(position, cx).show_completions_on_input {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let classifier = snapshot.char_classifier_at(position).for_completion(true);
|
let classifier = snapshot.char_classifier_at(position).for_completion(true);
|
||||||
|
|
|
@ -685,8 +685,9 @@ impl CompletionProvider for RustStyleCompletionProvider {
|
||||||
&self,
|
&self,
|
||||||
buffer: &Entity<language::Buffer>,
|
buffer: &Entity<language::Buffer>,
|
||||||
position: language::Anchor,
|
position: language::Anchor,
|
||||||
_: &str,
|
_text: &str,
|
||||||
_: bool,
|
_trigger_in_words: bool,
|
||||||
|
_menu_is_open: bool,
|
||||||
cx: &mut Context<Editor>,
|
cx: &mut Context<Editor>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
completion_replace_range(&buffer.read(cx).snapshot(), &position).is_some()
|
completion_replace_range(&buffer.read(cx).snapshot(), &position).is_some()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue