Skip to content

Commit c61370c

Browse files
committed
Improve renaming of multiline self-assignments and refactor
1 parent 2ba4128 commit c61370c

File tree

1 file changed

+98
-89
lines changed

1 file changed

+98
-89
lines changed

server/src/analyser.ts

Lines changed: 98 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)