Let the code action actually do edits

This commit is contained in:
Richard Feldman
2025-03-31 13:25:25 -05:00
parent d478a709ed
commit 84a67d82cb
2 changed files with 99 additions and 3 deletions

View File

@@ -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<serde_json::Value>,
},
}
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))
}
}
})

View File

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