diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 029aad5..d060705 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -36,6 +36,7 @@ jobs: with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} + allowed_bots: Copilot,app/copilot,dependabot,github-actions trigger_phrase: '@claude' mode: agent assignee_trigger: claude @@ -105,6 +106,7 @@ jobs: with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} + allowed_bots: Copilot,app/copilot,dependabot,github-actions trigger_phrase: '@claude' assignee_trigger: claude mode: agent @@ -128,6 +130,7 @@ jobs: with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} + allowed_bots: Copilot,app/copilot,dependabot,github-actions trigger_phrase: '@claude' assignee_trigger: claude label_trigger: claude @@ -151,6 +154,7 @@ jobs: with: claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} github_token: ${{ secrets.GITHUB_TOKEN }} + allowed_bots: Copilot,app/copilot,dependabot,github-actions trigger_phrase: '@claude' assignee_trigger: claude mode: agent diff --git a/crates/ast-engine/src/tree_sitter/mod.rs b/crates/ast-engine/src/tree_sitter/mod.rs index ff775b5..1d75054 100644 --- a/crates/ast-engine/src/tree_sitter/mod.rs +++ b/crates/ast-engine/src/tree_sitter/mod.rs @@ -144,6 +144,7 @@ fn parse_lang( /// /// ```rust,no_run /// # use thread_ast_engine::tree_sitter::StrDoc; +/// # #[derive(Clone, Debug)] /// # struct JavaScript; /// # impl thread_ast_engine::Language for JavaScript { /// # fn kind_to_id(&self, _: &str) -> u16 { 0 } @@ -401,7 +402,7 @@ pub fn perform_edit(tree: &mut Tree, input: &mut S, edit: &Edit u16 { 0 } @@ -56,6 +57,7 @@ //! # use thread_ast_engine::tree_sitter::traversal::Visitor; //! # use thread_ast_engine::Language; //! # use thread_ast_engine::tree_sitter::LanguageExt; +//! # #[derive(Clone, Debug)] //! # struct Tsx; //! # impl thread_ast_engine::Language for Tsx { //! # fn kind_to_id(&self, _: &str) -> u16 { 0 } @@ -117,6 +119,7 @@ use std::marker::PhantomData; /// # use thread_ast_engine::tree_sitter::traversal::Visitor; /// # use thread_ast_engine::Language; /// # use thread_ast_engine::tree_sitter::LanguageExt; +/// # #[derive(Clone, Debug)] /// # struct Tsx; /// # impl thread_ast_engine::Language for Tsx { /// # fn kind_to_id(&self, _: &str) -> u16 { 0 } diff --git a/crates/language/src/lib.rs b/crates/language/src/lib.rs index 721ddd6..7709c0e 100644 --- a/crates/language/src/lib.rs +++ b/crates/language/src/lib.rs @@ -1721,17 +1721,17 @@ pub fn from_extension(path: &Path) -> Option { } // Handle extensionless files or files with unknown extensions - if let Some(_file_name) = path.file_name().and_then(|n| n.to_str()) { + if let Some(file_name) = path.file_name().and_then(|n| n.to_str()) { // 1. Check if the full filename matches a known extension (e.g. .bashrc) #[cfg(any(feature = "bash", feature = "all-parsers"))] - if constants::BASH_EXTS.contains(&_file_name) { + if constants::BASH_EXTS.contains(&file_name) { return Some(SupportLang::Bash); } // 2. Check known extensionless file names #[cfg(any(feature = "bash", feature = "all-parsers", feature = "ruby"))] for (name, lang) in constants::LANG_RELATIONSHIPS_WITH_NO_EXTENSION { - if *name == _file_name { + if *name == file_name { return Some(*lang); } } diff --git a/crates/services/src/types.rs b/crates/services/src/types.rs index c9fdd9a..5b61b55 100644 --- a/crates/services/src/types.rs +++ b/crates/services/src/types.rs @@ -631,3 +631,85 @@ impl Range { self.start <= other.end && other.start <= self.end } } + +#[cfg(test)] +mod range_tests { + use super::*; + + // Helper to create positions + #[cfg(feature = "ast-grep-backend")] + fn pos(line: usize, byte_column: usize, byte_offset: usize) -> Position { + Position::new(line, byte_column, byte_offset) + } + + #[cfg(not(feature = "ast-grep-backend"))] + fn pos(row: usize, column: usize, index: usize) -> Position { + Position::new(row, column, index) + } + + #[test] + fn test_range_contains() { + let start = pos(1, 5, 15); + let end = pos(3, 10, 45); + let range = Range::new(start, end); + + // Before range + assert!(!range.contains(pos(1, 4, 14))); + assert!(!range.contains(pos(0, 5, 5))); + + // Exactly at start + assert!(range.contains(pos(1, 5, 15))); + + // Inside range + assert!(range.contains(pos(2, 0, 25))); + + // Exactly at end + assert!(range.contains(pos(3, 10, 45))); + + // After range + assert!(!range.contains(pos(3, 11, 46))); + assert!(!range.contains(pos(4, 0, 50))); + } + + #[test] + fn test_range_overlaps() { + let base_start = pos(2, 5, 25); + let base_end = pos(4, 10, 55); + let base = Range::new(base_start, base_end); + + // 1. Completely before (no overlap) + let before = Range::new(pos(1, 0, 10), pos(2, 4, 24)); + assert!(!base.overlaps(&before)); + assert!(!before.overlaps(&base)); + + // 2. Ending exactly at start (overlap) + let abut_before = Range::new(pos(1, 0, 10), pos(2, 5, 25)); + assert!(base.overlaps(&abut_before)); + assert!(abut_before.overlaps(&base)); + + // 3. Overlapping start + let overlap_start = Range::new(pos(1, 0, 10), pos(3, 0, 35)); + assert!(base.overlaps(&overlap_start)); + assert!(overlap_start.overlaps(&base)); + + // 4. Completely inside + let inside = Range::new(pos(3, 0, 35), pos(3, 5, 40)); + assert!(base.overlaps(&inside)); + assert!(inside.overlaps(&base)); + + // 5. Overlapping end + let overlap_end = Range::new(pos(3, 0, 35), pos(5, 0, 65)); + assert!(base.overlaps(&overlap_end)); + assert!(overlap_end.overlaps(&base)); + + // 6. Starting exactly at end (overlap) + let abut_after = Range::new(pos(4, 10, 55), pos(5, 0, 65)); + assert!(base.overlaps(&abut_after)); + assert!(abut_after.overlaps(&base)); + + // 7. Completely after (no overlap) + let after = Range::new(pos(4, 11, 56), pos(5, 0, 65)); + assert!(!base.overlaps(&after)); + assert!(!after.overlaps(&base)); + } +}