use crate::editor::Editor;
use crate::languages::LanguageName;
use crate::selector::{Operation, Selector};
use crate::state::SemanticEditTools;
use anyhow::Result;
use mcplease::{
traits::{Tool, WithExamples},
types::Example,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename = "stage_operation")]
pub struct StageOperation {
pub file_path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<LanguageName>,
#[serde(flatten)]
pub selector: Selector,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
}
impl WithExamples for StageOperation {
fn examples() -> Vec<Example<Self>> {
vec![
Example {
description: "Insert content after a function declaration",
item: Self {
file_path: "src/main.rs".into(),
selector: Selector {
anchor: "fn main() {".into(),
operation: Operation::InsertAfter,
end: None,
},
content: Some("\n println!(\"Hello, world!\");".to_string()),
language: None,
},
},
Example {
description: "Replace a function with new implementation",
item: Self {
file_path: "src/main.rs".into(),
selector: Selector {
anchor: "fn hello()".to_string(),
operation: Operation::ReplaceNode,
end: None,
},
content: Some("fn hello() { println!(\"Hello, world!\"); }".to_string()),
language: None,
},
},
Example {
description: "Replace a range of code with explicit boundaries",
item: Self {
file_path: "src/main.rs".into(),
selector: Selector {
operation: Operation::ReplaceRange,
anchor: "let user =".to_string(),
end: Some("return user;".into()),
},
content: Some(
"let user = User::new();\n validate_user(&user);\n return user;"
.into(),
),
language: None,
},
},
Example {
description: "Removing a function by omitting replacement content",
item: Self {
file_path: "src/main.rs".into(),
selector: Selector {
operation: Operation::ReplaceNode,
anchor: "fn main() {".to_string(),
end: None,
},
content: None,
language: None,
},
},
]
}
}
impl Tool<SemanticEditTools> for StageOperation {
fn execute(self, state: &mut SemanticEditTools) -> Result<String> {
let Self {
file_path,
selector,
content,
language,
} = self;
let file_path = state.resolve_path(&file_path, None)?;
let language = state
.language_registry()
.get_language_with_hint(&file_path, language)?;
let editor = Editor::new(
content.unwrap_or_default(),
selector,
language,
file_path,
None,
)?;
let (message, staged_operation) = editor.preview()?;
state.stage_operation(None, staged_operation)?;
Ok(message)
}
}