///|
pub(all) struct TextEdit {
file : String
start : Int
end : Int
original : String
replacement : String
line : Int
column : Int
} derive(Debug, Eq, ToJson)
///|
pub(all) struct PatchPreview {
file : String
line : Int
before : String
after : String
diff : String
} derive(Debug, Eq, ToJson)
///|
pub fn edit_for_candidate(candidate : MutationCandidate) -> TextEdit {
{
file: candidate.span.file,
start: candidate.span.start,
end: candidate.span.end,
original: candidate.original,
replacement: candidate.replacement,
line: candidate.span.line,
column: candidate.span.column,
}
}
///|
pub fn edit_for_project_mutation(mutation : ProjectMutation) -> TextEdit {
edit_for_candidate(mutation.candidate)
}
///|
pub fn validate_edit(source : String, edit : TextEdit) -> Bool {
edit.start >= 0 &&
edit.end >= edit.start &&
edit.end <= source.length() &&
source[edit.start:edit.end].to_owned() == edit.original
}
///|
pub fn apply_edit(source : String, edit : TextEdit) -> String? {
if validate_edit(source, edit) {
Some(
source[0:edit.start].to_owned() +
edit.replacement +
source[edit.end:source.length()].to_owned(),
)
} else {
None
}
}
///|
pub fn invert_edit(edit : TextEdit) -> TextEdit {
{
file: edit.file,
start: edit.start,
end: edit.start + edit.replacement.length(),
original: edit.replacement,
replacement: edit.original,
line: edit.line,
column: edit.column,
}
}
///|
pub fn preview_candidate_patch(
source : String,
candidate : MutationCandidate,
) -> PatchPreview? {
let edit = edit_for_candidate(candidate)
match apply_edit(source, edit) {
Some(mutated) => {
let before = line_around(source, edit.start)
let after = line_around(mutated, edit.start)
Some({
file: edit.file,
line: edit.line,
before,
after,
diff: format_unified_diff(edit.file, edit.line, before, after),
})
}
None => None
}
}
///|
pub fn format_unified_diff(
file : String,
line : Int,
before : String,
after : String,
) -> String {
[
"--- a/\{file}",
"+++ b/\{file}",
"@@ -\{line},1 +\{line},1 @@",
"-\{before}",
"+\{after}",
].join("\n")
}
///|
pub fn preview_project_mutation_patch(
source : String,
mutation : ProjectMutation,
) -> PatchPreview? {
preview_candidate_patch(source, mutation.candidate)
}
///|
fn line_around(source : String, index : Int) -> String {
let start = for i = index; i > 0; {
if source[i - 1] == '\n' {
break i
} else {
continue i - 1
}
} nobreak {
0
}
let end = for i = index; i < source.length(); {
if source[i] == '\n' {
break i
} else {
continue i + 1
}
} nobreak {
source.length()
}
source[start:end].to_owned()
}