1#![deny(unsafe_code)]
42#![deny(unreachable_pub)]
43#![deny(clippy::print_stderr)]
44#![deny(clippy::print_stdout)]
45#![warn(rust_2018_idioms)]
46#![warn(missing_docs)]
47#![allow(
48 clippy::module_name_repetitions,
49 clippy::must_use_candidate,
50 clippy::missing_errors_doc,
51 clippy::missing_panics_doc
52)]
53
54use perl_ast::{Node as AstNode, NodeKind};
55use perl_module::parse_module_import_head;
56use perl_parser_core::Parser as CoreParser;
57use perl_pragma::{PragmaState, PragmaTracker};
58use perl_semantic_analyzer::semantic::SemanticModel;
59
60pub use perl_parser_core::edit::Edit as InputEdit;
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69#[non_exhaustive]
70pub struct Point {
71 pub row: usize,
73 pub column: usize,
75}
76
77pub struct Parser {
92 _priv: (),
95}
96
97impl Parser {
98 pub fn new() -> Self {
100 Parser { _priv: () }
101 }
102
103 pub fn parse(&mut self, source: &str) -> Option<Tree> {
119 let mut core = CoreParser::new(source);
120 match core.parse() {
121 Ok(root) => Some(Tree { root, source: source.to_string(), pending_edits: Vec::new() }),
122 Err(_) => None,
123 }
124 }
125
126 pub fn parse_with_old_tree(&mut self, source: &str, old_tree: &Tree) -> Option<Tree> {
135 if source == old_tree.source() && old_tree.pending_edits.is_empty() {
138 return Some(old_tree.clone());
139 }
140
141 self.parse(source)
142 }
143}
144
145impl Default for Parser {
146 fn default() -> Self {
147 Self::new()
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167pub struct PerlLanguage {
168 kind_names: &'static [&'static str],
169}
170
171impl PerlLanguage {
172 pub fn node_kind_count(&self) -> usize {
174 self.kind_names.len()
175 }
176
177 pub fn node_kind_names(&self) -> &[&'static str] {
179 self.kind_names
180 }
181
182 pub fn node_kind_is_named(&self, kind: &str) -> bool {
184 self.kind_names.contains(&kind)
185 }
186}
187
188impl Default for PerlLanguage {
189 fn default() -> Self {
190 LANGUAGE
191 }
192}
193
194pub fn language() -> PerlLanguage {
198 LANGUAGE
199}
200
201pub static LANGUAGE: PerlLanguage = PerlLanguage { kind_names: perl_ast::NodeKind::ALL_KIND_NAMES };
203
204#[derive(Debug, Clone, PartialEq)]
208pub struct Tree {
209 root: AstNode,
210 source: String,
211 pending_edits: Vec<InputEdit>,
213}
214
215#[derive(Debug, Clone, Copy)]
220#[non_exhaustive]
221pub struct SemanticOverlay<'tree> {
222 tree: &'tree Tree,
223}
224
225#[derive(Debug, Clone, PartialEq, Eq)]
227#[non_exhaustive]
228pub struct OverlayDefinition {
229 pub name: String,
231 pub qualified_name: String,
233 pub kind: String,
235 pub start_byte: usize,
237 pub end_byte: usize,
239}
240
241#[derive(Debug, Clone, PartialEq, Eq)]
243#[non_exhaustive]
244pub struct VisibleImport {
245 pub module: String,
247 pub statement_start_byte: usize,
249 pub statement_end_byte: usize,
251}
252
253impl Tree {
254 pub fn root_node(&self) -> Node<'_> {
256 Node { inner: &self.root, tree_source: &self.source }
257 }
258
259 pub fn source(&self) -> &str {
261 &self.source
262 }
263
264 pub fn edit(&mut self, edit: &InputEdit) {
273 self.pending_edits.push(edit.clone());
274 }
275
276 pub fn walk(&self) -> TreeCursor<'_> {
281 self.root_node().walk()
282 }
283
284 pub fn semantic_overlay(&self) -> SemanticOverlay<'_> {
286 SemanticOverlay { tree: self }
287 }
288}
289
290impl<'tree> SemanticOverlay<'tree> {
291 pub fn definition_at_offset(&self, offset: usize) -> Option<OverlayDefinition> {
293 let model = SemanticModel::build(&self.tree.root, self.tree.source());
294 model.definition_at(offset).map(|symbol| OverlayDefinition {
295 name: symbol.name.clone(),
296 qualified_name: symbol.qualified_name.clone(),
297 kind: format!("{:?}", symbol.kind),
298 start_byte: symbol.location.start,
299 end_byte: symbol.location.end,
300 })
301 }
302
303 pub fn definition_for_node(&self, node: &Node<'_>) -> Option<OverlayDefinition> {
307 self.definition_at_offset(node.start_byte())
308 }
309
310 pub fn visible_imports_at_offset(&self, offset: usize) -> Vec<VisibleImport> {
315 let mut imports = Vec::new();
316 collect_visible_use_imports(&self.tree.root, self.tree.source(), offset, &mut imports);
317 let mut deduped = Vec::new();
318 for import in imports {
319 if !deduped.iter().any(|existing: &VisibleImport| existing.module == import.module) {
320 deduped.push(import);
321 }
322 }
323 deduped
324 }
325
326 pub fn pragma_state_at_offset(&self, offset: usize) -> PragmaState {
328 let pragma_map = PragmaTracker::build(&self.tree.root);
329 PragmaTracker::state_for_offset(&pragma_map, offset)
330 }
331}
332
333pub struct Node<'tree> {
338 inner: &'tree AstNode,
339 tree_source: &'tree str,
340}
341
342impl<'tree> Node<'tree> {
343 pub fn kind(&self) -> String {
349 self.grammar_kind()
350 }
351
352 pub fn native_kind(&self) -> &'static str {
354 self.inner.kind.kind_name()
355 }
356
357 pub fn grammar_kind(&self) -> String {
382 let sexp = self.to_sexp();
391 if sexp.starts_with("((") {
392 return pascal_to_snake(self.inner.kind.kind_name());
395 }
396 let inner = sexp.trim_start_matches('(');
397 let end = inner.find([' ', ')']).unwrap_or(inner.len());
399 inner[..end].to_string()
400 }
401
402 pub fn to_sexp(&self) -> String {
407 self.inner.to_sexp()
408 }
409
410 pub fn child_count(&self) -> usize {
412 ast_child_count(self.inner)
413 }
414
415 pub fn child(&self, i: usize) -> Option<Node<'tree>> {
417 ast_child_at(self.inner, i)
418 .map(|child| Node { inner: child, tree_source: self.tree_source })
419 }
420
421 pub fn children(&self) -> impl Iterator<Item = Node<'tree>> + '_ {
425 let kids = ast_children(self.inner);
428 kids.into_iter().map(move |child| Node { inner: child, tree_source: self.tree_source })
429 }
430
431 pub fn start_byte(&self) -> usize {
433 self.inner.location.start
434 }
435
436 pub fn end_byte(&self) -> usize {
438 self.inner.location.end.min(self.tree_source.len())
439 }
440
441 pub fn start_position(&self) -> Point {
445 byte_to_point(self.tree_source, self.start_byte())
446 }
447
448 pub fn end_position(&self) -> Point {
452 byte_to_point(self.tree_source, self.end_byte())
453 }
454
455 pub fn utf8_text<'a>(&self, source: &'a [u8]) -> Result<&'a str, std::str::Utf8Error> {
464 let start = self.inner.location.start.min(source.len());
465 let end = self.inner.location.end.min(source.len());
466 std::str::from_utf8(&source[start..end])
467 }
468
469 pub fn is_leaf(&self) -> bool {
471 self.inner.first_child().is_none()
472 }
473
474 pub fn tree_source(&self) -> &'tree str {
476 self.tree_source
477 }
478
479 pub fn inner(&self) -> &'tree AstNode {
484 self.inner
485 }
486
487 pub fn walk(&self) -> TreeCursor<'tree> {
492 TreeCursor { root: self.inner, tree_source: self.tree_source, path: Vec::new() }
493 }
494}
495
496pub use perl_ast::NodeKind as PerlNodeKind;
499
500pub struct TreeCursor<'tree> {
506 root: &'tree AstNode,
507 tree_source: &'tree str,
508 path: Vec<usize>,
510}
511
512impl<'tree> TreeCursor<'tree> {
513 pub fn node(&self) -> Node<'tree> {
515 Node { inner: self.current_ast_node(), tree_source: self.tree_source }
516 }
517
518 pub fn goto_first_child(&mut self) -> bool {
522 if self.current_ast_node().first_child().is_none() {
523 return false;
524 }
525 self.path.push(0);
526 true
527 }
528
529 pub fn goto_last_child(&mut self) -> bool {
533 let child_count = self.current_ast_node().children().len();
534 if child_count == 0 {
535 return false;
536 }
537 self.path.push(child_count - 1);
538 true
539 }
540
541 pub fn goto_next_sibling(&mut self) -> bool {
546 if self.path.is_empty() {
547 return false;
548 }
549
550 let parent = self.current_parent_ast_node();
551 let sibling_count = ast_child_count(parent);
552 let current_index = self.path[self.path.len() - 1];
553 let next = current_index + 1;
554 if next >= sibling_count {
555 return false;
556 }
557
558 let last_pos = self.path.len() - 1;
559 self.path[last_pos] = next;
560 true
561 }
562
563 pub fn goto_previous_sibling(&mut self) -> bool {
568 if self.path.is_empty() {
569 return false;
570 }
571
572 let current_index = self.path[self.path.len() - 1];
573 if current_index == 0 {
574 return false;
575 }
576
577 let last_pos = self.path.len() - 1;
578 self.path[last_pos] = current_index - 1;
579 true
580 }
581
582 pub fn goto_parent(&mut self) -> bool {
586 self.path.pop().is_some()
587 }
588
589 pub fn reset(&mut self) {
591 self.path.clear();
592 }
593
594 fn current_ast_node(&self) -> &'tree AstNode {
595 resolve_path(self.root, &self.path)
596 }
597
598 fn current_parent_ast_node(&self) -> &'tree AstNode {
599 debug_assert!(!self.path.is_empty(), "current_parent_ast_node requires a non-root cursor");
600 let parent_path_len = self.path.len() - 1;
601 resolve_path(self.root, &self.path[..parent_path_len])
602 }
603}
604
605#[inline]
614fn ast_children(node: &AstNode) -> Vec<&AstNode> {
615 node.children()
616}
617
618#[inline]
619fn ast_child_count(node: &AstNode) -> usize {
620 let mut count = 0usize;
621 node.for_each_child(|_| count += 1);
622 count
623}
624
625#[inline]
626fn ast_child_at(node: &AstNode, index: usize) -> Option<&AstNode> {
627 let mut idx = 0usize;
628 let mut found = None;
629 node.for_each_child(|child| {
630 if found.is_none() && idx == index {
631 found = Some(child);
632 }
633 idx += 1;
634 });
635 found
636}
637
638fn collect_visible_use_imports(
639 node: &AstNode,
640 source: &str,
641 offset: usize,
642 out: &mut Vec<VisibleImport>,
643) {
644 if matches!(node.kind, NodeKind::Use { .. }) && node.location.start <= offset {
650 let start = node.location.start.min(source.len());
651 let end = node.location.end.min(source.len());
652 let statement_text = &source[start..end];
653 if let Some(import_head) = parse_module_import_head(statement_text) {
654 out.push(VisibleImport {
655 module: import_head.token.to_string(),
656 statement_start_byte: start,
657 statement_end_byte: end,
658 });
659 }
660 }
661
662 node.for_each_child(|child| collect_visible_use_imports(child, source, offset, out));
663}
664
665fn resolve_path<'tree>(root: &'tree AstNode, path: &[usize]) -> &'tree AstNode {
669 let mut current = root;
670 for &index in path {
671 match ast_child_at(current, index) {
672 Some(child) => current = child,
673 None => {
674 debug_assert!(false, "TreeCursor path must reference a valid child");
675 break;
676 }
677 }
678 }
679 current
680}
681
682fn pascal_to_snake(s: &str) -> String {
686 let mut out = String::with_capacity(s.len() + 4);
687 for (i, c) in s.char_indices() {
688 if c.is_uppercase() && i > 0 {
689 out.push('_');
690 }
691 out.extend(c.to_lowercase());
692 }
693 out
694}
695
696fn byte_to_point(source: &str, byte: usize) -> Point {
697 let clamped = byte.min(source.len());
698 let mut row = 0usize;
699 let mut column = 0usize;
700
701 for b in source.as_bytes().iter().take(clamped) {
702 if *b == b'\n' {
703 row += 1;
704 column = 0;
705 } else {
706 column += 1;
707 }
708 }
709
710 Point { row, column }
711}
712
713#[cfg(test)]
718mod tests {
719 use super::*;
720 use perl_tdd_support::must_some;
721
722 #[test]
723 fn test_parser_creates_tree() {
724 let mut p = Parser::new();
725 let tree = p.parse("my $x = 42;");
726 assert!(tree.is_some());
727 }
728
729 #[test]
730 fn test_root_node_kind() {
731 let mut p = Parser::new();
732 let tree = must_some(p.parse("my $x = 42;"));
733 assert_eq!(tree.root_node().kind(), "source_file");
734 assert_eq!(tree.root_node().native_kind(), "Program");
735 }
736
737 #[test]
738 fn test_to_sexp_starts_with_source_file() {
739 let mut p = Parser::new();
740 let tree = must_some(p.parse("my $x = 42;"));
741 let sexp = tree.root_node().to_sexp();
742 assert!(
743 sexp.starts_with("(source_file"),
744 "sexp should start with (source_file, got: {}",
745 sexp
746 );
747 }
748
749 #[test]
750 fn test_child_count_for_program_with_statements() {
751 let mut p = Parser::new();
752 let tree = must_some(p.parse("my $x = 42;\nmy $y = 99;"));
753 let root = tree.root_node();
754 assert!(root.child_count() >= 1, "root should have children");
755 }
756
757 #[test]
758 fn test_start_and_end_byte() {
759 let source = "my $x = 42;";
760 let mut p = Parser::new();
761 let tree = must_some(p.parse(source));
762 let root = tree.root_node();
763 assert_eq!(root.start_byte(), 0);
764 assert_eq!(root.end_byte(), source.len(), "root end_byte should clamp to source length");
765 }
766
767 #[test]
768 fn test_start_and_end_position_are_tree_sitter_compatible() {
769 let source = "my $x = 1;\nmy $y = 2;";
770 let mut p = Parser::new();
771 let tree = must_some(p.parse(source));
772 let root = tree.root_node();
773
774 assert_eq!(root.start_position(), Point { row: 0, column: 0 });
775 assert_eq!(root.end_position(), Point { row: 1, column: 10 });
776 }
777
778 #[test]
779 fn test_end_position_column_uses_bytes_not_chars() {
780 let source = "my $emoji = \"😀\";";
781 let mut p = Parser::new();
782 let tree = must_some(p.parse(source));
783 let root = tree.root_node();
784
785 assert_eq!(root.end_byte(), source.len());
786 assert_eq!(root.end_position(), Point { row: 0, column: source.len() });
787 }
788
789 #[test]
795 fn test_end_byte_never_exceeds_source_len_for_all_nodes() {
796 let sources = [
797 "my $x = 42;",
798 "sub foo { return 1; }",
799 "use strict;\nuse warnings;\nmy @arr = (1, 2, 3);",
800 "",
802 ];
803 for source in sources {
804 let mut p = Parser::new();
805 let tree = match p.parse(source) {
806 Some(t) => t,
807 None => continue,
809 };
810 let source_len = tree.source().len();
811 let root = tree.root_node();
813 assert!(
814 root.end_byte() <= source_len,
815 "root end_byte {} > source_len {} for source {:?}",
816 root.end_byte(),
817 source_len,
818 source
819 );
820 for child in root.children() {
821 assert!(
822 child.end_byte() <= source_len,
823 "child end_byte {} > source_len {} for source {:?}",
824 child.end_byte(),
825 source_len,
826 source
827 );
828 }
829 }
830 }
831
832 #[test]
833 fn test_utf8_text_round_trip() {
834 let source = "my $x = 42;";
835 let mut p = Parser::new();
836 let tree = must_some(p.parse(source));
837 let root = tree.root_node();
838 let text = root.utf8_text(source.as_bytes());
839 assert!(text.is_ok(), "utf8_text should succeed");
840 let extracted = must_some(text.ok());
842 assert_eq!(extracted, source, "utf8_text should return the full source for the root node");
843 }
844
845 #[test]
846 fn test_utf8_text_multibyte_unicode() {
847 let source = "my $x = 'café';";
849 let mut p = Parser::new();
850 let tree = must_some(p.parse(source));
851 let root = tree.root_node();
852 let bytes = source.as_bytes();
853 let text = root.utf8_text(bytes);
854 assert!(text.is_ok(), "utf8_text should handle multi-byte UTF-8");
855 }
856
857 #[test]
858 fn test_utf8_text_mismatched_source_does_not_panic() {
859 let source = "my $x = 42;";
862 let mut p = Parser::new();
863 let tree = must_some(p.parse(source));
864 let root = tree.root_node();
865 let short = b"my";
867 let result = root.utf8_text(short);
868 assert!(result.is_ok(), "utf8_text should not panic with short source slice");
869 }
870
871 #[test]
872 fn test_invalid_perl_returns_some_tree() {
873 let mut p = Parser::new();
876 let tree = p.parse("sub { @@@@invalid{{{{");
877 assert!(tree.is_some(), "invalid Perl should still yield an error-recovery tree");
878 }
879
880 #[test]
881 fn test_children_iterator_matches_child_count() {
882 let mut p = Parser::new();
883 let tree = must_some(p.parse("my $x = 1; my $y = 2;"));
884 let root = tree.root_node();
885 let collected: Vec<_> = root.children().collect();
886 assert_eq!(collected.len(), root.child_count());
887 }
888
889 #[test]
890 fn test_child_by_index() {
891 let mut p = Parser::new();
892 let tree = must_some(p.parse("my $x = 1; my $y = 2;"));
893 let root = tree.root_node();
894 if root.child_count() > 0 {
895 let first = root.child(0);
896 assert!(first.is_some());
897 }
898 assert!(root.child(9999).is_none());
899 }
900
901 #[test]
902 fn test_empty_source_yields_tree() {
903 let mut p = Parser::new();
905 let tree = p.parse("");
906 assert!(tree.is_some(), "empty input should still yield a tree");
907 }
908
909 #[test]
910 fn test_source_accessor() {
911 let source = "sub foo { }";
912 let mut p = Parser::new();
913 let tree = must_some(p.parse(source));
914 assert_eq!(tree.source(), source);
915 }
916
917 #[test]
918 fn test_default_parser() {
919 let mut p = Parser::default();
920 let tree = p.parse("1;");
921 assert!(tree.is_some());
922 }
923
924 #[test]
925 fn test_is_leaf_for_leaf_nodes() {
926 let mut p = Parser::new();
927 let tree = must_some(p.parse("42"));
928 let root = tree.root_node();
929 assert!(!root.is_leaf());
931 }
932
933 #[test]
936 fn test_pascal_to_snake_helper() {
937 assert_eq!(pascal_to_snake("VariableWithAttributes"), "variable_with_attributes");
938 assert_eq!(pascal_to_snake("Program"), "program");
939 assert_eq!(pascal_to_snake("FunctionCall"), "function_call");
940 assert_eq!(pascal_to_snake("If"), "if");
941 }
942
943 #[test]
944 fn test_grammar_kind_returns_source_file_for_root() {
945 let mut p = Parser::new();
946 let tree = must_some(p.parse("my $x = 42;"));
947 assert_eq!(tree.root_node().grammar_kind(), "source_file");
948 }
949
950 #[test]
951 fn test_grammar_kind_returns_variable_with_attributes_for_list_form() {
952 let mut p = Parser::new();
953 let tree = must_some(p.parse("my ($x : lvalue);"));
956 let root = tree.root_node();
957 let mut found_var_with_attrs = false;
958 for child in root.children() {
959 if child.grammar_kind() == "my_declaration" {
960 for sub in child.children() {
961 if sub.grammar_kind() == "variable_with_attributes" {
962 found_var_with_attrs = true;
963 }
964 }
965 }
966 }
967 assert!(found_var_with_attrs, "should find variable_with_attributes");
968 }
969
970 #[test]
971 fn test_grammar_kind_double_paren_edge_case() {
972 let mut p = Parser::new();
976 let tree = must_some(p.parse("my $x : lvalue = 42;"));
977 let root = tree.root_node();
978 let sexp = root.to_sexp();
979 assert!(sexp.contains("my_declaration"), "sexp should include my_declaration");
981 }
982
983 #[test]
986 fn test_language_returns_descriptor_with_nonzero_kind_count() {
987 let lang = language();
988 assert!(lang.node_kind_count() > 0, "language should report at least one node kind");
989 }
990
991 #[test]
992 fn test_language_constant_has_nonzero_kind_count() {
993 assert!(LANGUAGE.node_kind_count() > 0, "LANGUAGE should have at least one node kind");
994 }
995
996 #[test]
997 fn test_language_reports_program_as_named_kind() {
998 let lang = language();
999 assert!(lang.node_kind_is_named("Program"), "'Program' should be a named kind");
1000 }
1001
1002 #[test]
1003 fn test_language_rejects_unknown_kind() {
1004 let lang = language();
1005 assert!(
1006 !lang.node_kind_is_named("__nonexistent_kind__"),
1007 "unknown kind should not be named"
1008 );
1009 }
1010
1011 #[test]
1012 fn test_language_kind_names_contains_program() {
1013 let lang = language();
1014 let names = lang.node_kind_names();
1015 assert!(names.contains(&"Program"), "kind names should include 'Program'");
1016 }
1017
1018 #[test]
1019 fn test_language_default_returns_same_as_language() {
1020 assert_eq!(language(), PerlLanguage::default());
1024 }
1025
1026 #[test]
1027 fn test_language_kind_names_are_sorted_alphabetically() {
1028 let lang = language();
1030 let names = lang.node_kind_names();
1031 let mut sorted = names.to_vec();
1032 sorted.sort_unstable();
1033 assert_eq!(
1034 names,
1035 sorted.as_slice(),
1036 "node_kind_names() must be in alphabetical order; \
1037 re-sort ALL_KIND_NAMES in perl-ast if a new variant was added out of order"
1038 );
1039 }
1040
1041 #[test]
1042 fn test_language_is_named_with_empty_string_returns_false() {
1043 assert!(!language().node_kind_is_named(""), "empty kind name must return false");
1045 }
1046
1047 #[test]
1048 fn test_tree_cursor_walks_children_and_siblings() {
1049 let mut parser = Parser::new();
1050 let tree = must_some(parser.parse("my $x = 1; my $y = 2;"));
1051 let root = tree.root_node();
1052 let mut cursor = root.walk();
1053
1054 assert_eq!(cursor.node().grammar_kind(), "source_file");
1055 assert!(cursor.goto_first_child(), "source_file should have at least one child");
1056 assert_eq!(cursor.node().grammar_kind(), "my_declaration");
1057 assert!(cursor.goto_next_sibling(), "first statement should have a sibling");
1058 assert_eq!(cursor.node().grammar_kind(), "my_declaration");
1059 assert!(!cursor.goto_next_sibling(), "second statement should be the last sibling");
1060 }
1061
1062 #[test]
1063 fn test_tree_walk_starts_cursor_at_root() {
1064 let mut parser = Parser::new();
1065 let tree = must_some(parser.parse("my $x = 1;"));
1066 let mut cursor = tree.walk();
1067
1068 assert_eq!(cursor.node().grammar_kind(), "source_file");
1069 assert!(cursor.goto_first_child(), "root should have a child");
1070 assert_eq!(cursor.node().grammar_kind(), "my_declaration");
1071 }
1072
1073 #[test]
1074 fn test_tree_cursor_parent_and_reset_behavior() {
1075 let mut parser = Parser::new();
1076 let tree = must_some(parser.parse("my $x = 1;"));
1077 let root = tree.root_node();
1078 let mut cursor = root.walk();
1079
1080 assert!(!cursor.goto_parent(), "cursor at root must not move to parent");
1081 assert!(cursor.goto_first_child(), "root should have a child");
1082 assert!(cursor.goto_parent(), "child should have root as parent");
1083 assert_eq!(cursor.node().grammar_kind(), "source_file");
1084
1085 assert!(cursor.goto_first_child(), "root should still have a child");
1086 cursor.reset();
1087 assert_eq!(cursor.node().grammar_kind(), "source_file");
1088 }
1089
1090 #[test]
1091 fn test_tree_cursor_last_child_and_previous_sibling_behavior() {
1092 let mut p = Parser::new();
1093 let tree = must_some(p.parse("my $a = 1; my $b = 2;"));
1094 let root = tree.root_node();
1095 let mut cursor = root.walk();
1096
1097 assert!(cursor.goto_last_child(), "root should have a last child");
1098 assert_eq!(cursor.node().grammar_kind(), "my_declaration");
1099 assert!(cursor.goto_previous_sibling(), "last child should have a previous sibling");
1100 assert_eq!(cursor.node().grammar_kind(), "my_declaration");
1101 assert!(!cursor.goto_previous_sibling(), "first sibling should not have previous sibling");
1102 }
1103
1104 #[test]
1105 fn test_tree_cursor_last_child_returns_false_for_leaf() {
1106 let mut p = Parser::new();
1107 let tree = must_some(p.parse("my $x = 42;"));
1108 let root = tree.root_node();
1109 let mut cursor = root.walk();
1110
1111 assert!(cursor.goto_first_child(), "root should have a child");
1112 assert!(cursor.goto_first_child(), "my_declaration should have a child");
1113 let at_leaf = !cursor.goto_last_child();
1114 assert!(at_leaf, "leaf nodes should not have a last child");
1115 }
1116
1117 #[test]
1118 fn test_tree_cursor_goto_first_child_returns_false_for_leaf() {
1119 let mut parser = Parser::new();
1122 let tree = must_some(parser.parse("my $x = 1;"));
1123 let root = tree.root_node();
1124 let mut cursor = root.walk();
1125
1126 assert!(cursor.goto_first_child(), "root should have a child");
1128 assert!(cursor.goto_first_child(), "my_declaration should have a child");
1129 let at_leaf = !cursor.goto_first_child();
1131 assert!(at_leaf, "goto_first_child must return false on a leaf node");
1132 }
1133
1134 #[test]
1135 fn test_tree_cursor_multiple_goto_next_sibling_exhausts() {
1136 let mut parser = Parser::new();
1139 let tree = must_some(parser.parse("1; 2; 3;"));
1140 let mut cursor = tree.walk();
1141
1142 assert!(cursor.goto_first_child());
1144 let mut _count = 1;
1145 while cursor.goto_next_sibling() {
1147 _count += 1;
1148 }
1149 let node = cursor.node();
1152 assert!(
1153 !node.kind().is_empty(),
1154 "cursor should remain at valid node after exhausting siblings"
1155 );
1156 }
1157
1158 #[test]
1159 fn test_tree_cursor_goto_parent_at_root_repeatedly() {
1160 let mut parser = Parser::new();
1162 let tree = must_some(parser.parse("my $x = 1;"));
1163 let mut cursor = tree.walk();
1164
1165 assert_eq!(cursor.node().grammar_kind(), "source_file");
1167
1168 for _ in 0..3 {
1170 let result = cursor.goto_parent();
1171 assert!(!result, "goto_parent at root must return false");
1172 assert_eq!(
1173 cursor.node().grammar_kind(),
1174 "source_file",
1175 "cursor must remain at root after failed goto_parent"
1176 );
1177 }
1178 }
1179
1180 #[test]
1181 fn test_tree_cursor_reset_from_deep_nesting() {
1182 let mut parser = Parser::new();
1184 let tree = must_some(parser.parse("sub foo { my $x = 1; }"));
1185 let mut cursor = tree.walk();
1186
1187 let mut depth = 0;
1189 while cursor.goto_first_child() && depth < 10 {
1190 depth += 1;
1191 }
1192 assert!(depth > 0, "should have navigated at least one level deep");
1193
1194 cursor.reset();
1196 assert_eq!(cursor.node().grammar_kind(), "source_file", "reset must return cursor to root");
1197 }
1198
1199 #[test]
1200 fn test_tree_cursor_empty_source_root_is_valid() {
1201 let mut parser = Parser::new();
1203 let tree = must_some(parser.parse(""));
1204 let cursor = tree.walk();
1205
1206 let node = cursor.node();
1207 assert_eq!(node.grammar_kind(), "source_file");
1208 assert!(node.child_count() == 0, "empty source tree should have no statements");
1209 }
1210
1211 #[test]
1212 fn test_tree_cursor_empty_source_goto_first_child_returns_false() {
1213 let mut parser = Parser::new();
1215 let tree = must_some(parser.parse(""));
1216 let mut cursor = tree.walk();
1217
1218 let result = cursor.goto_first_child();
1219 assert!(!result, "empty tree root should have no first child");
1220 }
1221
1222 #[test]
1223 fn test_tree_cursor_single_statement_navigation() {
1224 let mut parser = Parser::new();
1226 let tree = must_some(parser.parse("42;"));
1227 let mut cursor = tree.walk();
1228
1229 assert_eq!(cursor.node().grammar_kind(), "source_file");
1230 assert!(cursor.goto_first_child(), "root should have exactly one statement");
1231
1232 assert!(!cursor.goto_next_sibling(), "single statement should be the only child");
1234
1235 assert!(cursor.goto_parent(), "should be able to return to root");
1237 assert_eq!(cursor.node().grammar_kind(), "source_file");
1238 }
1239
1240 #[test]
1241 fn test_tree_cursor_sibling_navigation_exact_count() {
1242 let mut parser = Parser::new();
1244 let tree = must_some(parser.parse("1; 2; 3; 4;"));
1245 let root = tree.root_node();
1246 let child_count = root.child_count();
1247
1248 let mut cursor = tree.walk();
1249 assert!(cursor.goto_first_child());
1250
1251 let mut sibling_count = 1;
1252 while cursor.goto_next_sibling() {
1253 sibling_count += 1;
1254 }
1255
1256 assert_eq!(sibling_count, child_count, "sibling count should match root.child_count()");
1257 }
1258
1259 #[test]
1260 fn test_tree_cursor_alternating_parent_child_navigation() {
1261 let mut parser = Parser::new();
1263 let tree = must_some(parser.parse("sub a { 1; } sub b { 2; }"));
1264 let mut cursor = tree.walk();
1265
1266 assert!(cursor.goto_first_child());
1268 let first_kind = cursor.node().grammar_kind().to_string();
1269
1270 assert!(cursor.goto_parent());
1272 assert_eq!(cursor.node().grammar_kind(), "source_file");
1273
1274 assert!(cursor.goto_first_child());
1276 assert_eq!(
1277 cursor.node().grammar_kind(),
1278 first_kind,
1279 "re-navigating should reach the same node"
1280 );
1281 }
1282
1283 #[test]
1284 fn test_tree_cursor_complex_traversal_sequence() {
1285 let mut parser = Parser::new();
1287 let tree = must_some(parser.parse("my $a = 1; my $b = 2; my $c = 3;"));
1288 let mut cursor = tree.walk();
1289
1290 assert!(cursor.goto_first_child(), "down to first stmt");
1292 assert_eq!(cursor.node().grammar_kind(), "my_declaration");
1293
1294 assert!(cursor.goto_next_sibling(), "sibling to second stmt");
1296 assert_eq!(cursor.node().grammar_kind(), "my_declaration");
1297
1298 assert!(cursor.goto_next_sibling(), "sibling to third stmt");
1300 assert_eq!(cursor.node().grammar_kind(), "my_declaration");
1301
1302 assert!(!cursor.goto_next_sibling(), "no fourth statement");
1304
1305 assert!(cursor.goto_parent(), "back to root");
1307 assert_eq!(cursor.node().grammar_kind(), "source_file");
1308
1309 assert!(cursor.goto_first_child(), "down to first again");
1311 assert_eq!(cursor.node().grammar_kind(), "my_declaration");
1312 }
1313
1314 #[test]
1315 fn test_tree_cursor_node_identity_after_traversal() {
1316 let mut parser = Parser::new();
1318 let tree = must_some(parser.parse("my $x = 1;"));
1319 let mut cursor = tree.walk();
1320
1321 assert!(cursor.goto_first_child());
1323 let first_kind = cursor.node().grammar_kind().to_string();
1324
1325 cursor.reset();
1327 assert!(cursor.goto_first_child());
1328 let second_kind = cursor.node().grammar_kind().to_string();
1329
1330 assert_eq!(
1331 first_kind, second_kind,
1332 "node at the same path should have the same kind in both traversals"
1333 );
1334 }
1335
1336 #[test]
1337 fn test_tree_cursor_sibling_with_unicode_identifiers() {
1338 let mut parser = Parser::new();
1340 let tree = must_some(parser.parse("my $café = 1; my $naïve = 2;"));
1341 let mut cursor = tree.walk();
1342
1343 let root = tree.root_node();
1344 let expected_count = root.child_count();
1345
1346 assert!(cursor.goto_first_child());
1348 let mut count = 1;
1349 while cursor.goto_next_sibling() {
1350 count += 1;
1351 }
1352
1353 assert_eq!(
1354 count, expected_count,
1355 "sibling count should match even with Unicode identifiers"
1356 );
1357 }
1358
1359 #[test]
1360 fn test_tree_cursor_deeply_nested_structure() {
1361 let mut parser = Parser::new();
1363 let mut code = String::new();
1365 for i in 0..5 {
1366 code.push_str(&format!("sub level_{} {{ ", i));
1367 }
1368 code.push_str("1;");
1369 for _ in 0..5 {
1370 code.push_str(" }");
1371 }
1372
1373 let tree = must_some(parser.parse(&code));
1374 let mut cursor = tree.walk();
1375
1376 let mut depth = 0;
1378 while cursor.goto_first_child() && depth < 50 {
1379 depth += 1;
1380 }
1381
1382 assert!(depth > 2, "should navigate multiple levels in nested structure");
1384
1385 while cursor.goto_parent() {
1387 depth -= 1;
1388 }
1389
1390 assert_eq!(cursor.node().grammar_kind(), "source_file");
1392 assert_eq!(depth, 0, "should have gone back up to root (depth 0)");
1393 }
1394}