Avoid reallocation when ensuring trailing newline

This commit is contained in:
Richard Feldman 2022-03-06 11:08:59 -05:00
parent da5fdf6d2f
commit 8f6f20b57d
No known key found for this signature in database
GPG key ID: 7E4127D1E4241798
2 changed files with 44 additions and 5 deletions

View file

@ -181,8 +181,7 @@ fn fmt_all<'a>(arena: &'a Bump, buf: &mut Buf<'a>, ast: &'a Ast) {
fmt_def(buf, arena.alloc(def.value), 0); fmt_def(buf, arena.alloc(def.value), 0);
} }
buf.trim_end(); buf.fmt_end_of_file();
buf.newline();
} }
/// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting. /// RemoveSpaces normalizes the ast to something that we _expect_ to be invariant under formatting.

View file

@ -91,9 +91,49 @@ impl<'a> Buf<'a> {
} }
} }
pub fn trim_end(&mut self) { /// Ensures the text ends in a newline with no whitespace preceding it.
while self.text.ends_with(char::is_whitespace) { pub fn fmt_end_of_file(&mut self) {
self.text.truncate(self.text.len() - 1); fmt_text_eof(&mut self.text)
}
}
/// Ensures the text ends in a newline with no whitespace preceding it.
fn fmt_text_eof(text: &mut bumpalo::collections::String<'_>) {
let mut chars_rev = text.chars().rev();
let mut last_whitespace = None;
let mut last_whitespace_index = text.len();
// Keep going until we either run out of characters or encounter one
// that isn't whitespace.
loop {
match chars_rev.next() {
Some(ch) if ch.is_whitespace() => {
last_whitespace = Some(ch);
last_whitespace_index -= 1;
}
_ => {
break;
}
}
}
match last_whitespace {
Some('\n') => {
// There may have been more whitespace after this newline; remove it!
text.truncate(last_whitespace_index + 1);
}
Some(_) => {
// There's some whitespace at the end of this file, but the first
// whitespace char after the last non-whitespace char isn't a newline.
// So replace that whitespace char (and everything after it) with a newline.
text.replace_range(last_whitespace_index.., "\n");
}
None => {
debug_assert!(last_whitespace_index == text.len());
debug_assert!(!text.ends_with(char::is_whitespace));
// This doesn't end in whitespace at all, so add a newline.
text.push('\n');
} }
} }
} }