Add support for insert_text_mode
of a completion (#28171)
I wanted this for CONL (https://conl.dev )'s nascent langauge server, and it seems like most of the support was already wired up on the LSP side, so this surfaces it into the editor. Release Notes: - Added support for the `insert_text_mode` field of completions from the language server protocol.
This commit is contained in:
parent
5a7222edc5
commit
a577a72f69
10 changed files with 91 additions and 4 deletions
|
@ -112,6 +112,7 @@ impl ContextPickerCompletionProvider {
|
||||||
icon_path: Some(mode.icon().path().into()),
|
icon_path: Some(mode.icon().path().into()),
|
||||||
documentation: None,
|
documentation: None,
|
||||||
source: project::CompletionSource::Custom,
|
source: project::CompletionSource::Custom,
|
||||||
|
insert_text_mode: None,
|
||||||
// This ensures that when a user accepts this completion, the
|
// This ensures that when a user accepts this completion, the
|
||||||
// completion menu will still be shown after "@category " is
|
// completion menu will still be shown after "@category " is
|
||||||
// inserted
|
// inserted
|
||||||
|
@ -163,6 +164,7 @@ impl ContextPickerCompletionProvider {
|
||||||
new_text,
|
new_text,
|
||||||
label: CodeLabel::plain(thread_entry.summary.to_string(), None),
|
label: CodeLabel::plain(thread_entry.summary.to_string(), None),
|
||||||
documentation: None,
|
documentation: None,
|
||||||
|
insert_text_mode: None,
|
||||||
source: project::CompletionSource::Custom,
|
source: project::CompletionSource::Custom,
|
||||||
icon_path: Some(icon_for_completion.path().into()),
|
icon_path: Some(icon_for_completion.path().into()),
|
||||||
confirm: Some(confirm_completion_callback(
|
confirm: Some(confirm_completion_callback(
|
||||||
|
@ -209,6 +211,7 @@ impl ContextPickerCompletionProvider {
|
||||||
documentation: None,
|
documentation: None,
|
||||||
source: project::CompletionSource::Custom,
|
source: project::CompletionSource::Custom,
|
||||||
icon_path: Some(IconName::Globe.path().into()),
|
icon_path: Some(IconName::Globe.path().into()),
|
||||||
|
insert_text_mode: None,
|
||||||
confirm: Some(confirm_completion_callback(
|
confirm: Some(confirm_completion_callback(
|
||||||
IconName::Globe.path().into(),
|
IconName::Globe.path().into(),
|
||||||
url_to_fetch.clone(),
|
url_to_fetch.clone(),
|
||||||
|
@ -290,6 +293,7 @@ impl ContextPickerCompletionProvider {
|
||||||
documentation: None,
|
documentation: None,
|
||||||
source: project::CompletionSource::Custom,
|
source: project::CompletionSource::Custom,
|
||||||
icon_path: Some(completion_icon_path),
|
icon_path: Some(completion_icon_path),
|
||||||
|
insert_text_mode: None,
|
||||||
confirm: Some(confirm_completion_callback(
|
confirm: Some(confirm_completion_callback(
|
||||||
crease_icon_path,
|
crease_icon_path,
|
||||||
file_name,
|
file_name,
|
||||||
|
@ -352,6 +356,7 @@ impl ContextPickerCompletionProvider {
|
||||||
documentation: None,
|
documentation: None,
|
||||||
source: project::CompletionSource::Custom,
|
source: project::CompletionSource::Custom,
|
||||||
icon_path: Some(IconName::Code.path().into()),
|
icon_path: Some(IconName::Code.path().into()),
|
||||||
|
insert_text_mode: None,
|
||||||
confirm: Some(confirm_completion_callback(
|
confirm: Some(confirm_completion_callback(
|
||||||
IconName::Code.path().into(),
|
IconName::Code.path().into(),
|
||||||
symbol.name.clone().into(),
|
symbol.name.clone().into(),
|
||||||
|
|
|
@ -127,6 +127,7 @@ impl SlashCommandCompletionProvider {
|
||||||
new_text,
|
new_text,
|
||||||
label: command.label(cx),
|
label: command.label(cx),
|
||||||
icon_path: None,
|
icon_path: None,
|
||||||
|
insert_text_mode: None,
|
||||||
confirm,
|
confirm,
|
||||||
source: CompletionSource::Custom,
|
source: CompletionSource::Custom,
|
||||||
})
|
})
|
||||||
|
@ -228,6 +229,7 @@ impl SlashCommandCompletionProvider {
|
||||||
new_text,
|
new_text,
|
||||||
documentation: None,
|
documentation: None,
|
||||||
confirm,
|
confirm,
|
||||||
|
insert_text_mode: None,
|
||||||
source: CompletionSource::Custom,
|
source: CompletionSource::Custom,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -315,6 +315,7 @@ impl MessageEditor {
|
||||||
icon_path: None,
|
icon_path: None,
|
||||||
confirm: None,
|
confirm: None,
|
||||||
documentation: None,
|
documentation: None,
|
||||||
|
insert_text_mode: None,
|
||||||
source: CompletionSource::Custom,
|
source: CompletionSource::Custom,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -367,6 +367,7 @@ impl ConsoleQueryBarCompletionProvider {
|
||||||
documentation: None,
|
documentation: None,
|
||||||
confirm: None,
|
confirm: None,
|
||||||
source: project::CompletionSource::Custom,
|
source: project::CompletionSource::Custom,
|
||||||
|
insert_text_mode: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
|
@ -409,6 +410,7 @@ impl ConsoleQueryBarCompletionProvider {
|
||||||
documentation: None,
|
documentation: None,
|
||||||
confirm: None,
|
confirm: None,
|
||||||
source: project::CompletionSource::Custom,
|
source: project::CompletionSource::Custom,
|
||||||
|
insert_text_mode: None,
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
|
|
|
@ -240,6 +240,7 @@ impl CompletionsMenu {
|
||||||
icon_path: None,
|
icon_path: None,
|
||||||
documentation: None,
|
documentation: None,
|
||||||
confirm: None,
|
confirm: None,
|
||||||
|
insert_text_mode: None,
|
||||||
source: CompletionSource::Custom,
|
source: CompletionSource::Custom,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
|
@ -136,7 +136,7 @@ use task::{ResolvedTask, TaskTemplate, TaskVariables};
|
||||||
pub use lsp::CompletionContext;
|
pub use lsp::CompletionContext;
|
||||||
use lsp::{
|
use lsp::{
|
||||||
CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
|
CodeActionKind, CompletionItemKind, CompletionTriggerKind, DiagnosticSeverity,
|
||||||
InsertTextFormat, LanguageServerId, LanguageServerName,
|
InsertTextFormat, InsertTextMode, LanguageServerId, LanguageServerName,
|
||||||
};
|
};
|
||||||
|
|
||||||
use language::BufferSnapshot;
|
use language::BufferSnapshot;
|
||||||
|
@ -4442,6 +4442,7 @@ impl Editor {
|
||||||
word_range,
|
word_range,
|
||||||
resolved: false,
|
resolved: false,
|
||||||
},
|
},
|
||||||
|
insert_text_mode: Some(InsertTextMode::AS_IS),
|
||||||
confirm: None,
|
confirm: None,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -4687,7 +4688,13 @@ impl Editor {
|
||||||
} else {
|
} else {
|
||||||
this.buffer.update(cx, |buffer, cx| {
|
this.buffer.update(cx, |buffer, cx| {
|
||||||
let edits = ranges.iter().map(|range| (range.clone(), text));
|
let edits = ranges.iter().map(|range| (range.clone(), text));
|
||||||
buffer.edit(edits, this.autoindent_mode.clone(), cx);
|
let auto_indent = if completion.insert_text_mode == Some(InsertTextMode::AS_IS)
|
||||||
|
{
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
this.autoindent_mode.clone()
|
||||||
|
};
|
||||||
|
buffer.edit(edits, auto_indent, cx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (buffer, edits) in linked_edits {
|
for (buffer, edits) in linked_edits {
|
||||||
|
@ -18637,6 +18644,7 @@ fn snippet_completions(
|
||||||
.description
|
.description
|
||||||
.clone()
|
.clone()
|
||||||
.map(|description| CompletionDocumentation::SingleLine(description.into())),
|
.map(|description| CompletionDocumentation::SingleLine(description.into())),
|
||||||
|
insert_text_mode: None,
|
||||||
confirm: None,
|
confirm: None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -10235,6 +10235,62 @@ async fn test_completion_sort(cx: &mut TestAppContext) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_as_is_completions(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx, |_| {});
|
||||||
|
let mut cx = EditorLspTestContext::new_rust(
|
||||||
|
lsp::ServerCapabilities {
|
||||||
|
completion_provider: Some(lsp::CompletionOptions {
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
cx.lsp
|
||||||
|
.set_request_handler::<lsp::request::Completion, _, _>(move |_, _| async move {
|
||||||
|
Ok(Some(lsp::CompletionResponse::Array(vec![
|
||||||
|
lsp::CompletionItem {
|
||||||
|
label: "unsafe".into(),
|
||||||
|
text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit {
|
||||||
|
range: lsp::Range {
|
||||||
|
start: lsp::Position {
|
||||||
|
line: 1,
|
||||||
|
character: 2,
|
||||||
|
},
|
||||||
|
end: lsp::Position {
|
||||||
|
line: 1,
|
||||||
|
character: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
new_text: "unsafe".to_string(),
|
||||||
|
})),
|
||||||
|
insert_text_mode: Some(lsp::InsertTextMode::AS_IS),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
])))
|
||||||
|
});
|
||||||
|
cx.set_state("fn a() {}\n nˇ");
|
||||||
|
cx.executor().run_until_parked();
|
||||||
|
cx.update_editor(|editor, window, cx| {
|
||||||
|
editor.show_completions(
|
||||||
|
&ShowCompletions {
|
||||||
|
trigger: Some("\n".into()),
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
cx.executor().run_until_parked();
|
||||||
|
|
||||||
|
cx.update_editor(|editor, window, cx| {
|
||||||
|
editor.confirm_completion(&Default::default(), window, cx)
|
||||||
|
});
|
||||||
|
cx.executor().run_until_parked();
|
||||||
|
cx.assert_editor_state("fn a() {}\n unsafeˇ");
|
||||||
|
}
|
||||||
|
|
||||||
#[gpui::test]
|
#[gpui::test]
|
||||||
async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
|
async fn test_no_duplicated_completion_requests(cx: &mut TestAppContext) {
|
||||||
init_test(cx, |_| {});
|
init_test(cx, |_| {});
|
||||||
|
|
|
@ -704,8 +704,15 @@ impl LanguageServer {
|
||||||
}),
|
}),
|
||||||
insert_replace_support: Some(true),
|
insert_replace_support: Some(true),
|
||||||
label_details_support: Some(true),
|
label_details_support: Some(true),
|
||||||
|
insert_text_mode_support: Some(InsertTextModeSupport {
|
||||||
|
value_set: vec![
|
||||||
|
InsertTextMode::AS_IS,
|
||||||
|
InsertTextMode::ADJUST_INDENTATION,
|
||||||
|
],
|
||||||
|
}),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}),
|
}),
|
||||||
|
insert_text_mode: Some(InsertTextMode::ADJUST_INDENTATION),
|
||||||
completion_list: Some(CompletionListCapability {
|
completion_list: Some(CompletionListCapability {
|
||||||
item_defaults: Some(vec![
|
item_defaults: Some(vec![
|
||||||
"commitCharacters".to_owned(),
|
"commitCharacters".to_owned(),
|
||||||
|
|
|
@ -8053,6 +8053,7 @@ impl LspStore {
|
||||||
runs: Default::default(),
|
runs: Default::default(),
|
||||||
filter_range: Default::default(),
|
filter_range: Default::default(),
|
||||||
},
|
},
|
||||||
|
insert_text_mode: None,
|
||||||
icon_path: None,
|
icon_path: None,
|
||||||
confirm: None,
|
confirm: None,
|
||||||
}]))),
|
}]))),
|
||||||
|
@ -9342,6 +9343,7 @@ async fn populate_labels_for_completions(
|
||||||
documentation,
|
documentation,
|
||||||
old_range: completion.old_range,
|
old_range: completion.old_range,
|
||||||
new_text: completion.new_text,
|
new_text: completion.new_text,
|
||||||
|
insert_text_mode: lsp_completion.insert_text_mode,
|
||||||
source: completion.source,
|
source: completion.source,
|
||||||
icon_path: None,
|
icon_path: None,
|
||||||
confirm: None,
|
confirm: None,
|
||||||
|
@ -9356,6 +9358,7 @@ async fn populate_labels_for_completions(
|
||||||
old_range: completion.old_range,
|
old_range: completion.old_range,
|
||||||
new_text: completion.new_text,
|
new_text: completion.new_text,
|
||||||
source: completion.source,
|
source: completion.source,
|
||||||
|
insert_text_mode: None,
|
||||||
icon_path: None,
|
icon_path: None,
|
||||||
confirm: None,
|
confirm: None,
|
||||||
});
|
});
|
||||||
|
|
|
@ -68,8 +68,8 @@ use language::{
|
||||||
language_settings::InlayHintKind, proto::split_operations,
|
language_settings::InlayHintKind, proto::split_operations,
|
||||||
};
|
};
|
||||||
use lsp::{
|
use lsp::{
|
||||||
CodeActionKind, CompletionContext, CompletionItemKind, DocumentHighlightKind, LanguageServerId,
|
CodeActionKind, CompletionContext, CompletionItemKind, DocumentHighlightKind, InsertTextMode,
|
||||||
LanguageServerName, MessageActionItem,
|
LanguageServerId, LanguageServerName, MessageActionItem,
|
||||||
};
|
};
|
||||||
use lsp_command::*;
|
use lsp_command::*;
|
||||||
use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle};
|
use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle};
|
||||||
|
@ -392,6 +392,8 @@ pub struct Completion {
|
||||||
pub source: CompletionSource,
|
pub source: CompletionSource,
|
||||||
/// A path to an icon for this completion that is shown in the menu.
|
/// A path to an icon for this completion that is shown in the menu.
|
||||||
pub icon_path: Option<SharedString>,
|
pub icon_path: Option<SharedString>,
|
||||||
|
/// Whether to adjust indentation (the default) or not.
|
||||||
|
pub insert_text_mode: Option<InsertTextMode>,
|
||||||
/// An optional callback to invoke when this completion is confirmed.
|
/// An optional callback to invoke when this completion is confirmed.
|
||||||
/// Returns, whether new completions should be retriggered after the current one.
|
/// Returns, whether new completions should be retriggered after the current one.
|
||||||
/// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.
|
/// If `true` is returned, the editor will show a new completion menu after this completion is confirmed.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue