Skip to content

Commit 20bf4fc

Browse files
authored
Cache name table on SourceFile (#2363)
1 parent 20cf78d commit 20bf4fc

File tree

5 files changed

+58
-53
lines changed

5 files changed

+58
-53
lines changed

internal/ast/ast.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10801,6 +10801,8 @@ type SourceFile struct {
1080110801
tokenFactory *NodeFactory
1080210802
declarationMapMu sync.Mutex
1080310803
declarationMap map[string][]*Node
10804+
nameTableOnce sync.Once
10805+
nameTable map[string]int
1080410806
}
1080510807

1080610808
func (f *NodeFactory) NewSourceFile(opts SourceFileParseOptions, text string, statements *NodeList, endOfFileToken *TokenNode) *Node {
@@ -10944,6 +10946,39 @@ func (node *SourceFile) ECMALineMap() []core.TextPos {
1094410946
return lineMap
1094510947
}
1094610948

10949+
// GetNameTable returns a map of all names in the file to their positions.
10950+
// If the name appears more than once, the value is -1.
10951+
func (file *SourceFile) GetNameTable() map[string]int {
10952+
file.nameTableOnce.Do(func() {
10953+
nameTable := make(map[string]int, file.IdentifierCount)
10954+
10955+
var walk func(node *Node) bool
10956+
walk = func(node *Node) bool {
10957+
if IsIdentifier(node) && !isTagName(node) && node.Text() != "" ||
10958+
IsStringOrNumericLiteralLike(node) && literalIsName(node) ||
10959+
IsPrivateIdentifier(node) {
10960+
text := node.Text()
10961+
if _, ok := nameTable[text]; ok {
10962+
nameTable[text] = -1
10963+
} else {
10964+
nameTable[text] = node.Pos()
10965+
}
10966+
}
10967+
10968+
node.ForEachChild(walk)
10969+
jsdocNodes := node.JSDoc(file)
10970+
for _, jsdoc := range jsdocNodes {
10971+
jsdoc.ForEachChild(walk)
10972+
}
10973+
return false
10974+
}
10975+
file.ForEachChild(walk)
10976+
10977+
file.nameTable = nameTable
10978+
})
10979+
return file.nameTable
10980+
}
10981+
1094710982
func (node *SourceFile) IsBound() bool {
1094810983
return node.isBound.Load()
1094910984
}

internal/ast/utilities.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4159,3 +4159,24 @@ func GetRestParameterElementType(node *ParameterDeclarationNode) *Node {
41594159
}
41604160
return nil
41614161
}
4162+
4163+
func isTagName(node *Node) bool {
4164+
return node.Parent != nil && IsJSDocTag(node.Parent) && node.Parent.TagName() == node
4165+
}
4166+
4167+
// We want to store any numbers/strings if they were a name that could be
4168+
// related to a declaration. So, if we have 'import x = require("something")'
4169+
// then we want 'something' to be in the name table. Similarly, if we have
4170+
// "a['propname']" then we want to store "propname" in the name table.
4171+
func literalIsName(node *Node) bool {
4172+
return IsDeclarationName(node) ||
4173+
node.Parent.Kind == KindExternalModuleReference ||
4174+
isArgumentOfElementAccessExpression(node) ||
4175+
IsLiteralComputedPropertyDeclarationName(node)
4176+
}
4177+
4178+
func isArgumentOfElementAccessExpression(node *Node) bool {
4179+
return node != nil && node.Parent != nil &&
4180+
node.Parent.Kind == KindElementAccessExpression &&
4181+
node.Parent.AsElementAccessExpression().ArgumentExpression == node
4182+
}

internal/ls/completions.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3582,7 +3582,7 @@ func (l *LanguageService) getJSCompletionEntries(
35823582
uniqueNames *collections.Set[string],
35833583
sortedEntries []*lsproto.CompletionItem,
35843584
) []*lsproto.CompletionItem {
3585-
nameTable := getNameTable(file)
3585+
nameTable := file.GetNameTable()
35863586
for name, pos := range nameTable {
35873587
// Skip identifiers produced only from the current location
35883588
if pos == position {

internal/ls/findallreferences.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2391,7 +2391,7 @@ func (state *refState) forEachRelatedSymbol(
23912391

23922392
// Search for all occurrences of an identifier in a source file (and filter out the ones that match).
23932393
func (state *refState) searchForName(sourceFile *ast.SourceFile, search *refSearch) {
2394-
if _, ok := getNameTable(sourceFile)[search.escapedText]; ok {
2394+
if _, ok := sourceFile.GetNameTable()[search.escapedText]; ok {
23952395
state.getReferencesInSourceFile(sourceFile, search, true /*addReferencesHere*/)
23962396
}
23972397
}

internal/ls/utilities.go

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -452,47 +452,6 @@ func isSeparator(node *ast.Node, candidate *ast.Node) bool {
452452
return candidate != nil && node.Parent != nil && (candidate.Kind == ast.KindCommaToken || (candidate.Kind == ast.KindSemicolonToken && node.Parent.Kind == ast.KindObjectLiteralExpression))
453453
}
454454

455-
// Returns a map of all names in the file to their positions.
456-
// !!! cache this
457-
func getNameTable(file *ast.SourceFile) map[string]int {
458-
nameTable := make(map[string]int)
459-
var walk func(node *ast.Node) bool
460-
461-
walk = func(node *ast.Node) bool {
462-
if ast.IsIdentifier(node) && !isTagName(node) && node.Text() != "" ||
463-
ast.IsStringOrNumericLiteralLike(node) && literalIsName(node) ||
464-
ast.IsPrivateIdentifier(node) {
465-
text := node.Text()
466-
if _, ok := nameTable[text]; ok {
467-
nameTable[text] = -1
468-
} else {
469-
nameTable[text] = node.Pos()
470-
}
471-
}
472-
473-
node.ForEachChild(walk)
474-
jsdocNodes := node.JSDoc(file)
475-
for _, jsdoc := range jsdocNodes {
476-
jsdoc.ForEachChild(walk)
477-
}
478-
return false
479-
}
480-
481-
file.ForEachChild(walk)
482-
return nameTable
483-
}
484-
485-
// We want to store any numbers/strings if they were a name that could be
486-
// related to a declaration. So, if we have 'import x = require("something")'
487-
// then we want 'something' to be in the name table. Similarly, if we have
488-
// "a['propname']" then we want to store "propname" in the name table.
489-
func literalIsName(node *ast.NumericOrStringLikeLiteral) bool {
490-
return ast.IsDeclarationName(node) ||
491-
node.Parent.Kind == ast.KindExternalModuleReference ||
492-
isArgumentOfElementAccessExpression(node) ||
493-
ast.IsLiteralComputedPropertyDeclarationName(node)
494-
}
495-
496455
func isLiteralNameOfPropertyDeclarationOrIndexAccess(node *ast.Node) bool {
497456
// utilities
498457
switch node.Parent.Kind {
@@ -524,12 +483,6 @@ func isObjectBindingElementWithoutPropertyName(bindingElement *ast.Node) bool {
524483
bindingElement.PropertyName() == nil
525484
}
526485

527-
func isArgumentOfElementAccessExpression(node *ast.Node) bool {
528-
return node != nil && node.Parent != nil &&
529-
node.Parent.Kind == ast.KindElementAccessExpression &&
530-
node.Parent.AsElementAccessExpression().ArgumentExpression == node
531-
}
532-
533486
func isRightSideOfPropertyAccess(node *ast.Node) bool {
534487
return node.Parent.Kind == ast.KindPropertyAccessExpression && node.Parent.Name() == node
535488
}
@@ -582,10 +535,6 @@ func findReferenceInPosition(refs []*ast.FileReference, pos int) *ast.FileRefere
582535
return core.Find(refs, func(ref *ast.FileReference) bool { return ref.TextRange.ContainsInclusive(pos) })
583536
}
584537

585-
func isTagName(node *ast.Node) bool {
586-
return node.Parent != nil && ast.IsJSDocTag(node.Parent) && node.Parent.TagName() == node
587-
}
588-
589538
// Assumes `candidate.pos <= position` holds.
590539
func positionBelongsToNode(candidate *ast.Node, position int, file *ast.SourceFile) bool {
591540
if candidate.Pos() > position {

0 commit comments

Comments
 (0)