Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/base/src/input/base/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ actions!(
MoveToNextWord,
Escape,
ToggleCodeActions,
ShowCompletions,
Search,
Replace,
GoToDefinition,
Expand Down
120 changes: 96 additions & 24 deletions crates/base/src/input/editor/lsp/completions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use lsp_types::{
use ropey::Rope;
use std::{cell::RefCell, ops::Range, rc::Rc, time::Duration};

use crate::input::InputBaseState;
use crate::input::{InputBaseState, ShowCompletions};

/// Default debounce duration for inline completions.
const DEFAULT_INLINE_COMPLETION_DEBOUNCE: Duration = Duration::from_millis(300);
Expand Down Expand Up @@ -183,6 +183,93 @@ impl InputBaseState<EditorMode> {
)
.map(|s| s.trim().to_string())
.unwrap_or_default();
self.request_completions(
provider,
start_offset,
query.clone(),
CompletionContext {
trigger_kind: lsp_types::CompletionTriggerKind::TRIGGER_CHARACTER,
trigger_character: Some(query),
},
window,
cx,
);
}

pub(crate) fn on_action_show_completions(
&mut self,
_: &ShowCompletions,
window: &mut Window,
cx: &mut Context<Self>,
) {
self.show_completions(window, cx);
}

/// Request completions at the caret and focus the editor, without editing text.
///
/// The current identifier prefix is used to refine the menu. An empty
/// prefix still asks the provider for suggestions. There is no default
/// keybinding; applications can dispatch [`ShowCompletions`] in the
/// `Input && mode == editor` context or call this method from a command.
///
/// Disabled/readonly editors, IME preedit, and an already-open completion
/// menu are left alone. A new request cancels the previous overlay request
/// and inline suggestion, and closes the code-action menu.
pub fn show_completions(&mut self, window: &mut Window, cx: &mut Context<Self>) {
if self.disabled
|| self.readonly
|| self.completion_inserting
|| self.ime_marked_range.is_some()
|| self.extras.context_menu_content.completion.open
{
return;
}
let Some(provider) = self.extras.lsp.completion_provider.clone() else {
return;
};

let offset = self.cursor();
let word_len: usize = self
.text
.chars_at(offset)
.reversed()
.take_while(|ch| ch.is_alphanumeric() || *ch == '_')
.map(char::len_utf8)
.sum();
let start_offset = offset - word_len;
let query = self.text.slice(start_offset..offset).to_string();

self.hide_context_menu(cx);
self.clear_inline_completion(cx);
self.focus(window, cx);

self.request_completions(
provider,
start_offset,
query,
CompletionContext {
trigger_kind: lsp_types::CompletionTriggerKind::INVOKED,
trigger_character: None,
},
window,
cx,
);
}

/// Record the word a menu is for and ask `provider` about the caret.
fn request_completions(
&mut self,
provider: Rc<dyn CompletionProvider>,
start_offset: usize,
query: String,
completion_context: CompletionContext,
window: &mut Window,
cx: &mut Context<Self>,
) {
let new_offset = self.cursor();
let revision = self.document_revision;
self.extras.context_menu_task = Task::ready(Ok(()));
self.extras.context_menu_content.code_action.open = false;
self.extras
.context_menu_content
.completion
Expand All @@ -193,11 +280,6 @@ impl InputBaseState<EditorMode> {
.query
.clone_from(&query);

let completion_context = CompletionContext {
trigger_kind: lsp_types::CompletionTriggerKind::TRIGGER_CHARACTER,
trigger_character: Some(query),
};

let provider_responses =
provider.completions(&self.text, new_offset, completion_context, window, cx);
self.extras.context_menu_task = cx.spawn_in(window, async move |editor, cx| {
Expand All @@ -209,31 +291,21 @@ impl InputBaseState<EditorMode> {
}
}

if completions.is_empty() {
editor.update(cx, |editor, cx| {
editor.extras.context_menu_content.completion.open = false;
editor.extras.context_menu_content.completion.items.clear();
editor.extras.context_menu_content.completion.bump();
cx.notify();
})?;
return Ok(());
}

editor
.update_in(cx, |editor, window, cx| {
if !editor.focus_handle.is_focused(window) {
if !editor.focus_handle.is_focused(window)
|| editor.document_revision != revision
|| editor.cursor() != new_offset
|| editor.ime_marked_range.is_some()
|| editor.disabled
|| editor.readonly
{
return;
}

editor.extras.context_menu_content.completion.open = !completions.is_empty();
editor.extras.context_menu_content.completion.items = completions;
editor.extras.context_menu_content.completion.open = !editor
.extras
.context_menu_content
.completion
.items
.is_empty();
editor.extras.context_menu_content.completion.bump();

cx.notify();
})
.ok();
Expand Down
2 changes: 2 additions & 0 deletions crates/base/src/input/editor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ impl InputModeKind for EditorMode {
window: &mut Window,
) -> Stateful<Div> {
element
.key_context("Input mode=editor")
.on_action(window.listener_for(entity, InputBaseState::on_action_toggle_code_actions))
.on_action(window.listener_for(entity, InputBaseState::on_action_show_completions))
.on_action(window.listener_for(entity, InputBaseState::on_action_go_to_definition))
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/component/src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ pub use gpui_base::input::{
MoveUp, Outdent, OutdentInline, Paste, Point, RangeDecoration, RangeDecorationCollection,
RangeDecorationStyle, Redo, Replace, Rope, RopeExt, RopeLines, Search, SelectAll, SelectToEnd,
SelectToEndOfLine, SelectToNextWordEnd, SelectToPreviousWordStart, SelectToStart,
SelectToStartOfLine, Selection, ShowCharacterPalette, ShowDocumentHandler, TabSize,
TextDecoration, TextDecorationCollection, TextareaState, ToggleCodeActions, Undo,
SelectToStartOfLine, Selection, ShowCharacterPalette, ShowCompletions, ShowDocumentHandler,
TabSize, TextDecoration, TextDecorationCollection, TextareaState, ToggleCodeActions, Undo,
WrappingIndent,
};
pub use gpui_base::input::{EditorMode, InputMode, InputModeKind, TextareaMode};
Expand Down
Loading
Loading