diff options
Diffstat (limited to 'cli/lint.rs')
-rw-r--r-- | cli/lint.rs | 104 |
1 files changed, 77 insertions, 27 deletions
diff --git a/cli/lint.rs b/cli/lint.rs index 2edadecbf..bbb9a73ad 100644 --- a/cli/lint.rs +++ b/cli/lint.rs @@ -73,10 +73,11 @@ pub async fn lint_files( let mut reporter = reporter_lock.lock().unwrap(); match r { - Ok(file_diagnostics) => { + Ok((mut file_diagnostics, source)) => { + sort_diagnostics(&mut file_diagnostics); for d in file_diagnostics.iter() { has_error.store(true, Ordering::Relaxed); - reporter.visit(&d); + reporter.visit(&d, source.split('\n').collect()); } } Err(err) => { @@ -124,7 +125,9 @@ fn create_linter(syntax: Syntax, rules: Vec<Box<dyn LintRule>>) -> Linter { .build() } -fn lint_file(file_path: PathBuf) -> Result<Vec<LintDiagnostic>, ErrBox> { +fn lint_file( + file_path: PathBuf, +) -> Result<(Vec<LintDiagnostic>, String), ErrBox> { let file_name = file_path.to_string_lossy().to_string(); let source_code = fs::read_to_string(&file_path)?; let media_type = map_file_extension(&file_path); @@ -133,9 +136,9 @@ fn lint_file(file_path: PathBuf) -> Result<Vec<LintDiagnostic>, ErrBox> { let lint_rules = rules::get_recommended_rules(); let mut linter = create_linter(syntax, lint_rules); - let file_diagnostics = linter.lint(file_name, source_code)?; + let file_diagnostics = linter.lint(file_name, source_code.clone())?; - Ok(file_diagnostics) + Ok((file_diagnostics, source_code)) } /// Lint stdin and write result to stdout. @@ -159,13 +162,13 @@ fn lint_stdin(json: bool) -> Result<(), ErrBox> { let mut has_error = false; let pseudo_file_name = "_stdin.ts"; match linter - .lint(pseudo_file_name.to_string(), source) + .lint(pseudo_file_name.to_string(), source.clone()) .map_err(|e| e.into()) { Ok(diagnostics) => { for d in diagnostics { has_error = true; - reporter.visit(&d); + reporter.visit(&d, source.split('\n').collect()); } } Err(err) => { @@ -184,7 +187,7 @@ fn lint_stdin(json: bool) -> Result<(), ErrBox> { } trait LintReporter { - fn visit(&mut self, d: &LintDiagnostic); + fn visit(&mut self, d: &LintDiagnostic, source_lines: Vec<&str>); fn visit_error(&mut self, file_path: &str, err: &ErrBox); fn close(&mut self); } @@ -206,24 +209,21 @@ impl PrettyLintReporter { } impl LintReporter for PrettyLintReporter { - fn visit(&mut self, d: &LintDiagnostic) { + fn visit(&mut self, d: &LintDiagnostic, source_lines: Vec<&str>) { self.lint_count += 1; let pretty_message = format!("({}) {}", colors::gray(&d.code), d.message.clone()); - let message = fmt_errors::format_stack( - true, + let message = format_diagnostic( &pretty_message, - Some(&d.line_src), - Some(d.location.col as i64), - Some((d.location.col + d.snippet_length) as i64), - &[fmt_errors::format_location( + &source_lines, + d.range.clone(), + &fmt_errors::format_location( &d.filename, - d.location.line as i64, - d.location.col as i64, - )], - 0, + d.range.start.line as i64, + d.range.start.col as i64, + ), ); eprintln!("{}\n", message); @@ -243,6 +243,46 @@ impl LintReporter for PrettyLintReporter { } } +pub fn format_diagnostic( + message_line: &str, + source_lines: &[&str], + range: deno_lint::diagnostic::Range, + formatted_location: &str, +) -> String { + let mut lines = vec![]; + + for i in range.start.line..=range.end.line { + lines.push(source_lines[i - 1].to_string()); + if range.start.line == range.end.line { + lines.push(format!( + "{}{}", + " ".repeat(range.start.col), + colors::red(&"^".repeat(range.end.col - range.start.col)) + )); + } else { + let line_len = source_lines[i - 1].len(); + if range.start.line == i { + lines.push(format!( + "{}{}", + " ".repeat(range.start.col), + colors::red(&"^".repeat(line_len - range.start.col)) + )); + } else if range.end.line == i { + lines.push(format!("{}", colors::red(&"^".repeat(range.end.col)))); + } else if line_len != 0 { + lines.push(format!("{}", colors::red(&"^".repeat(line_len)))); + } + } + } + + format!( + "{}\n{}\n at {}", + message_line, + lines.join("\n"), + formatted_location + ) +} + #[derive(Serialize)] struct JsonLintReporter { diagnostics: Vec<LintDiagnostic>, @@ -259,7 +299,7 @@ impl JsonLintReporter { } impl LintReporter for JsonLintReporter { - fn visit(&mut self, d: &LintDiagnostic) { + fn visit(&mut self, d: &LintDiagnostic, _source_lines: Vec<&str>) { self.diagnostics.push(d.clone()); } @@ -271,16 +311,26 @@ impl LintReporter for JsonLintReporter { } fn close(&mut self) { - // Sort so that we guarantee a deterministic output which is useful for tests - self.diagnostics.sort_by_key(|key| get_sort_key(&key)); - + sort_diagnostics(&mut self.diagnostics); let json = serde_json::to_string_pretty(&self); eprintln!("{}", json.unwrap()); } } -pub fn get_sort_key(a: &LintDiagnostic) -> String { - let location = &a.location; - - return format!("{}:{}:{}", a.filename, location.line, location.col); +fn sort_diagnostics(diagnostics: &mut Vec<LintDiagnostic>) { + // Sort so that we guarantee a deterministic output which is useful for tests + diagnostics.sort_by(|a, b| { + use std::cmp::Ordering; + let file_order = a.filename.cmp(&b.filename); + match file_order { + Ordering::Equal => { + let line_order = a.range.start.line.cmp(&b.range.start.line); + match line_order { + Ordering::Equal => a.range.start.col.cmp(&b.range.start.col), + _ => line_order, + } + } + _ => file_order, + } + }); } |