editor: Change to pass cursor as offset for completions method. (#1271)

And to support `filter_text` as completion item highlight match.
This commit is contained in:
Jason Lee 2025-09-22 16:29:51 +08:00 committed by GitHub
parent 62748aad9b
commit f93e9a9475
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 30 additions and 29 deletions

View file

@ -32,7 +32,7 @@ jobs:
run: script/bootstrap run: script/bootstrap
- name: Machete - name: Machete
if: ${{ matrix.run_on == 'macos-latest' }} if: ${{ matrix.run_on == 'macos-latest' }}
uses: bnjbvr/cargo-machete@main uses: bnjbvr/cargo-machete@v0.9.1
- name: Setup | Cache Cargo - name: Setup | Cache Cargo
uses: actions/cache@v4 uses: actions/cache@v4
with: with:

View file

@ -167,7 +167,7 @@ impl ExampleLspStore {
} }
fn completion_item( fn completion_item(
range: &lsp_types::Range, replace_range: &lsp_types::Range,
label: &str, label: &str,
replace_text: &str, replace_text: &str,
documentation: &str, documentation: &str,
@ -177,8 +177,8 @@ fn completion_item(
kind: Some(lsp_types::CompletionItemKind::FUNCTION), kind: Some(lsp_types::CompletionItemKind::FUNCTION),
text_edit: Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit { text_edit: Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit {
new_text: replace_text.to_string(), new_text: replace_text.to_string(),
insert: range.clone(), insert: replace_range.clone(),
replace: range.clone(), replace: replace_range.clone(),
})), })),
documentation: Some(lsp_types::Documentation::String(documentation.to_string())), documentation: Some(lsp_types::Documentation::String(documentation.to_string())),
insert_text: None, insert_text: None,
@ -204,33 +204,28 @@ impl CompletionProvider for ExampleLspStore {
let rope = rope.clone(); let rope = rope.clone();
let items = self.completions.clone(); let items = self.completions.clone();
cx.background_spawn(async move { cx.background_spawn(async move {
let pos = rope.offset_to_position(offset);
// Simulate a slow completion source, to test Editor async handling. // Simulate a slow completion source, to test Editor async handling.
smol::Timer::after(Duration::from_millis(20)).await; smol::Timer::after(Duration::from_millis(20)).await;
let range = lsp_types::Range::new(
pos,
lsp_types::Position {
line: pos.line,
character: pos.character + 1,
},
);
if trigger_character.starts_with("/") { if trigger_character.starts_with("/") {
let start = offset.saturating_sub(trigger_character.len());
let start_pos = rope.offset_to_position(start);
let end_pos = rope.offset_to_position(offset);
let replace_range = lsp_types::Range::new(start_pos, end_pos);
let items = vec![ let items = vec![
completion_item( completion_item(
&range, &replace_range,
"/date", "/date",
format!("{}", chrono::Local::now().date_naive()).as_str(), format!("{}", chrono::Local::now().date_naive()).as_str(),
"Insert current date", "Insert current date",
), ),
completion_item(&range, "/thanks", "Thank you!", "Insert Thank you!"), completion_item(&replace_range, "/thanks", "Thank you!", "Insert Thank you!"),
completion_item(&range, "/+1", "👍", "Insert 👍"), completion_item(&replace_range, "/+1", "👍", "Insert 👍"),
completion_item(&range, "/-1", "👎", "Insert 👎"), completion_item(&replace_range, "/-1", "👎", "Insert 👎"),
completion_item(&range, "/smile", "😊", "Insert 😊"), completion_item(&replace_range, "/smile", "😊", "Insert 😊"),
completion_item(&range, "/sad", "😢", "Insert 😢"), completion_item(&replace_range, "/sad", "😢", "Insert 😢"),
completion_item(&range, "/launch", "🚀", "Insert 🚀"), completion_item(&replace_range, "/launch", "🚀", "Insert 🚀"),
]; ];
return Ok(CompletionResponse::Array(items)); return Ok(CompletionResponse::Array(items));
} }

View file

@ -13,6 +13,8 @@ use crate::input::{
pub trait CompletionProvider { pub trait CompletionProvider {
/// Fetches completions based on the given byte offset. /// Fetches completions based on the given byte offset.
/// ///
/// - The `offset` is in bytes of current cursor.
///
/// textDocument/completion /// textDocument/completion
/// ///
/// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion /// https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_completion
@ -107,7 +109,7 @@ impl InputState {
}; };
let provider_responses = let provider_responses =
provider.completions(&self.text, start_offset, completion_context, window, cx); provider.completions(&self.text, new_offset, completion_context, window, cx);
self._context_menu_task = cx.spawn_in(window, async move |editor, cx| { self._context_menu_task = cx.spawn_in(window, async move |editor, cx| {
let mut completions: Vec<CompletionItem> = vec![]; let mut completions: Vec<CompletionItem> = vec![];
if let Some(provider_responses) = provider_responses.await.ok() { if let Some(provider_responses) = provider_responses.await.ok() {

View file

@ -48,7 +48,7 @@ struct CompletionMenuItem {
item: Rc<CompletionItem>, item: Rc<CompletionItem>,
children: Vec<AnyElement>, children: Vec<AnyElement>,
selected: bool, selected: bool,
highlight_prefix_len: usize, highlight_prefix: SharedString,
} }
impl CompletionMenuItem { impl CompletionMenuItem {
@ -58,12 +58,12 @@ impl CompletionMenuItem {
item, item,
children: vec![], children: vec![],
selected: false, selected: false,
highlight_prefix_len: 0, highlight_prefix: "".into(),
} }
} }
fn highlight_prefix(mut self, len: usize) -> Self { fn highlight_prefix(mut self, s: impl Into<SharedString>) -> Self {
self.highlight_prefix_len = len; self.highlight_prefix = s.into();
self self
} }
} }
@ -88,7 +88,12 @@ impl RenderOnce for CompletionMenuItem {
let item = self.item; let item = self.item;
let deprecated = item.deprecated.unwrap_or(false); let deprecated = item.deprecated.unwrap_or(false);
let matched_len = self.highlight_prefix_len; let matched_len = item
.filter_text
.as_ref()
.map(|s| s.len())
.unwrap_or(self.highlight_prefix.len());
let highlights = vec![( let highlights = vec![(
0..matched_len, 0..matched_len,
HighlightStyle { HighlightStyle {
@ -139,8 +144,7 @@ impl ListDelegate for ContextMenuDelegate {
_: &mut Context<List<Self>>, _: &mut Context<List<Self>>,
) -> Option<Self::Item> { ) -> Option<Self::Item> {
let item = self.items.get(ix.row)?; let item = self.items.get(ix.row)?;
let matched_len = self.query.len(); Some(CompletionMenuItem::new(ix.row, item.clone()).highlight_prefix(self.query.clone()))
Some(CompletionMenuItem::new(ix.row, item.clone()).highlight_prefix(matched_len))
} }
fn set_selected_index( fn set_selected_index(