Allow specifying a custom limit for /search results (#12423)

<img width="497" alt="image"
src="https://github.com/zed-industries/zed/assets/482957/94e94326-fb3c-4f9b-b4d9-7dd6f6f8d537">


e.g.

```
/search --9 foobar
```

Release Notes:

- N/A
This commit is contained in:
Antonio Scandurra 2024-05-29 14:11:05 +02:00 committed by GitHub
parent f3e6a0beab
commit a0644ac601
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 93 additions and 49 deletions

View file

@ -73,57 +73,58 @@ impl SlashCommandCompletionProvider {
let command_name = command_name.to_string();
let editor = self.editor.clone();
let workspace = self.workspace.clone();
let executor = cx.background_executor().clone();
executor.clone().spawn(async move {
cx.spawn(|mut cx| async move {
let matches = match_strings(
&candidates,
&command_name,
true,
usize::MAX,
&Default::default(),
executor,
cx.background_executor().clone(),
)
.await;
Ok(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(' ');
}
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: CodeLabel::plain(mat.string.clone(), None),
server_id: LanguageServerId(0),
lsp_completion: Default::default(),
confirm: (!requires_argument).then(|| {
let command_name = mat.string.clone();
let command_range = command_range.clone();
let editor = editor.clone();
let workspace = workspace.clone();
Arc::new(move |cx: &mut WindowContext| {
editor
.update(cx, |editor, cx| {
editor.run_command(
command_range.clone(),
&command_name,
None,
workspace.clone(),
cx,
);
})
.ok();
}) as Arc<_>
}),
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(),
confirm: (!requires_argument).then(|| {
let command_name = mat.string.clone();
let command_range = command_range.clone();
let editor = editor.clone();
let workspace = workspace.clone();
Arc::new(move |cx: &mut WindowContext| {
editor
.update(cx, |editor, cx| {
editor.run_command(
command_range.clone(),
&command_name,
None,
workspace.clone(),
cx,
);
})
.ok();
}) as Arc<_>
}),
})
})
})
.collect())
.collect()
})
})
}

View file

@ -2,7 +2,7 @@ use super::{file_command::FilePlaceholder, SlashCommand, SlashCommandOutput};
use anyhow::Result;
use assistant_slash_command::SlashCommandOutputSection;
use gpui::{AppContext, Task, WeakView};
use language::{LineEnding, LspAdapterDelegate};
use language::{CodeLabel, HighlightId, LineEnding, LspAdapterDelegate};
use semantic_index::SemanticIndex;
use std::{
fmt::Write,
@ -20,6 +20,17 @@ impl SlashCommand for SearchSlashCommand {
"search".into()
}
fn label(&self, cx: &AppContext) -> CodeLabel {
let mut label = CodeLabel::default();
label.push_str("search ", None);
label.push_str(
"--n",
cx.theme().syntax().highlight_id("comment").map(HighlightId),
);
label.filter_range = 0.."search".len();
label
}
fn description(&self) -> String {
"semantically search files".into()
}
@ -54,12 +65,27 @@ impl SlashCommand for SearchSlashCommand {
let Some(argument) = argument else {
return Task::ready(Err(anyhow::anyhow!("missing search query")));
};
if argument.is_empty() {
let mut limit = None;
let mut query = String::new();
for part in argument.split(' ') {
if let Some(parameter) = part.strip_prefix("--") {
if let Ok(count) = parameter.parse::<usize>() {
limit = Some(count);
continue;
}
}
query.push_str(part);
query.push(' ');
}
query.pop();
if query.is_empty() {
return Task::ready(Err(anyhow::anyhow!("missing search query")));
}
let project = workspace.read(cx).project().clone();
let argument = argument.to_string();
let fs = project.read(cx).fs().clone();
let project_index =
cx.update_global(|index: &mut SemanticIndex, cx| index.project_index(project, cx));
@ -67,7 +93,7 @@ impl SlashCommand for SearchSlashCommand {
cx.spawn(|cx| async move {
let results = project_index
.read_with(&cx, |project_index, cx| {
project_index.search(argument.clone(), 5, cx)
project_index.search(query.clone(), limit.unwrap_or(5), cx)
})?
.await?;
@ -92,7 +118,7 @@ impl SlashCommand for SearchSlashCommand {
let output = cx
.background_executor()
.spawn(async move {
let mut text = format!("Search results for {argument}:\n");
let mut text = format!("Search results for {query}:\n");
let mut sections = Vec::new();
for (result, full_path, file_content) in loaded_results {
let range_start = result.range.start.min(file_content.len());
@ -140,7 +166,7 @@ impl SlashCommand for SearchSlashCommand {
});
}
let argument = SharedString::from(argument);
let query = SharedString::from(query);
sections.push(SlashCommandOutputSection {
range: 0..text.len(),
render_placeholder: Arc::new(move |id, unfold, _cx| {
@ -148,7 +174,7 @@ impl SlashCommand for SearchSlashCommand {
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ElevatedSurface)
.child(Icon::new(IconName::MagnifyingGlass))
.child(Label::new(argument.clone()))
.child(Label::new(query.clone()))
.on_click(move |_, cx| unfold(cx))
.into_any_element()
}),