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:
Marshall Bowers 2025-01-16 17:17:07 -05:00 committed by GitHub
parent 1a8303b020
commit 8030c0025a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 452 additions and 347 deletions

49
Cargo.lock generated
View file

@ -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"

View file

@ -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" }

View file

@ -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

View file

@ -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);
} }
} }

View file

@ -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,

View file

@ -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;

View file

@ -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());

View file

@ -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,

View file

@ -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
}

View file

@ -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))
}
}

View 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

View file

@ -0,0 +1 @@
../../LICENSE-GPL

View file

@ -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
}

View file

@ -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 {

View file

@ -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> {

View file

@ -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>,

View file

@ -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 {

View file

@ -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 {

View file

@ -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(

View file

@ -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,

View file

@ -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> {

View file

@ -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() {

View file

@ -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 {

View file

@ -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>,
} }

View file

@ -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 {

View file

@ -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 {

View 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)
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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";

View file

@ -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()