1use std::borrow::Cow;
2use std::collections::HashMap;
3use std::sync::Arc;
4
5use self_cell::self_cell;
6use tree_sitter::{InputEdit, Node, Parser, Point, Tree, TreeCursor};
7
8use crate::ast::modern::Expression;
9use crate::error::CompileError;
10use crate::parse::is_component_name;
11use crate::primitives::{BytePos, Span};
12use crate::source::SourceText;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum Language {
17 Svelte,
19}
20
21#[derive(Debug, Default)]
27pub struct ExpressionCache {
28 expressions: HashMap<usize, Expression>,
29}
30
31impl ExpressionCache {
32 pub fn from_tree(source: &str, tree: &Tree) -> Self {
34 let mut cache = Self::default();
35 cache.walk_and_parse(source, tree.root_node());
36 cache
37 }
38
39 pub fn get(&self, start_byte: usize) -> Option<&Expression> {
41 self.expressions.get(&start_byte)
42 }
43
44 pub fn len(&self) -> usize {
46 self.expressions.len()
47 }
48
49 pub fn is_empty(&self) -> bool {
51 self.expressions.is_empty()
52 }
53
54 fn walk_and_parse(&mut self, source: &str, node: Node<'_>) {
55 match node.kind() {
56 "expression" | "expression_value" => {
57 self.parse_and_insert(source, node);
58 }
59 _ => {}
60 }
61
62 let mut cursor = node.walk();
63 for child in node.children(&mut cursor) {
64 self.walk_and_parse(source, child);
65 }
66 }
67
68 fn parse_and_insert(&mut self, source: &str, node: Node<'_>) {
69 if let Some(expr) = parse_expression_from_node(source, node) {
70 self.expressions.insert(node.start_byte(), expr);
71 }
72 }
73}
74
75fn parse_expression_from_node(source: &str, node: Node<'_>) -> Option<Expression> {
77 let raw = node.utf8_text(source.as_bytes()).ok()?;
78
79 let (text, start_byte) = if node.kind() == "expression" {
81 if let Some(content) = node.child_by_field_name("content") {
82 let t = content.utf8_text(source.as_bytes()).ok()?;
83 (t, content.start_byte())
84 } else if raw.len() >= 2 && raw.starts_with('{') && raw.ends_with('}') {
85 (&raw[1..raw.len() - 1], node.start_byte() + 1)
86 } else {
87 (raw, node.start_byte())
88 }
89 } else {
90 (raw, node.start_byte())
91 };
92
93 let trimmed = text.trim();
94 if trimmed.is_empty() {
95 return None;
96 }
97
98 let leading = text.find(trimmed).unwrap_or(0);
99 let abs = start_byte + leading;
100 let (line, column) = crate::parse::line_column_at_offset(source, abs);
101 crate::parse::parse_modern_expression_from_text(trimmed, abs, line, column)
102}
103
104struct ParsedDocumentOwner {
109 source: Arc<str>,
110 tree: Tree,
111 expressions: ExpressionCache,
112}
113
114struct ParsedDocumentDependent<'a> {
116 root: Root<'a>,
117}
118
119self_cell! {
120 pub struct ParsedDocument {
125 owner: ParsedDocumentOwner,
126
127 #[covariant]
128 dependent: ParsedDocumentDependent,
129 }
130}
131
132impl std::fmt::Debug for ParsedDocument {
133 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134 f.debug_struct("ParsedDocument")
135 .field("source_len", &self.source().len())
136 .field("expressions", &self.expressions().len())
137 .finish()
138 }
139}
140
141unsafe impl Send for ParsedDocument {}
145unsafe impl Sync for ParsedDocument {}
146
147impl ParsedDocument {
148 pub fn parse(source: &str) -> Result<Self, CompileError> {
150 let tree = {
151 let mut parser = CstParser::new().configure(Language::Svelte)?;
152 let st = SourceText::new(crate::primitives::SourceId::new(0), source, None);
153 let doc = parser.parse(st)?;
154 doc.tree
155 };
156 let expressions = ExpressionCache::from_tree(source, &tree);
157 let source_arc: Arc<str> = Arc::from(source);
158
159 Ok(ParsedDocument::new(
160 ParsedDocumentOwner {
161 source: source_arc,
162 tree,
163 expressions,
164 },
165 |owner| ParsedDocumentDependent {
166 root: Root::new(&owner.source, owner.tree.root_node()),
167 },
168 ))
169 }
170
171 pub fn source(&self) -> &str {
173 &self.borrow_owner().source
174 }
175
176 pub fn root(&self) -> &Root<'_> {
178 &self.borrow_dependent().root
179 }
180
181 pub fn expressions(&self) -> &ExpressionCache {
183 &self.borrow_owner().expressions
184 }
185
186 pub fn tree(&self) -> &Tree {
188 &self.borrow_owner().tree
189 }
190}
191
192#[derive(Debug)]
202pub struct Document<'src> {
203 pub language: Language,
205 pub source: SourceText<'src>,
207 pub tree: Tree,
209}
210
211impl<'src> Document<'src> {
212 pub fn root_node(&self) -> Node<'_> {
214 self.tree.root_node()
215 }
216
217 pub fn root_kind(&self) -> &str {
219 self.root_node().kind()
220 }
221
222 pub fn has_error(&self) -> bool {
224 self.root_node().has_error()
225 }
226
227 pub fn root_span(&self) -> Span {
229 node_span(self.root_node())
230 }
231
232 pub fn apply_edit(&mut self, edit: CstEdit) {
234 self.tree.edit(&edit.into_input_edit());
235 }
236
237 pub fn clone_for_incremental(&self) -> Document<'src> {
241 Document {
242 language: self.language,
243 source: self.source,
244 tree: self.tree.clone(),
245 }
246 }
247
248 pub fn changed_ranges(&self, old: &Document<'_>) -> Vec<std::ops::Range<usize>> {
251 old.tree
252 .changed_ranges(&self.tree)
253 .map(|r| r.start_byte..r.end_byte)
254 .collect()
255 }
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260pub struct CstPoint {
261 pub row: usize,
263 pub column: usize,
265}
266
267#[derive(Debug, Clone, PartialEq, Eq)]
286pub struct CstEdit {
287 pub start_byte: usize,
289 pub old_end_byte: usize,
291 pub new_end_byte: usize,
293 pub start_position: CstPoint,
295 pub old_end_position: CstPoint,
297 pub new_end_position: CstPoint,
299}
300
301impl CstEdit {
302 pub fn replace(
305 old_source: &str,
306 start_byte: usize,
307 old_end_byte: usize,
308 new_text: &str,
309 ) -> Self {
310 let start_position = byte_point_at_offset(old_source, start_byte);
311 let old_end_position = byte_point_at_offset(old_source, old_end_byte);
312 let new_end_byte = start_byte.saturating_add(new_text.len());
313 let new_end_position = advance_point(start_position, new_text);
314
315 Self {
316 start_byte,
317 old_end_byte,
318 new_end_byte,
319 start_position,
320 old_end_position,
321 new_end_position,
322 }
323 }
324
325 pub fn insert(old_source: &str, start_byte: usize, new_text: &str) -> Self {
328 Self::replace(old_source, start_byte, start_byte, new_text)
329 }
330
331 pub fn delete(old_source: &str, start_byte: usize, old_end_byte: usize) -> Self {
333 Self::replace(old_source, start_byte, old_end_byte, "")
334 }
335
336 fn into_input_edit(self) -> InputEdit {
337 InputEdit {
338 start_byte: self.start_byte,
339 old_end_byte: self.old_end_byte,
340 new_end_byte: self.new_end_byte,
341 start_position: self.start_position.into_point(),
342 old_end_position: self.old_end_position.into_point(),
343 new_end_position: self.new_end_position.into_point(),
344 }
345 }
346}
347
348impl CstPoint {
349 fn into_point(self) -> Point {
350 Point {
351 row: self.row,
352 column: self.column,
353 }
354 }
355}
356
357pub struct Unconfigured;
359pub struct Configured {
361 language: Language,
362}
363
364pub struct CstParser<State> {
386 parser: Parser,
387 state: State,
388}
389
390impl CstParser<Unconfigured> {
391 pub fn new() -> Self {
393 Self {
394 parser: Parser::new(),
395 state: Unconfigured,
396 }
397 }
398
399 pub fn configure(mut self, language: Language) -> Result<CstParser<Configured>, CompileError> {
401 let ts_lang = match language {
402 Language::Svelte => tree_sitter_svelte::language(),
403 };
404
405 self.parser
406 .set_language(&ts_lang)
407 .map_err(|_| CompileError::internal("failed to configure tree-sitter language"))?;
408
409 Ok(CstParser {
410 parser: self.parser,
411 state: Configured { language },
412 })
413 }
414}
415
416impl Default for CstParser<Unconfigured> {
417 fn default() -> Self {
418 Self::new()
419 }
420}
421
422impl CstParser<Configured> {
423 pub fn parse<'src>(
425 &mut self,
426 source: SourceText<'src>,
427 ) -> Result<Document<'src>, CompileError> {
428 let tree = self
429 .parser
430 .parse(source.text, None)
431 .ok_or_else(|| CompileError::internal("tree-sitter parser returned no syntax tree"))?;
432
433 Ok(Document {
434 language: self.state.language,
435 source,
436 tree,
437 })
438 }
439
440 pub fn parse_incremental<'src>(
442 &mut self,
443 source: SourceText<'src>,
444 previous: &Document<'_>,
445 edit: CstEdit,
446 ) -> Result<Document<'src>, CompileError> {
447 let mut previous_tree = previous.tree.clone();
448 previous_tree.edit(&edit.into_input_edit());
449
450 let tree = self
451 .parser
452 .parse(source.text, Some(&previous_tree))
453 .ok_or_else(|| CompileError::internal("tree-sitter parser returned no syntax tree"))?;
454
455 Ok(Document {
456 language: self.state.language,
457 source,
458 tree,
459 })
460 }
461}
462
463pub fn parse_svelte<'src>(source: SourceText<'src>) -> Result<Document<'src>, CompileError> {
480 let mut parser = CstParser::new().configure(Language::Svelte)?;
481 parser.parse(source)
482}
483
484pub fn parse_svelte_with_old_tree<'src>(
488 source: SourceText<'src>,
489 edited_old: &Document<'_>,
490) -> Result<Document<'src>, CompileError> {
491 let ts_lang = match edited_old.language {
492 Language::Svelte => tree_sitter_svelte::language(),
493 };
494 let mut parser = Parser::new();
495 parser
496 .set_language(&ts_lang)
497 .map_err(|_| CompileError::internal("failed to configure tree-sitter language"))?;
498 let tree = parser
499 .parse(source.text, Some(&edited_old.tree))
500 .ok_or_else(|| CompileError::internal("tree-sitter parser returned no syntax tree"))?;
501 Ok(Document {
502 language: edited_old.language,
503 source,
504 tree,
505 })
506}
507
508pub fn parse_svelte_incremental<'src>(
510 source: SourceText<'src>,
511 previous: &Document<'_>,
512 edit: CstEdit,
513) -> Result<Document<'src>, CompileError> {
514 let mut parser = CstParser::new().configure(Language::Svelte)?;
515 parser.parse_incremental(source, previous, edit)
516}
517
518fn node_span(node: Node<'_>) -> Span {
519 let start = byte_pos_saturating(node.start_byte());
520 let end = byte_pos_saturating(node.end_byte());
521 Span::new(start, end)
522}
523
524fn byte_pos_saturating(offset: usize) -> BytePos {
525 u32::try_from(offset)
526 .map(BytePos::from)
527 .unwrap_or_else(|_| BytePos::from(u32::MAX))
528}
529
530fn byte_point_at_offset(source: &str, offset: usize) -> CstPoint {
531 let bounded = offset.min(source.len());
532 let mut row = 0usize;
533 let mut column = 0usize;
534
535 for byte in source.as_bytes().iter().take(bounded) {
536 if *byte == b'\n' {
537 row += 1;
538 column = 0;
539 } else {
540 column += 1;
541 }
542 }
543
544 CstPoint { row, column }
545}
546
547fn advance_point(start: CstPoint, inserted_text: &str) -> CstPoint {
548 let mut point = start;
549
550 for byte in inserted_text.as_bytes() {
551 if *byte == b'\n' {
552 point.row += 1;
553 point.column = 0;
554 } else {
555 point.column += 1;
556 }
557 }
558
559 point
560}
561
562fn node_text<'src>(source: &'src str, node: Node<'_>) -> &'src str {
568 &source[node.start_byte()..node.end_byte()]
569}
570
571macro_rules! define_wrapper {
574 ($($(#[$meta:meta])* $name:ident),* $(,)?) => {
575 $(
576 $(#[$meta])*
577 #[derive(Clone, Copy)]
578 pub struct $name<'src> {
579 source: &'src str,
580 node: Node<'src>,
581 }
582
583 impl<'src> $name<'src> {
584 pub fn new(source: &'src str, node: Node<'src>) -> Self {
586 Self { source, node }
587 }
588
589 pub fn ts_node(&self) -> Node<'src> {
591 self.node
592 }
593
594 pub fn start(&self) -> usize {
596 self.node.start_byte()
597 }
598
599 pub fn end(&self) -> usize {
601 self.node.end_byte()
602 }
603
604 pub fn span(&self) -> Span {
606 node_span(self.node)
607 }
608
609 pub fn text(&self) -> &'src str {
611 node_text(self.source, self.node)
612 }
613
614 pub fn has_error(&self) -> bool {
616 self.node.has_error()
617 }
618 }
619
620 impl std::fmt::Debug for $name<'_> {
621 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
622 f.debug_struct(stringify!($name))
623 .field("kind", &self.node.kind())
624 .field("range", &(self.start()..self.end()))
625 .finish()
626 }
627 }
628 )*
629 };
630}
631
632define_wrapper!(
633 Root,
635 Element,
637 TextNode,
639 CommentNode,
641 IfBlock,
643 EachBlock,
645 AwaitBlock,
647 KeyBlock,
649 SnippetBlock,
651 ExpressionTag,
653 HtmlTag,
655 ConstTag,
657 DebugTag,
659 RenderTag,
661 AttachTag,
663 AttributeNode,
665 StartTag,
667);
668
669impl<'src> Root<'src> {
672 pub fn children(&self) -> ChildIter<'src> {
674 ChildIter::new(self.source, self.node)
675 }
676}
677
678impl<'src> Element<'src> {
681 pub fn name(&self) -> &'src str {
683 let tag = self.node.child(0).expect("element must have a tag");
685 if let Some(name_node) = tag.child_by_field_name("name") {
686 node_text(self.source, name_node)
687 } else {
688 let mut cursor = tag.walk();
690 for child in tag.children(&mut cursor) {
691 if child.kind() == "tag_name" {
692 return node_text(self.source, child);
693 }
694 }
695 ""
696 }
697 }
698
699 pub fn is_self_closing(&self) -> bool {
701 self.node.child(0)
702 .is_some_and(|tag| tag.kind() == "self_closing_tag")
703 }
704
705 pub fn has_end_tag(&self) -> bool {
707 let mut cursor = self.node.walk();
708 self.node.children(&mut cursor).any(|c| c.kind() == "end_tag")
709 }
710
711 pub fn start_tag(&self) -> Option<StartTag<'src>> {
713 let first = self.node.child(0)?;
714 match first.kind() {
715 "start_tag" | "self_closing_tag" => Some(StartTag::new(self.source, first)),
716 _ => None,
717 }
718 }
719
720 pub fn attributes(&self) -> AttributeIter<'src> {
722 let tag = self.node.child(0).expect("element must have a tag");
723 AttributeIter {
724 source: self.source,
725 cursor: tag.walk(),
726 started: false,
727 }
728 }
729
730 pub fn children(&self) -> ChildIter<'src> {
732 ChildIter::new(self.source, self.node)
733 }
734
735 pub fn is_component(&self) -> bool {
737 is_component_name(self.name())
738 }
739
740 pub fn classify(&self) -> TemplateNode<'src> {
742 classify_element(self.source, self.node)
743 }
744}
745
746impl<'src> StartTag<'src> {
749 pub fn name(&self) -> &'src str {
751 if let Some(name_node) = self.node.child_by_field_name("name") {
752 node_text(self.source, name_node)
753 } else {
754 ""
755 }
756 }
757
758 pub fn attributes(&self) -> AttributeIter<'src> {
760 AttributeIter {
761 source: self.source,
762 cursor: self.node.walk(),
763 started: false,
764 }
765 }
766}
767
768impl<'src> TextNode<'src> {
771 pub fn raw(&self) -> &'src str {
773 node_text(self.source, self.node)
774 }
775
776 pub fn data(&self) -> Cow<'src, str> {
778 Cow::Borrowed(self.raw())
780 }
781}
782
783impl<'src> CommentNode<'src> {
786 pub fn data(&self) -> &'src str {
788 let raw = self.raw();
789 raw.strip_prefix("<!--")
790 .and_then(|s| s.strip_suffix("-->"))
791 .unwrap_or(raw)
792 }
793
794 fn raw(&self) -> &'src str {
795 node_text(self.source, self.node)
796 }
797}
798
799impl<'src> IfBlock<'src> {
802 pub fn test_node(&self) -> Option<Node<'src>> {
804 self.node.child_by_field_name("expression")
805 }
806
807 pub fn test_text(&self) -> &'src str {
809 self.test_node()
810 .map(|n| node_text(self.source, n))
811 .unwrap_or("")
812 }
813
814 pub fn test_expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
816 self.test_node().and_then(|n| cache.get(n.start_byte()))
817 }
818
819 pub fn consequent(&self) -> ChildIter<'src> {
821 ChildIter::new(self.source, self.node)
822 }
823
824 pub fn alternate(&self) -> Option<Alternate<'src>> {
826 if self.node.kind() == "else_if_clause" {
827 let mut sibling = self.node.next_named_sibling();
830 while let Some(s) = sibling {
831 match s.kind() {
832 "else_clause" => return Some(Alternate::Else(ElseClause::new(self.source, s))),
833 "else_if_clause" => return Some(Alternate::ElseIf(IfBlock::new(self.source, s))),
834 _ => {}
835 }
836 sibling = s.next_named_sibling();
837 }
838 return None;
839 }
840 let mut cursor = self.node.walk();
841 for child in self.node.children(&mut cursor) {
842 match child.kind() {
843 "else_clause" => return Some(Alternate::Else(ElseClause::new(self.source, child))),
844 "else_if_clause" => return Some(Alternate::ElseIf(IfBlock::new(self.source, child))),
845 _ => {}
846 }
847 }
848 None
849 }
850}
851
852impl<'src> EachBlock<'src> {
853 pub fn expression_node(&self) -> Option<Node<'src>> {
855 self.node.child_by_field_name("expression")
856 }
857
858 pub fn expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
860 self.expression_node().and_then(|n| cache.get(n.start_byte()))
861 }
862
863 pub fn binding_node(&self) -> Option<Node<'src>> {
865 self.node.child_by_field_name("binding")
866 }
867
868 pub fn key_node(&self) -> Option<Node<'src>> {
870 self.node.child_by_field_name("key")
871 }
872
873 pub fn index_node(&self) -> Option<Node<'src>> {
875 self.node.child_by_field_name("index")
876 }
877
878 pub fn body(&self) -> ChildIter<'src> {
880 ChildIter::new(self.source, self.node)
881 }
882
883 pub fn fallback(&self) -> Option<ElseClause<'src>> {
885 let mut cursor = self.node.walk();
886 for child in self.node.children(&mut cursor) {
887 if child.kind() == "else_clause" {
888 return Some(ElseClause::new(self.source, child));
889 }
890 }
891 None
892 }
893}
894
895impl<'src> AwaitBlock<'src> {
896 pub fn expression_node(&self) -> Option<Node<'src>> {
898 self.node.child_by_field_name("expression")
899 }
900
901 pub fn expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
903 self.expression_node().and_then(|n| cache.get(n.start_byte()))
904 }
905
906 pub fn pending(&self) -> Option<ChildIter<'src>> {
908 let mut cursor = self.node.walk();
909 for child in self.node.children(&mut cursor) {
910 if child.kind() == "await_pending" {
911 return Some(ChildIter::new(self.source, child));
912 }
913 }
914 None
915 }
916
917 pub fn then_children(&self) -> Option<ChildIter<'src>> {
919 if let Some(shorthand) = self.node.child_by_field_name("shorthand") {
921 if node_text(self.source, shorthand) == "then" {
922 if let Some(children) = self.node.child_by_field_name("shorthand_children") {
923 return Some(ChildIter::new(self.source, children));
924 }
925 }
926 }
927 self.branch_children("then")
928 }
929
930 pub fn catch_children(&self) -> Option<ChildIter<'src>> {
932 if let Some(shorthand) = self.node.child_by_field_name("shorthand") {
934 if node_text(self.source, shorthand) == "catch" {
935 if let Some(children) = self.node.child_by_field_name("shorthand_children") {
936 return Some(ChildIter::new(self.source, children));
937 }
938 }
939 }
940 self.branch_children("catch")
941 }
942
943 pub fn then_binding_text(&self) -> Option<&'src str> {
945 if let Some(shorthand) = self.node.child_by_field_name("shorthand") {
947 if node_text(self.source, shorthand) == "then" {
948 return self.node.child_by_field_name("binding")
949 .map(|n| node_text(self.source, n));
950 }
951 }
952 self.branch_binding("then")
953 }
954
955 pub fn catch_binding_text(&self) -> Option<&'src str> {
957 if let Some(shorthand) = self.node.child_by_field_name("shorthand") {
959 if node_text(self.source, shorthand) == "catch" {
960 return self.node.child_by_field_name("binding")
961 .map(|n| node_text(self.source, n));
962 }
963 }
964 self.branch_binding("catch")
965 }
966
967 fn branch_binding(&self, kind_name: &str) -> Option<&'src str> {
968 let mut cursor = self.node.walk();
969 for child in self.node.children(&mut cursor) {
970 if child.kind() == "await_branch" {
971 let mut inner = child.walk();
972 for c in child.children(&mut inner) {
973 if c.kind() == "branch_kind" && node_text(self.source, c) == kind_name {
974 return child.child_by_field_name("binding")
975 .or_else(|| child.child_by_field_name("expression"))
976 .map(|n| node_text(self.source, n));
977 }
978 }
979 }
980 }
981 None
982 }
983
984 fn branch_children(&self, kind_name: &str) -> Option<ChildIter<'src>> {
985 let mut cursor = self.node.walk();
986 for child in self.node.children(&mut cursor) {
987 if child.kind() == "await_branch" {
988 let mut inner = child.walk();
990 for c in child.children(&mut inner) {
991 if c.kind() == "branch_kind" && node_text(self.source, c) == kind_name {
992 let mut inner2 = child.walk();
994 for c2 in child.children(&mut inner2) {
995 if c2.kind() == "await_branch_children" {
996 return Some(ChildIter::new(self.source, c2));
997 }
998 }
999 }
1000 }
1001 }
1002 }
1003 None
1004 }
1005}
1006
1007impl<'src> KeyBlock<'src> {
1008 pub fn expression_node(&self) -> Option<Node<'src>> {
1010 self.node.child_by_field_name("expression")
1011 }
1012
1013 pub fn expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
1015 self.expression_node().and_then(|n| cache.get(n.start_byte()))
1016 }
1017
1018 pub fn body(&self) -> ChildIter<'src> {
1020 ChildIter::new(self.source, self.node)
1021 }
1022}
1023
1024impl<'src> SnippetBlock<'src> {
1025 pub fn name(&self) -> &'src str {
1027 let mut cursor = self.node.walk();
1028 for child in self.node.children(&mut cursor) {
1029 if child.kind() == "snippet_name" {
1030 return node_text(self.source, child);
1031 }
1032 }
1033 ""
1034 }
1035
1036 pub fn parameters_node(&self) -> Option<Node<'src>> {
1038 let mut cursor = self.node.walk();
1039 for child in self.node.children(&mut cursor) {
1040 if child.kind() == "snippet_parameters" {
1041 return Some(child);
1042 }
1043 }
1044 None
1045 }
1046
1047 pub fn parameters_text(&self) -> Option<&'src str> {
1049 self.parameters_node().map(|n| node_text(self.source, n))
1050 }
1051
1052 pub fn body(&self) -> ChildIter<'src> {
1054 ChildIter::new(self.source, self.node)
1055 }
1056}
1057
1058impl<'src> ExpressionTag<'src> {
1061 pub fn content_node(&self) -> Option<Node<'src>> {
1063 self.node.child_by_field_name("content")
1064 }
1065
1066 pub fn content_text(&self) -> &'src str {
1068 self.content_node()
1069 .map(|n| node_text(self.source, n))
1070 .unwrap_or("")
1071 }
1072
1073 pub fn expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
1075 cache.get(self.node.start_byte())
1076 }
1077}
1078
1079impl<'src> HtmlTag<'src> {
1080 pub fn expression_node(&self) -> Option<Node<'src>> {
1082 self.node.child_by_field_name("expression")
1083 .or_else(|| {
1084 let mut cursor = self.node.walk();
1085 self.node.children(&mut cursor)
1086 .find(|c| c.kind() == "expression_value")
1087 })
1088 }
1089
1090 pub fn expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
1092 self.expression_node().and_then(|n| cache.get(n.start_byte()))
1093 }
1094}
1095
1096impl<'src> ConstTag<'src> {
1097 pub fn expression_node(&self) -> Option<Node<'src>> {
1099 let mut cursor = self.node.walk();
1100 self.node.children(&mut cursor)
1101 .find(|c| c.kind() == "expression_value")
1102 }
1103
1104 pub fn expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
1106 self.expression_node().and_then(|n| cache.get(n.start_byte()))
1107 }
1108}
1109
1110impl<'src> DebugTag<'src> {
1111 pub fn expression_node(&self) -> Option<Node<'src>> {
1113 let mut cursor = self.node.walk();
1114 self.node.children(&mut cursor)
1115 .find(|c| c.kind() == "expression_value")
1116 }
1117
1118 pub fn expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
1120 self.expression_node().and_then(|n| cache.get(n.start_byte()))
1121 }
1122}
1123
1124impl<'src> RenderTag<'src> {
1125 pub fn expression_node(&self) -> Option<Node<'src>> {
1127 self.node.child_by_field_name("expression")
1128 .or_else(|| {
1129 let mut cursor = self.node.walk();
1130 self.node.children(&mut cursor)
1131 .find(|c| c.kind() == "expression_value")
1132 })
1133 }
1134
1135 pub fn expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
1137 self.expression_node().and_then(|n| cache.get(n.start_byte()))
1138 }
1139}
1140
1141impl<'src> AttachTag<'src> {
1142 pub fn expression_node(&self) -> Option<Node<'src>> {
1144 let mut cursor = self.node.walk();
1145 self.node.children(&mut cursor)
1146 .find(|c| c.kind() == "expression_value" || c.kind() == "expression")
1147 }
1148
1149 pub fn expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
1151 self.expression_node().and_then(|n| cache.get(n.start_byte()))
1152 }
1153}
1154
1155impl<'src> AttributeNode<'src> {
1158 pub fn name(&self) -> &'src str {
1160 if let Some(name_node) = self.node.child_by_field_name("name") {
1161 node_text(self.source, name_node)
1162 } else {
1163 node_text(self.source, self.node)
1165 }
1166 }
1167
1168 pub fn value_node(&self) -> Option<Node<'src>> {
1170 self.node.child_by_field_name("value")
1171 }
1172
1173 pub fn is_shorthand(&self) -> bool {
1175 self.node.kind() == "shorthand_attribute"
1176 }
1177
1178 pub fn is_spread(&self) -> bool {
1180 self.is_shorthand() && self.text().starts_with("{...")
1181 }
1182
1183 pub fn is_directive(&self) -> bool {
1185 self.directive_prefix().is_some()
1186 }
1187
1188 pub fn directive_prefix(&self) -> Option<&'src str> {
1190 let name_node = self.node.child_by_field_name("name")?;
1191 let mut cursor = name_node.walk();
1192 for child in name_node.children(&mut cursor) {
1193 if child.kind() == "attribute_directive" {
1194 return Some(node_text(self.source, child));
1195 }
1196 }
1197 None
1198 }
1199
1200 pub fn directive_name(&self) -> Option<&'src str> {
1202 let name_node = self.node.child_by_field_name("name")?;
1203 let mut cursor = name_node.walk();
1204 for child in name_node.children(&mut cursor) {
1205 if child.kind() == "attribute_identifier" {
1206 return Some(node_text(self.source, child));
1207 }
1208 }
1209 None
1210 }
1211
1212 pub fn is_class_directive(&self) -> bool {
1214 self.directive_prefix() == Some("class")
1215 }
1216
1217 pub fn is_bind_directive(&self) -> bool {
1219 self.directive_prefix() == Some("bind")
1220 }
1221
1222 pub fn is_style_directive(&self) -> bool {
1224 self.directive_prefix() == Some("style")
1225 }
1226
1227 pub fn has_shorthand_child(&self) -> bool {
1229 let mut cursor = self.node.walk();
1230 self.node.children(&mut cursor).any(|c| c.kind() == "shorthand_attribute")
1231 }
1232
1233 pub fn static_value(&self) -> Option<&'src str> {
1235 let value = self.value_node()?;
1236 match value.kind() {
1237 "quoted_attribute_value" => {
1238 let mut cursor = value.walk();
1239 for child in value.children(&mut cursor) {
1240 if child.kind() == "attribute_value" {
1241 return Some(node_text(self.source, child));
1242 }
1243 }
1244 None
1245 }
1246 "attribute_value" => Some(node_text(self.source, value)),
1247 _ => None,
1248 }
1249 }
1250
1251 pub fn value_expression<'c>(&self, cache: &'c ExpressionCache) -> Option<&'c Expression> {
1253 let value = self.value_node()?;
1254 match value.kind() {
1255 "expression" => cache.get(value.start_byte()),
1256 "quoted_attribute_value" => {
1257 let mut cursor = value.walk();
1258 for child in value.children(&mut cursor) {
1259 if child.kind() == "expression" {
1260 return cache.get(child.start_byte());
1261 }
1262 }
1263 None
1264 }
1265 _ => None,
1266 }
1267 }
1268
1269 pub fn has_expression_value(&self) -> bool {
1271 let Some(value) = self.value_node() else { return false };
1272 match value.kind() {
1273 "expression" => true,
1274 "quoted_attribute_value" => {
1275 let mut cursor = value.walk();
1276 value.children(&mut cursor).any(|c| c.kind() == "expression")
1277 }
1278 _ => false,
1279 }
1280 }
1281
1282 pub fn has_mixed_value(&self) -> bool {
1284 let Some(value) = self.value_node() else { return false };
1285 if value.kind() != "quoted_attribute_value" { return false; }
1286 let mut has_text = false;
1287 let mut has_expr = false;
1288 let mut cursor = value.walk();
1289 for child in value.children(&mut cursor) {
1290 match child.kind() {
1291 "attribute_value" => has_text = true,
1292 "expression" => has_expr = true,
1293 _ => {}
1294 }
1295 }
1296 has_text && has_expr
1297 }
1298
1299 pub fn source_text(&self) -> &'src str {
1301 self.source
1302 }
1303
1304 pub fn value_parts(&self) -> Vec<AttributeValuePart<'src>> {
1309 let Some(value) = self.value_node() else { return vec![] };
1310 match value.kind() {
1311 "expression" => {
1312 vec![AttributeValuePart::Expression(
1313 value.start_byte(),
1314 node_text(self.source, value),
1315 )]
1316 }
1317 "quoted_attribute_value" => {
1318 let mut parts = Vec::new();
1319 let mut cursor = value.walk();
1320 for child in value.children(&mut cursor) {
1321 match child.kind() {
1322 "attribute_value" => {
1323 parts.push(AttributeValuePart::Text(
1324 node_text(self.source, child),
1325 ));
1326 }
1327 "expression" => {
1328 parts.push(AttributeValuePart::Expression(
1329 child.start_byte(),
1330 node_text(self.source, child),
1331 ));
1332 }
1333 _ => {}
1334 }
1335 }
1336 parts
1337 }
1338 _ => vec![],
1339 }
1340 }
1341}
1342
1343#[derive(Debug, Clone, Copy)]
1345pub enum AttributeValuePart<'src> {
1346 Text(&'src str),
1348 Expression(usize, &'src str),
1350}
1351
1352define_wrapper!(
1355 ElseClause,
1357);
1358
1359impl<'src> ElseClause<'src> {
1360 pub fn children(&self) -> ChildIter<'src> {
1362 ChildIter::new(self.source, self.node)
1363 }
1364}
1365
1366#[derive(Debug, Clone, Copy)]
1370pub enum Alternate<'src> {
1371 Else(ElseClause<'src>),
1372 ElseIf(IfBlock<'src>),
1373}
1374
1375#[derive(Debug, Clone, Copy)]
1379pub enum TemplateNode<'src> {
1380 Text(TextNode<'src>),
1381 Comment(CommentNode<'src>),
1382 ExpressionTag(ExpressionTag<'src>),
1383 HtmlTag(HtmlTag<'src>),
1384 ConstTag(ConstTag<'src>),
1385 DebugTag(DebugTag<'src>),
1386 RenderTag(RenderTag<'src>),
1387 AttachTag(AttachTag<'src>),
1388 IfBlock(IfBlock<'src>),
1389 EachBlock(EachBlock<'src>),
1390 AwaitBlock(AwaitBlock<'src>),
1391 KeyBlock(KeyBlock<'src>),
1392 SnippetBlock(SnippetBlock<'src>),
1393 RegularElement(Element<'src>),
1395 Component(Element<'src>),
1396 SlotElement(Element<'src>),
1397 SvelteHead(Element<'src>),
1398 SvelteBody(Element<'src>),
1399 SvelteWindow(Element<'src>),
1400 SvelteDocument(Element<'src>),
1401 SvelteComponent(Element<'src>),
1402 SvelteElement(Element<'src>),
1403 SvelteSelf(Element<'src>),
1404 SvelteFragment(Element<'src>),
1405 SvelteBoundary(Element<'src>),
1406 TitleElement(Element<'src>),
1407}
1408
1409impl<'src> TemplateNode<'src> {
1410 pub fn start(&self) -> usize {
1412 self.ts_node().start_byte()
1413 }
1414
1415 pub fn end(&self) -> usize {
1417 self.ts_node().end_byte()
1418 }
1419
1420 pub fn ts_node(&self) -> Node<'src> {
1422 match self {
1423 Self::Text(n) => n.ts_node(),
1424 Self::Comment(n) => n.ts_node(),
1425 Self::ExpressionTag(n) => n.ts_node(),
1426 Self::HtmlTag(n) => n.ts_node(),
1427 Self::ConstTag(n) => n.ts_node(),
1428 Self::DebugTag(n) => n.ts_node(),
1429 Self::RenderTag(n) => n.ts_node(),
1430 Self::AttachTag(n) => n.ts_node(),
1431 Self::IfBlock(n) => n.ts_node(),
1432 Self::EachBlock(n) => n.ts_node(),
1433 Self::AwaitBlock(n) => n.ts_node(),
1434 Self::KeyBlock(n) => n.ts_node(),
1435 Self::SnippetBlock(n) => n.ts_node(),
1436 Self::RegularElement(n)
1437 | Self::Component(n)
1438 | Self::SlotElement(n)
1439 | Self::SvelteHead(n)
1440 | Self::SvelteBody(n)
1441 | Self::SvelteWindow(n)
1442 | Self::SvelteDocument(n)
1443 | Self::SvelteComponent(n)
1444 | Self::SvelteElement(n)
1445 | Self::SvelteSelf(n)
1446 | Self::SvelteFragment(n)
1447 | Self::SvelteBoundary(n)
1448 | Self::TitleElement(n) => n.ts_node(),
1449 }
1450 }
1451
1452 pub fn as_element(&self) -> Option<&Element<'src>> {
1454 match self {
1455 Self::RegularElement(e)
1456 | Self::Component(e)
1457 | Self::SlotElement(e)
1458 | Self::SvelteHead(e)
1459 | Self::SvelteBody(e)
1460 | Self::SvelteWindow(e)
1461 | Self::SvelteDocument(e)
1462 | Self::SvelteComponent(e)
1463 | Self::SvelteElement(e)
1464 | Self::SvelteSelf(e)
1465 | Self::SvelteFragment(e)
1466 | Self::SvelteBoundary(e)
1467 | Self::TitleElement(e) => Some(e),
1468 _ => None,
1469 }
1470 }
1471
1472 pub fn is_component_like(&self) -> bool {
1474 matches!(
1475 self,
1476 Self::Component(_)
1477 | Self::SvelteComponent(_)
1478 | Self::SvelteSelf(_)
1479 | Self::SvelteFragment(_)
1480 | Self::SvelteBoundary(_)
1481 | Self::SvelteHead(_)
1482 | Self::SvelteBody(_)
1483 | Self::SvelteWindow(_)
1484 | Self::SvelteDocument(_)
1485 | Self::TitleElement(_)
1486 )
1487 }
1488
1489 pub fn is_svelte_element(&self) -> bool {
1491 matches!(self, Self::SvelteElement(_))
1492 }
1493
1494 pub fn for_each_child_iter<F>(&self, mut f: F)
1497 where
1498 F: FnMut(ChildIter<'src>),
1499 {
1500 match self {
1501 Self::RegularElement(el)
1502 | Self::Component(el)
1503 | Self::SlotElement(el)
1504 | Self::SvelteHead(el)
1505 | Self::SvelteBody(el)
1506 | Self::SvelteWindow(el)
1507 | Self::SvelteDocument(el)
1508 | Self::SvelteComponent(el)
1509 | Self::SvelteElement(el)
1510 | Self::SvelteSelf(el)
1511 | Self::SvelteFragment(el)
1512 | Self::SvelteBoundary(el)
1513 | Self::TitleElement(el) => {
1514 f(el.children());
1515 }
1516 Self::IfBlock(block) => {
1517 f(block.consequent());
1518 match block.alternate() {
1519 Some(Alternate::Else(clause)) => f(clause.children()),
1520 Some(Alternate::ElseIf(nested)) => {
1521 TemplateNode::IfBlock(nested).for_each_child_iter(f);
1522 }
1523 None => {}
1524 }
1525 }
1526 Self::EachBlock(block) => {
1527 f(block.body());
1528 if let Some(clause) = block.fallback() {
1529 f(clause.children());
1530 }
1531 }
1532 Self::AwaitBlock(block) => {
1533 if let Some(iter) = block.pending() {
1534 f(iter);
1535 }
1536 if let Some(iter) = block.then_children() {
1537 f(iter);
1538 }
1539 if let Some(iter) = block.catch_children() {
1540 f(iter);
1541 }
1542 }
1543 Self::KeyBlock(block) => {
1544 f(block.body());
1545 }
1546 Self::SnippetBlock(block) => {
1547 f(block.body());
1548 }
1549 _ => {}
1550 }
1551 }
1552
1553 pub fn walk<F>(&self, f: &mut F)
1555 where
1556 F: FnMut(TemplateNode<'src>),
1557 {
1558 self.for_each_child_iter(|iter| {
1559 for child in iter {
1560 f(child);
1561 child.walk(f);
1562 }
1563 });
1564 }
1565}
1566
1567impl<'src> Root<'src> {
1568 pub fn walk<F>(&self, f: &mut F)
1570 where
1571 F: FnMut(TemplateNode<'src>),
1572 {
1573 for child in self.children() {
1574 f(child);
1575 child.walk(f);
1576 }
1577 }
1578
1579 pub fn any<F>(&self, mut f: F) -> bool
1581 where
1582 F: FnMut(TemplateNode<'src>) -> bool,
1583 {
1584 let mut found = false;
1585 self.walk(&mut |node| {
1586 if !found && f(node) {
1587 found = true;
1588 }
1589 });
1590 found
1591 }
1592}
1593
1594fn classify_element<'src>(source: &'src str, node: Node<'src>) -> TemplateNode<'src> {
1598 let el = Element::new(source, node);
1599 let name = el.name();
1600
1601 match name {
1602 "slot" => TemplateNode::SlotElement(el),
1603 "title" => TemplateNode::TitleElement(el),
1604 _ if name.starts_with("svelte:") => {
1605 match &name[7..] {
1606 "head" => TemplateNode::SvelteHead(el),
1607 "body" => TemplateNode::SvelteBody(el),
1608 "window" => TemplateNode::SvelteWindow(el),
1609 "document" => TemplateNode::SvelteDocument(el),
1610 "component" => TemplateNode::SvelteComponent(el),
1611 "element" => TemplateNode::SvelteElement(el),
1612 "self" => TemplateNode::SvelteSelf(el),
1613 "fragment" => TemplateNode::SvelteFragment(el),
1614 "boundary" => TemplateNode::SvelteBoundary(el),
1615 _ => TemplateNode::RegularElement(el),
1616 }
1617 }
1618 _ if is_component_name(name) => TemplateNode::Component(el),
1619 _ => TemplateNode::RegularElement(el),
1620 }
1621}
1622
1623pub fn classify_node<'src>(source: &'src str, node: Node<'src>) -> Option<TemplateNode<'src>> {
1625 match node.kind() {
1626 "text" => Some(TemplateNode::Text(TextNode::new(source, node))),
1627 "comment" => Some(TemplateNode::Comment(CommentNode::new(source, node))),
1628 "expression" => Some(TemplateNode::ExpressionTag(ExpressionTag::new(source, node))),
1629 "html_tag" => Some(TemplateNode::HtmlTag(HtmlTag::new(source, node))),
1630 "const_tag" => Some(TemplateNode::ConstTag(ConstTag::new(source, node))),
1631 "debug_tag" => Some(TemplateNode::DebugTag(DebugTag::new(source, node))),
1632 "render_tag" => Some(TemplateNode::RenderTag(RenderTag::new(source, node))),
1633 "attach_tag" => Some(TemplateNode::AttachTag(AttachTag::new(source, node))),
1634 "if_block" => Some(TemplateNode::IfBlock(IfBlock::new(source, node))),
1635 "each_block" => Some(TemplateNode::EachBlock(EachBlock::new(source, node))),
1636 "await_block" => Some(TemplateNode::AwaitBlock(AwaitBlock::new(source, node))),
1637 "key_block" => Some(TemplateNode::KeyBlock(KeyBlock::new(source, node))),
1638 "snippet_block" => Some(TemplateNode::SnippetBlock(SnippetBlock::new(source, node))),
1639 "element" => Some(classify_element(source, node)),
1640 _ => None,
1641 }
1642}
1643
1644pub struct ChildIter<'src> {
1648 source: &'src str,
1649 cursor: TreeCursor<'src>,
1650 started: bool,
1651}
1652
1653impl<'src> ChildIter<'src> {
1654 fn new(source: &'src str, parent: Node<'src>) -> Self {
1655 Self {
1656 source,
1657 cursor: parent.walk(),
1658 started: false,
1659 }
1660 }
1661}
1662
1663fn is_structural_kind(kind: &str) -> bool {
1665 matches!(
1666 kind,
1667 "block_open"
1668 | "block_close"
1669 | "block_end"
1670 | "block_keyword"
1671 | "block_sigil"
1672 | "branch_kind"
1673 | "start_tag"
1674 | "end_tag"
1675 | "self_closing_tag"
1676 | "else_clause"
1677 | "else_if_clause"
1678 | "await_branch"
1679 | "await_pending"
1680 | "await_branch_children"
1681 | "snippet_name"
1682 | "snippet_parameters"
1683 | "snippet_type_parameters"
1684 | "pattern"
1685 | "expression_value"
1686 | "shorthand_kind"
1687 | "raw_text"
1688 )
1689}
1690
1691impl<'src> Iterator for ChildIter<'src> {
1692 type Item = TemplateNode<'src>;
1693
1694 fn next(&mut self) -> Option<TemplateNode<'src>> {
1695 loop {
1696 let moved = if self.started {
1697 self.cursor.goto_next_sibling()
1698 } else {
1699 self.started = true;
1700 self.cursor.goto_first_child()
1701 };
1702
1703 if !moved {
1704 return None;
1705 }
1706
1707 let node = self.cursor.node();
1708 if !node.is_named() {
1709 continue;
1710 }
1711
1712 if self.cursor.field_name().is_some() {
1715 continue;
1716 }
1717
1718 let kind = node.kind();
1719 if is_structural_kind(kind) {
1720 continue;
1721 }
1722
1723 if let Some(template_node) = classify_node(self.source, node) {
1724 return Some(template_node);
1725 }
1726 }
1727 }
1728}
1729
1730pub struct AttributeIter<'src> {
1734 source: &'src str,
1735 cursor: TreeCursor<'src>,
1736 started: bool,
1737}
1738
1739impl<'src> Iterator for AttributeIter<'src> {
1740 type Item = AttributeNode<'src>;
1741
1742 fn next(&mut self) -> Option<AttributeNode<'src>> {
1743 loop {
1744 let moved = if self.started {
1745 self.cursor.goto_next_sibling()
1746 } else {
1747 self.started = true;
1748 self.cursor.goto_first_child()
1749 };
1750
1751 if !moved {
1752 return None;
1753 }
1754
1755 let node = self.cursor.node();
1756 match node.kind() {
1757 "attribute" | "shorthand_attribute" | "attach_tag" => {
1758 return Some(AttributeNode::new(self.source, node));
1759 }
1760 _ => continue,
1761 }
1762 }
1763 }
1764}
1765
1766#[cfg(test)]
1767mod tests {
1768 use super::*;
1769 use crate::primitives::SourceId;
1770
1771 #[test]
1772 fn parses_svelte_cst_document() {
1773 let source = SourceText::new(SourceId::new(1), "<div>Hello</div>", None);
1774 let cst = parse_svelte(source).expect("expected tree-sitter CST parse to succeed");
1775
1776 assert!(!cst.root_kind().is_empty());
1777 assert!(cst.root_span().end.as_usize() >= cst.source.len());
1778 }
1779
1780 #[test]
1781 fn cst_contains_attribute_nodes() {
1782 let source = SourceText::new(SourceId::new(2), "<div class='foo'></div>", None);
1783 let cst = parse_svelte(source).expect("expected cst parse to succeed");
1784 let sexp = cst.root_node().to_sexp();
1785
1786 assert!(sexp.contains("(attribute"));
1787 assert!(sexp.contains("(attribute_name"));
1788 }
1789
1790 #[test]
1791 fn cst_style_directive_shape() {
1792 let source = SourceText::new(SourceId::new(3), "<div style:color={myColor}></div>", None);
1793 let cst = parse_svelte(source).expect("expected cst parse to succeed");
1794 let sexp = cst.root_node().to_sexp();
1795
1796 assert!(sexp.contains("attribute_directive"));
1797 assert!(sexp.contains("attribute_identifier"));
1798 }
1799
1800 #[test]
1801 fn cst_if_block_shape() {
1802 let source = SourceText::new(SourceId::new(4), "{#if foo}bar{/if}", None);
1803 let cst = parse_svelte(source).expect("expected cst parse to succeed");
1804 let sexp = cst.root_node().to_sexp();
1805
1806 assert!(sexp.contains("if_block"));
1807 assert!(sexp.contains("block_end"));
1808 }
1809
1810 #[test]
1811 fn cst_breaks_unterminated_tags_before_block_branches() {
1812 let source = SourceText::new(
1813 SourceId::new(5),
1814 "{#if true}\n\t<input>\n{:else}\n{/if}\n\n{#await true}\n\t<input>\n{:then f}\n{/await}",
1815 None,
1816 );
1817 let cst = parse_svelte(source).expect("expected cst parse to succeed");
1818 let sexp = cst.root_node().to_sexp();
1819
1820 assert!(sexp.matches("(else_clause").count() + sexp.matches("(await_branch").count() >= 2);
1821 }
1822
1823 #[test]
1824 fn cst_directive_and_debug_tag_shapes() {
1825 let source = SourceText::new(
1826 SourceId::new(6),
1827 "<div let:x style:color={c} transition:fade={t} animate:flip={a} use:act={u}></div>{@debug x, y}",
1828 None,
1829 );
1830 let cst = parse_svelte(source).expect("expected cst parse to succeed");
1831 let sexp = cst.root_node().to_sexp();
1832
1833 assert!(sexp.contains("attribute_name"));
1834 assert!(sexp.contains("debug_tag"));
1835 assert!(sexp.contains("expression_value"));
1836 }
1837
1838 #[test]
1839 fn cst_malformed_snippet_headers_report_error_shape() {
1840 let source = SourceText::new(SourceId::new(7), "{#snippet children()hi{/snippet}", None);
1841 let cst = parse_svelte(source).expect("expected cst parse to succeed");
1842 let sexp = cst.root_node().to_sexp();
1843 assert!(
1844 cst.has_error(),
1845 "expected malformed snippet header CST error"
1846 );
1847 assert!(sexp.contains("(snippet_name"));
1848
1849 let source = SourceText::new(SourceId::new(8), "{#snippet children(hi{/snippet}", None);
1850 let cst = parse_svelte(source).expect("expected cst parse to succeed");
1851 let sexp = cst.root_node().to_sexp();
1852 assert!(sexp.contains("(snippet_name"));
1853 assert!(sexp.contains("(snippet_parameters"));
1854 }
1855
1856 #[test]
1857 fn incremental_parse_matches_fresh_parse_after_insert() {
1858 let before_text = "<div>Hello</div>";
1859 let after_text = "<div>Hello {name}</div>";
1860 let before = SourceText::new(SourceId::new(9), before_text, None);
1861 let after = SourceText::new(SourceId::new(10), after_text, None);
1862
1863 let mut parser = CstParser::new()
1864 .configure(Language::Svelte)
1865 .expect("parser");
1866 let previous = parser.parse(before).expect("initial parse");
1867 let edit = CstEdit::insert(before_text, "<div>Hello".len(), " {name}");
1868
1869 let incremental = parser
1870 .parse_incremental(after, &previous, edit)
1871 .expect("incremental parse");
1872 let fresh = parse_svelte(after).expect("fresh parse");
1873
1874 assert_eq!(
1875 incremental.root_node().to_sexp(),
1876 fresh.root_node().to_sexp()
1877 );
1878 }
1879
1880 #[test]
1881 fn document_apply_edit_keeps_tree_reusable() {
1882 let before_text = "<div>Hello</div>";
1883 let after_text = "<div>Hi</div>";
1884 let before = SourceText::new(SourceId::new(11), before_text, None);
1885 let after = SourceText::new(SourceId::new(12), after_text, None);
1886
1887 let mut parser = CstParser::new()
1888 .configure(Language::Svelte)
1889 .expect("parser");
1890 let mut previous = parser.parse(before).expect("initial parse");
1891 let edit = CstEdit::replace(before_text, "<div>".len(), "<div>Hello".len(), "Hi");
1892 previous.apply_edit(edit.clone());
1893
1894 let incremental = parser
1895 .parse_incremental(after, &previous, edit)
1896 .expect("incremental parse");
1897 let fresh = parse_svelte(after).expect("fresh parse");
1898
1899 assert_eq!(
1900 incremental.root_node().to_sexp(),
1901 fresh.root_node().to_sexp()
1902 );
1903 }
1904
1905 fn parse_source(text: &str) -> Document<'_> {
1908 let source = SourceText::new(SourceId::new(100), text, None);
1909 parse_svelte(source).expect("parse")
1910 }
1911
1912 #[test]
1913 fn wrapper_element_name_via_field() {
1914 let text = r#"<div class="foo">hello</div>"#;
1915 let doc = parse_source(text);
1916 let root = Root::new(text, doc.root_node());
1917 let children: Vec<_> = root.children().collect();
1918 assert_eq!(children.len(), 1);
1919 let TemplateNode::RegularElement(el) = &children[0] else {
1920 panic!("expected RegularElement");
1921 };
1922 assert_eq!(el.name(), "div");
1923 assert!(!el.is_self_closing());
1924 assert!(el.has_end_tag());
1925 }
1926
1927 #[test]
1928 fn wrapper_self_closing_element() {
1929 let text = r#"<br />"#;
1930 let doc = parse_source(text);
1931 let root = Root::new(text, doc.root_node());
1932 let children: Vec<_> = root.children().collect();
1933 assert_eq!(children.len(), 1);
1934 let TemplateNode::RegularElement(el) = &children[0] else {
1935 panic!("expected RegularElement");
1936 };
1937 assert_eq!(el.name(), "br");
1938 assert!(el.is_self_closing());
1939 }
1940
1941 #[test]
1942 fn wrapper_component_classification() {
1943 let text = r#"<Button>Click</Button>"#;
1944 let doc = parse_source(text);
1945 let root = Root::new(text, doc.root_node());
1946 let children: Vec<_> = root.children().collect();
1947 assert!(matches!(&children[0], TemplateNode::Component(el) if el.name() == "Button"));
1948 }
1949
1950 #[test]
1951 fn wrapper_svelte_element_classification() {
1952 let text = r#"<svelte:head><title>Hi</title></svelte:head>"#;
1953 let doc = parse_source(text);
1954 let root = Root::new(text, doc.root_node());
1955 let children: Vec<_> = root.children().collect();
1956 assert!(matches!(&children[0], TemplateNode::SvelteHead(_)));
1957 }
1958
1959 #[test]
1960 fn wrapper_if_block() {
1961 let text = r#"{#if visible}<p>Hello</p>{/if}"#;
1962 let doc = parse_source(text);
1963 let root = Root::new(text, doc.root_node());
1964 let children: Vec<_> = root.children().collect();
1965 assert_eq!(children.len(), 1);
1966 let TemplateNode::IfBlock(block) = &children[0] else {
1967 panic!("expected IfBlock");
1968 };
1969 assert!(block.test_node().is_some());
1970 let consequent: Vec<_> = block.consequent().collect();
1971 assert!(!consequent.is_empty());
1972 }
1973
1974 #[test]
1975 fn wrapper_each_block() {
1976 let text = r#"{#each items as item}<li>{item}</li>{/each}"#;
1977 let doc = parse_source(text);
1978 let root = Root::new(text, doc.root_node());
1979 let children: Vec<_> = root.children().collect();
1980 assert_eq!(children.len(), 1);
1981 let TemplateNode::EachBlock(block) = &children[0] else {
1982 panic!("expected EachBlock");
1983 };
1984 assert!(block.expression_node().is_some());
1985 let body: Vec<_> = block.body().collect();
1986 assert!(!body.is_empty());
1987 }
1988
1989 #[test]
1990 fn wrapper_text_node() {
1991 let text = r#"<div>hello world</div>"#;
1992 let doc = parse_source(text);
1993 let root = Root::new(text, doc.root_node());
1994 let children: Vec<_> = root.children().collect();
1995 let TemplateNode::RegularElement(el) = &children[0] else {
1996 panic!("expected element");
1997 };
1998 let inner: Vec<_> = el.children().collect();
1999 assert_eq!(inner.len(), 1);
2000 let TemplateNode::Text(t) = &inner[0] else {
2001 panic!("expected text");
2002 };
2003 assert_eq!(t.raw(), "hello world");
2004 }
2005
2006 #[test]
2007 fn wrapper_attributes() {
2008 let text = r#"<div class="foo" id="bar">x</div>"#;
2009 let doc = parse_source(text);
2010 let root = Root::new(text, doc.root_node());
2011 let children: Vec<_> = root.children().collect();
2012 let TemplateNode::RegularElement(el) = &children[0] else {
2013 panic!("expected element");
2014 };
2015 let attrs: Vec<_> = el.attributes().collect();
2016 assert_eq!(attrs.len(), 2);
2017 assert_eq!(attrs[0].name(), "class");
2018 assert_eq!(attrs[1].name(), "id");
2019 }
2020
2021 #[test]
2022 fn wrapper_expression_tag() {
2023 let text = r#"<p>{count}</p>"#;
2024 let doc = parse_source(text);
2025 let root = Root::new(text, doc.root_node());
2026 let children: Vec<_> = root.children().collect();
2027 let TemplateNode::RegularElement(el) = &children[0] else {
2028 panic!("expected element");
2029 };
2030 let inner: Vec<_> = el.children().collect();
2031 assert!(matches!(&inner[0], TemplateNode::ExpressionTag(_)));
2032 }
2033
2034 #[test]
2035 fn wrapper_snippet_block() {
2036 let text = r#"{#snippet btn(text)}<button>{text}</button>{/snippet}"#;
2037 let doc = parse_source(text);
2038 let root = Root::new(text, doc.root_node());
2039 let children: Vec<_> = root.children().collect();
2040 let TemplateNode::SnippetBlock(block) = &children[0] else {
2041 panic!("expected SnippetBlock");
2042 };
2043 assert_eq!(block.name(), "btn");
2044 assert!(block.parameters_node().is_some());
2045 }
2046
2047 #[test]
2048 fn wrapper_child_iter_skips_structural_nodes() {
2049 let text = r#"{#if x}<div>A</div>{:else}<span>B</span>{/if}"#;
2051 let doc = parse_source(text);
2052 let root = Root::new(text, doc.root_node());
2053 let children: Vec<_> = root.children().collect();
2054 let TemplateNode::IfBlock(block) = &children[0] else {
2055 panic!("expected IfBlock");
2056 };
2057 let consequent: Vec<_> = block.consequent().collect();
2059 assert!(consequent.iter().all(|n| matches!(n, TemplateNode::RegularElement(_))));
2060 }
2061
2062 #[test]
2065 fn attribute_directive_accessors() {
2066 let text = r#"<div class:active={isActive} bind:value={name} style:color="red" />"#;
2067 let doc = parse_source(text);
2068 let root = Root::new(text, doc.root_node());
2069 let children: Vec<_> = root.children().collect();
2070 let TemplateNode::RegularElement(el) = &children[0] else {
2071 panic!("expected element");
2072 };
2073 let attrs: Vec<_> = el.attributes().collect();
2074 assert_eq!(attrs.len(), 3);
2075
2076 assert!(attrs[0].is_class_directive());
2078 assert_eq!(attrs[0].directive_prefix(), Some("class"));
2079 assert_eq!(attrs[0].directive_name(), Some("active"));
2080
2081 assert!(attrs[1].is_bind_directive());
2083 assert_eq!(attrs[1].directive_prefix(), Some("bind"));
2084 assert_eq!(attrs[1].directive_name(), Some("value"));
2085
2086 assert!(attrs[2].is_style_directive());
2088 assert_eq!(attrs[2].directive_prefix(), Some("style"));
2089 assert_eq!(attrs[2].directive_name(), Some("color"));
2090 assert_eq!(attrs[2].static_value(), Some("red"));
2091 }
2092
2093 #[test]
2094 fn attribute_static_and_expression_values() {
2095 let text = r#"<div class="foo" id={myId} />"#;
2096 let doc = ParsedDocument::parse(text).unwrap();
2097 let children: Vec<_> = doc.root().children().collect();
2098 let TemplateNode::RegularElement(el) = &children[0] else {
2099 panic!("expected element");
2100 };
2101 let attrs: Vec<_> = el.attributes().collect();
2102 assert_eq!(attrs.len(), 2);
2103
2104 assert_eq!(attrs[0].static_value(), Some("foo"));
2106 assert!(!attrs[0].has_expression_value());
2107
2108 assert!(attrs[1].has_expression_value());
2110 let expr = attrs[1].value_expression(doc.expressions());
2111 assert!(expr.is_some(), "id expression should be cached");
2112 }
2113
2114 #[test]
2115 fn attribute_spread_detection() {
2116 let text = r#"<div {...props} {shorthand} />"#;
2117 let doc = parse_source(text);
2118 let root = Root::new(text, doc.root_node());
2119 let children: Vec<_> = root.children().collect();
2120 let TemplateNode::RegularElement(el) = &children[0] else {
2121 panic!("expected element");
2122 };
2123 let attrs: Vec<_> = el.attributes().collect();
2124 assert_eq!(attrs.len(), 2);
2125
2126 assert!(attrs[0].has_shorthand_child());
2128 assert!(attrs[1].has_shorthand_child());
2130 }
2131
2132 #[test]
2133 fn walk_visits_all_descendants() {
2134 let text = r#"<div><p>A</p><span>{x}</span></div>{#if y}<b>B</b>{/if}"#;
2135 let doc = parse_source(text);
2136 let root = Root::new(text, doc.root_node());
2137 let mut count = 0;
2138 root.walk(&mut |_| count += 1);
2139 assert!(count >= 7, "expected at least 7 descendants, got {count}");
2141 }
2142
2143 #[test]
2146 fn parsed_document_basic() {
2147 let doc = ParsedDocument::parse("<div>{count}</div>").unwrap();
2148 assert_eq!(doc.root().children().count(), 1);
2149 assert!(!doc.expressions().is_empty());
2150 }
2151
2152 #[test]
2153 fn parsed_document_expression_cache() {
2154 let doc = ParsedDocument::parse("{#if visible}<p>Hello</p>{/if}").unwrap();
2155 let children: Vec<_> = doc.root().children().collect();
2156 let TemplateNode::IfBlock(block) = &children[0] else {
2157 panic!("expected IfBlock");
2158 };
2159 let expr = block.test_expression(doc.expressions());
2160 assert!(expr.is_some(), "test expression should be cached");
2161 }
2162
2163 #[test]
2164 fn parsed_document_each_expression() {
2165 let doc = ParsedDocument::parse("{#each items as item}<li>{item}</li>{/each}").unwrap();
2166 let children: Vec<_> = doc.root().children().collect();
2167 let TemplateNode::EachBlock(block) = &children[0] else {
2168 panic!("expected EachBlock");
2169 };
2170 let expr = block.expression(doc.expressions());
2171 assert!(expr.is_some(), "each expression should be cached");
2172 }
2173
2174 #[test]
2175 fn parsed_document_expression_tag() {
2176 let doc = ParsedDocument::parse("<p>{count + 1}</p>").unwrap();
2177 let children: Vec<_> = doc.root().children().collect();
2178 let TemplateNode::RegularElement(el) = &children[0] else {
2179 panic!("expected element");
2180 };
2181 let inner: Vec<_> = el.children().collect();
2182 let TemplateNode::ExpressionTag(tag) = &inner[0] else {
2183 panic!("expected ExpressionTag");
2184 };
2185 let expr = tag.expression(doc.expressions());
2186 assert!(expr.is_some(), "expression tag should be cached");
2187 }
2188}