@@ -741,4 +741,115 @@ mod tests {
741741 insta:: assert_yaml_snapshot!( ast) ;
742742 }
743743 }
744+
745+ mod span_tracking {
746+ use super :: * ;
747+
748+ #[ test]
749+ fn test_span_tracking ( ) {
750+ let mut tokens = TokenStream :: default ( ) ;
751+ // First line: "Hello\n"
752+ tokens. add_token ( Token :: new ( TokenType :: Text ( "Hello" . to_string ( ) ) , 0 , Some ( 0 ) ) ) ;
753+ tokens. add_token ( Token :: new ( TokenType :: Newline , 0 , Some ( 5 ) ) ) ;
754+ // Second line: "{{ name }}\n"
755+ tokens. add_token ( Token :: new (
756+ TokenType :: DjangoVariable ( "name" . to_string ( ) ) ,
757+ 1 ,
758+ Some ( 6 ) ,
759+ ) ) ;
760+ tokens. add_token ( Token :: new ( TokenType :: Newline , 1 , Some ( 16 ) ) ) ;
761+ // Third line: "{% if condition %}\n"
762+ tokens. add_token ( Token :: new (
763+ TokenType :: DjangoBlock ( "if condition" . to_string ( ) ) ,
764+ 2 ,
765+ Some ( 17 ) ,
766+ ) ) ;
767+ tokens. add_token ( Token :: new ( TokenType :: Newline , 2 , Some ( 34 ) ) ) ;
768+ // Fourth line: " Content\n"
769+ tokens. add_token ( Token :: new ( TokenType :: Whitespace ( 2 ) , 3 , Some ( 35 ) ) ) ;
770+ tokens. add_token ( Token :: new (
771+ TokenType :: Text ( "Content" . to_string ( ) ) ,
772+ 3 ,
773+ Some ( 37 ) ,
774+ ) ) ;
775+ tokens. add_token ( Token :: new ( TokenType :: Newline , 3 , Some ( 44 ) ) ) ;
776+ // Fifth line: "{% endif %}"
777+ tokens. add_token ( Token :: new (
778+ TokenType :: DjangoBlock ( "endif" . to_string ( ) ) ,
779+ 4 ,
780+ Some ( 45 ) ,
781+ ) ) ;
782+ tokens. finalize ( 4 ) ;
783+
784+ let mut parser = Parser :: new ( tokens) ;
785+ let ast = parser. parse ( ) . unwrap ( ) ;
786+
787+ // Verify line offsets
788+ let offsets = ast. line_offsets ( ) ;
789+ assert_eq ! ( offsets. position_to_line_col( 0 ) , ( 0 , 0 ) ) ; // Start of first line
790+ assert_eq ! ( offsets. position_to_line_col( 6 ) , ( 1 , 0 ) ) ; // Start of second line
791+ assert_eq ! ( offsets. position_to_line_col( 17 ) , ( 2 , 0 ) ) ; // Start of third line
792+ assert_eq ! ( offsets. position_to_line_col( 35 ) , ( 3 , 0 ) ) ; // Start of fourth line
793+ assert_eq ! ( offsets. position_to_line_col( 45 ) , ( 4 , 0 ) ) ; // Start of fifth line
794+
795+ // Verify node spans
796+ let nodes = ast. nodes ( ) ;
797+
798+ // First node: Text "Hello"
799+ if let Node :: Text { content, span } = & nodes[ 0 ] {
800+ assert_eq ! ( content, "Hello" ) ;
801+ assert_eq ! ( * span. start( ) , 0 ) ;
802+ assert_eq ! ( * span. length( ) , 5 ) ;
803+ } else {
804+ panic ! ( "Expected Text node" ) ;
805+ }
806+
807+ // Second node: Variable "name"
808+ if let Node :: Variable {
809+ bits,
810+ filters,
811+ span,
812+ } = & nodes[ 1 ]
813+ {
814+ assert_eq ! ( bits[ 0 ] , "name" ) ;
815+ assert ! ( filters. is_empty( ) ) ;
816+ assert_eq ! ( * span. start( ) , 6 ) ;
817+ assert_eq ! ( * span. length( ) , 4 ) ;
818+ } else {
819+ panic ! ( "Expected Variable node" ) ;
820+ }
821+
822+ // Third node: Block "if condition"
823+ if let Node :: Block {
824+ name,
825+ bits,
826+ children,
827+ span,
828+ tag_span,
829+ ..
830+ } = & nodes[ 2 ]
831+ {
832+ assert_eq ! ( name, "if" ) ;
833+ assert_eq ! ( bits[ 1 ] , "condition" ) ;
834+ assert_eq ! ( * span. start( ) , 17 ) ;
835+ assert_eq ! ( * tag_span. start( ) , 17 ) ;
836+ assert_eq ! ( * tag_span. length( ) , 11 ) ;
837+
838+ // Check content node
839+ if let Some ( child_nodes) = children {
840+ if let Node :: Text { content, span } = & child_nodes[ 0 ] {
841+ assert_eq ! ( content. trim( ) , "Content" ) ;
842+ assert_eq ! ( * span. start( ) , 37 ) ;
843+ assert_eq ! ( * span. length( ) , 7 ) ;
844+ } else {
845+ panic ! ( "Expected Text node as child" ) ;
846+ }
847+ } else {
848+ panic ! ( "Expected children in if block" ) ;
849+ }
850+ } else {
851+ panic ! ( "Expected Block node" ) ;
852+ }
853+ }
854+ }
744855}
0 commit comments