@@ -54,12 +54,15 @@ pub fn rewrite(input: &str, mode: &Mode, force: bool) -> Result<String, TlaError
54
54
//println!("{:#?}", tla_lines);
55
55
replace_symbols ( & mut tla_lines) ;
56
56
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
+
57
63
// 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
+
63
66
let output_tree = parser. parse ( & output, None ) . unwrap ( ) ;
64
67
if !force {
65
68
if output_tree. root_node ( ) . has_error ( ) {
@@ -276,6 +279,28 @@ impl TlaLine {
276
279
. collect ( )
277
280
}
278
281
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
+
279
304
fn shift_jlists ( & mut self , & diff: & CharDiff , & start_index: & CharQuantity ) {
280
305
for jlist in & mut self . jlists {
281
306
if jlist. column > start_index {
@@ -432,7 +457,7 @@ fn mark_symbols(tree: &Tree, cursor: &mut QueryCursor, tla_lines: &mut [TlaLine]
432
457
}
433
458
434
459
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 ) {
436
461
let ( prefix, suffix) = tla_lines. split_at_mut ( line_number + 1 ) ;
437
462
let line = & mut prefix[ line_number] ;
438
463
while let Some ( symbol) = line. symbols . pop ( ) {
@@ -850,4 +875,44 @@ op == /\ A
850
875
===="# ,
851
876
) ;
852
877
}
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
+ }
853
918
}
0 commit comments