From b063e0e656c32564d3a104cc4e36995a69da71c7 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:42:39 +0000 Subject: [PATCH] feat(ast): add `is_inside_comment` method (#14907) @Dunqing or @Boshen do you mind reviewing + merging please. --- crates/oxc_ast/src/lib.rs | 2 +- crates/oxc_ast/src/trivia.rs | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/crates/oxc_ast/src/lib.rs b/crates/oxc_ast/src/lib.rs index de94ca9376543..2f4b357b52dc1 100644 --- a/crates/oxc_ast/src/lib.rs +++ b/crates/oxc_ast/src/lib.rs @@ -78,7 +78,7 @@ pub use crate::{ ast_builder_impl::{AstBuilder, NONE}, ast_kind::{AstKind, AstType}, ast_kind_impl::{MemberExpressionKind, ModuleDeclarationKind}, - trivia::{CommentsRange, comments_range, has_comments_between}, + trivia::{CommentsRange, comments_range, has_comments_between, is_inside_comment}, }; // After experimenting with two types of boxed enum variants: diff --git a/crates/oxc_ast/src/trivia.rs b/crates/oxc_ast/src/trivia.rs index 66016e6db0bba..37ab02f0a0c56 100644 --- a/crates/oxc_ast/src/trivia.rs +++ b/crates/oxc_ast/src/trivia.rs @@ -4,6 +4,7 @@ //! including efficient range-based queries and iteration over comment collections. use std::{ + cmp::Ordering, iter::FusedIterator, ops::{Bound, RangeBounds}, }; @@ -57,6 +58,39 @@ pub fn has_comments_between(comments: &[Comment], span: Span) -> bool { comments_range(comments, span.start..span.end).count() > 0 } +/// Check if a position falls within any comment +/// +/// Returns `true` if the specified position is inside any comment's span, +/// including the start and end positions. +/// +/// Uses binary search for efficient lookup in O(log n) time. +/// +/// # Arguments +/// +/// * `comments` - A slice of comments sorted by starting position +/// * `pos` - The position to check +/// +/// # Examples +/// +/// ```ignore +/// // Comment spans from position 10 to 20 +/// assert!(is_inside_comment(&comments, 15)); // Inside comment +/// assert!(!is_inside_comment(&comments, 25)); // Outside comment +/// ``` +pub fn is_inside_comment(comments: &[Comment], pos: u32) -> bool { + comments + .binary_search_by(|c| { + if pos < c.span.start { + Ordering::Greater + } else if pos > c.span.end { + Ordering::Less + } else { + Ordering::Equal + } + }) + .is_ok() +} + /// Double-ended iterator over a range of comments, by starting position /// /// This iterator efficiently filters comments based on their starting positions, @@ -162,4 +196,16 @@ mod test { assert_eq!(comments_range(&comments, ..18).count(), full_len.saturating_sub(1)); assert_eq!(comments_range(&comments, ..=18).count(), full_len); } + + #[test] + fn test_is_inside_comment() { + let comments = + vec![Comment::new(0, 4, CommentKind::Line), Comment::new(10, 20, CommentKind::Block)] + .into_boxed_slice(); + + assert!(is_inside_comment(&comments, 2)); + assert!(!is_inside_comment(&comments, 5)); + assert!(is_inside_comment(&comments, 15)); + assert!(!is_inside_comment(&comments, 21)); + } }