Autocomplete commands that don't require access to workspace in prompt library (#12674)
This is useful to autocomplete prompts when writing a new one in the prompt library. Release Notes: - N/A
This commit is contained in:
parent
ad2ddf1200
commit
27e9c68988
14 changed files with 88 additions and 196 deletions
|
@ -1,5 +1,5 @@
|
|||
use crate::{
|
||||
slash_command::SlashCommandLine, CompletionProvider, LanguageModelRequest,
|
||||
slash_command::SlashCommandCompletionProvider, CompletionProvider, LanguageModelRequest,
|
||||
LanguageModelRequestMessage, Role,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
@ -11,17 +11,14 @@ use futures::{
|
|||
future::{self, BoxFuture, Shared},
|
||||
FutureExt,
|
||||
};
|
||||
use fuzzy::{match_strings, StringMatchCandidate};
|
||||
use fuzzy::StringMatchCandidate;
|
||||
use gpui::{
|
||||
actions, point, size, AnyElement, AppContext, BackgroundExecutor, Bounds, DevicePixels,
|
||||
EventEmitter, Global, Model, PromptLevel, ReadGlobal, Subscription, Task, TitlebarOptions,
|
||||
View, WindowBounds, WindowHandle, WindowOptions,
|
||||
EventEmitter, Global, PromptLevel, ReadGlobal, Subscription, Task, TitlebarOptions, View,
|
||||
WindowBounds, WindowHandle, WindowOptions,
|
||||
};
|
||||
use heed::{types::SerdeBincode, Database, RoTxn};
|
||||
use language::{
|
||||
language_settings::SoftWrap, Buffer, Documentation, LanguageRegistry, LanguageServerId, Point,
|
||||
ToPoint as _,
|
||||
};
|
||||
use language::{language_settings::SoftWrap, Buffer, LanguageRegistry};
|
||||
use parking_lot::RwLock;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use rope::Rope;
|
||||
|
@ -482,6 +479,7 @@ impl PromptLibrary {
|
|||
self.set_active_prompt(Some(prompt_id), cx);
|
||||
} else {
|
||||
let language_registry = self.language_registry.clone();
|
||||
let commands = SlashCommandRegistry::global(cx);
|
||||
let prompt = self.store.load(prompt_id);
|
||||
self.pending_load = cx.spawn(|this, mut cx| async move {
|
||||
let prompt = prompt.await;
|
||||
|
@ -500,8 +498,9 @@ impl PromptLibrary {
|
|||
editor.set_show_gutter(false, cx);
|
||||
editor.set_show_wrap_guides(false, cx);
|
||||
editor.set_show_indent_guides(false, cx);
|
||||
editor
|
||||
.set_completion_provider(Box::new(SlashCommandCompletionProvider));
|
||||
editor.set_completion_provider(Box::new(
|
||||
SlashCommandCompletionProvider::new(commands, None, None),
|
||||
));
|
||||
if focus {
|
||||
editor.focus(cx);
|
||||
}
|
||||
|
@ -1092,123 +1091,3 @@ fn title_from_body(body: impl IntoIterator<Item = char>) -> Option<SharedString>
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct SlashCommandCompletionProvider;
|
||||
|
||||
impl editor::CompletionProvider for SlashCommandCompletionProvider {
|
||||
fn completions(
|
||||
&self,
|
||||
buffer: &Model<Buffer>,
|
||||
buffer_position: language::Anchor,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> Task<Result<Vec<project::Completion>>> {
|
||||
let Some((command_name, name_range)) = buffer.update(cx, |buffer, _cx| {
|
||||
let position = buffer_position.to_point(buffer);
|
||||
let line_start = Point::new(position.row, 0);
|
||||
let mut lines = buffer.text_for_range(line_start..position).lines();
|
||||
let line = lines.next()?;
|
||||
let call = SlashCommandLine::parse(line)?;
|
||||
|
||||
if call.argument.is_some() {
|
||||
// Don't autocomplete arguments.
|
||||
None
|
||||
} else {
|
||||
let name = line[call.name.clone()].to_string();
|
||||
let name_range_start = Point::new(position.row, call.name.start as u32);
|
||||
let name_range_end = Point::new(position.row, call.name.end as u32);
|
||||
let name_range =
|
||||
buffer.anchor_after(name_range_start)..buffer.anchor_after(name_range_end);
|
||||
Some((name, name_range))
|
||||
}
|
||||
}) else {
|
||||
return Task::ready(Ok(Vec::new()));
|
||||
};
|
||||
|
||||
let commands = SlashCommandRegistry::global(cx);
|
||||
let candidates = commands
|
||||
.command_names()
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(ix, def)| StringMatchCandidate {
|
||||
id: ix,
|
||||
string: def.to_string(),
|
||||
char_bag: def.as_ref().into(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let command_name = command_name.to_string();
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
let matches = match_strings(
|
||||
&candidates,
|
||||
&command_name,
|
||||
true,
|
||||
usize::MAX,
|
||||
&Default::default(),
|
||||
cx.background_executor().clone(),
|
||||
)
|
||||
.await;
|
||||
cx.update(|cx| {
|
||||
matches
|
||||
.into_iter()
|
||||
.filter_map(|mat| {
|
||||
let command = commands.command(&mat.string)?;
|
||||
let mut new_text = mat.string.clone();
|
||||
let requires_argument = command.requires_argument();
|
||||
if requires_argument {
|
||||
new_text.push(' ');
|
||||
}
|
||||
|
||||
Some(project::Completion {
|
||||
old_range: name_range.clone(),
|
||||
documentation: Some(Documentation::SingleLine(command.description())),
|
||||
new_text,
|
||||
label: command.label(cx),
|
||||
server_id: LanguageServerId(0),
|
||||
lsp_completion: Default::default(),
|
||||
show_new_completions_on_confirm: false,
|
||||
confirm: None,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn resolve_completions(
|
||||
&self,
|
||||
_: Model<Buffer>,
|
||||
_: Vec<usize>,
|
||||
_: Arc<RwLock<Box<[project::Completion]>>>,
|
||||
_: &mut ViewContext<Editor>,
|
||||
) -> Task<Result<bool>> {
|
||||
Task::ready(Ok(true))
|
||||
}
|
||||
|
||||
fn apply_additional_edits_for_completion(
|
||||
&self,
|
||||
_: Model<Buffer>,
|
||||
_: project::Completion,
|
||||
_: bool,
|
||||
_: &mut ViewContext<Editor>,
|
||||
) -> Task<Result<Option<language::Transaction>>> {
|
||||
Task::ready(Ok(None))
|
||||
}
|
||||
|
||||
fn is_completion_trigger(
|
||||
&self,
|
||||
buffer: &Model<Buffer>,
|
||||
position: language::Anchor,
|
||||
_text: &str,
|
||||
_trigger_in_words: bool,
|
||||
cx: &mut ViewContext<Editor>,
|
||||
) -> bool {
|
||||
let buffer = buffer.read(cx);
|
||||
let position = position.to_point(buffer);
|
||||
let line_start = Point::new(position.row, 0);
|
||||
let mut lines = buffer.text_for_range(line_start..position).lines();
|
||||
if let Some(line) = lines.next() {
|
||||
SlashCommandLine::parse(line).is_some()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue