extension: Make `worktree` argument to `run_slash_command` optional (#15658)

Marshall Bowers created

This PR updates the extension API to make the `worktree` argument to
`run_slash_command` optional.

We may not always have a worktree, and not all slash commands need them,
so by making it optional we can allow individual slash commands to
decide what to do when there is no worktree.

Release Notes:

- N/A

Change summary

crates/extension/src/extension_slash_command.rs     | 9 +++++----
crates/extension/src/wasm_host/wit.rs               | 2 +-
crates/extension_api/src/extension_api.rs           | 4 ++--
crates/extension_api/wit/since_v0.0.7/extension.wit | 2 +-
extensions/gleam/src/gleam.rs                       | 4 +++-
5 files changed, 12 insertions(+), 9 deletions(-)

Detailed changes

crates/extension/src/extension_slash_command.rs 🔗

@@ -91,10 +91,11 @@ impl SlashCommand for ExtensionSlashCommand {
                     let this = self.clone();
                     move |extension, store| {
                         async move {
-                            let delegate = delegate.ok_or_else(|| {
-                                anyhow!("no worktree for extension slash command")
-                            })?;
-                            let resource = store.data_mut().table().push(delegate)?;
+                            let resource = if let Some(delegate) = delegate {
+                                Some(store.data_mut().table().push(delegate)?)
+                            } else {
+                                None
+                            };
                             let output = extension
                                 .call_run_slash_command(
                                     store,

crates/extension/src/wasm_host/wit.rs 🔗

@@ -277,7 +277,7 @@ impl Extension {
         store: &mut Store<WasmState>,
         command: &SlashCommand,
         argument: Option<&str>,
-        resource: Resource<Arc<dyn LspAdapterDelegate>>,
+        resource: Option<Resource<Arc<dyn LspAdapterDelegate>>>,
     ) -> Result<Result<SlashCommandOutput, String>> {
         match self {
             Extension::V007(ext) => {

crates/extension_api/src/extension_api.rs 🔗

@@ -125,7 +125,7 @@ pub trait Extension: Send + Sync {
         &self,
         _command: SlashCommand,
         _argument: Option<String>,
-        _worktree: &Worktree,
+        _worktree: Option<&Worktree>,
     ) -> Result<SlashCommandOutput, String> {
         Err("`run_slash_command` not implemented".to_string())
     }
@@ -256,7 +256,7 @@ impl wit::Guest for Component {
     fn run_slash_command(
         command: SlashCommand,
         argument: Option<String>,
-        worktree: &Worktree,
+        worktree: Option<&Worktree>,
     ) -> Result<SlashCommandOutput, String> {
         extension().run_slash_command(command, argument, worktree)
     }

crates/extension_api/wit/since_v0.0.7/extension.wit 🔗

@@ -133,7 +133,7 @@ world extension {
     export complete-slash-command-argument: func(command: slash-command, query: string) -> result<list<slash-command-argument-completion>, string>;
 
     /// Returns the output from running the provided slash command.
-    export run-slash-command: func(command: slash-command, argument: option<string>, worktree: borrow<worktree>) -> result<slash-command-output, string>;
+    export run-slash-command: func(command: slash-command, argument: option<string>, worktree: option<borrow<worktree>>) -> result<slash-command-output, string>;
 
     /// Indexes the docs for the specified package.
     export index-docs: func(provider-name: string, package-name: string, database: borrow<key-value-store>) -> result<_, string>;

extensions/gleam/src/gleam.rs 🔗

@@ -181,7 +181,7 @@ impl zed::Extension for GleamExtension {
         &self,
         command: SlashCommand,
         argument: Option<String>,
-        worktree: &zed::Worktree,
+        worktree: Option<&zed::Worktree>,
     ) -> Result<SlashCommandOutput, String> {
         match command.name.as_str() {
             "gleam-docs" => {
@@ -218,6 +218,8 @@ impl zed::Extension for GleamExtension {
                 })
             }
             "gleam-project" => {
+                let worktree = worktree.ok_or_else(|| "no worktree")?;
+
                 let mut text = String::new();
                 text.push_str("You are in a Gleam project.\n");