Extract ExtensionSlashCommand to assistant_slash_command crate (#20617)

This PR extracts the `ExtensionSlashCommand` implementation to the
`assistant_slash_command` crate.

The slash command related methods have been added to the `Extension`
trait. We also create separate data types for the slash command data
within the `extension` crate so that we can talk about them without
depending on the `extension_host` or `assistant_slash_command`.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-11-13 14:34:58 -05:00 committed by GitHub
parent b913cf2e02
commit 254ce74036
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 309 additions and 161 deletions

View file

@ -132,9 +132,8 @@ pub trait ExtensionRegistrationHooks: Send + Sync + 'static {
fn register_slash_command(
&self,
_slash_command: wit::SlashCommand,
_extension: WasmExtension,
_host: Arc<WasmHost>,
_extension: Arc<dyn Extension>,
_command: extension::SlashCommand,
) {
}
@ -1250,7 +1249,8 @@ impl ExtensionStore {
for (slash_command_name, slash_command) in &manifest.slash_commands {
this.registration_hooks.register_slash_command(
crate::wit::SlashCommand {
extension.clone(),
extension::SlashCommand {
name: slash_command_name.to_string(),
description: slash_command.description.to_string(),
// We don't currently expose this as a configurable option, as it currently drives
@ -1259,8 +1259,6 @@ impl ExtensionStore {
tooltip_text: String::new(),
requires_argument: slash_command.requires_argument,
},
wasm_extension.clone(),
this.wasm_host.clone(),
);
}

View file

@ -3,7 +3,10 @@ pub mod wit;
use crate::{ExtensionManifest, ExtensionRegistrationHooks};
use anyhow::{anyhow, bail, Context as _, Result};
use async_trait::async_trait;
use extension::KeyValueStoreDelegate;
use extension::{
KeyValueStoreDelegate, SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput,
WorktreeDelegate,
};
use fs::{normalize_path, Fs};
use futures::future::LocalBoxFuture;
use futures::{
@ -29,7 +32,7 @@ use wasmtime::{
};
use wasmtime_wasi::{self as wasi, WasiView};
use wit::Extension;
pub use wit::{ExtensionProject, SlashCommand};
pub use wit::ExtensionProject;
pub struct WasmHost {
engine: Engine,
@ -62,6 +65,51 @@ impl extension::Extension for WasmExtension {
self.work_dir.clone()
}
async fn complete_slash_command_argument(
&self,
command: SlashCommand,
arguments: Vec<String>,
) -> Result<Vec<SlashCommandArgumentCompletion>> {
self.call(|extension, store| {
async move {
let completions = extension
.call_complete_slash_command_argument(store, &command.into(), &arguments)
.await?
.map_err(|err| anyhow!("{err}"))?;
Ok(completions.into_iter().map(Into::into).collect())
}
.boxed()
})
.await
}
async fn run_slash_command(
&self,
command: SlashCommand,
arguments: Vec<String>,
delegate: Option<Arc<dyn WorktreeDelegate>>,
) -> Result<SlashCommandOutput> {
self.call(|extension, store| {
async move {
let resource = if let Some(delegate) = delegate {
Some(store.data_mut().table().push(delegate)?)
} else {
None
};
let output = extension
.call_run_slash_command(store, &command.into(), &arguments, resource)
.await?
.map_err(|err| anyhow!("{err}"))?;
Ok(output.into())
}
.boxed()
})
.await
}
async fn suggest_docs_packages(&self, provider: Arc<str>) -> Result<Vec<String>> {
self.call(|extension, store| {
async move {

View file

@ -1,3 +1,4 @@
use crate::wasm_host::wit::since_v0_2_0::slash_command::SlashCommandOutputSection;
use crate::wasm_host::{wit::ToWasmtimeResult, WasmState};
use ::http_client::{AsyncBody, HttpRequestExt};
use ::settings::{Settings, WorktreeId};
@ -54,6 +55,45 @@ pub fn linker() -> &'static Linker<WasmState> {
LINKER.get_or_init(|| super::new_linker(Extension::add_to_linker))
}
impl From<extension::SlashCommand> for SlashCommand {
fn from(value: extension::SlashCommand) -> Self {
Self {
name: value.name,
description: value.description,
tooltip_text: value.tooltip_text,
requires_argument: value.requires_argument,
}
}
}
impl From<SlashCommandOutput> for extension::SlashCommandOutput {
fn from(value: SlashCommandOutput) -> Self {
Self {
text: value.text,
sections: value.sections.into_iter().map(Into::into).collect(),
}
}
}
impl From<SlashCommandOutputSection> for extension::SlashCommandOutputSection {
fn from(value: SlashCommandOutputSection) -> Self {
Self {
range: value.range.start as usize..value.range.end as usize,
label: value.label,
}
}
}
impl From<SlashCommandArgumentCompletion> for extension::SlashCommandArgumentCompletion {
fn from(value: SlashCommandArgumentCompletion) -> Self {
Self {
label: value.label,
new_text: value.new_text,
run_command: value.run_command,
}
}
}
#[async_trait]
impl HostKeyValueStore for WasmState {
async fn insert(