Extract slash commands to their own crate (#23261)
This PR extracts the slash command definitions out of the `assistant` crate and into their own `assistant_slash_commands` crate. Release Notes: - N/A
This commit is contained in:
parent
1a8303b020
commit
8030c0025a
33 changed files with 452 additions and 347 deletions
49
Cargo.lock
generated
49
Cargo.lock
generated
|
@ -375,9 +375,9 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"assistant_settings",
|
"assistant_settings",
|
||||||
"assistant_slash_command",
|
"assistant_slash_command",
|
||||||
|
"assistant_slash_commands",
|
||||||
"assistant_tool",
|
"assistant_tool",
|
||||||
"async-watch",
|
"async-watch",
|
||||||
"cargo_toml",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"client",
|
"client",
|
||||||
"clock",
|
"clock",
|
||||||
|
@ -392,10 +392,7 @@ dependencies = [
|
||||||
"fs",
|
"fs",
|
||||||
"futures 0.3.31",
|
"futures 0.3.31",
|
||||||
"fuzzy",
|
"fuzzy",
|
||||||
"globset",
|
|
||||||
"gpui",
|
"gpui",
|
||||||
"html_to_markdown",
|
|
||||||
"http_client",
|
|
||||||
"indexed_docs",
|
"indexed_docs",
|
||||||
"indoc",
|
"indoc",
|
||||||
"language",
|
"language",
|
||||||
|
@ -405,7 +402,6 @@ dependencies = [
|
||||||
"languages",
|
"languages",
|
||||||
"log",
|
"log",
|
||||||
"lsp",
|
"lsp",
|
||||||
"markdown",
|
|
||||||
"menu",
|
"menu",
|
||||||
"multi_buffer",
|
"multi_buffer",
|
||||||
"open_ai",
|
"open_ai",
|
||||||
|
@ -439,7 +435,6 @@ dependencies = [
|
||||||
"terminal_view",
|
"terminal_view",
|
||||||
"text",
|
"text",
|
||||||
"theme",
|
"theme",
|
||||||
"toml 0.8.19",
|
|
||||||
"tree-sitter-md",
|
"tree-sitter-md",
|
||||||
"ui",
|
"ui",
|
||||||
"unindent",
|
"unindent",
|
||||||
|
@ -554,6 +549,48 @@ dependencies = [
|
||||||
"workspace",
|
"workspace",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assistant_slash_commands"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"assistant_slash_command",
|
||||||
|
"cargo_toml",
|
||||||
|
"chrono",
|
||||||
|
"collections",
|
||||||
|
"context_server",
|
||||||
|
"editor",
|
||||||
|
"env_logger 0.11.6",
|
||||||
|
"feature_flags",
|
||||||
|
"fs",
|
||||||
|
"futures 0.3.31",
|
||||||
|
"fuzzy",
|
||||||
|
"globset",
|
||||||
|
"gpui",
|
||||||
|
"html_to_markdown",
|
||||||
|
"http_client",
|
||||||
|
"indexed_docs",
|
||||||
|
"language",
|
||||||
|
"language_model",
|
||||||
|
"log",
|
||||||
|
"pretty_assertions",
|
||||||
|
"project",
|
||||||
|
"prompt_library",
|
||||||
|
"rope",
|
||||||
|
"schemars",
|
||||||
|
"semantic_index",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"settings",
|
||||||
|
"smol",
|
||||||
|
"terminal_view",
|
||||||
|
"text",
|
||||||
|
"toml 0.8.19",
|
||||||
|
"ui",
|
||||||
|
"util",
|
||||||
|
"workspace",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "assistant_tool"
|
name = "assistant_tool"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -8,6 +8,7 @@ members = [
|
||||||
"crates/assistant2",
|
"crates/assistant2",
|
||||||
"crates/assistant_settings",
|
"crates/assistant_settings",
|
||||||
"crates/assistant_slash_command",
|
"crates/assistant_slash_command",
|
||||||
|
"crates/assistant_slash_commands",
|
||||||
"crates/assistant_tool",
|
"crates/assistant_tool",
|
||||||
"crates/assistant_tools",
|
"crates/assistant_tools",
|
||||||
"crates/audio",
|
"crates/audio",
|
||||||
|
@ -198,6 +199,7 @@ assistant = { path = "crates/assistant" }
|
||||||
assistant2 = { path = "crates/assistant2" }
|
assistant2 = { path = "crates/assistant2" }
|
||||||
assistant_settings = { path = "crates/assistant_settings" }
|
assistant_settings = { path = "crates/assistant_settings" }
|
||||||
assistant_slash_command = { path = "crates/assistant_slash_command" }
|
assistant_slash_command = { path = "crates/assistant_slash_command" }
|
||||||
|
assistant_slash_commands = { path = "crates/assistant_slash_commands" }
|
||||||
assistant_tool = { path = "crates/assistant_tool" }
|
assistant_tool = { path = "crates/assistant_tool" }
|
||||||
assistant_tools = { path = "crates/assistant_tools" }
|
assistant_tools = { path = "crates/assistant_tools" }
|
||||||
audio = { path = "crates/audio" }
|
audio = { path = "crates/audio" }
|
||||||
|
|
|
@ -24,9 +24,9 @@ test-support = [
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
assistant_settings.workspace = true
|
assistant_settings.workspace = true
|
||||||
assistant_slash_command.workspace = true
|
assistant_slash_command.workspace = true
|
||||||
|
assistant_slash_commands.workspace = true
|
||||||
assistant_tool.workspace = true
|
assistant_tool.workspace = true
|
||||||
async-watch.workspace = true
|
async-watch.workspace = true
|
||||||
cargo_toml.workspace = true
|
|
||||||
chrono.workspace = true
|
chrono.workspace = true
|
||||||
client.workspace = true
|
client.workspace = true
|
||||||
clock.workspace = true
|
clock.workspace = true
|
||||||
|
@ -39,10 +39,7 @@ feature_flags.workspace = true
|
||||||
fs.workspace = true
|
fs.workspace = true
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
fuzzy.workspace = true
|
fuzzy.workspace = true
|
||||||
globset.workspace = true
|
|
||||||
gpui.workspace = true
|
gpui.workspace = true
|
||||||
html_to_markdown.workspace = true
|
|
||||||
http_client.workspace = true
|
|
||||||
indexed_docs.workspace = true
|
indexed_docs.workspace = true
|
||||||
indoc.workspace = true
|
indoc.workspace = true
|
||||||
language.workspace = true
|
language.workspace = true
|
||||||
|
@ -51,7 +48,6 @@ language_model_selector.workspace = true
|
||||||
language_models.workspace = true
|
language_models.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
lsp.workspace = true
|
lsp.workspace = true
|
||||||
markdown.workspace = true
|
|
||||||
menu.workspace = true
|
menu.workspace = true
|
||||||
multi_buffer.workspace = true
|
multi_buffer.workspace = true
|
||||||
open_ai = { workspace = true, features = ["schemars"] }
|
open_ai = { workspace = true, features = ["schemars"] }
|
||||||
|
@ -82,7 +78,6 @@ terminal.workspace = true
|
||||||
terminal_view.workspace = true
|
terminal_view.workspace = true
|
||||||
text.workspace = true
|
text.workspace = true
|
||||||
theme.workspace = true
|
theme.workspace = true
|
||||||
toml.workspace = true
|
|
||||||
ui.workspace = true
|
ui.workspace = true
|
||||||
util.workspace = true
|
util.workspace = true
|
||||||
uuid.workspace = true
|
uuid.workspace = true
|
||||||
|
|
|
@ -12,13 +12,13 @@ pub mod slash_command_settings;
|
||||||
mod streaming_diff;
|
mod streaming_diff;
|
||||||
mod terminal_inline_assistant;
|
mod terminal_inline_assistant;
|
||||||
|
|
||||||
use crate::slash_command::project_command::ProjectSlashCommandFeatureFlag;
|
|
||||||
pub use ::prompt_library::PromptBuilder;
|
pub use ::prompt_library::PromptBuilder;
|
||||||
use ::prompt_library::PromptLoadingParams;
|
use ::prompt_library::PromptLoadingParams;
|
||||||
pub use assistant_panel::{AssistantPanel, AssistantPanelEvent};
|
pub use assistant_panel::{AssistantPanel, AssistantPanelEvent};
|
||||||
use assistant_settings::AssistantSettings;
|
use assistant_settings::AssistantSettings;
|
||||||
use assistant_slash_command::SlashCommandRegistry;
|
use assistant_slash_command::SlashCommandRegistry;
|
||||||
pub use assistant_slash_command::{SlashCommandId, SlashCommandWorkingSet};
|
pub use assistant_slash_command::{SlashCommandId, SlashCommandWorkingSet};
|
||||||
|
use assistant_slash_commands::{ProjectSlashCommandFeatureFlag, SearchSlashCommandFeatureFlag};
|
||||||
use client::{proto, Client};
|
use client::{proto, Client};
|
||||||
use command_palette_hooks::CommandPaletteFilter;
|
use command_palette_hooks::CommandPaletteFilter;
|
||||||
pub use context::*;
|
pub use context::*;
|
||||||
|
@ -35,18 +35,11 @@ pub use patch::*;
|
||||||
use semantic_index::{CloudEmbeddingProvider, SemanticDb};
|
use semantic_index::{CloudEmbeddingProvider, SemanticDb};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use settings::{Settings, SettingsStore};
|
use settings::{Settings, SettingsStore};
|
||||||
use slash_command::search_command::SearchSlashCommandFeatureFlag;
|
|
||||||
use slash_command::{
|
|
||||||
auto_command, cargo_workspace_command, default_command, delta_command, diagnostics_command,
|
|
||||||
docs_command, fetch_command, file_command, now_command, project_command, prompt_command,
|
|
||||||
search_command, selection_command, symbols_command, tab_command, terminal_command,
|
|
||||||
};
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
pub(crate) use streaming_diff::*;
|
pub(crate) use streaming_diff::*;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
|
|
||||||
use crate::slash_command::streaming_example_command;
|
|
||||||
use crate::slash_command_settings::SlashCommandSettings;
|
use crate::slash_command_settings::SlashCommandSettings;
|
||||||
|
|
||||||
actions!(
|
actions!(
|
||||||
|
@ -317,27 +310,28 @@ fn update_active_language_model_from_settings(cx: &mut AppContext) {
|
||||||
fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut AppContext) {
|
fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut AppContext) {
|
||||||
let slash_command_registry = SlashCommandRegistry::global(cx);
|
let slash_command_registry = SlashCommandRegistry::global(cx);
|
||||||
|
|
||||||
slash_command_registry.register_command(file_command::FileSlashCommand, true);
|
slash_command_registry.register_command(assistant_slash_commands::FileSlashCommand, true);
|
||||||
slash_command_registry.register_command(delta_command::DeltaSlashCommand, true);
|
slash_command_registry.register_command(assistant_slash_commands::DeltaSlashCommand, true);
|
||||||
slash_command_registry.register_command(symbols_command::OutlineSlashCommand, true);
|
slash_command_registry.register_command(assistant_slash_commands::OutlineSlashCommand, true);
|
||||||
slash_command_registry.register_command(tab_command::TabSlashCommand, true);
|
slash_command_registry.register_command(assistant_slash_commands::TabSlashCommand, true);
|
||||||
slash_command_registry
|
slash_command_registry
|
||||||
.register_command(cargo_workspace_command::CargoWorkspaceSlashCommand, true);
|
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
|
||||||
slash_command_registry.register_command(prompt_command::PromptSlashCommand, true);
|
slash_command_registry.register_command(assistant_slash_commands::PromptSlashCommand, true);
|
||||||
slash_command_registry.register_command(selection_command::SelectionCommand, true);
|
slash_command_registry.register_command(assistant_slash_commands::SelectionCommand, true);
|
||||||
slash_command_registry.register_command(default_command::DefaultSlashCommand, false);
|
slash_command_registry.register_command(assistant_slash_commands::DefaultSlashCommand, false);
|
||||||
slash_command_registry.register_command(terminal_command::TerminalSlashCommand, true);
|
slash_command_registry.register_command(assistant_slash_commands::TerminalSlashCommand, true);
|
||||||
slash_command_registry.register_command(now_command::NowSlashCommand, false);
|
slash_command_registry.register_command(assistant_slash_commands::NowSlashCommand, false);
|
||||||
slash_command_registry.register_command(diagnostics_command::DiagnosticsSlashCommand, true);
|
slash_command_registry
|
||||||
slash_command_registry.register_command(fetch_command::FetchSlashCommand, true);
|
.register_command(assistant_slash_commands::DiagnosticsSlashCommand, true);
|
||||||
|
slash_command_registry.register_command(assistant_slash_commands::FetchSlashCommand, true);
|
||||||
|
|
||||||
if let Some(prompt_builder) = prompt_builder {
|
if let Some(prompt_builder) = prompt_builder {
|
||||||
cx.observe_flag::<project_command::ProjectSlashCommandFeatureFlag, _>({
|
cx.observe_flag::<assistant_slash_commands::ProjectSlashCommandFeatureFlag, _>({
|
||||||
let slash_command_registry = slash_command_registry.clone();
|
let slash_command_registry = slash_command_registry.clone();
|
||||||
move |is_enabled, _cx| {
|
move |is_enabled, _cx| {
|
||||||
if is_enabled {
|
if is_enabled {
|
||||||
slash_command_registry.register_command(
|
slash_command_registry.register_command(
|
||||||
project_command::ProjectSlashCommand::new(prompt_builder.clone()),
|
assistant_slash_commands::ProjectSlashCommand::new(prompt_builder.clone()),
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -346,23 +340,24 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
cx.observe_flag::<auto_command::AutoSlashCommandFeatureFlag, _>({
|
cx.observe_flag::<assistant_slash_commands::AutoSlashCommandFeatureFlag, _>({
|
||||||
let slash_command_registry = slash_command_registry.clone();
|
let slash_command_registry = slash_command_registry.clone();
|
||||||
move |is_enabled, _cx| {
|
move |is_enabled, _cx| {
|
||||||
if is_enabled {
|
if is_enabled {
|
||||||
// [#auto-staff-ship] TODO remove this when /auto is no longer staff-shipped
|
// [#auto-staff-ship] TODO remove this when /auto is no longer staff-shipped
|
||||||
slash_command_registry.register_command(auto_command::AutoCommand, true);
|
slash_command_registry
|
||||||
|
.register_command(assistant_slash_commands::AutoCommand, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
cx.observe_flag::<streaming_example_command::StreamingExampleSlashCommandFeatureFlag, _>({
|
cx.observe_flag::<assistant_slash_commands::StreamingExampleSlashCommandFeatureFlag, _>({
|
||||||
let slash_command_registry = slash_command_registry.clone();
|
let slash_command_registry = slash_command_registry.clone();
|
||||||
move |is_enabled, _cx| {
|
move |is_enabled, _cx| {
|
||||||
if is_enabled {
|
if is_enabled {
|
||||||
slash_command_registry.register_command(
|
slash_command_registry.register_command(
|
||||||
streaming_example_command::StreamingExampleSlashCommand,
|
assistant_slash_commands::StreamingExampleSlashCommand,
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -374,11 +369,12 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
|
||||||
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
|
cx.observe_global::<SettingsStore>(update_slash_commands_from_settings)
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
cx.observe_flag::<search_command::SearchSlashCommandFeatureFlag, _>({
|
cx.observe_flag::<assistant_slash_commands::SearchSlashCommandFeatureFlag, _>({
|
||||||
let slash_command_registry = slash_command_registry.clone();
|
let slash_command_registry = slash_command_registry.clone();
|
||||||
move |is_enabled, _cx| {
|
move |is_enabled, _cx| {
|
||||||
if is_enabled {
|
if is_enabled {
|
||||||
slash_command_registry.register_command(search_command::SearchSlashCommand, true);
|
slash_command_registry
|
||||||
|
.register_command(assistant_slash_commands::SearchSlashCommand, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -390,17 +386,17 @@ fn update_slash_commands_from_settings(cx: &mut AppContext) {
|
||||||
let settings = SlashCommandSettings::get_global(cx);
|
let settings = SlashCommandSettings::get_global(cx);
|
||||||
|
|
||||||
if settings.docs.enabled {
|
if settings.docs.enabled {
|
||||||
slash_command_registry.register_command(docs_command::DocsSlashCommand, true);
|
slash_command_registry.register_command(assistant_slash_commands::DocsSlashCommand, true);
|
||||||
} else {
|
} else {
|
||||||
slash_command_registry.unregister_command(docs_command::DocsSlashCommand);
|
slash_command_registry.unregister_command(assistant_slash_commands::DocsSlashCommand);
|
||||||
}
|
}
|
||||||
|
|
||||||
if settings.cargo_workspace.enabled {
|
if settings.cargo_workspace.enabled {
|
||||||
slash_command_registry
|
slash_command_registry
|
||||||
.register_command(cargo_workspace_command::CargoWorkspaceSlashCommand, true);
|
.register_command(assistant_slash_commands::CargoWorkspaceSlashCommand, true);
|
||||||
} else {
|
} else {
|
||||||
slash_command_registry
|
slash_command_registry
|
||||||
.unregister_command(cargo_workspace_command::CargoWorkspaceSlashCommand);
|
.unregister_command(assistant_slash_commands::CargoWorkspaceSlashCommand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,22 @@
|
||||||
use crate::slash_command::file_command::codeblock_fence_for_path;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
humanize_token_count,
|
humanize_token_count, prompt_library::open_prompt_library,
|
||||||
prompt_library::open_prompt_library,
|
slash_command::SlashCommandCompletionProvider, slash_command_picker,
|
||||||
slash_command::{
|
terminal_inline_assistant::TerminalInlineAssistant, Assist, AssistantPatch,
|
||||||
default_command::DefaultSlashCommand,
|
AssistantPatchStatus, CacheStatus, ConfirmCommand, Content, Context, ContextEvent, ContextId,
|
||||||
docs_command::{DocsSlashCommand, DocsSlashCommandArgs},
|
ContextStore, ContextStoreEvent, CopyCode, CycleMessageRole, DeployHistory,
|
||||||
file_command, SlashCommandCompletionProvider,
|
DeployPromptLibrary, Edit, InlineAssistant, InsertDraggedFiles, InsertIntoEditor,
|
||||||
},
|
InvokedSlashCommandId, InvokedSlashCommandStatus, Message, MessageId, MessageMetadata,
|
||||||
slash_command_picker,
|
MessageStatus, NewContext, ParsedSlashCommand, PendingSlashCommandStatus, QuoteSelection,
|
||||||
terminal_inline_assistant::TerminalInlineAssistant,
|
RemoteContextMetadata, RequestType, SavedContextMetadata, Split, ToggleFocus,
|
||||||
Assist, AssistantPatch, AssistantPatchStatus, CacheStatus, ConfirmCommand, Content, Context,
|
|
||||||
ContextEvent, ContextId, ContextStore, ContextStoreEvent, CopyCode, CycleMessageRole,
|
|
||||||
DeployHistory, DeployPromptLibrary, Edit, InlineAssistant, InsertDraggedFiles,
|
|
||||||
InsertIntoEditor, InvokedSlashCommandId, InvokedSlashCommandStatus, Message, MessageId,
|
|
||||||
MessageMetadata, MessageStatus, NewContext, ParsedSlashCommand, PendingSlashCommandStatus,
|
|
||||||
QuoteSelection, RemoteContextMetadata, RequestType, SavedContextMetadata, Split, ToggleFocus,
|
|
||||||
ToggleModelSelector,
|
ToggleModelSelector,
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
use assistant_settings::{AssistantDockPosition, AssistantSettings};
|
||||||
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection, SlashCommandWorkingSet};
|
use assistant_slash_command::{SlashCommand, SlashCommandOutputSection, SlashCommandWorkingSet};
|
||||||
|
use assistant_slash_commands::{
|
||||||
|
selections_creases, DefaultSlashCommand, DocsSlashCommand, DocsSlashCommandArgs,
|
||||||
|
FileSlashCommand,
|
||||||
|
};
|
||||||
use assistant_tool::ToolWorkingSet;
|
use assistant_tool::ToolWorkingSet;
|
||||||
use client::{proto, zed_urls, Client, Status};
|
use client::{proto, zed_urls, Client, Status};
|
||||||
use collections::{hash_map, BTreeSet, HashMap, HashSet};
|
use collections::{hash_map, BTreeSet, HashMap, HashSet};
|
||||||
|
@ -3032,7 +3029,7 @@ impl ContextEditor {
|
||||||
|
|
||||||
cx.spawn(|_, mut cx| async move {
|
cx.spawn(|_, mut cx| async move {
|
||||||
let (paths, dragged_file_worktrees) = paths.await;
|
let (paths, dragged_file_worktrees) = paths.await;
|
||||||
let cmd_name = file_command::FileSlashCommand.name();
|
let cmd_name = FileSlashCommand.name();
|
||||||
|
|
||||||
context_editor_view
|
context_editor_view
|
||||||
.update(&mut cx, |context_editor, cx| {
|
.update(&mut cx, |context_editor, cx| {
|
||||||
|
@ -3994,99 +3991,6 @@ fn find_surrounding_code_block(snapshot: &BufferSnapshot, offset: usize) -> Opti
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn selections_creases(
|
|
||||||
workspace: &mut workspace::Workspace,
|
|
||||||
cx: &mut ViewContext<Workspace>,
|
|
||||||
) -> Option<Vec<(String, String)>> {
|
|
||||||
let editor = workspace
|
|
||||||
.active_item(cx)
|
|
||||||
.and_then(|item| item.act_as::<Editor>(cx))?;
|
|
||||||
|
|
||||||
let mut creases = vec![];
|
|
||||||
editor.update(cx, |editor, cx| {
|
|
||||||
let selections = editor.selections.all_adjusted(cx);
|
|
||||||
let buffer = editor.buffer().read(cx).snapshot(cx);
|
|
||||||
for selection in selections {
|
|
||||||
let range = editor::ToOffset::to_offset(&selection.start, &buffer)
|
|
||||||
..editor::ToOffset::to_offset(&selection.end, &buffer);
|
|
||||||
let selected_text = buffer.text_for_range(range.clone()).collect::<String>();
|
|
||||||
if selected_text.is_empty() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let start_language = buffer.language_at(range.start);
|
|
||||||
let end_language = buffer.language_at(range.end);
|
|
||||||
let language_name = if start_language == end_language {
|
|
||||||
start_language.map(|language| language.code_fence_block_name())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let language_name = language_name.as_deref().unwrap_or("");
|
|
||||||
let filename = buffer
|
|
||||||
.file_at(selection.start)
|
|
||||||
.map(|file| file.full_path(cx));
|
|
||||||
let text = if language_name == "markdown" {
|
|
||||||
selected_text
|
|
||||||
.lines()
|
|
||||||
.map(|line| format!("> {}", line))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n")
|
|
||||||
} else {
|
|
||||||
let start_symbols = buffer
|
|
||||||
.symbols_containing(selection.start, None)
|
|
||||||
.map(|(_, symbols)| symbols);
|
|
||||||
let end_symbols = buffer
|
|
||||||
.symbols_containing(selection.end, None)
|
|
||||||
.map(|(_, symbols)| symbols);
|
|
||||||
|
|
||||||
let outline_text =
|
|
||||||
if let Some((start_symbols, end_symbols)) = start_symbols.zip(end_symbols) {
|
|
||||||
Some(
|
|
||||||
start_symbols
|
|
||||||
.into_iter()
|
|
||||||
.zip(end_symbols)
|
|
||||||
.take_while(|(a, b)| a == b)
|
|
||||||
.map(|(a, _)| a.text)
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join(" > "),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let line_comment_prefix = start_language
|
|
||||||
.and_then(|l| l.default_scope().line_comment_prefixes().first().cloned());
|
|
||||||
|
|
||||||
let fence = codeblock_fence_for_path(
|
|
||||||
filename.as_deref(),
|
|
||||||
Some(selection.start.row..=selection.end.row),
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some((line_comment_prefix, outline_text)) =
|
|
||||||
line_comment_prefix.zip(outline_text)
|
|
||||||
{
|
|
||||||
let breadcrumb = format!("{line_comment_prefix}Excerpt from: {outline_text}\n");
|
|
||||||
format!("{fence}{breadcrumb}{selected_text}\n```")
|
|
||||||
} else {
|
|
||||||
format!("{fence}{selected_text}\n```")
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let crease_title = if let Some(path) = filename {
|
|
||||||
let start_line = selection.start.row + 1;
|
|
||||||
let end_line = selection.end.row + 1;
|
|
||||||
if start_line == end_line {
|
|
||||||
format!("{}, Line {}", path.display(), start_line)
|
|
||||||
} else {
|
|
||||||
format!("{}, Lines {} to {}", path.display(), start_line, end_line)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
"Quoted selection".to_string()
|
|
||||||
};
|
|
||||||
creases.push((text, crease_title));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Some(creases)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render_fold_icon_button(
|
fn render_fold_icon_button(
|
||||||
editor: WeakView<Editor>,
|
editor: WeakView<Editor>,
|
||||||
icon: IconName,
|
icon: IconName,
|
||||||
|
|
|
@ -2,14 +2,15 @@
|
||||||
mod context_tests;
|
mod context_tests;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
slash_command::{file_command::FileCommandMetadata, SlashCommandLine},
|
slash_command::SlashCommandLine, AssistantEdit, AssistantPatch, AssistantPatchStatus,
|
||||||
AssistantEdit, AssistantPatch, AssistantPatchStatus, MessageId, MessageStatus,
|
MessageId, MessageStatus,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context as _, Result};
|
use anyhow::{anyhow, Context as _, Result};
|
||||||
use assistant_slash_command::{
|
use assistant_slash_command::{
|
||||||
SlashCommandContent, SlashCommandEvent, SlashCommandOutputSection, SlashCommandResult,
|
SlashCommandContent, SlashCommandEvent, SlashCommandOutputSection, SlashCommandResult,
|
||||||
SlashCommandWorkingSet,
|
SlashCommandWorkingSet,
|
||||||
};
|
};
|
||||||
|
use assistant_slash_commands::FileCommandMetadata;
|
||||||
use assistant_tool::ToolWorkingSet;
|
use assistant_tool::ToolWorkingSet;
|
||||||
use client::{self, proto, telemetry::Telemetry};
|
use client::{self, proto, telemetry::Telemetry};
|
||||||
use clock::ReplicaId;
|
use clock::ReplicaId;
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use super::{AssistantEdit, MessageCacheMetadata};
|
use super::{AssistantEdit, MessageCacheMetadata};
|
||||||
use crate::{
|
use crate::{
|
||||||
assistant_panel, slash_command::file_command, AssistantEditKind, CacheStatus, Context,
|
assistant_panel, AssistantEditKind, CacheStatus, Context, ContextEvent, ContextId,
|
||||||
ContextEvent, ContextId, ContextOperation, InvokedSlashCommandId, MessageId, MessageStatus,
|
ContextOperation, InvokedSlashCommandId, MessageId, MessageStatus, PromptBuilder,
|
||||||
PromptBuilder,
|
|
||||||
};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use assistant_slash_command::{
|
use assistant_slash_command::{
|
||||||
ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent, SlashCommandOutput,
|
ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent, SlashCommandOutput,
|
||||||
SlashCommandOutputSection, SlashCommandRegistry, SlashCommandResult, SlashCommandWorkingSet,
|
SlashCommandOutputSection, SlashCommandRegistry, SlashCommandResult, SlashCommandWorkingSet,
|
||||||
};
|
};
|
||||||
|
use assistant_slash_commands::FileSlashCommand;
|
||||||
use assistant_tool::ToolWorkingSet;
|
use assistant_tool::ToolWorkingSet;
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use fs::FakeFs;
|
use fs::FakeFs;
|
||||||
|
@ -407,7 +407,7 @@ async fn test_slash_commands(cx: &mut TestAppContext) {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let slash_command_registry = cx.update(SlashCommandRegistry::default_global);
|
let slash_command_registry = cx.update(SlashCommandRegistry::default_global);
|
||||||
slash_command_registry.register_command(file_command::FileSlashCommand, false);
|
slash_command_registry.register_command(FileSlashCommand, false);
|
||||||
|
|
||||||
let registry = Arc::new(LanguageRegistry::test(cx.executor()));
|
let registry = Arc::new(LanguageRegistry::test(cx.executor()));
|
||||||
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
|
let prompt_builder = Arc::new(PromptBuilder::new(None).unwrap());
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::slash_command::context_server_command;
|
|
||||||
use crate::SlashCommandId;
|
use crate::SlashCommandId;
|
||||||
use crate::{
|
use crate::{
|
||||||
Context, ContextEvent, ContextId, ContextOperation, ContextVersion, SavedContext,
|
Context, ContextEvent, ContextId, ContextOperation, ContextVersion, SavedContext,
|
||||||
|
@ -837,14 +836,14 @@ impl ContextStore {
|
||||||
if let Some(prompts) = protocol.list_prompts().await.log_err() {
|
if let Some(prompts) = protocol.list_prompts().await.log_err() {
|
||||||
let slash_command_ids = prompts
|
let slash_command_ids = prompts
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(context_server_command::acceptable_prompt)
|
.filter(assistant_slash_commands::acceptable_prompt)
|
||||||
.map(|prompt| {
|
.map(|prompt| {
|
||||||
log::info!(
|
log::info!(
|
||||||
"registering context server command: {:?}",
|
"registering context server command: {:?}",
|
||||||
prompt.name
|
prompt.name
|
||||||
);
|
);
|
||||||
slash_command_working_set.insert(Arc::new(
|
slash_command_working_set.insert(Arc::new(
|
||||||
context_server_command::ContextServerSlashCommand::new(
|
assistant_slash_commands::ContextServerSlashCommand::new(
|
||||||
context_server_manager.clone(),
|
context_server_manager.clone(),
|
||||||
&server,
|
&server,
|
||||||
prompt,
|
prompt,
|
||||||
|
|
|
@ -2,11 +2,11 @@ use crate::assistant_panel::ContextEditor;
|
||||||
use crate::SlashCommandWorkingSet;
|
use crate::SlashCommandWorkingSet;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use assistant_slash_command::AfterCompletion;
|
use assistant_slash_command::AfterCompletion;
|
||||||
pub use assistant_slash_command::{SlashCommand, SlashCommandOutput};
|
pub use assistant_slash_command::SlashCommand;
|
||||||
use editor::{CompletionProvider, Editor};
|
use editor::{CompletionProvider, Editor};
|
||||||
use fuzzy::{match_strings, StringMatchCandidate};
|
use fuzzy::{match_strings, StringMatchCandidate};
|
||||||
use gpui::{AppContext, Model, Task, ViewContext, WeakView, WindowContext};
|
use gpui::{Model, Task, ViewContext, WeakView, WindowContext};
|
||||||
use language::{Anchor, Buffer, CodeLabel, Documentation, HighlightId, LanguageServerId, ToPoint};
|
use language::{Anchor, Buffer, Documentation, LanguageServerId, ToPoint};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::CompletionIntent;
|
use project::CompletionIntent;
|
||||||
use rope::Point;
|
use rope::Point;
|
||||||
|
@ -19,26 +19,7 @@ use std::{
|
||||||
Arc,
|
Arc,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ui::ActiveTheme;
|
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
pub mod auto_command;
|
|
||||||
pub mod cargo_workspace_command;
|
|
||||||
pub mod context_server_command;
|
|
||||||
pub mod default_command;
|
|
||||||
pub mod delta_command;
|
|
||||||
pub mod diagnostics_command;
|
|
||||||
pub mod docs_command;
|
|
||||||
pub mod fetch_command;
|
|
||||||
pub mod file_command;
|
|
||||||
pub mod now_command;
|
|
||||||
pub mod project_command;
|
|
||||||
pub mod prompt_command;
|
|
||||||
pub mod search_command;
|
|
||||||
pub mod selection_command;
|
|
||||||
pub mod streaming_example_command;
|
|
||||||
pub mod symbols_command;
|
|
||||||
pub mod tab_command;
|
|
||||||
pub mod terminal_command;
|
|
||||||
|
|
||||||
pub(crate) struct SlashCommandCompletionProvider {
|
pub(crate) struct SlashCommandCompletionProvider {
|
||||||
cancel_flag: Mutex<Arc<AtomicBool>>,
|
cancel_flag: Mutex<Arc<AtomicBool>>,
|
||||||
|
@ -409,19 +390,3 @@ impl SlashCommandLine {
|
||||||
call
|
call
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_label_for_command(
|
|
||||||
command_name: &str,
|
|
||||||
arguments: &[&str],
|
|
||||||
cx: &AppContext,
|
|
||||||
) -> CodeLabel {
|
|
||||||
let mut label = CodeLabel::default();
|
|
||||||
label.push_str(command_name, None);
|
|
||||||
label.push_str(" ", None);
|
|
||||||
label.push_str(
|
|
||||||
&arguments.join(" "),
|
|
||||||
cx.theme().syntax().highlight_id("comment").map(HighlightId),
|
|
||||||
);
|
|
||||||
label.filter_range = 0..command_name.len();
|
|
||||||
label
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
use crate::assistant_panel::selections_creases;
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use assistant_slash_command::{
|
|
||||||
ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent,
|
|
||||||
SlashCommandOutputSection, SlashCommandResult,
|
|
||||||
};
|
|
||||||
use futures::StreamExt;
|
|
||||||
use gpui::{AppContext, Task, WeakView};
|
|
||||||
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
|
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::Arc;
|
|
||||||
use ui::{IconName, SharedString, WindowContext};
|
|
||||||
use workspace::Workspace;
|
|
||||||
|
|
||||||
pub(crate) struct SelectionCommand;
|
|
||||||
|
|
||||||
impl SlashCommand for SelectionCommand {
|
|
||||||
fn name(&self) -> String {
|
|
||||||
"selection".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn label(&self, _cx: &AppContext) -> CodeLabel {
|
|
||||||
CodeLabel::plain(self.name(), None)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn description(&self) -> String {
|
|
||||||
"Insert editor selection".into()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn icon(&self) -> IconName {
|
|
||||||
IconName::Quote
|
|
||||||
}
|
|
||||||
|
|
||||||
fn menu_text(&self) -> String {
|
|
||||||
self.description()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn requires_argument(&self) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
fn accepts_arguments(&self) -> bool {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn complete_argument(
|
|
||||||
self: Arc<Self>,
|
|
||||||
_arguments: &[String],
|
|
||||||
_cancel: Arc<AtomicBool>,
|
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
|
||||||
_cx: &mut WindowContext,
|
|
||||||
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
|
||||||
Task::ready(Err(anyhow!("this command does not require argument")))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
self: Arc<Self>,
|
|
||||||
_arguments: &[String],
|
|
||||||
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
|
||||||
_context_buffer: BufferSnapshot,
|
|
||||||
workspace: WeakView<Workspace>,
|
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
|
||||||
cx: &mut WindowContext,
|
|
||||||
) -> Task<SlashCommandResult> {
|
|
||||||
let mut events = vec![];
|
|
||||||
|
|
||||||
let Some(creases) = workspace
|
|
||||||
.update(cx, selections_creases)
|
|
||||||
.unwrap_or_else(|e| {
|
|
||||||
events.push(Err(e));
|
|
||||||
None
|
|
||||||
})
|
|
||||||
else {
|
|
||||||
return Task::ready(Err(anyhow!("no active selection")));
|
|
||||||
};
|
|
||||||
|
|
||||||
for (text, title) in creases {
|
|
||||||
events.push(Ok(SlashCommandEvent::StartSection {
|
|
||||||
icon: IconName::TextSnippet,
|
|
||||||
label: SharedString::from(title),
|
|
||||||
metadata: None,
|
|
||||||
}));
|
|
||||||
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
|
|
||||||
text,
|
|
||||||
run_commands_in_text: false,
|
|
||||||
})));
|
|
||||||
events.push(Ok(SlashCommandEvent::EndSection));
|
|
||||||
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
|
|
||||||
text: "\n".to_string(),
|
|
||||||
run_commands_in_text: false,
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = futures::stream::iter(events).boxed();
|
|
||||||
|
|
||||||
Task::ready(Ok(result))
|
|
||||||
}
|
|
||||||
}
|
|
52
crates/assistant_slash_commands/Cargo.toml
Normal file
52
crates/assistant_slash_commands/Cargo.toml
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
[package]
|
||||||
|
name = "assistant_slash_commands"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
path = "src/assistant_slash_commands.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow.workspace = true
|
||||||
|
assistant_slash_command.workspace = true
|
||||||
|
cargo_toml.workspace = true
|
||||||
|
chrono.workspace = true
|
||||||
|
collections.workspace = true
|
||||||
|
context_server.workspace = true
|
||||||
|
editor.workspace = true
|
||||||
|
feature_flags.workspace = true
|
||||||
|
fs.workspace = true
|
||||||
|
futures.workspace = true
|
||||||
|
fuzzy.workspace = true
|
||||||
|
globset.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
html_to_markdown.workspace = true
|
||||||
|
http_client.workspace = true
|
||||||
|
indexed_docs.workspace = true
|
||||||
|
language.workspace = true
|
||||||
|
language_model.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
project.workspace = true
|
||||||
|
prompt_library.workspace = true
|
||||||
|
rope.workspace = true
|
||||||
|
schemars.workspace = true
|
||||||
|
semantic_index.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
smol.workspace = true
|
||||||
|
terminal_view.workspace = true
|
||||||
|
text.workspace = true
|
||||||
|
toml.workspace = true
|
||||||
|
ui.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
workspace.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
env_logger.workspace = true
|
||||||
|
pretty_assertions.workspace = true
|
||||||
|
settings.workspace = true
|
1
crates/assistant_slash_commands/LICENSE-GPL
Symbolic link
1
crates/assistant_slash_commands/LICENSE-GPL
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE-GPL
|
|
@ -0,0 +1,57 @@
|
||||||
|
mod auto_command;
|
||||||
|
mod cargo_workspace_command;
|
||||||
|
mod context_server_command;
|
||||||
|
mod default_command;
|
||||||
|
mod delta_command;
|
||||||
|
mod diagnostics_command;
|
||||||
|
mod docs_command;
|
||||||
|
mod fetch_command;
|
||||||
|
mod file_command;
|
||||||
|
mod now_command;
|
||||||
|
mod project_command;
|
||||||
|
mod prompt_command;
|
||||||
|
mod search_command;
|
||||||
|
mod selection_command;
|
||||||
|
mod streaming_example_command;
|
||||||
|
mod symbols_command;
|
||||||
|
mod tab_command;
|
||||||
|
mod terminal_command;
|
||||||
|
|
||||||
|
use gpui::AppContext;
|
||||||
|
use language::{CodeLabel, HighlightId};
|
||||||
|
use ui::ActiveTheme as _;
|
||||||
|
|
||||||
|
pub use crate::auto_command::*;
|
||||||
|
pub use crate::cargo_workspace_command::*;
|
||||||
|
pub use crate::context_server_command::*;
|
||||||
|
pub use crate::default_command::*;
|
||||||
|
pub use crate::delta_command::*;
|
||||||
|
pub use crate::diagnostics_command::*;
|
||||||
|
pub use crate::docs_command::*;
|
||||||
|
pub use crate::fetch_command::*;
|
||||||
|
pub use crate::file_command::*;
|
||||||
|
pub use crate::now_command::*;
|
||||||
|
pub use crate::project_command::*;
|
||||||
|
pub use crate::prompt_command::*;
|
||||||
|
pub use crate::search_command::*;
|
||||||
|
pub use crate::selection_command::*;
|
||||||
|
pub use crate::streaming_example_command::*;
|
||||||
|
pub use crate::symbols_command::*;
|
||||||
|
pub use crate::tab_command::*;
|
||||||
|
pub use crate::terminal_command::*;
|
||||||
|
|
||||||
|
pub fn create_label_for_command(
|
||||||
|
command_name: &str,
|
||||||
|
arguments: &[&str],
|
||||||
|
cx: &AppContext,
|
||||||
|
) -> CodeLabel {
|
||||||
|
let mut label = CodeLabel::default();
|
||||||
|
label.push_str(command_name, None);
|
||||||
|
label.push_str(" ", None);
|
||||||
|
label.push_str(
|
||||||
|
&arguments.join(" "),
|
||||||
|
cx.theme().syntax().highlight_id("comment").map(HighlightId),
|
||||||
|
);
|
||||||
|
label.filter_range = 0..command_name.len();
|
||||||
|
label
|
||||||
|
}
|
|
@ -18,7 +18,7 @@ use ui::{prelude::*, BorrowAppContext};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::slash_command::create_label_for_command;
|
use crate::create_label_for_command;
|
||||||
|
|
||||||
pub struct AutoSlashCommandFeatureFlag;
|
pub struct AutoSlashCommandFeatureFlag;
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ impl FeatureFlag for AutoSlashCommandFeatureFlag {
|
||||||
const NAME: &'static str = "auto-slash-command";
|
const NAME: &'static str = "auto-slash-command";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct AutoCommand;
|
pub struct AutoCommand;
|
||||||
|
|
||||||
impl SlashCommand for AutoCommand {
|
impl SlashCommand for AutoCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
|
@ -15,7 +15,7 @@ use std::{
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct CargoWorkspaceSlashCommand;
|
pub struct CargoWorkspaceSlashCommand;
|
||||||
|
|
||||||
impl CargoWorkspaceSlashCommand {
|
impl CargoWorkspaceSlashCommand {
|
||||||
async fn build_message(fs: Arc<dyn Fs>, path_to_cargo_toml: &Path) -> Result<String> {
|
async fn build_message(fs: Arc<dyn Fs>, path_to_cargo_toml: &Path) -> Result<String> {
|
|
@ -16,7 +16,7 @@ use text::LineEnding;
|
||||||
use ui::{IconName, SharedString};
|
use ui::{IconName, SharedString};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::slash_command::create_label_for_command;
|
use crate::create_label_for_command;
|
||||||
|
|
||||||
pub struct ContextServerSlashCommand {
|
pub struct ContextServerSlashCommand {
|
||||||
server_manager: Model<ContextServerManager>,
|
server_manager: Model<ContextServerManager>,
|
|
@ -13,7 +13,7 @@ use std::{
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct DefaultSlashCommand;
|
pub struct DefaultSlashCommand;
|
||||||
|
|
||||||
impl SlashCommand for DefaultSlashCommand {
|
impl SlashCommand for DefaultSlashCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::slash_command::file_command::{FileCommandMetadata, FileSlashCommand};
|
use crate::file_command::{FileCommandMetadata, FileSlashCommand};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use assistant_slash_command::{
|
use assistant_slash_command::{
|
||||||
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||||
|
@ -13,7 +13,7 @@ use text::OffsetRangeExt;
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct DeltaSlashCommand;
|
pub struct DeltaSlashCommand;
|
||||||
|
|
||||||
impl SlashCommand for DeltaSlashCommand {
|
impl SlashCommand for DeltaSlashCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
|
@ -21,9 +21,9 @@ use util::paths::PathMatcher;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::slash_command::create_label_for_command;
|
use crate::create_label_for_command;
|
||||||
|
|
||||||
pub(crate) struct DiagnosticsSlashCommand;
|
pub struct DiagnosticsSlashCommand;
|
||||||
|
|
||||||
impl DiagnosticsSlashCommand {
|
impl DiagnosticsSlashCommand {
|
||||||
fn search_paths(
|
fn search_paths(
|
|
@ -19,7 +19,7 @@ use ui::prelude::*;
|
||||||
use util::{maybe, ResultExt};
|
use util::{maybe, ResultExt};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct DocsSlashCommand;
|
pub struct DocsSlashCommand;
|
||||||
|
|
||||||
impl DocsSlashCommand {
|
impl DocsSlashCommand {
|
||||||
pub const NAME: &'static str = "docs";
|
pub const NAME: &'static str = "docs";
|
||||||
|
@ -367,7 +367,7 @@ fn is_item_path_delimiter(char: char) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub(crate) enum DocsSlashCommandArgs {
|
pub enum DocsSlashCommandArgs {
|
||||||
NoProvider,
|
NoProvider,
|
||||||
SearchPackageDocs {
|
SearchPackageDocs {
|
||||||
provider: ProviderId,
|
provider: ProviderId,
|
|
@ -23,7 +23,7 @@ enum ContentType {
|
||||||
Json,
|
Json,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct FetchSlashCommand;
|
pub struct FetchSlashCommand;
|
||||||
|
|
||||||
impl FetchSlashCommand {
|
impl FetchSlashCommand {
|
||||||
async fn build_message(http_client: Arc<HttpClientWithUrl>, url: &str) -> Result<String> {
|
async fn build_message(http_client: Arc<HttpClientWithUrl>, url: &str) -> Result<String> {
|
|
@ -21,7 +21,7 @@ use ui::prelude::*;
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct FileSlashCommand;
|
pub struct FileSlashCommand;
|
||||||
|
|
||||||
impl FileSlashCommand {
|
impl FileSlashCommand {
|
||||||
fn search_paths(
|
fn search_paths(
|
||||||
|
@ -561,7 +561,7 @@ mod test {
|
||||||
use settings::SettingsStore;
|
use settings::SettingsStore;
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
|
|
||||||
use crate::slash_command::file_command::collect_files;
|
use super::collect_files;
|
||||||
|
|
||||||
pub fn init_test(cx: &mut gpui::TestAppContext) {
|
pub fn init_test(cx: &mut gpui::TestAppContext) {
|
||||||
if std::env::var("RUST_LOG").is_ok() {
|
if std::env::var("RUST_LOG").is_ok() {
|
|
@ -12,7 +12,7 @@ use language::{BufferSnapshot, LspAdapterDelegate};
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct NowSlashCommand;
|
pub struct NowSlashCommand;
|
||||||
|
|
||||||
impl SlashCommand for NowSlashCommand {
|
impl SlashCommand for NowSlashCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
|
@ -1,33 +1,33 @@
|
||||||
use super::{
|
|
||||||
create_label_for_command, search_command::add_search_result_section, SlashCommand,
|
|
||||||
SlashCommandOutput,
|
|
||||||
};
|
|
||||||
use crate::PromptBuilder;
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection, SlashCommandResult};
|
|
||||||
use feature_flags::FeatureFlag;
|
|
||||||
use gpui::{AppContext, Task, WeakView, WindowContext};
|
|
||||||
use language::{Anchor, CodeLabel, LspAdapterDelegate};
|
|
||||||
use language_model::{LanguageModelRegistry, LanguageModelTool};
|
|
||||||
use schemars::JsonSchema;
|
|
||||||
use semantic_index::SemanticDb;
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
pub struct ProjectSlashCommandFeatureFlag;
|
|
||||||
|
|
||||||
impl FeatureFlag for ProjectSlashCommandFeatureFlag {
|
|
||||||
const NAME: &'static str = "project-slash-command";
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Write as _,
|
fmt::Write as _,
|
||||||
ops::DerefMut,
|
ops::DerefMut,
|
||||||
sync::{atomic::AtomicBool, Arc},
|
sync::{atomic::AtomicBool, Arc},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use assistant_slash_command::{
|
||||||
|
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
|
||||||
|
SlashCommandResult,
|
||||||
|
};
|
||||||
|
use feature_flags::FeatureFlag;
|
||||||
|
use gpui::{AppContext, Task, WeakView, WindowContext};
|
||||||
|
use language::{Anchor, CodeLabel, LspAdapterDelegate};
|
||||||
|
use language_model::{LanguageModelRegistry, LanguageModelTool};
|
||||||
|
use prompt_library::PromptBuilder;
|
||||||
|
use schemars::JsonSchema;
|
||||||
|
use semantic_index::SemanticDb;
|
||||||
|
use serde::Deserialize;
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
use super::{create_label_for_command, search_command::add_search_result_section};
|
||||||
|
|
||||||
|
pub struct ProjectSlashCommandFeatureFlag;
|
||||||
|
|
||||||
|
impl FeatureFlag for ProjectSlashCommandFeatureFlag {
|
||||||
|
const NAME: &'static str = "project-slash-command";
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ProjectSlashCommand {
|
pub struct ProjectSlashCommand {
|
||||||
prompt_builder: Arc<PromptBuilder>,
|
prompt_builder: Arc<PromptBuilder>,
|
||||||
}
|
}
|
|
@ -10,7 +10,7 @@ use std::sync::{atomic::AtomicBool, Arc};
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct PromptSlashCommand;
|
pub struct PromptSlashCommand;
|
||||||
|
|
||||||
impl SlashCommand for PromptSlashCommand {
|
impl SlashCommand for PromptSlashCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
|
@ -14,10 +14,10 @@ use std::{
|
||||||
use ui::{prelude::*, IconName};
|
use ui::{prelude::*, IconName};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::slash_command::create_label_for_command;
|
use crate::create_label_for_command;
|
||||||
use crate::slash_command::file_command::{build_entry_output_section, codeblock_fence_for_path};
|
use crate::file_command::{build_entry_output_section, codeblock_fence_for_path};
|
||||||
|
|
||||||
pub(crate) struct SearchSlashCommandFeatureFlag;
|
pub struct SearchSlashCommandFeatureFlag;
|
||||||
|
|
||||||
impl FeatureFlag for SearchSlashCommandFeatureFlag {
|
impl FeatureFlag for SearchSlashCommandFeatureFlag {
|
||||||
const NAME: &'static str = "search-slash-command";
|
const NAME: &'static str = "search-slash-command";
|
||||||
|
@ -27,7 +27,7 @@ impl FeatureFlag for SearchSlashCommandFeatureFlag {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct SearchSlashCommand;
|
pub struct SearchSlashCommand;
|
||||||
|
|
||||||
impl SlashCommand for SearchSlashCommand {
|
impl SlashCommand for SearchSlashCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
194
crates/assistant_slash_commands/src/selection_command.rs
Normal file
194
crates/assistant_slash_commands/src/selection_command.rs
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
use assistant_slash_command::{
|
||||||
|
ArgumentCompletion, SlashCommand, SlashCommandContent, SlashCommandEvent,
|
||||||
|
SlashCommandOutputSection, SlashCommandResult,
|
||||||
|
};
|
||||||
|
use editor::Editor;
|
||||||
|
use futures::StreamExt;
|
||||||
|
use gpui::{AppContext, Task, WeakView};
|
||||||
|
use gpui::{SharedString, ViewContext, WindowContext};
|
||||||
|
use language::{BufferSnapshot, CodeLabel, LspAdapterDelegate};
|
||||||
|
use std::sync::atomic::AtomicBool;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use ui::IconName;
|
||||||
|
use workspace::Workspace;
|
||||||
|
|
||||||
|
use crate::file_command::codeblock_fence_for_path;
|
||||||
|
|
||||||
|
pub struct SelectionCommand;
|
||||||
|
|
||||||
|
impl SlashCommand for SelectionCommand {
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"selection".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn label(&self, _cx: &AppContext) -> CodeLabel {
|
||||||
|
CodeLabel::plain(self.name(), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> String {
|
||||||
|
"Insert editor selection".into()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn icon(&self) -> IconName {
|
||||||
|
IconName::Quote
|
||||||
|
}
|
||||||
|
|
||||||
|
fn menu_text(&self) -> String {
|
||||||
|
self.description()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn requires_argument(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn accepts_arguments(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn complete_argument(
|
||||||
|
self: Arc<Self>,
|
||||||
|
_arguments: &[String],
|
||||||
|
_cancel: Arc<AtomicBool>,
|
||||||
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
|
_cx: &mut WindowContext,
|
||||||
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
|
Task::ready(Err(anyhow!("this command does not require argument")))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
self: Arc<Self>,
|
||||||
|
_arguments: &[String],
|
||||||
|
_context_slash_command_output_sections: &[SlashCommandOutputSection<language::Anchor>],
|
||||||
|
_context_buffer: BufferSnapshot,
|
||||||
|
workspace: WeakView<Workspace>,
|
||||||
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> Task<SlashCommandResult> {
|
||||||
|
let mut events = vec![];
|
||||||
|
|
||||||
|
let Some(creases) = workspace
|
||||||
|
.update(cx, selections_creases)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
events.push(Err(e));
|
||||||
|
None
|
||||||
|
})
|
||||||
|
else {
|
||||||
|
return Task::ready(Err(anyhow!("no active selection")));
|
||||||
|
};
|
||||||
|
|
||||||
|
for (text, title) in creases {
|
||||||
|
events.push(Ok(SlashCommandEvent::StartSection {
|
||||||
|
icon: IconName::TextSnippet,
|
||||||
|
label: SharedString::from(title),
|
||||||
|
metadata: None,
|
||||||
|
}));
|
||||||
|
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||||
|
text,
|
||||||
|
run_commands_in_text: false,
|
||||||
|
})));
|
||||||
|
events.push(Ok(SlashCommandEvent::EndSection));
|
||||||
|
events.push(Ok(SlashCommandEvent::Content(SlashCommandContent::Text {
|
||||||
|
text: "\n".to_string(),
|
||||||
|
run_commands_in_text: false,
|
||||||
|
})));
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = futures::stream::iter(events).boxed();
|
||||||
|
|
||||||
|
Task::ready(Ok(result))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selections_creases(
|
||||||
|
workspace: &mut workspace::Workspace,
|
||||||
|
cx: &mut ViewContext<Workspace>,
|
||||||
|
) -> Option<Vec<(String, String)>> {
|
||||||
|
let editor = workspace
|
||||||
|
.active_item(cx)
|
||||||
|
.and_then(|item| item.act_as::<Editor>(cx))?;
|
||||||
|
|
||||||
|
let mut creases = vec![];
|
||||||
|
editor.update(cx, |editor, cx| {
|
||||||
|
let selections = editor.selections.all_adjusted(cx);
|
||||||
|
let buffer = editor.buffer().read(cx).snapshot(cx);
|
||||||
|
for selection in selections {
|
||||||
|
let range = editor::ToOffset::to_offset(&selection.start, &buffer)
|
||||||
|
..editor::ToOffset::to_offset(&selection.end, &buffer);
|
||||||
|
let selected_text = buffer.text_for_range(range.clone()).collect::<String>();
|
||||||
|
if selected_text.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let start_language = buffer.language_at(range.start);
|
||||||
|
let end_language = buffer.language_at(range.end);
|
||||||
|
let language_name = if start_language == end_language {
|
||||||
|
start_language.map(|language| language.code_fence_block_name())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let language_name = language_name.as_deref().unwrap_or("");
|
||||||
|
let filename = buffer
|
||||||
|
.file_at(selection.start)
|
||||||
|
.map(|file| file.full_path(cx));
|
||||||
|
let text = if language_name == "markdown" {
|
||||||
|
selected_text
|
||||||
|
.lines()
|
||||||
|
.map(|line| format!("> {}", line))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
} else {
|
||||||
|
let start_symbols = buffer
|
||||||
|
.symbols_containing(selection.start, None)
|
||||||
|
.map(|(_, symbols)| symbols);
|
||||||
|
let end_symbols = buffer
|
||||||
|
.symbols_containing(selection.end, None)
|
||||||
|
.map(|(_, symbols)| symbols);
|
||||||
|
|
||||||
|
let outline_text =
|
||||||
|
if let Some((start_symbols, end_symbols)) = start_symbols.zip(end_symbols) {
|
||||||
|
Some(
|
||||||
|
start_symbols
|
||||||
|
.into_iter()
|
||||||
|
.zip(end_symbols)
|
||||||
|
.take_while(|(a, b)| a == b)
|
||||||
|
.map(|(a, _)| a.text)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" > "),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let line_comment_prefix = start_language
|
||||||
|
.and_then(|l| l.default_scope().line_comment_prefixes().first().cloned());
|
||||||
|
|
||||||
|
let fence = codeblock_fence_for_path(
|
||||||
|
filename.as_deref(),
|
||||||
|
Some(selection.start.row..=selection.end.row),
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some((line_comment_prefix, outline_text)) =
|
||||||
|
line_comment_prefix.zip(outline_text)
|
||||||
|
{
|
||||||
|
let breadcrumb = format!("{line_comment_prefix}Excerpt from: {outline_text}\n");
|
||||||
|
format!("{fence}{breadcrumb}{selected_text}\n```")
|
||||||
|
} else {
|
||||||
|
format!("{fence}{selected_text}\n```")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let crease_title = if let Some(path) = filename {
|
||||||
|
let start_line = selection.start.row + 1;
|
||||||
|
let end_line = selection.end.row + 1;
|
||||||
|
if start_line == end_line {
|
||||||
|
format!("{}, Line {}", path.display(), start_line)
|
||||||
|
} else {
|
||||||
|
format!("{}, Lines {} to {}", path.display(), start_line, end_line)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
"Quoted selection".to_string()
|
||||||
|
};
|
||||||
|
creases.push((text, crease_title));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Some(creases)
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ impl FeatureFlag for StreamingExampleSlashCommandFeatureFlag {
|
||||||
const NAME: &'static str = "streaming-example-slash-command";
|
const NAME: &'static str = "streaming-example-slash-command";
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct StreamingExampleSlashCommand;
|
pub struct StreamingExampleSlashCommand;
|
||||||
|
|
||||||
impl SlashCommand for StreamingExampleSlashCommand {
|
impl SlashCommand for StreamingExampleSlashCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
|
@ -11,7 +11,7 @@ use std::{path::Path, sync::atomic::AtomicBool};
|
||||||
use ui::{IconName, WindowContext};
|
use ui::{IconName, WindowContext};
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct OutlineSlashCommand;
|
pub struct OutlineSlashCommand;
|
||||||
|
|
||||||
impl SlashCommand for OutlineSlashCommand {
|
impl SlashCommand for OutlineSlashCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
|
@ -16,9 +16,9 @@ use ui::{prelude::*, ActiveTheme, WindowContext};
|
||||||
use util::ResultExt;
|
use util::ResultExt;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
use crate::slash_command::file_command::append_buffer_to_output;
|
use crate::file_command::append_buffer_to_output;
|
||||||
|
|
||||||
pub(crate) struct TabSlashCommand;
|
pub struct TabSlashCommand;
|
||||||
|
|
||||||
const ALL_TABS_COMPLETION_ITEM: &str = "all";
|
const ALL_TABS_COMPLETION_ITEM: &str = "all";
|
||||||
|
|
|
@ -12,14 +12,14 @@ use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use workspace::{dock::Panel, Workspace};
|
use workspace::{dock::Panel, Workspace};
|
||||||
|
|
||||||
use crate::DEFAULT_CONTEXT_LINES;
|
|
||||||
|
|
||||||
use super::create_label_for_command;
|
use super::create_label_for_command;
|
||||||
|
|
||||||
pub(crate) struct TerminalSlashCommand;
|
pub struct TerminalSlashCommand;
|
||||||
|
|
||||||
const LINE_COUNT_ARG: &str = "--line-count";
|
const LINE_COUNT_ARG: &str = "--line-count";
|
||||||
|
|
||||||
|
const DEFAULT_CONTEXT_LINES: usize = 50;
|
||||||
|
|
||||||
impl SlashCommand for TerminalSlashCommand {
|
impl SlashCommand for TerminalSlashCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
"terminal".into()
|
"terminal".into()
|
Loading…
Add table
Add a link
Reference in a new issue