#[derive(Debug, Clone)]
pub struct InsertTextCodeFixEdit {
pub pos: usize,
pub text: String,
}
#[derive(Debug, Clone)]
pub struct ReplaceTextCodeFixEdit {
pub pos: usize,
pub end: usize,
pub text: String,
}
#[derive(Debug, Clone)]
pub enum CodeFixEdit {
InsertText(InsertTextCodeFixEdit),
ReplaceText(ReplaceTextCodeFixEdit),
}
pub fn apply_code_fixes_on_text(content: &str, edits: &[CodeFixEdit]) -> String {
let mut segments: Vec<String> = Vec::new();
let mut last: usize = 0;
let mut sorted_edits: Vec<&CodeFixEdit> = edits.iter().collect();
sorted_edits.sort_by_key(|e| match e {
CodeFixEdit::InsertText(e) => e.pos,
CodeFixEdit::ReplaceText(e) => e.pos,
});
for edit in sorted_edits {
match edit {
CodeFixEdit::InsertText(edit) => {
segments.push(content[last..edit.pos].to_string());
segments.push(edit.text.clone());
last = edit.pos;
}
CodeFixEdit::ReplaceText(edit) => {
segments.push(content[last..edit.pos].to_string());
segments.push(edit.text.clone());
last = edit.end;
}
}
}
segments.push(content[last..].to_string());
segments.join("")
}
pub struct CodeFixContext {
edits: Vec<CodeFixEdit>,
}
impl CodeFixContext {
pub fn new() -> Self {
Self { edits: Vec::new() }
}
pub fn prepend_text(&mut self, pos: usize, text: &str) -> CodeFixEdit {
let edit = CodeFixEdit::InsertText(InsertTextCodeFixEdit {
pos,
text: text.to_string(),
});
self.edits.push(edit.clone());
edit
}
pub fn append_text(&mut self, end: usize, text: &str) -> CodeFixEdit {
let edit = CodeFixEdit::InsertText(InsertTextCodeFixEdit {
pos: end,
text: text.to_string(),
});
self.edits.push(edit.clone());
edit
}
pub fn replace_text(&mut self, pos: usize, end: usize, text: &str) -> CodeFixEdit {
let edit = CodeFixEdit::ReplaceText(ReplaceTextCodeFixEdit {
pos,
end,
text: text.to_string(),
});
self.edits.push(edit.clone());
edit
}
pub fn into_edits(self) -> Vec<CodeFixEdit> {
self.edits
}
}
impl Default for CodeFixContext {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_apply_insert_text() {
let content = "hello world";
let edits = [CodeFixEdit::InsertText(InsertTextCodeFixEdit {
pos: 5,
text: " beautiful".to_string(),
})];
let result = apply_code_fixes_on_text(content, &edits);
assert_eq!(result, "hello beautiful world");
}
#[test]
fn test_apply_replace_text() {
let content = "hello world";
let edits = [CodeFixEdit::ReplaceText(ReplaceTextCodeFixEdit {
pos: 0,
end: 5,
text: "hi".to_string(),
})];
let result = apply_code_fixes_on_text(content, &edits);
assert_eq!(result, "hi world");
}
#[test]
fn test_apply_multiple_edits() {
let content = "hello world";
let edits = [
CodeFixEdit::ReplaceText(ReplaceTextCodeFixEdit {
pos: 0,
end: 5,
text: "hi".to_string(),
}),
CodeFixEdit::InsertText(InsertTextCodeFixEdit {
pos: 7,
text: " again".to_string(),
}),
];
let result = apply_code_fixes_on_text(content, &edits);
assert_eq!(result, "hi w againorld");
}
#[test]
fn test_apply_empty_edits() {
let content = "hello world";
let edits: [CodeFixEdit; 0] = [];
let result = apply_code_fixes_on_text(content, &edits);
assert_eq!(result, "hello world");
}
#[test]
fn test_apply_unsorted_edits() {
let content = "hello world";
let edits = [
CodeFixEdit::InsertText(InsertTextCodeFixEdit {
pos: 7,
text: " beautiful".to_string(),
}),
CodeFixEdit::ReplaceText(ReplaceTextCodeFixEdit {
pos: 0,
end: 5,
text: "hi".to_string(),
}),
];
let result = apply_code_fixes_on_text(content, &edits);
assert_eq!(result, "hi w beautifulorld");
}
#[test]
fn test_code_fix_context_new() {
let ctx = CodeFixContext::new();
assert!(ctx.edits.is_empty());
}
#[test]
fn test_code_fix_context_prepend_text() {
let mut ctx = CodeFixContext::new();
let _ = ctx.prepend_text(5, " inserted");
assert_eq!(ctx.edits.len(), 1);
}
#[test]
fn test_code_fix_context_replace_text() {
let mut ctx = CodeFixContext::new();
let _ = ctx.replace_text(0, 5, "replaced");
assert_eq!(ctx.edits.len(), 1);
}
#[test]
fn test_code_fix_context_into_edits() {
let mut ctx = CodeFixContext::new();
ctx.prepend_text(5, "text");
let edits = ctx.into_edits();
assert_eq!(edits.len(), 1);
}
#[test]
fn test_prepend_at_pos() {
let mut ctx = CodeFixContext::new();
ctx.prepend_text(3, "123");
let edits = ctx.into_edits();
let result = apply_code_fixes_on_text("abcdef", &edits);
assert_eq!(result, "abc123def");
}
#[test]
fn test_replace_at_range() {
let mut ctx = CodeFixContext::new();
ctx.replace_text(3, 5, "123");
let edits = ctx.into_edits();
let result = apply_code_fixes_on_text("abcdef", &edits);
assert_eq!(result, "abc123f");
}
#[test]
fn test_prepend_multiple_items() {
let mut ctx = CodeFixContext::new();
ctx.prepend_text(1, "123");
ctx.prepend_text(2, "456");
let edits = ctx.into_edits();
let result = apply_code_fixes_on_text("abc", &edits);
assert_eq!(result, "a123b456c");
}
#[test]
fn test_prepend_multiple_items_out_of_order() {
let mut ctx = CodeFixContext::new();
ctx.prepend_text(2, "456");
ctx.prepend_text(1, "123");
let edits = ctx.into_edits();
let result = apply_code_fixes_on_text("abc", &edits);
assert_eq!(result, "a123b456c");
}
#[test]
fn test_replace_multiple_items() {
let mut ctx = CodeFixContext::new();
ctx.replace_text(1, 2, "123");
ctx.replace_text(2, 3, "456");
let edits = ctx.into_edits();
let result = apply_code_fixes_on_text("abc", &edits);
assert_eq!(result, "a123456");
}
#[test]
fn test_replace_multiple_items_out_of_order() {
let mut ctx = CodeFixContext::new();
ctx.replace_text(2, 3, "456");
ctx.replace_text(1, 2, "123");
let edits = ctx.into_edits();
let result = apply_code_fixes_on_text("abc", &edits);
assert_eq!(result, "a123456");
}
}