mirror of
https://github.com/crate-ci/typos.git
synced 2025-01-25 07:58:58 -05:00
fix: Arg diff reports immediately
This commit is contained in:
parent
c900e48593
commit
5f82dd6017
5 changed files with 130 additions and 209 deletions
125
src/checks.rs
125
src/checks.rs
|
@ -58,6 +58,14 @@ impl TyposSettings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_diff_typos(&self) -> DiffTypos {
|
||||||
|
DiffTypos {
|
||||||
|
check_filenames: self.check_filenames,
|
||||||
|
check_files: self.check_files,
|
||||||
|
binary: self.binary,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_identifier_parser(&self) -> Identifiers {
|
pub fn build_identifier_parser(&self) -> Identifiers {
|
||||||
Identifiers {
|
Identifiers {
|
||||||
check_filenames: self.check_filenames,
|
check_filenames: self.check_filenames,
|
||||||
|
@ -238,6 +246,123 @@ impl Check for FixTypos {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct DiffTypos {
|
||||||
|
check_filenames: bool,
|
||||||
|
check_files: bool,
|
||||||
|
binary: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Check for DiffTypos {
|
||||||
|
fn check_file(
|
||||||
|
&self,
|
||||||
|
path: &std::path::Path,
|
||||||
|
explicit: bool,
|
||||||
|
tokenizer: &tokens::Tokenizer,
|
||||||
|
dictionary: &dyn Dictionary,
|
||||||
|
reporter: &dyn report::Report,
|
||||||
|
) -> Result<(), std::io::Error> {
|
||||||
|
let parser = typos::ParserBuilder::new()
|
||||||
|
.tokenizer(tokenizer)
|
||||||
|
.dictionary(dictionary)
|
||||||
|
.typos();
|
||||||
|
|
||||||
|
let mut content = Vec::new();
|
||||||
|
let mut new_content = Vec::new();
|
||||||
|
if self.check_files {
|
||||||
|
let (buffer, content_type) = read_file(path, reporter)?;
|
||||||
|
if !explicit && !self.binary && content_type.is_binary() {
|
||||||
|
let msg = report::BinaryFile { path };
|
||||||
|
reporter.report(msg.into())?;
|
||||||
|
} else {
|
||||||
|
let mut fixes = Vec::new();
|
||||||
|
let mut accum_line_num = AccumulateLineNum::new();
|
||||||
|
for typo in parser.parse_bytes(&buffer) {
|
||||||
|
if is_fixable(&typo) {
|
||||||
|
fixes.push(typo.into_owned());
|
||||||
|
} else {
|
||||||
|
let line_num = accum_line_num.line_num(&buffer, typo.byte_offset);
|
||||||
|
let (line, line_offset) = extract_line(&buffer, typo.byte_offset);
|
||||||
|
let msg = report::Typo {
|
||||||
|
context: Some(report::FileContext { path, line_num }.into()),
|
||||||
|
buffer: std::borrow::Cow::Borrowed(line),
|
||||||
|
byte_offset: line_offset,
|
||||||
|
typo: typo.typo.as_ref(),
|
||||||
|
corrections: typo.corrections,
|
||||||
|
};
|
||||||
|
reporter.report(msg.into())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !fixes.is_empty() {
|
||||||
|
new_content = fix_buffer(buffer.clone(), fixes.into_iter());
|
||||||
|
content = buffer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match FixTypos ordering for easy diffing.
|
||||||
|
let mut new_path = None;
|
||||||
|
if self.check_filenames {
|
||||||
|
if let Some(file_name) = path.file_name().and_then(|s| s.to_str()) {
|
||||||
|
let mut fixes = Vec::new();
|
||||||
|
for typo in parser.parse_str(file_name) {
|
||||||
|
if is_fixable(&typo) {
|
||||||
|
fixes.push(typo.into_owned());
|
||||||
|
} else {
|
||||||
|
let msg = report::Typo {
|
||||||
|
context: Some(report::PathContext { path }.into()),
|
||||||
|
buffer: std::borrow::Cow::Borrowed(file_name.as_bytes()),
|
||||||
|
byte_offset: typo.byte_offset,
|
||||||
|
typo: typo.typo.as_ref(),
|
||||||
|
corrections: typo.corrections,
|
||||||
|
};
|
||||||
|
reporter.report(msg.into())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !fixes.is_empty() {
|
||||||
|
let file_name = file_name.to_owned().into_bytes();
|
||||||
|
let new_name = fix_buffer(file_name, fixes.into_iter());
|
||||||
|
let new_name =
|
||||||
|
String::from_utf8(new_name).expect("corrections are valid utf-8");
|
||||||
|
new_path = Some(path.with_file_name(new_name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if new_path.is_some() || !content.is_empty() {
|
||||||
|
let original_path = path.display().to_string();
|
||||||
|
let fixed_path = new_path
|
||||||
|
.as_ref()
|
||||||
|
.map(|p| p.as_path())
|
||||||
|
.unwrap_or(path)
|
||||||
|
.display()
|
||||||
|
.to_string();
|
||||||
|
let original_content: Vec<_> = content
|
||||||
|
.lines_with_terminator()
|
||||||
|
.map(|s| String::from_utf8_lossy(s).into_owned())
|
||||||
|
.collect();
|
||||||
|
let fixed_content: Vec<_> = new_content
|
||||||
|
.lines_with_terminator()
|
||||||
|
.map(|s| String::from_utf8_lossy(s).into_owned())
|
||||||
|
.collect();
|
||||||
|
let diff = difflib::unified_diff(
|
||||||
|
&original_content,
|
||||||
|
&fixed_content,
|
||||||
|
original_path.as_str(),
|
||||||
|
fixed_path.as_str(),
|
||||||
|
"original",
|
||||||
|
"fixed",
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
for line in diff {
|
||||||
|
print!("{}", line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Identifiers {
|
pub struct Identifiers {
|
||||||
check_filenames: bool,
|
check_filenames: bool,
|
||||||
|
|
93
src/diff.rs
93
src/diff.rs
|
@ -1,93 +0,0 @@
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::sync;
|
|
||||||
|
|
||||||
use bstr::ByteSlice;
|
|
||||||
|
|
||||||
pub struct Diff<'r> {
|
|
||||||
reporter: &'r dyn crate::report::Report,
|
|
||||||
deferred: sync::Mutex<crate::replace::Deferred>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'r> Diff<'r> {
|
|
||||||
pub fn new(reporter: &'r dyn crate::report::Report) -> Self {
|
|
||||||
Self {
|
|
||||||
reporter,
|
|
||||||
deferred: sync::Mutex::new(crate::replace::Deferred::default()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn show(&self) -> Result<(), std::io::Error> {
|
|
||||||
let deferred = self.deferred.lock().unwrap();
|
|
||||||
|
|
||||||
for (path, corrections) in deferred.content.iter() {
|
|
||||||
let buffer = std::fs::read(path)?;
|
|
||||||
|
|
||||||
let mut original = Vec::new();
|
|
||||||
let mut corrected = Vec::new();
|
|
||||||
for (line_idx, line) in buffer.lines_with_terminator().enumerate() {
|
|
||||||
original.push(String::from_utf8_lossy(line).into_owned());
|
|
||||||
|
|
||||||
let line_num = line_idx + 1;
|
|
||||||
let line = if let Some(corrections) = corrections.get(&line_num) {
|
|
||||||
let line = line.to_vec();
|
|
||||||
crate::replace::correct(line, &corrections)
|
|
||||||
} else {
|
|
||||||
line.to_owned()
|
|
||||||
};
|
|
||||||
corrected.push(String::from_utf8_lossy(&line).into_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
let display_path = path.display().to_string();
|
|
||||||
let diff = difflib::unified_diff(
|
|
||||||
&original,
|
|
||||||
&corrected,
|
|
||||||
display_path.as_str(),
|
|
||||||
display_path.as_str(),
|
|
||||||
"original",
|
|
||||||
"corrected",
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
for line in diff {
|
|
||||||
print!("{}", line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'r> crate::report::Report for Diff<'r> {
|
|
||||||
fn report(&self, msg: crate::report::Message<'_>) -> Result<(), std::io::Error> {
|
|
||||||
let typo = match &msg {
|
|
||||||
crate::report::Message::Typo(typo) => typo,
|
|
||||||
_ => return self.reporter.report(msg),
|
|
||||||
};
|
|
||||||
|
|
||||||
let corrections = match &typo.corrections {
|
|
||||||
typos::Status::Corrections(corrections) if corrections.len() == 1 => corrections,
|
|
||||||
_ => return self.reporter.report(msg),
|
|
||||||
};
|
|
||||||
|
|
||||||
match &typo.context {
|
|
||||||
Some(crate::report::Context::File(file)) => {
|
|
||||||
let path = file.path.to_owned();
|
|
||||||
let line_num = file.line_num;
|
|
||||||
let correction = crate::replace::Correction::new(
|
|
||||||
typo.byte_offset,
|
|
||||||
typo.typo,
|
|
||||||
corrections[0].as_ref(),
|
|
||||||
);
|
|
||||||
let mut deferred = self.deferred.lock().unwrap();
|
|
||||||
let content = deferred
|
|
||||||
.content
|
|
||||||
.entry(path)
|
|
||||||
.or_insert_with(BTreeMap::new)
|
|
||||||
.entry(line_num)
|
|
||||||
.or_insert_with(Vec::new);
|
|
||||||
content.push(correction);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
_ => self.reporter.report(msg),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,4 @@
|
||||||
pub mod checks;
|
pub mod checks;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod dict;
|
pub mod dict;
|
||||||
pub mod diff;
|
|
||||||
pub(crate) mod replace;
|
|
||||||
pub mod report;
|
pub mod report;
|
||||||
|
|
16
src/main.rs
16
src/main.rs
|
@ -10,7 +10,6 @@ mod args;
|
||||||
use typos_cli::checks;
|
use typos_cli::checks;
|
||||||
use typos_cli::config;
|
use typos_cli::config;
|
||||||
use typos_cli::dict;
|
use typos_cli::dict;
|
||||||
use typos_cli::diff;
|
|
||||||
use typos_cli::report;
|
use typos_cli::report;
|
||||||
|
|
||||||
use proc_exit::WithCodeResultExt;
|
use proc_exit::WithCodeResultExt;
|
||||||
|
@ -99,13 +98,9 @@ fn run() -> proc_exit::ExitResult {
|
||||||
args.format.reporter()
|
args.format.reporter()
|
||||||
};
|
};
|
||||||
let status_reporter = report::MessageStatus::new(output_reporter);
|
let status_reporter = report::MessageStatus::new(output_reporter);
|
||||||
let mut reporter: &dyn report::Report = &status_reporter;
|
let reporter: &dyn report::Report = &status_reporter;
|
||||||
let diff_reporter = diff::Diff::new(reporter);
|
|
||||||
if args.diff {
|
|
||||||
reporter = &diff_reporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (files, identifier_parser, word_parser, checks, fixer);
|
let (files, identifier_parser, word_parser, checks, fixer, differ);
|
||||||
let selected_checks: &dyn checks::Check = if args.files {
|
let selected_checks: &dyn checks::Check = if args.files {
|
||||||
files = settings.build_files();
|
files = settings.build_files();
|
||||||
&files
|
&files
|
||||||
|
@ -118,6 +113,9 @@ fn run() -> proc_exit::ExitResult {
|
||||||
} else if args.write_changes {
|
} else if args.write_changes {
|
||||||
fixer = settings.build_fix_typos();
|
fixer = settings.build_fix_typos();
|
||||||
&fixer
|
&fixer
|
||||||
|
} else if args.diff {
|
||||||
|
differ = settings.build_diff_typos();
|
||||||
|
&differ
|
||||||
} else {
|
} else {
|
||||||
checks = settings.build_typos();
|
checks = settings.build_typos();
|
||||||
&checks
|
&checks
|
||||||
|
@ -152,10 +150,6 @@ fn run() -> proc_exit::ExitResult {
|
||||||
if status_reporter.errors_found() {
|
if status_reporter.errors_found() {
|
||||||
errors_found = true;
|
errors_found = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if args.diff {
|
|
||||||
diff_reporter.show().with_code(proc_exit::Code::FAILURE)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if errors_found {
|
if errors_found {
|
||||||
|
|
103
src/replace.rs
103
src/replace.rs
|
@ -1,103 +0,0 @@
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::path;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub(crate) struct Deferred {
|
|
||||||
pub(crate) content: BTreeMap<path::PathBuf, BTreeMap<usize, Vec<Correction>>>,
|
|
||||||
pub(crate) paths: BTreeMap<path::PathBuf, Vec<Correction>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
|
||||||
pub(crate) struct Correction {
|
|
||||||
pub(crate) byte_offset: usize,
|
|
||||||
pub(crate) typo: Vec<u8>,
|
|
||||||
pub(crate) correction: Vec<u8>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Correction {
|
|
||||||
pub(crate) fn new(byte_offset: usize, typo: &str, correction: &str) -> Self {
|
|
||||||
Self {
|
|
||||||
byte_offset,
|
|
||||||
typo: typo.as_bytes().to_vec(),
|
|
||||||
correction: correction.as_bytes().to_vec(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn correct(mut line: Vec<u8>, corrections: &[Correction]) -> Vec<u8> {
|
|
||||||
let mut corrections: Vec<_> = corrections.iter().collect();
|
|
||||||
corrections.sort_unstable();
|
|
||||||
corrections.reverse();
|
|
||||||
|
|
||||||
for correction in corrections {
|
|
||||||
let start = correction.byte_offset;
|
|
||||||
let end = start + correction.typo.len();
|
|
||||||
line.splice(start..end, correction.correction.iter().copied());
|
|
||||||
}
|
|
||||||
|
|
||||||
line
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
fn simple_correct(line: &str, corrections: Vec<(usize, &str, &str)>) -> String {
|
|
||||||
let line = line.as_bytes().to_vec();
|
|
||||||
let corrections: Vec<_> = corrections
|
|
||||||
.into_iter()
|
|
||||||
.map(|(byte_offset, typo, correction)| Correction {
|
|
||||||
byte_offset,
|
|
||||||
typo: typo.as_bytes().to_vec(),
|
|
||||||
correction: correction.as_bytes().to_vec(),
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
let actual = correct(line, &corrections);
|
|
||||||
String::from_utf8(actual).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_correct_single() {
|
|
||||||
let actual = simple_correct("foo foo foo", vec![(4, "foo", "bar")]);
|
|
||||||
assert_eq!(actual, "foo bar foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_correct_single_grow() {
|
|
||||||
let actual = simple_correct("foo foo foo", vec![(4, "foo", "happy")]);
|
|
||||||
assert_eq!(actual, "foo happy foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_correct_single_shrink() {
|
|
||||||
let actual = simple_correct("foo foo foo", vec![(4, "foo", "if")]);
|
|
||||||
assert_eq!(actual, "foo if foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_correct_start() {
|
|
||||||
let actual = simple_correct("foo foo foo", vec![(0, "foo", "bar")]);
|
|
||||||
assert_eq!(actual, "bar foo foo");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_correct_end() {
|
|
||||||
let actual = simple_correct("foo foo foo", vec![(8, "foo", "bar")]);
|
|
||||||
assert_eq!(actual, "foo foo bar");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_correct_end_grow() {
|
|
||||||
let actual = simple_correct("foo foo foo", vec![(8, "foo", "happy")]);
|
|
||||||
assert_eq!(actual, "foo foo happy");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_correct_multiple() {
|
|
||||||
let actual = simple_correct(
|
|
||||||
"foo foo foo",
|
|
||||||
vec![(4, "foo", "happy"), (8, "foo", "world")],
|
|
||||||
);
|
|
||||||
assert_eq!(actual, "foo happy world");
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue