@@ -101,25 +101,16 @@ pub struct TreesitterContext<'a> {
101101 pub text : & ' a str ,
102102 pub position : usize ,
103103
104- /// If the cursor is on a node that uses dot notation
105- /// to specify an alias or schema, this will hold the schema's or
106- /// alias's name.
104+ /// Tuple containing up to two qualifiers for identifier-node under the cursor: (head, tail)
107105 ///
108- /// Here, `auth` is a schema name:
109- /// ```sql
110- /// select * from auth.users;
111- /// ```
112- ///
113- /// Here, `u` is an alias name:
114- /// ```sql
115- /// select
116- /// *
117- /// from
118- /// auth.users u
119- /// left join identities i
120- /// on u.id = i.user_id;
121- /// ```
122- pub schema_or_alias_name : Option < String > ,
106+ /// The qualifiers represent different "parents" based on the context, for example:
107+ /// - `column` -> (None, None)
108+ /// - `table.column` -> (None, Some("table"))
109+ /// - `alias.column` -> (None, Some("alias"))
110+ /// - `schema.table` -> (None, Some("schema"))
111+ /// - `schema.table.column` -> (Some("schema"), Some("table"))
112+ /// - `table` -> (None, None)
113+ pub identifier_qualifiers : ( Option < String > , Option < String > ) ,
123114
124115 pub wrapping_clause_type : Option < WrappingClause < ' a > > ,
125116
@@ -140,7 +131,7 @@ impl<'a> TreesitterContext<'a> {
140131 text : params. text ,
141132 position : usize:: from ( params. position ) ,
142133 node_under_cursor : None ,
143- schema_or_alias_name : None ,
134+ identifier_qualifiers : ( None , None ) ,
144135 wrapping_clause_type : None ,
145136 wrapping_node_kind : None ,
146137 wrapping_statement_range : None ,
@@ -333,10 +324,20 @@ impl<'a> TreesitterContext<'a> {
333324 let content = self . get_ts_node_content ( & current_node) ;
334325 if let Some ( txt) = content {
335326 let parts: Vec < & str > = txt. split ( '.' ) . collect ( ) ;
336- // we do not want to set it if we're on the schema or alias node itself
337- let is_on_schema_node = start + parts[ 0 ] . len ( ) >= self . position ;
338- if parts. len ( ) == 2 && !is_on_schema_node {
339- self . schema_or_alias_name = Some ( parts[ 0 ] . to_string ( ) ) ;
327+ // we do not want to set it if we're on the first qualifier itself
328+ let is_on_first_part = start + parts[ 0 ] . len ( ) >= self . position ;
329+
330+ if parts. len ( ) == 2 && !is_on_first_part {
331+ self . identifier_qualifiers = ( None , Some ( parts[ 0 ] . to_string ( ) ) ) ;
332+ } else if parts. len ( ) == 3 && !is_on_first_part {
333+ let is_on_second_part =
334+ start + parts[ 0 ] . len ( ) + 1 + parts[ 1 ] . len ( ) >= self . position ;
335+ if !is_on_second_part {
336+ self . identifier_qualifiers =
337+ ( Some ( parts[ 0 ] . to_string ( ) ) , Some ( parts[ 1 ] . to_string ( ) ) ) ;
338+ } else {
339+ self . identifier_qualifiers = ( None , Some ( parts[ 0 ] . to_string ( ) ) ) ;
340+ }
340341 }
341342 }
342343 }
@@ -778,6 +779,37 @@ impl<'a> TreesitterContext<'a> {
778779 pub fn has_mentioned_columns ( & self ) -> bool {
779780 !self . mentioned_columns . is_empty ( )
780781 }
782+
783+ /// Returns the head qualifier (leftmost), sanitized (quotes removed)
784+ /// For `schema.table.<column>`: returns `Some("schema")`
785+ /// For `table.<column>`: returns `None`
786+ pub fn head_qualifier_sanitized ( & self ) -> Option < String > {
787+ self . identifier_qualifiers
788+ . 0
789+ . as_ref ( )
790+ . map ( |s| s. replace ( '"' , "" ) )
791+ }
792+
793+ /// Returns the tail qualifier (rightmost), sanitized (quotes removed)
794+ /// For `schema.table.<column>`: returns `Some("table")`
795+ /// For `table.<column>`: returns `Some("table")`
796+ pub fn tail_qualifier_sanitized ( & self ) -> Option < String > {
797+ self . identifier_qualifiers
798+ . 1
799+ . as_ref ( )
800+ . map ( |s| s. replace ( '"' , "" ) )
801+ }
802+
803+ /// Returns true if there is at least one qualifier present
804+ pub fn has_any_qualifier ( & self ) -> bool {
805+ match self . identifier_qualifiers {
806+ ( Some ( _) , Some ( _) ) => true ,
807+ ( None , Some ( _) ) => true ,
808+ ( None , None ) => false ,
809+
810+ ( Some ( _) , None ) => unreachable ! ( ) ,
811+ }
812+ }
781813}
782814
783815#[ cfg( test) ]
@@ -926,7 +958,7 @@ mod tests {
926958 let ctx = TreesitterContext :: new ( params) ;
927959
928960 assert_eq ! (
929- ctx. schema_or_alias_name ,
961+ ctx. identifier_qualifiers . 1 ,
930962 expected_schema. map( |f| f. to_string( ) )
931963 ) ;
932964 }
0 commit comments