@@ -589,114 +589,123 @@ export default class Analyzer {
589589 const startPosition = start
590590 ? { row : start . line , column : start . character }
591591 : baseNode . startPosition
592- const endPosition = end
593- ? { row : end . line + 1 , column : end . character + 1 }
594- : baseNode . endPosition
592+ // Special handling for cases like
593+ // var="
594+ // $var
595+ // "
596+ let endPosition
597+ if ( end ) {
598+ const endNode = baseNode . descendantForPosition ( {
599+ row : end . line ,
600+ column : end . character ,
601+ } )
602+ const parentNode =
603+ endNode . type === 'variable_name'
604+ ? TreeSitterUtil . findParentOfType ( endNode , 'variable_assignment' )
605+ : null
606+ parentNode && ( { endPosition } = parentNode )
607+ }
595608
596609 const ignoredRanges : LSP . Range [ ] = [ ]
597- const filter =
598- kind === LSP . SymbolKind . Variable
599- ? ( n : Parser . SyntaxNode ) => {
600- if ( n . text !== word ) {
601- return false
602- }
610+ const filterVariables = ( n : Parser . SyntaxNode ) => {
611+ if ( n . text !== word ) {
612+ return false
613+ }
603614
604- const parentScope = this . parentScope ( n )
605- const definition = TreeSitterUtil . findParentOfType (
606- n ,
607- parentScope ?. type === 'function_definition'
608- ? 'declaration_command'
609- : 'variable_assignment' ,
610- )
611- const definedVariable = definition ?. descendantsOfType ( 'variable_name' ) ?. at ( 0 )
612-
613- // Special handling for var="$var" cases
614- if ( definedVariable ?. text === word ) {
615- if ( n . equals ( definedVariable ) ) {
616- // `end?.line` is assumed to be the same as the variable's
617- // declaration line.
618- if ( definition ?. startPosition . row === end ?. line ) {
619- return false
620- }
621- } else {
622- // `start?.line` is assumed to be the same as the variable's
623- // original declaration line.
624- if ( definition ?. startPosition . row === start ?. line ) {
625- return false
626- }
627-
628- // Returning true here is a good enough heuristic for most
629- // cases. It breaks down when redeclaration happens in multiple
630- // nested scopes, handling those more complex situations can be
631- // done later on if use cases arise.
632- return true
633- }
634- }
615+ const parentScope = this . parentScope ( n )
616+ const definition = TreeSitterUtil . findParentOfType (
617+ n ,
618+ parentScope ?. type === 'function_definition'
619+ ? 'declaration_command'
620+ : 'variable_assignment' ,
621+ )
622+ const definedVariable = definition ?. descendantsOfType ( 'variable_name' ) ?. at ( 0 )
623+
624+ // Special handling for var="$var" cases
625+ if ( definedVariable ?. text === word ) {
626+ if ( n . equals ( definedVariable ) ) {
627+ // `end?.line` is assumed to be the same as the variable's declaration
628+ // line; handles cases where $var should be renamed but var shouldn't.
629+ if ( definition ?. startPosition . row === end ?. line ) {
630+ return false
631+ }
632+ } else {
633+ // `start?.line` is assumed to be the same as the variable's original
634+ // declaration line; handles cases where var should be renamed but
635+ // $var shouldn't.
636+ if ( definition ?. startPosition . row === start ?. line ) {
637+ return false
638+ }
635639
636- if ( ! parentScope || baseNode . equals ( parentScope ) ) {
637- return true
638- }
640+ // Returning true here is a good enough heuristic for most cases. It
641+ // breaks down when redeclaration happens in multiple nested scopes,
642+ // handling those more complex situations can be done later on if use
643+ // cases arise.
644+ return true
645+ }
646+ }
639647
640- const includeDeclaration = ! ignoredRanges . some (
641- ( r ) => n . startPosition . row > r . start . line && n . endPosition . row < r . end . line ,
642- )
648+ if ( ! parentScope || baseNode . equals ( parentScope ) ) {
649+ return true
650+ }
643651
644- if ( ! definition ) {
645- return includeDeclaration
646- }
652+ const includeDeclaration = ! ignoredRanges . some (
653+ ( r ) => n . startPosition . row > r . start . line && n . endPosition . row < r . end . line ,
654+ )
647655
648- const isLocalDefinition =
649- definedVariable ?. text === word &&
650- ( parentScope . type === 'subshell'
651- ? ! ! definition
652- : [ 'local' , 'declare' , 'typeset' ] . includes (
653- definition . firstChild ?. text as any ,
654- ) )
655- if ( isLocalDefinition ) {
656- if ( includeDeclaration ) {
657- ignoredRanges . push ( TreeSitterUtil . range ( parentScope ) )
658- }
656+ if ( ! definition ) {
657+ return includeDeclaration
658+ }
659659
660- return false
661- }
660+ const isLocalDefinition =
661+ definedVariable ?. text === word &&
662+ ( parentScope . type === 'subshell'
663+ ? ! ! definition
664+ : [ 'local' , 'declare' , 'typeset' ] . includes ( definition . firstChild ?. text as any ) )
665+ if ( isLocalDefinition ) {
666+ if ( includeDeclaration ) {
667+ ignoredRanges . push ( TreeSitterUtil . range ( parentScope ) )
668+ }
662669
663- return true
664- }
665- : ( n : Parser . SyntaxNode ) => {
666- const text =
667- n . type === 'function_definition' ? n . firstNamedChild ?. text : n . text
668- if ( text !== word ) {
669- return false
670- }
670+ return false
671+ }
671672
672- const parentScope = TreeSitterUtil . findParentOfType ( n , 'subshell' )
673+ return true
674+ }
675+ const filterFunctions = ( n : Parser . SyntaxNode ) => {
676+ const text = n . type === 'function_definition' ? n . firstNamedChild ?. text : n . text
677+ if ( text !== word ) {
678+ return false
679+ }
673680
674- if ( ! parentScope || baseNode . equals ( parentScope ) ) {
675- return true
676- }
681+ const parentScope = TreeSitterUtil . findParentOfType ( n , 'subshell' )
677682
678- const includeDeclaration = ! ignoredRanges . some (
679- ( r ) => n . startPosition . row > r . start . line && n . endPosition . row < r . end . line ,
680- )
683+ if ( ! parentScope || baseNode . equals ( parentScope ) ) {
684+ return true
685+ }
681686
682- if ( n . type === 'command_name' ) {
683- return includeDeclaration
684- }
687+ const includeDeclaration = ! ignoredRanges . some (
688+ ( r ) => n . startPosition . row > r . start . line && n . endPosition . row < r . end . line ,
689+ )
685690
686- if ( n . type === 'function_definition' ) {
687- if ( includeDeclaration ) {
688- ignoredRanges . push ( TreeSitterUtil . range ( parentScope ) )
689- }
691+ if ( n . type === 'command_name' ) {
692+ return includeDeclaration
693+ }
690694
691- return false
692- }
695+ if ( n . type === 'function_definition' ) {
696+ if ( includeDeclaration ) {
697+ ignoredRanges . push ( TreeSitterUtil . range ( parentScope ) )
698+ }
693699
694- return true
695- }
700+ return false
701+ }
702+
703+ return true
704+ }
696705
697706 return baseNode
698707 . descendantsOfType ( typeOfDescendants , startPosition , endPosition )
699- . filter ( filter )
708+ . filter ( kind === LSP . SymbolKind . Variable ? filterVariables : filterFunctions )
700709 . map ( ( n ) => {
701710 if ( n . type === 'function_definition' && n . firstNamedChild ) {
702711 return TreeSitterUtil . range ( n . firstNamedChild )
0 commit comments