diff --git a/crates/assistant_tools/src/code_action_tool.rs b/crates/assistant_tools/src/code_action_tool.rs index 9557a681b5..eede8bcaef 100644 --- a/crates/assistant_tools/src/code_action_tool.rs +++ b/crates/assistant_tools/src/code_action_tool.rs @@ -55,6 +55,16 @@ pub enum CodeActionType { /// List available code actions for the text range ListAvailable, + + /// Execute a specific code action that matches the provided regex pattern + ExecuteAction { + /// Regex pattern to match against code action titles + /// Must match exactly one code action + pattern: String, + + /// Optional arguments to pass to the code action, as arbitrary JSON + arguments: Option, + }, } pub struct CodeActionTool; @@ -91,6 +101,9 @@ impl Tool for CodeActionTool { CodeActionType::ListAvailable => { format!("List available code actions for '{}'", input.text_range) } + CodeActionType::ExecuteAction { pattern, .. } => { + format!("Execute code action matching '{}' for '{}'", pattern, input.text_range) + } } } Err(_) => "Perform code action".to_string(), @@ -226,6 +239,76 @@ impl Tool for CodeActionTool { } Ok(result) + }, + CodeActionType::ExecuteAction { pattern, arguments } => { + // Get code actions for the range + let actions = project + .update(cx, |project, cx| { + project.code_actions(&buffer, range.clone(), None, cx) + })? + .await?; + + if actions.is_empty() { + return Err(anyhow!("No code actions available for this range")); + } + + // Compile the regex pattern + let regex = match regex::Regex::new(&pattern) { + Ok(regex) => regex, + Err(err) => return Err(anyhow!("Invalid regex pattern: {}", err)), + }; + + // Find all matching actions + let matching_actions: Vec<_> = actions + .iter() + .enumerate() + .filter(|(_, action)| { + let title = action.lsp_action.title(); + regex.is_match(title) + }) + .collect(); + + // Ensure exactly one action matches + if matching_actions.is_empty() { + return Err(anyhow!("No code actions match the pattern: {}", pattern)); + } else if matching_actions.len() > 1 { + let titles: Vec<_> = matching_actions + .iter() + .map(|(_, action)| action.lsp_action.title().to_string()) + .collect(); + + return Err(anyhow!( + "Pattern '{}' matches multiple code actions: {}", + pattern, + titles.join(", ") + )); + } + + // Get the single matching action + let (_, action) = matching_actions[0]; + let action = action.clone(); + + // If arguments are provided and this is a command action, + // we could theoretically modify the command's arguments here, + // but for now we'll just log that arguments were provided but ignored + if arguments.is_some() { + eprintln!("Note: arguments provided to ExecuteAction are currently ignored"); + } + + let title = action.lsp_action.title().to_string(); + + // Apply the selected code action + let _transaction = project + .update(cx, |project, cx| { + project.apply_code_action(buffer.clone(), action, true, cx) + })? + .await?; + + action_log.update(cx, |log, cx| { + log.buffer_edited(buffer.clone(), Vec::new(), cx) + })?; + + Ok(format!("Executed code action: {}", title)) } } }) diff --git a/crates/assistant_tools/src/code_action_tool/description.md b/crates/assistant_tools/src/code_action_tool/description.md index 02d0dfe859..33badc7313 100644 --- a/crates/assistant_tools/src/code_action_tool/description.md +++ b/crates/assistant_tools/src/code_action_tool/description.md @@ -4,11 +4,24 @@ This tool performs code actions on text ranges in the project, such as: - **Rename**: Renames a text range (typically a symbol) across the codebase - **ListAvailable**: Lists all available code actions for a specific text range +- **ExecuteAction**: Executes a specific code action matching a regex pattern To use this tool, you need to: 1. Specify the path to the file containing the text range you want to run an action on -2. Choose the code action type (Rename or ListAvailable) -3. For both action types: provide context around the text range to identify it uniquely +2. Choose the code action type (Rename, ListAvailable, or ExecuteAction) +3. For all action types: provide context around the text range to identify it uniquely 4. For Rename actions: provide the new name +5. For ExecuteAction: provide a regex pattern that uniquely identifies the action to execute, and optional arguments -The tool will apply the requested code action to all relevant occurrences in the project, or list all available actions for the specified text range. +Typical workflow: +1. Use the ListAvailable action to discover available code actions for a text range +2. Choose one of the listed actions by creating a regex pattern that uniquely matches it +3. Execute that specific action using ExecuteAction with the pattern + +The regex pattern must match exactly one code action title. If it matches zero or multiple actions, the tool will return an error with appropriate guidance. + +Examples: +- Use `^Extract function$` to match an action titled exactly "Extract function" +- Use `Rename to '.*'` to match any action that starts with "Rename to '" + +You can also use the tool to directly guess code actions without first listing them, by providing a regex pattern that might match common code actions like "Extract function", "Extract variable", etc.