Loading acorn-lib/src/analyzer/check.rs +29 −11 Original line number Diff line number Diff line Loading @@ -262,6 +262,7 @@ impl Check { } /// Fancy output in contrast with more compressed output of Display impl pub fn report(&self) { const MAX_PREFIX_BEFORE_SPAN: usize = 60; let index = self.index; let Check { category, Loading @@ -287,7 +288,7 @@ impl Check { | Some(value) => value.trim().to_string(), | None => "No URL".to_string(), }; let source_text = if text.is_empty() { " ".to_string() } else { text.clone() }; let source_text = if text.is_empty() { " ".to_string() } else { text }; let source = Source::from(source_text.clone()); let span = 0..=source_text.len().saturating_sub(1); let _ = Report::build(kind, (&title, 1..=1)) Loading Loading @@ -315,13 +316,16 @@ impl Check { span, .. } = item; let source = Source::from(text.clone()); let full_source = Source::from(text.clone()); let full_span = highlighted_span(&full_source, span, *line); let (source_text, span) = truncate_span(text, full_span, MAX_PREFIX_BEFORE_SPAN); let source = Source::from(source_text); let _ = Report::build(severity.into(), (&title, 1..=1)) .with_code(index + i) .with_config(Config::default().with_compact(false)) .with_message(check) .with_label( ariadne::Label::new((&title, highlighted_span(&source, span, *line))) ariadne::Label::new((&title, span)) .with_message(message.italic()) .with_color(severity.into()), ) Loading Loading @@ -381,6 +385,8 @@ impl Check { | Some(span) => span, | None => 0..=source_text.len().saturating_sub(1), }; let (source_text, span) = truncate_span(&source_text, span, MAX_PREFIX_BEFORE_SPAN); let source = Source::from(source_text); let _ = Report::build(severity.clone().into(), (&title, 1..=1)) .with_code(index + issue_index) .with_config(Config::default().with_compact(false)) Loading Loading @@ -772,14 +778,8 @@ pub fn highlighted_span_from_params(source: &Source, code: &ErrorCode, params: & // TODO: Implement highlighting for longitude errors None } | ErrorCode::Section => match highlighted_span_for_array(source, params) { | Some(span) => Some(span), | None => None, }, | _ if params.get("index").is_some() => match highlighted_span_for_array(source, params) { | Some(span) => Some(span), | None => None, }, | ErrorCode::Section => highlighted_span_for_array(source, params), | _ if params.get("index").is_some() => highlighted_span_for_array(source, params), | _ => None, } } Loading @@ -803,3 +803,21 @@ pub fn summary(issues: Vec<Check>) -> Vec<Vec<String>> { }) .collect::<Vec<_>>() } /// Truncate source text with ellipsis when prefix before span exceeds maximum length. pub fn truncate_span(source_text: &str, span: RangeInclusive<usize>, max_prefix: usize) -> (String, RangeInclusive<usize>) { fn floor_char_boundary(value: &str, index: usize) -> usize { (0..=index.min(value.len())).rev().find(|&i| value.is_char_boundary(i)).unwrap_or(0) } let start = *span.start(); let end = *span.end(); if start <= max_prefix { (source_text.to_string(), start..=end) } else { let prefix_start = floor_char_boundary(source_text, start.saturating_sub(max_prefix)); let sliced = &source_text[prefix_start..]; let ellipsis = "..."; let adjusted_start = start.saturating_sub(prefix_start) + ellipsis.len(); let adjusted_end = end.saturating_sub(prefix_start) + ellipsis.len(); (format!("{ellipsis}{sliced}"), adjusted_start..=adjusted_end) } } acorn-lib/src/analyzer/mod.rs +2 −1 Original line number Diff line number Diff line Loading @@ -48,7 +48,8 @@ pub mod vale; #[cfg(feature = "analysis")] pub use check::{checks_to_csv, checks_to_dataframe, Analysis, IntoRow}; pub use check::{ highlighted_span, highlighted_span_for_array, highlighted_span_from_params, summary, Check, CheckCategory, CheckOptions, CheckSeverity, Standard, highlighted_span, highlighted_span_for_array, highlighted_span_from_params, summary, truncate_span, Check, CheckCategory, CheckOptions, CheckSeverity, Standard, }; #[cfg(feature = "analysis")] Loading acorn-lib/src/analyzer/tests/mod.rs +19 −2 Original line number Diff line number Diff line Loading @@ -2,8 +2,8 @@ use crate::analyzer::error::ErrorCode; use crate::analyzer::error::ErrorKind; use crate::analyzer::vale::{preprocess_vale_output, ValeOutput, ValeOutputItem, ValeOutputItemSeverity}; use crate::analyzer::{ checks_to_dataframe, collect_validation_checks, highlighted_span, highlighted_span_for_array, highlighted_span_from_params, Analysis, Check, CheckCategory, CheckSeverity, StaticAnalyzerConfig, checks_to_dataframe, collect_validation_checks, highlighted_span, highlighted_span_for_array, highlighted_span_from_params, truncate_span, Analysis, Check, CheckCategory, CheckSeverity, StaticAnalyzerConfig, }; use crate::prelude::{create_dir_all, remove_file, write, HashMap, PathBuf}; use crate::schema::standard::cff::Cff; Loading Loading @@ -536,3 +536,20 @@ fn test_resolve_vale_package_source() { ] ); } #[test] fn test_truncate_source_before_span_no_truncation() { let source_text = "short line"; let span = 2..=5; let (result_text, result_span) = truncate_span(source_text, span, 10); assert_eq!(result_text, source_text); assert_eq!(result_span, 2..=5); } #[test] fn test_truncate_source_before_span_with_ellipsis() { let source_text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; let span = 20..=24; let (result_text, result_span) = truncate_span(source_text, span, 5); assert!(result_text.starts_with("...")); assert_eq!(result_span, 8..=12); assert_eq!(&result_text[8..=12], "KLMNO"); } acorn-lib/src/io/docx/tests/mod.rs +2 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,8 @@ fn test_extract_acorn_fixture() { #[test] fn test_extract_acorn_fixture_readability() { let text = extract_text_from_path(fixture_path("acorn.docx")).unwrap(); assert_eq!(17.34, flesch_kincaid_grade_level(&text)); let grade = flesch_kincaid_grade_level(&text); assert!((grade - 17.34).abs() < 0.2, "Expected readability near 17.34, got {grade}"); } #[test] fn test_extract_nonexistent_file() { Loading acorn-lib/src/schema/standard/tests/text.rs +1 −1 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ use crate::io::InputOutput; use crate::schema::standard::text::{Docx, Text}; #[cfg(feature = "std")] use crate::test_utils::{fixture_path, unique_path}; use crate::util::{Unstructured, ToMarkdown, ToProse}; use crate::util::{ToMarkdown, ToProse, Unstructured}; use validator::Validate; #[test] Loading Loading
acorn-lib/src/analyzer/check.rs +29 −11 Original line number Diff line number Diff line Loading @@ -262,6 +262,7 @@ impl Check { } /// Fancy output in contrast with more compressed output of Display impl pub fn report(&self) { const MAX_PREFIX_BEFORE_SPAN: usize = 60; let index = self.index; let Check { category, Loading @@ -287,7 +288,7 @@ impl Check { | Some(value) => value.trim().to_string(), | None => "No URL".to_string(), }; let source_text = if text.is_empty() { " ".to_string() } else { text.clone() }; let source_text = if text.is_empty() { " ".to_string() } else { text }; let source = Source::from(source_text.clone()); let span = 0..=source_text.len().saturating_sub(1); let _ = Report::build(kind, (&title, 1..=1)) Loading Loading @@ -315,13 +316,16 @@ impl Check { span, .. } = item; let source = Source::from(text.clone()); let full_source = Source::from(text.clone()); let full_span = highlighted_span(&full_source, span, *line); let (source_text, span) = truncate_span(text, full_span, MAX_PREFIX_BEFORE_SPAN); let source = Source::from(source_text); let _ = Report::build(severity.into(), (&title, 1..=1)) .with_code(index + i) .with_config(Config::default().with_compact(false)) .with_message(check) .with_label( ariadne::Label::new((&title, highlighted_span(&source, span, *line))) ariadne::Label::new((&title, span)) .with_message(message.italic()) .with_color(severity.into()), ) Loading Loading @@ -381,6 +385,8 @@ impl Check { | Some(span) => span, | None => 0..=source_text.len().saturating_sub(1), }; let (source_text, span) = truncate_span(&source_text, span, MAX_PREFIX_BEFORE_SPAN); let source = Source::from(source_text); let _ = Report::build(severity.clone().into(), (&title, 1..=1)) .with_code(index + issue_index) .with_config(Config::default().with_compact(false)) Loading Loading @@ -772,14 +778,8 @@ pub fn highlighted_span_from_params(source: &Source, code: &ErrorCode, params: & // TODO: Implement highlighting for longitude errors None } | ErrorCode::Section => match highlighted_span_for_array(source, params) { | Some(span) => Some(span), | None => None, }, | _ if params.get("index").is_some() => match highlighted_span_for_array(source, params) { | Some(span) => Some(span), | None => None, }, | ErrorCode::Section => highlighted_span_for_array(source, params), | _ if params.get("index").is_some() => highlighted_span_for_array(source, params), | _ => None, } } Loading @@ -803,3 +803,21 @@ pub fn summary(issues: Vec<Check>) -> Vec<Vec<String>> { }) .collect::<Vec<_>>() } /// Truncate source text with ellipsis when prefix before span exceeds maximum length. pub fn truncate_span(source_text: &str, span: RangeInclusive<usize>, max_prefix: usize) -> (String, RangeInclusive<usize>) { fn floor_char_boundary(value: &str, index: usize) -> usize { (0..=index.min(value.len())).rev().find(|&i| value.is_char_boundary(i)).unwrap_or(0) } let start = *span.start(); let end = *span.end(); if start <= max_prefix { (source_text.to_string(), start..=end) } else { let prefix_start = floor_char_boundary(source_text, start.saturating_sub(max_prefix)); let sliced = &source_text[prefix_start..]; let ellipsis = "..."; let adjusted_start = start.saturating_sub(prefix_start) + ellipsis.len(); let adjusted_end = end.saturating_sub(prefix_start) + ellipsis.len(); (format!("{ellipsis}{sliced}"), adjusted_start..=adjusted_end) } }
acorn-lib/src/analyzer/mod.rs +2 −1 Original line number Diff line number Diff line Loading @@ -48,7 +48,8 @@ pub mod vale; #[cfg(feature = "analysis")] pub use check::{checks_to_csv, checks_to_dataframe, Analysis, IntoRow}; pub use check::{ highlighted_span, highlighted_span_for_array, highlighted_span_from_params, summary, Check, CheckCategory, CheckOptions, CheckSeverity, Standard, highlighted_span, highlighted_span_for_array, highlighted_span_from_params, summary, truncate_span, Check, CheckCategory, CheckOptions, CheckSeverity, Standard, }; #[cfg(feature = "analysis")] Loading
acorn-lib/src/analyzer/tests/mod.rs +19 −2 Original line number Diff line number Diff line Loading @@ -2,8 +2,8 @@ use crate::analyzer::error::ErrorCode; use crate::analyzer::error::ErrorKind; use crate::analyzer::vale::{preprocess_vale_output, ValeOutput, ValeOutputItem, ValeOutputItemSeverity}; use crate::analyzer::{ checks_to_dataframe, collect_validation_checks, highlighted_span, highlighted_span_for_array, highlighted_span_from_params, Analysis, Check, CheckCategory, CheckSeverity, StaticAnalyzerConfig, checks_to_dataframe, collect_validation_checks, highlighted_span, highlighted_span_for_array, highlighted_span_from_params, truncate_span, Analysis, Check, CheckCategory, CheckSeverity, StaticAnalyzerConfig, }; use crate::prelude::{create_dir_all, remove_file, write, HashMap, PathBuf}; use crate::schema::standard::cff::Cff; Loading Loading @@ -536,3 +536,20 @@ fn test_resolve_vale_package_source() { ] ); } #[test] fn test_truncate_source_before_span_no_truncation() { let source_text = "short line"; let span = 2..=5; let (result_text, result_span) = truncate_span(source_text, span, 10); assert_eq!(result_text, source_text); assert_eq!(result_span, 2..=5); } #[test] fn test_truncate_source_before_span_with_ellipsis() { let source_text = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; let span = 20..=24; let (result_text, result_span) = truncate_span(source_text, span, 5); assert!(result_text.starts_with("...")); assert_eq!(result_span, 8..=12); assert_eq!(&result_text[8..=12], "KLMNO"); }
acorn-lib/src/io/docx/tests/mod.rs +2 −1 Original line number Diff line number Diff line Loading @@ -55,7 +55,8 @@ fn test_extract_acorn_fixture() { #[test] fn test_extract_acorn_fixture_readability() { let text = extract_text_from_path(fixture_path("acorn.docx")).unwrap(); assert_eq!(17.34, flesch_kincaid_grade_level(&text)); let grade = flesch_kincaid_grade_level(&text); assert!((grade - 17.34).abs() < 0.2, "Expected readability near 17.34, got {grade}"); } #[test] fn test_extract_nonexistent_file() { Loading
acorn-lib/src/schema/standard/tests/text.rs +1 −1 Original line number Diff line number Diff line Loading @@ -3,7 +3,7 @@ use crate::io::InputOutput; use crate::schema::standard::text::{Docx, Text}; #[cfg(feature = "std")] use crate::test_utils::{fixture_path, unique_path}; use crate::util::{Unstructured, ToMarkdown, ToProse}; use crate::util::{ToMarkdown, ToProse, Unstructured}; use validator::Validate; #[test] Loading