use lex_babel::formats::lex::formatting_rules::FormattingRules;
use lex_babel::transforms::{serialize_to_lex, serialize_to_lex_with_rules};
use lex_core::lex::ast::Document;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TextEditSpan {
pub start: usize,
pub end: usize,
pub new_text: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LineRange {
pub start: usize,
pub end: usize,
}
pub fn format_document(
document: &Document,
source: &str,
rules: Option<FormattingRules>,
) -> Vec<TextEditSpan> {
let formatted = match rules {
Some(r) => serialize_to_lex_with_rules(document, r),
None => serialize_to_lex(document),
};
let formatted = match formatted {
Ok(text) => text,
Err(_) => return Vec::new(),
};
if formatted == source {
return Vec::new();
}
vec![TextEditSpan {
start: 0,
end: source.len(),
new_text: formatted,
}]
}
pub fn format_range(
document: &Document,
source: &str,
_range: LineRange,
rules: Option<FormattingRules>,
) -> Vec<TextEditSpan> {
format_document(document, source, rules)
}
#[cfg(test)]
mod tests {
use super::*;
use lex_core::lex::parsing;
const FULL_FIXTURE: &str = "Section:\n\n - item one \n\n\n\n\n - item two\n\n";
fn parse(source: &str) -> Document {
parsing::parse_document(source).expect("parse fixture")
}
fn apply_span(source: &str, edit: &TextEditSpan) -> String {
let mut result = source.to_string();
result.replace_range(edit.start..edit.end, &edit.new_text);
result
}
#[test]
fn formats_entire_document() {
let source = FULL_FIXTURE;
let document = parse(source);
let formatted = serialize_to_lex(&document).unwrap();
assert_ne!(formatted, source);
let edits = format_document(&document, source, None);
assert_eq!(edits.len(), 1, "should return single full-replacement edit");
let edit = &edits[0];
assert_eq!(edit.start, 0);
assert_eq!(edit.end, source.len());
let applied = apply_span(source, edit);
assert_eq!(applied, formatted);
}
#[test]
fn range_formatting_does_full_replacement() {
let source = FULL_FIXTURE;
let document = parse(source);
let range = LineRange { start: 2, end: 5 }; let edits = format_range(&document, source, range, None);
assert_eq!(edits.len(), 1);
assert_eq!(edits[0].start, 0);
assert_eq!(edits[0].end, source.len());
}
#[test]
fn no_edits_when_already_formatted() {
let source = "Section:\n - item\n";
let document = parse(source);
let edits = format_document(&document, source, None);
assert!(edits.is_empty());
}
#[test]
fn format_with_custom_rules() {
let source = "Section:\n - item\n";
let document = parse(source);
let rules = FormattingRules {
indent_string: " ".to_string(), ..Default::default()
};
let edits = format_document(&document, source, Some(rules));
assert_eq!(edits.len(), 1);
let applied = apply_span(source, &edits[0]);
assert!(applied.contains(" - item")); }
}