diff --git a/Cargo.lock b/Cargo.lock index ded8be8..28392a8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -213,6 +213,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "env_filter" version = "0.1.2" @@ -326,6 +332,15 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -648,6 +663,7 @@ dependencies = [ "env_logger", "exitcode", "glob", + "itertools", "js-sys", "log", "markdown", diff --git a/Cargo.toml b/Cargo.toml index f22341e..2dfa62d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ clap = { version = "4.5.20", features = ["derive"] } console_error_panic_hook = { version = "0.1.7", optional = true } exitcode = "1.1.2" glob = "0.3.1" +itertools = "0.13.0" log = "0.4.22" markdown = "1.0.0-alpha.21" once_cell = "1.20.2" diff --git a/src/parser.rs b/src/parser.rs index 6ae2cce..d35f066 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, Result}; +use itertools::Itertools; use log::{debug, warn}; use markdown::{mdast::Node, to_mdast, Constructs, ParseOptions}; use regex::Regex; @@ -288,11 +289,13 @@ impl LintDisables { /// corresponding disables. fn collect_lint_disables( ast: &Node, + next_node: Option<&Node>, ) -> Result>, DisableParseError> { let mut disables = HashMap::>::new(); fn collect_lint_disables_internal( ast: &Node, + next_node: Option<&Node>, disables: &mut HashMap>, #[allow(non_snake_case)] ALL_MARKER: &str, ) -> std::result::Result<(), DisableParseError> { @@ -369,7 +372,17 @@ impl LintDisables { return Err(DisableParseError::new("Could not disable all rules because underlying node is missing line number.")); }; let end_line = if next_line_only { - Some(start_line.unwrap() + 2) + match next_node.and_then(|node| node.position()) { + Some(position) => { + let next_node_start = position.start.line; + if next_node_start > start_line.unwrap() { + Some(next_node_start + 1) + } else { + Some(start_line.unwrap() + 2) + } + } + None => Some(start_line.unwrap() + 2), + } } else { None }; @@ -401,7 +414,17 @@ impl LintDisables { return Err(DisableParseError::new(format!("Could not disable rule {} because underlying node is missing line number.", rule_name))); }; let end_line = if next_line_only { - Some(start_line.unwrap() + 2) + match next_node.and_then(|node| node.position()) { + Some(position) => { + let next_node_start = position.start.line; + if next_node_start > start_line.unwrap() { + Some(next_node_start + 1) + } else { + Some(start_line.unwrap() + 2) + } + } + None => Some(start_line.unwrap() + 2), + } } else { None }; @@ -421,8 +444,10 @@ impl LintDisables { } _ => { if let Some(children) = ast.children() { - for child in children { - collect_lint_disables_internal(child, disables, ALL_MARKER)?; + for node_pair in children.iter().zip_longest(children.iter().skip(1)) { + let child = node_pair.clone().left().unwrap(); + let next = node_pair.right(); + collect_lint_disables_internal(child, next, disables, ALL_MARKER)?; } } @@ -430,7 +455,7 @@ impl LintDisables { } } } - collect_lint_disables_internal(ast, &mut disables, LintDisables::all_marker())?; + collect_lint_disables_internal(ast, next_node, &mut disables, LintDisables::all_marker())?; for (_, value) in disables.iter_mut() { value.sort_by_key(|k| k.line_range.start); @@ -481,7 +506,7 @@ impl TryFrom<&Node> for LintDisables { type Error = DisableParseError; fn try_from(node: &Node) -> Result { - let disables = LintDisables::collect_lint_disables(node)?; + let disables = LintDisables::collect_lint_disables(node, None)?; Ok(Self(disables)) } } @@ -745,4 +770,22 @@ This should error because there was no disable"#; let parse_result = parse(input).unwrap(); assert!(TryInto::::try_into(&parse_result.ast).is_err()); } + + #[test] + fn test_collect_lint_disables_skip_blank_lines() { + let input = r#"{/* supa-mdx-lint-disable-next-line foo */} + +This line is ignored +This line is not ignored"#; + + let parse_result = parse(input).unwrap(); + let disables: LintDisables = (&parse_result.ast).try_into().unwrap(); + debug!("Disables: {:?}", disables); + + assert_eq!(disables.0.len(), 1); + assert_eq!( + disables.0["foo"][0].line_range.end, + Some(NonZeroUsize::new(4).unwrap()) + ); + } }