Skip to content

Commit 0e62498

Browse files
authored
fix: keep the newline char if file ends with that (#20)
* fix: keep the newline char if file ends with that * fix: add some tests for newline * fix: add output_from_lines
1 parent 01f9367 commit 0e62498

File tree

1 file changed

+71
-6
lines changed

1 file changed

+71
-6
lines changed

src/lib.rs

+71-6
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,15 @@ pub fn rewrite(input: &str, mode: &Mode, force: bool) -> Result<String, TlaError
5454
//println!("{:#?}", tla_lines);
5555
replace_symbols(&mut tla_lines);
5656

57+
// if the input ends with '\n', we should put the '\n' back to output
58+
let extra_newline = input
59+
.chars()
60+
.last()
61+
.map_or("", |x| if x == '\n' { "\n" } else { "" });
62+
5763
// Ensure output parse tree is identical to input parse tree
58-
let output = tla_lines
59-
.iter()
60-
.map(|l| l.text.as_ref())
61-
.collect::<Vec<&str>>()
62-
.join("\n");
64+
let output = TlaLine::output_from_lines(&tla_lines, &extra_newline);
65+
6366
let output_tree = parser.parse(&output, None).unwrap();
6467
if !force {
6568
if output_tree.root_node().has_error() {
@@ -276,6 +279,28 @@ impl TlaLine {
276279
.collect()
277280
}
278281

282+
// same as join("\n") + extra,
283+
// but to avoid unnecessary the reallocation,
284+
// ref: https://doc.rust-lang.org/src/alloc/slice.rs.html#787
285+
fn output_from_lines(tla_lines: &Vec<Self>, extra: &str) -> String {
286+
let mut iter = tla_lines.iter();
287+
let first = match iter.next() {
288+
Some(first) => first,
289+
None => return extra.to_string(),
290+
};
291+
let text_size = tla_lines.iter().map(|v| v.text.len()).sum::<usize>();
292+
// Note: tla_lines.len() > 0 is always true
293+
let size = text_size + tla_lines.len() - 1 + extra.len();
294+
let mut result = String::with_capacity(size);
295+
result.push_str(&first.text);
296+
for v in iter {
297+
result.push('\n');
298+
result.push_str(&v.text);
299+
}
300+
result.push_str(extra);
301+
result
302+
}
303+
279304
fn shift_jlists(&mut self, &diff: &CharDiff, &start_index: &CharQuantity) {
280305
for jlist in &mut self.jlists {
281306
if jlist.column > start_index {
@@ -432,7 +457,7 @@ fn mark_symbols(tree: &Tree, cursor: &mut QueryCursor, tla_lines: &mut [TlaLine]
432457
}
433458

434459
fn replace_symbols(tla_lines: &mut [TlaLine]) {
435-
for line_number in 0..tla_lines.len() - 1 {
460+
for line_number in 0..tla_lines.len().saturating_add_signed(-1) {
436461
let (prefix, suffix) = tla_lines.split_at_mut(line_number + 1);
437462
let line = &mut prefix[line_number];
438463
while let Some(symbol) = line.symbols.pop() {
@@ -850,4 +875,44 @@ op == /\ A
850875
===="#,
851876
);
852877
}
878+
879+
// Tests that file ends with newline (or without newline)
880+
#[test]
881+
fn test_empty_input() {
882+
let input = "";
883+
let output = rewrite(&input, &Mode::UnicodeToAscii, true);
884+
assert_eq!(input, output.unwrap());
885+
let output = rewrite(&input, &Mode::AsciiToUnicode, true);
886+
assert_eq!(input, output.unwrap());
887+
}
888+
889+
#[test]
890+
fn test_single_newline() {
891+
let input = "\n";
892+
let output = rewrite(&input, &Mode::UnicodeToAscii, true);
893+
assert_eq!(input, output.unwrap());
894+
let output = rewrite(&input, &Mode::AsciiToUnicode, true);
895+
assert_eq!(input, output.unwrap());
896+
}
897+
898+
#[test]
899+
fn test_normal_input_without_newline() {
900+
run_roundtrip_test(
901+
r#"
902+
---- MODULE Test ----
903+
op == 1
904+
===="#,
905+
);
906+
}
907+
908+
#[test]
909+
fn test_normal_input_with_newline() {
910+
run_roundtrip_test(
911+
r#"
912+
---- MODULE Test ----
913+
op == 1
914+
====
915+
"#,
916+
);
917+
}
853918
}

0 commit comments

Comments
 (0)