Make word completions less intrusive (#36745)
Introduce `min_words_query_len` threshold for automatic word completion display, and set it to 3 by default. Re-enable word completions in Markdown and Plaintext. Release Notes: - Introduced `min_words_query_len` threshold for automatic word completion display, and set it to 3 by default to make them less intrusive
This commit is contained in:
parent
92bbcdeb7d
commit
3d2fa72d1f
6 changed files with 109 additions and 21 deletions
|
@ -1503,6 +1503,11 @@
|
|||
//
|
||||
// Default: fallback
|
||||
"words": "fallback",
|
||||
// Minimum number of characters required to automatically trigger word-based completions.
|
||||
// Before that value, it's still possible to trigger the words-based completion manually with the corresponding editor command.
|
||||
//
|
||||
// Default: 3
|
||||
"words_min_length": 3,
|
||||
// Whether to fetch LSP completions or not.
|
||||
//
|
||||
// Default: true
|
||||
|
@ -1642,9 +1647,6 @@
|
|||
"use_on_type_format": false,
|
||||
"allow_rewrap": "anywhere",
|
||||
"soft_wrap": "editor_width",
|
||||
"completions": {
|
||||
"words": "disabled"
|
||||
},
|
||||
"prettier": {
|
||||
"allowed": true
|
||||
}
|
||||
|
@ -1658,9 +1660,6 @@
|
|||
}
|
||||
},
|
||||
"Plain Text": {
|
||||
"completions": {
|
||||
"words": "disabled"
|
||||
},
|
||||
"allow_rewrap": "anywhere"
|
||||
},
|
||||
"Python": {
|
||||
|
|
|
@ -301,6 +301,7 @@ mod tests {
|
|||
init_test(cx, |settings| {
|
||||
settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Disabled,
|
||||
words_min_length: 0,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
lsp_insert_mode: LspInsertMode::Insert,
|
||||
|
@ -533,6 +534,7 @@ mod tests {
|
|||
init_test(cx, |settings| {
|
||||
settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Disabled,
|
||||
words_min_length: 0,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
lsp_insert_mode: LspInsertMode::Insert,
|
||||
|
|
|
@ -5576,6 +5576,11 @@ impl Editor {
|
|||
.as_ref()
|
||||
.is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
|
||||
|
||||
let omit_word_completions = match &query {
|
||||
Some(query) => query.chars().count() < completion_settings.words_min_length,
|
||||
None => completion_settings.words_min_length != 0,
|
||||
};
|
||||
|
||||
let (mut words, provider_responses) = match &provider {
|
||||
Some(provider) => {
|
||||
let provider_responses = provider.completions(
|
||||
|
@ -5587,9 +5592,11 @@ impl Editor {
|
|||
cx,
|
||||
);
|
||||
|
||||
let words = match completion_settings.words {
|
||||
WordsCompletionMode::Disabled => Task::ready(BTreeMap::default()),
|
||||
WordsCompletionMode::Enabled | WordsCompletionMode::Fallback => cx
|
||||
let words = match (omit_word_completions, completion_settings.words) {
|
||||
(true, _) | (_, WordsCompletionMode::Disabled) => {
|
||||
Task::ready(BTreeMap::default())
|
||||
}
|
||||
(false, WordsCompletionMode::Enabled | WordsCompletionMode::Fallback) => cx
|
||||
.background_spawn(async move {
|
||||
buffer_snapshot.words_in_range(WordsQuery {
|
||||
fuzzy_contents: None,
|
||||
|
@ -5601,16 +5608,20 @@ impl Editor {
|
|||
|
||||
(words, provider_responses)
|
||||
}
|
||||
None => (
|
||||
None => {
|
||||
let words = if omit_word_completions {
|
||||
Task::ready(BTreeMap::default())
|
||||
} else {
|
||||
cx.background_spawn(async move {
|
||||
buffer_snapshot.words_in_range(WordsQuery {
|
||||
fuzzy_contents: None,
|
||||
range: word_search_range,
|
||||
skip_digits,
|
||||
})
|
||||
}),
|
||||
Task::ready(Ok(Vec::new())),
|
||||
),
|
||||
})
|
||||
};
|
||||
(words, Task::ready(Ok(Vec::new())))
|
||||
}
|
||||
};
|
||||
|
||||
let snippet_sort_order = EditorSettings::get_global(cx).snippet_sort_order;
|
||||
|
|
|
@ -12237,6 +12237,7 @@ async fn test_completion_mode(cx: &mut TestAppContext) {
|
|||
settings.defaults.completions = Some(CompletionSettings {
|
||||
lsp_insert_mode,
|
||||
words: WordsCompletionMode::Disabled,
|
||||
words_min_length: 0,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
});
|
||||
|
@ -12295,6 +12296,7 @@ async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext)
|
|||
update_test_language_settings(&mut cx, |settings| {
|
||||
settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Disabled,
|
||||
words_min_length: 0,
|
||||
// set the opposite here to ensure that the action is overriding the default behavior
|
||||
lsp_insert_mode: LspInsertMode::Insert,
|
||||
lsp: true,
|
||||
|
@ -12331,6 +12333,7 @@ async fn test_completion_with_mode_specified_by_action(cx: &mut TestAppContext)
|
|||
update_test_language_settings(&mut cx, |settings| {
|
||||
settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Disabled,
|
||||
words_min_length: 0,
|
||||
// set the opposite here to ensure that the action is overriding the default behavior
|
||||
lsp_insert_mode: LspInsertMode::Replace,
|
||||
lsp: true,
|
||||
|
@ -13072,6 +13075,7 @@ async fn test_word_completion(cx: &mut TestAppContext) {
|
|||
init_test(cx, |language_settings| {
|
||||
language_settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Fallback,
|
||||
words_min_length: 0,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 10,
|
||||
lsp_insert_mode: LspInsertMode::Insert,
|
||||
|
@ -13168,6 +13172,7 @@ async fn test_word_completions_do_not_duplicate_lsp_ones(cx: &mut TestAppContext
|
|||
init_test(cx, |language_settings| {
|
||||
language_settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Enabled,
|
||||
words_min_length: 0,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
lsp_insert_mode: LspInsertMode::Insert,
|
||||
|
@ -13231,6 +13236,7 @@ async fn test_word_completions_continue_on_typing(cx: &mut TestAppContext) {
|
|||
init_test(cx, |language_settings| {
|
||||
language_settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Disabled,
|
||||
words_min_length: 0,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
lsp_insert_mode: LspInsertMode::Insert,
|
||||
|
@ -13304,6 +13310,7 @@ async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
|
|||
init_test(cx, |language_settings| {
|
||||
language_settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Fallback,
|
||||
words_min_length: 0,
|
||||
lsp: false,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
lsp_insert_mode: LspInsertMode::Insert,
|
||||
|
@ -13361,6 +13368,56 @@ async fn test_word_completions_usually_skip_digits(cx: &mut TestAppContext) {
|
|||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
async fn test_word_completions_do_not_show_before_threshold(cx: &mut TestAppContext) {
|
||||
init_test(cx, |language_settings| {
|
||||
language_settings.defaults.completions = Some(CompletionSettings {
|
||||
words: WordsCompletionMode::Enabled,
|
||||
words_min_length: 3,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
lsp_insert_mode: LspInsertMode::Insert,
|
||||
});
|
||||
});
|
||||
|
||||
let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
|
||||
cx.set_state(indoc! {"ˇ
|
||||
wow
|
||||
wowen
|
||||
wowser
|
||||
"});
|
||||
cx.simulate_keystroke("w");
|
||||
cx.executor().run_until_parked();
|
||||
cx.update_editor(|editor, _, _| {
|
||||
if editor.context_menu.borrow_mut().is_some() {
|
||||
panic!(
|
||||
"expected completion menu to be hidden, as words completion threshold is not met"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
cx.simulate_keystroke("o");
|
||||
cx.executor().run_until_parked();
|
||||
cx.update_editor(|editor, _, _| {
|
||||
if editor.context_menu.borrow_mut().is_some() {
|
||||
panic!(
|
||||
"expected completion menu to be hidden, as words completion threshold is not met still"
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
cx.simulate_keystroke("w");
|
||||
cx.executor().run_until_parked();
|
||||
cx.update_editor(|editor, _, _| {
|
||||
if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
|
||||
{
|
||||
assert_eq!(completion_menu_entries(menu), &["wowen", "wowser"], "After word completion threshold is met, matching words should be shown, excluding the already typed word");
|
||||
} else {
|
||||
panic!("expected completion menu to be open after the word completions threshold is met");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
|
||||
let position = || lsp::Position {
|
||||
line: params.text_document_position.position.line,
|
||||
|
|
|
@ -350,6 +350,12 @@ pub struct CompletionSettings {
|
|||
/// Default: `fallback`
|
||||
#[serde(default = "default_words_completion_mode")]
|
||||
pub words: WordsCompletionMode,
|
||||
/// How many characters has to be in the completions query to automatically show the words-based completions.
|
||||
/// Before that value, it's still possible to trigger the words-based completion manually with the corresponding editor command.
|
||||
///
|
||||
/// Default: 3
|
||||
#[serde(default = "default_3")]
|
||||
pub words_min_length: usize,
|
||||
/// Whether to fetch LSP completions or not.
|
||||
///
|
||||
/// Default: true
|
||||
|
@ -359,7 +365,7 @@ pub struct CompletionSettings {
|
|||
/// When set to 0, waits indefinitely.
|
||||
///
|
||||
/// Default: 0
|
||||
#[serde(default = "default_lsp_fetch_timeout_ms")]
|
||||
#[serde(default)]
|
||||
pub lsp_fetch_timeout_ms: u64,
|
||||
/// Controls how LSP completions are inserted.
|
||||
///
|
||||
|
@ -405,8 +411,8 @@ fn default_lsp_insert_mode() -> LspInsertMode {
|
|||
LspInsertMode::ReplaceSuffix
|
||||
}
|
||||
|
||||
fn default_lsp_fetch_timeout_ms() -> u64 {
|
||||
0
|
||||
fn default_3() -> usize {
|
||||
3
|
||||
}
|
||||
|
||||
/// The settings for a particular language.
|
||||
|
@ -1468,6 +1474,7 @@ impl settings::Settings for AllLanguageSettings {
|
|||
} else {
|
||||
d.completions = Some(CompletionSettings {
|
||||
words: mode,
|
||||
words_min_length: 3,
|
||||
lsp: true,
|
||||
lsp_fetch_timeout_ms: 0,
|
||||
lsp_insert_mode: LspInsertMode::ReplaceSuffix,
|
||||
|
|
|
@ -2425,6 +2425,7 @@ Examples:
|
|||
{
|
||||
"completions": {
|
||||
"words": "fallback",
|
||||
"words_min_length": 3,
|
||||
"lsp": true,
|
||||
"lsp_fetch_timeout_ms": 0,
|
||||
"lsp_insert_mode": "replace_suffix"
|
||||
|
@ -2444,6 +2445,17 @@ Examples:
|
|||
2. `fallback` - Only if LSP response errors or times out, use document's words to show completions
|
||||
3. `disabled` - Never fetch or complete document's words for completions (word-based completions can still be queried via a separate action)
|
||||
|
||||
### Min Words Query Length
|
||||
|
||||
- Description: Minimum number of characters required to automatically trigger word-based completions.
|
||||
Before that value, it's still possible to trigger the words-based completion manually with the corresponding editor command.
|
||||
- Setting: `words_min_length`
|
||||
- Default: `3`
|
||||
|
||||
**Options**
|
||||
|
||||
Positive integer values
|
||||
|
||||
### LSP
|
||||
|
||||
- Description: Whether to fetch LSP completions or not.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue