From f93e9a9475d29fc1ac825c38134f009ab66f564c Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Mon, 22 Sep 2025 16:29:51 +0800 Subject: [PATCH] editor: Change to pass cursor as `offset` for `completions` method. (#1271) And to support `filter_text` as completion item highlight match. --- .github/workflows/ci.yml | 2 +- crates/story/examples/editor.rs | 35 ++++++++----------- crates/ui/src/input/lsp/completions.rs | 4 ++- .../ui/src/input/popovers/completion_menu.rs | 18 ++++++---- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 875cf255..fd089d3c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,7 +32,7 @@ jobs: run: script/bootstrap - name: Machete if: ${{ matrix.run_on == 'macos-latest' }} - uses: bnjbvr/cargo-machete@main + uses: bnjbvr/cargo-machete@v0.9.1 - name: Setup | Cache Cargo uses: actions/cache@v4 with: diff --git a/crates/story/examples/editor.rs b/crates/story/examples/editor.rs index cf8a2ea3..c4ee0345 100644 --- a/crates/story/examples/editor.rs +++ b/crates/story/examples/editor.rs @@ -167,7 +167,7 @@ impl ExampleLspStore { } fn completion_item( - range: &lsp_types::Range, + replace_range: &lsp_types::Range, label: &str, replace_text: &str, documentation: &str, @@ -177,8 +177,8 @@ fn completion_item( kind: Some(lsp_types::CompletionItemKind::FUNCTION), text_edit: Some(CompletionTextEdit::InsertAndReplace(InsertReplaceEdit { new_text: replace_text.to_string(), - insert: range.clone(), - replace: range.clone(), + insert: replace_range.clone(), + replace: replace_range.clone(), })), documentation: Some(lsp_types::Documentation::String(documentation.to_string())), insert_text: None, @@ -204,33 +204,28 @@ impl CompletionProvider for ExampleLspStore { let rope = rope.clone(); let items = self.completions.clone(); cx.background_spawn(async move { - let pos = rope.offset_to_position(offset); - // Simulate a slow completion source, to test Editor async handling. 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("/") { + 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![ completion_item( - &range, + &replace_range, "/date", format!("{}", chrono::Local::now().date_naive()).as_str(), "Insert current date", ), - completion_item(&range, "/thanks", "Thank you!", "Insert Thank you!"), - completion_item(&range, "/+1", "👍", "Insert 👍"), - completion_item(&range, "/-1", "👎", "Insert 👎"), - completion_item(&range, "/smile", "😊", "Insert 😊"), - completion_item(&range, "/sad", "😢", "Insert 😢"), - completion_item(&range, "/launch", "🚀", "Insert 🚀"), + completion_item(&replace_range, "/thanks", "Thank you!", "Insert Thank you!"), + completion_item(&replace_range, "/+1", "👍", "Insert 👍"), + completion_item(&replace_range, "/-1", "👎", "Insert 👎"), + completion_item(&replace_range, "/smile", "😊", "Insert 😊"), + completion_item(&replace_range, "/sad", "😢", "Insert 😢"), + completion_item(&replace_range, "/launch", "🚀", "Insert 🚀"), ]; return Ok(CompletionResponse::Array(items)); } diff --git a/crates/ui/src/input/lsp/completions.rs b/crates/ui/src/input/lsp/completions.rs index 4946d2ee..395c8afb 100644 --- a/crates/ui/src/input/lsp/completions.rs +++ b/crates/ui/src/input/lsp/completions.rs @@ -13,6 +13,8 @@ use crate::input::{ pub trait CompletionProvider { /// Fetches completions based on the given byte offset. /// + /// - The `offset` is in bytes of current cursor. + /// /// 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 = - 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| { let mut completions: Vec = vec![]; if let Some(provider_responses) = provider_responses.await.ok() { diff --git a/crates/ui/src/input/popovers/completion_menu.rs b/crates/ui/src/input/popovers/completion_menu.rs index e9aff30a..9ef14f7f 100644 --- a/crates/ui/src/input/popovers/completion_menu.rs +++ b/crates/ui/src/input/popovers/completion_menu.rs @@ -48,7 +48,7 @@ struct CompletionMenuItem { item: Rc, children: Vec, selected: bool, - highlight_prefix_len: usize, + highlight_prefix: SharedString, } impl CompletionMenuItem { @@ -58,12 +58,12 @@ impl CompletionMenuItem { item, children: vec![], selected: false, - highlight_prefix_len: 0, + highlight_prefix: "".into(), } } - fn highlight_prefix(mut self, len: usize) -> Self { - self.highlight_prefix_len = len; + fn highlight_prefix(mut self, s: impl Into) -> Self { + self.highlight_prefix = s.into(); self } } @@ -88,7 +88,12 @@ impl RenderOnce for CompletionMenuItem { let item = self.item; 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![( 0..matched_len, HighlightStyle { @@ -139,8 +144,7 @@ impl ListDelegate for ContextMenuDelegate { _: &mut Context>, ) -> Option { let item = self.items.get(ix.row)?; - let matched_len = self.query.len(); - Some(CompletionMenuItem::new(ix.row, item.clone()).highlight_prefix(matched_len)) + Some(CompletionMenuItem::new(ix.row, item.clone()).highlight_prefix(self.query.clone())) } fn set_selected_index(