Introduce a new /workflow command (#15854)

This subsumes the previous built-in prompt.

Release Notes:

- N/A
This commit is contained in:
Antonio Scandurra 2024-08-06 16:18:07 +02:00 committed by GitHub
parent 889a14a2c2
commit 411934bb61
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 85 additions and 50 deletions

1
assets/icons/route.svg Normal file
View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-route"><circle cx="6" cy="19" r="3"/><path d="M9 19h8.5a3.5 3.5 0 0 0 0-7h-11a3.5 3.5 0 0 1 0-7H15"/><circle cx="18" cy="5" r="3"/></svg>

After

Width:  |  Height:  |  Size: 340 B

View file

@ -31,7 +31,7 @@ use settings::{update_settings_file, Settings, SettingsStore};
use slash_command::{ use slash_command::{
active_command, default_command, diagnostics_command, docs_command, fetch_command, active_command, default_command, diagnostics_command, docs_command, fetch_command,
file_command, now_command, project_command, prompt_command, search_command, symbols_command, file_command, now_command, project_command, prompt_command, search_command, symbols_command,
tabs_command, term_command, tabs_command, term_command, workflow_command,
}; };
use std::sync::Arc; use std::sync::Arc;
pub(crate) use streaming_diff::*; pub(crate) use streaming_diff::*;
@ -260,6 +260,7 @@ fn register_slash_commands(cx: &mut AppContext) {
slash_command_registry.register_command(now_command::NowSlashCommand, true); slash_command_registry.register_command(now_command::NowSlashCommand, true);
slash_command_registry.register_command(diagnostics_command::DiagnosticsSlashCommand, true); slash_command_registry.register_command(diagnostics_command::DiagnosticsSlashCommand, true);
slash_command_registry.register_command(docs_command::DocsSlashCommand, true); slash_command_registry.register_command(docs_command::DocsSlashCommand, true);
slash_command_registry.register_command(workflow_command::WorkflowSlashCommand, true);
slash_command_registry.register_command(fetch_command::FetchSlashCommand, false); slash_command_registry.register_command(fetch_command::FetchSlashCommand, false);
} }

View file

@ -1201,6 +1201,12 @@ impl PromptStore {
let mut txn = db_env.write_txn()?; let mut txn = db_env.write_txn()?;
let metadata = db_env.create_database(&mut txn, Some("metadata.v2"))?; let metadata = db_env.create_database(&mut txn, Some("metadata.v2"))?;
let bodies = db_env.create_database(&mut txn, Some("bodies.v2"))?; let bodies = db_env.create_database(&mut txn, Some("bodies.v2"))?;
// Remove edit workflow prompt, as we decided to opt into it using
// a slash command instead.
metadata.delete(&mut txn, &PromptId::EditWorkflow).ok();
bodies.delete(&mut txn, &PromptId::EditWorkflow).ok();
txn.commit()?; txn.commit()?;
Self::upgrade_dbs(&db_env, metadata, bodies).log_err(); Self::upgrade_dbs(&db_env, metadata, bodies).log_err();
@ -1209,17 +1215,13 @@ impl PromptStore {
let metadata_cache = MetadataCache::from_db(metadata, &txn)?; let metadata_cache = MetadataCache::from_db(metadata, &txn)?;
txn.commit()?; txn.commit()?;
let store = PromptStore { Ok(PromptStore {
executor, executor,
env: db_env, env: db_env,
metadata_cache: RwLock::new(metadata_cache), metadata_cache: RwLock::new(metadata_cache),
metadata, metadata,
bodies, bodies,
}; })
store.save_built_in_prompts().log_err();
Ok(store)
} }
}) })
} }
@ -1425,49 +1427,6 @@ impl PromptStore {
}) })
} }
fn save_built_in_prompts(&self) -> Result<()> {
self.save_built_in_prompt(
PromptId::EditWorkflow,
"Built-in: Editing Workflow",
"prompts/edit_workflow.md",
)?;
Ok(())
}
/// Write a built-in prompt to the database, preserving the value of the default field
/// if a prompt with this id already exists. This method blocks.
fn save_built_in_prompt(
&self,
id: PromptId,
title: impl Into<SharedString>,
body_path: &str,
) -> Result<()> {
let mut metadata_cache = self.metadata_cache.write();
let existing_metadata = metadata_cache.metadata_by_id.get(&id).cloned();
let prompt_metadata = PromptMetadata {
id,
title: Some(title.into()),
default: existing_metadata.map_or(true, |m| m.default),
saved_at: Utc::now(),
};
metadata_cache.insert(prompt_metadata.clone());
let db_connection = self.env.clone();
let bodies = self.bodies;
let metadata_db = self.metadata;
let mut txn = db_connection.write_txn()?;
metadata_db.put(&mut txn, &id, &prompt_metadata)?;
let body = String::from_utf8(Assets.load(body_path)?.unwrap().to_vec())?;
bodies.put(&mut txn, &id, &body)?;
txn.commit()?;
Ok(())
}
fn save_metadata( fn save_metadata(
&self, &self,
id: PromptId, id: PromptId,

View file

@ -30,6 +30,7 @@ pub mod search_command;
pub mod symbols_command; pub mod symbols_command;
pub mod tabs_command; pub mod tabs_command;
pub mod term_command; pub mod term_command;
pub mod workflow_command;
pub(crate) struct SlashCommandCompletionProvider { pub(crate) struct SlashCommandCompletionProvider {
cancel_flag: Mutex<Arc<AtomicBool>>, cancel_flag: Mutex<Arc<AtomicBool>>,

View file

@ -0,0 +1,71 @@
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use anyhow::{Context as _, Result};
use assets::Assets;
use assistant_slash_command::{
ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
};
use gpui::{AppContext, AssetSource, Task, WeakView};
use language::LspAdapterDelegate;
use text::LineEnding;
use ui::prelude::*;
use workspace::Workspace;
pub(crate) struct WorkflowSlashCommand;
impl SlashCommand for WorkflowSlashCommand {
fn name(&self) -> String {
"workflow".into()
}
fn description(&self) -> String {
"insert a prompt that opts into the edit workflow".into()
}
fn menu_text(&self) -> String {
"Insert Workflow Prompt".into()
}
fn requires_argument(&self) -> bool {
false
}
fn complete_argument(
self: Arc<Self>,
_query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Ok(Vec::new()))
}
fn run(
self: Arc<Self>,
_argument: Option<&str>,
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
_cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let mut text = match Assets
.load("prompts/edit_workflow.md")
.and_then(|prompt| prompt.context("prompts/edit_workflow.md not found"))
{
Ok(prompt) => String::from_utf8_lossy(&prompt).into_owned(),
Err(error) => return Task::ready(Err(error)),
};
LineEnding::normalize(&mut text);
let range = 0..text.len();
Task::ready(Ok(SlashCommandOutput {
text,
sections: vec![SlashCommandOutputSection {
range,
icon: IconName::Route,
label: "Workflow".into(),
}],
run_commands_in_text: false,
}))
}
}

View file

@ -223,6 +223,7 @@ pub enum IconName {
Rerun, Rerun,
Return, Return,
Reveal, Reveal,
Route,
RotateCcw, RotateCcw,
RotateCw, RotateCw,
Save, Save,
@ -385,6 +386,7 @@ impl IconName {
IconName::Reveal => "icons/reveal.svg", IconName::Reveal => "icons/reveal.svg",
IconName::RotateCcw => "icons/rotate_ccw.svg", IconName::RotateCcw => "icons/rotate_ccw.svg",
IconName::RotateCw => "icons/rotate_cw.svg", IconName::RotateCw => "icons/rotate_cw.svg",
IconName::Route => "icons/route.svg",
IconName::Save => "icons/save.svg", IconName::Save => "icons/save.svg",
IconName::Screen => "icons/desktop.svg", IconName::Screen => "icons/desktop.svg",
IconName::SearchSelection => "icons/search_selection.svg", IconName::SearchSelection => "icons/search_selection.svg",