mirror of
https://github.com/denoland/deno.git
synced 2024-12-23 07:44:48 -05:00
d5634164cb
Closes https://github.com/denoland/deno/issues/2699 Closes https://github.com/denoland/deno/issues/2347 Uses unstable rustfmt features. Since dprint invokes `rustfmt` we do not need to switch the cargo toolchain to nightly. Do we care about formatting stability of our codebase across Rust versions? (I don't)
228 lines
5.3 KiB
Rust
228 lines
5.3 KiB
Rust
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
|
|
use crate::colors;
|
|
use dissimilar::diff as difference;
|
|
use dissimilar::Chunk;
|
|
use std::fmt::Write as _;
|
|
|
|
/// Print diff of the same file_path, before and after formatting.
|
|
///
|
|
/// Diff format is loosely based on GitHub diff formatting.
|
|
pub fn diff(orig_text: &str, edit_text: &str) -> String {
|
|
if orig_text == edit_text {
|
|
return String::new();
|
|
}
|
|
|
|
// normalize newlines as it adds too much noise if they differ
|
|
let orig_text = orig_text.replace("\r\n", "\n");
|
|
let edit_text = edit_text.replace("\r\n", "\n");
|
|
|
|
if orig_text == edit_text {
|
|
return " | Text differed by line endings.\n".to_string();
|
|
}
|
|
|
|
DiffBuilder::build(&orig_text, &edit_text)
|
|
}
|
|
|
|
struct DiffBuilder {
|
|
output: String,
|
|
line_number_width: usize,
|
|
orig_line: usize,
|
|
edit_line: usize,
|
|
orig: String,
|
|
edit: String,
|
|
has_changes: bool,
|
|
}
|
|
|
|
impl DiffBuilder {
|
|
pub fn build(orig_text: &str, edit_text: &str) -> String {
|
|
let mut diff_builder = DiffBuilder {
|
|
output: String::new(),
|
|
orig_line: 1,
|
|
edit_line: 1,
|
|
orig: String::new(),
|
|
edit: String::new(),
|
|
has_changes: false,
|
|
line_number_width: {
|
|
let line_count = std::cmp::max(
|
|
orig_text.split('\n').count(),
|
|
edit_text.split('\n').count(),
|
|
);
|
|
line_count.to_string().chars().count()
|
|
},
|
|
};
|
|
|
|
let chunks = difference(orig_text, edit_text);
|
|
diff_builder.handle_chunks(chunks);
|
|
diff_builder.output
|
|
}
|
|
|
|
fn handle_chunks<'a>(&'a mut self, chunks: Vec<Chunk<'a>>) {
|
|
for chunk in chunks {
|
|
match chunk {
|
|
Chunk::Delete(s) => {
|
|
let split = s.split('\n').enumerate();
|
|
for (i, s) in split {
|
|
if i > 0 {
|
|
self.orig.push('\n');
|
|
}
|
|
self.orig.push_str(&fmt_rem_text_highlight(s));
|
|
}
|
|
self.has_changes = true
|
|
}
|
|
Chunk::Insert(s) => {
|
|
let split = s.split('\n').enumerate();
|
|
for (i, s) in split {
|
|
if i > 0 {
|
|
self.edit.push('\n');
|
|
}
|
|
self.edit.push_str(&fmt_add_text_highlight(s));
|
|
}
|
|
self.has_changes = true
|
|
}
|
|
Chunk::Equal(s) => {
|
|
let split = s.split('\n').enumerate();
|
|
for (i, s) in split {
|
|
if i > 0 {
|
|
self.flush_changes();
|
|
}
|
|
self.orig.push_str(&fmt_rem_text(s));
|
|
self.edit.push_str(&fmt_add_text(s));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
self.flush_changes();
|
|
}
|
|
|
|
fn flush_changes(&mut self) {
|
|
if self.has_changes {
|
|
self.write_line_diff();
|
|
|
|
self.orig_line += self.orig.split('\n').count();
|
|
self.edit_line += self.edit.split('\n').count();
|
|
self.has_changes = false;
|
|
} else {
|
|
self.orig_line += 1;
|
|
self.edit_line += 1;
|
|
}
|
|
|
|
self.orig.clear();
|
|
self.edit.clear();
|
|
}
|
|
|
|
fn write_line_diff(&mut self) {
|
|
let split = self.orig.split('\n').enumerate();
|
|
for (i, s) in split {
|
|
write!(
|
|
self.output,
|
|
"{:width$}{} ",
|
|
self.orig_line + i,
|
|
colors::gray(" |"),
|
|
width = self.line_number_width
|
|
)
|
|
.unwrap();
|
|
self.output.push_str(&fmt_rem());
|
|
self.output.push_str(s);
|
|
self.output.push('\n');
|
|
}
|
|
|
|
let split = self.edit.split('\n').enumerate();
|
|
for (i, s) in split {
|
|
write!(
|
|
self.output,
|
|
"{:width$}{} ",
|
|
self.edit_line + i,
|
|
colors::gray(" |"),
|
|
width = self.line_number_width
|
|
)
|
|
.unwrap();
|
|
self.output.push_str(&fmt_add());
|
|
self.output.push_str(s);
|
|
self.output.push('\n');
|
|
}
|
|
}
|
|
}
|
|
|
|
fn fmt_add() -> String {
|
|
colors::green_bold("+").to_string()
|
|
}
|
|
|
|
fn fmt_add_text(x: &str) -> String {
|
|
colors::green(x).to_string()
|
|
}
|
|
|
|
fn fmt_add_text_highlight(x: &str) -> String {
|
|
colors::black_on_green(x).to_string()
|
|
}
|
|
|
|
fn fmt_rem() -> String {
|
|
colors::red_bold("-").to_string()
|
|
}
|
|
|
|
fn fmt_rem_text(x: &str) -> String {
|
|
colors::red(x).to_string()
|
|
}
|
|
|
|
fn fmt_rem_text_highlight(x: &str) -> String {
|
|
colors::white_on_red(x).to_string()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_diff() {
|
|
run_test(
|
|
"console.log('Hello World')",
|
|
"console.log(\"Hello World\");",
|
|
concat!(
|
|
"1 | -console.log('Hello World')\n",
|
|
"1 | +console.log(\"Hello World\");\n",
|
|
),
|
|
);
|
|
|
|
run_test(
|
|
"\n\n\n\nconsole.log(\n'Hello World'\n)",
|
|
"console.log(\n\"Hello World\"\n);",
|
|
concat!(
|
|
"1 | -\n",
|
|
"2 | -\n",
|
|
"3 | -\n",
|
|
"4 | -\n",
|
|
"5 | -console.log(\n",
|
|
"1 | +console.log(\n",
|
|
"6 | -'Hello World'\n",
|
|
"2 | +\"Hello World\"\n",
|
|
"7 | -)\n3 | +);\n",
|
|
),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_eof_newline_missing() {
|
|
run_test(
|
|
"test\nsome line text test",
|
|
"test\nsome line text test\n",
|
|
concat!(
|
|
"2 | -some line text test\n",
|
|
"2 | +some line text test\n",
|
|
"3 | +\n",
|
|
),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn test_newlines_differing() {
|
|
run_test("test\n", "test\r\n", " | Text differed by line endings.\n");
|
|
}
|
|
|
|
fn run_test(diff_text1: &str, diff_text2: &str, expected_output: &str) {
|
|
assert_eq!(
|
|
test_util::strip_ansi_codes(&diff(diff_text1, diff_text2,)),
|
|
expected_output,
|
|
);
|
|
}
|
|
}
|