Skip to content

Commit 9a33548

Browse files
committed
Cache name table on SourceFile
1 parent 032909d commit 9a33548

File tree

4 files changed

+62
-47
lines changed

4 files changed

+62
-47
lines changed

internal/ast/ast.go

Lines changed: 60 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,64 @@ 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+
file.nameTable = file.initializeNameTable()
10954+
})
10955+
return file.nameTable
10956+
}
10957+
10958+
func (file *SourceFile) initializeNameTable() map[string]int {
10959+
nameTable := make(map[string]int)
10960+
10961+
isTagName := func(node *Node) bool {
10962+
return node.Parent != nil && IsJSDocTag(node.Parent) && node.Parent.TagName() == node
10963+
}
10964+
10965+
isArgumentOfElementAccessExpression := func(node *Node) bool {
10966+
return node != nil && node.Parent != nil &&
10967+
node.Parent.Kind == KindElementAccessExpression &&
10968+
node.Parent.AsElementAccessExpression().ArgumentExpression == node
10969+
}
10970+
10971+
// We want to store any numbers/strings if they were a name that could be
10972+
// related to a declaration. So, if we have 'import x = require("something")'
10973+
// then we want 'something' to be in the name table. Similarly, if we have
10974+
// "a['propname']" then we want to store "propname" in the name table.
10975+
literalIsName := func(node *Node) bool {
10976+
return IsDeclarationName(node) ||
10977+
node.Parent.Kind == KindExternalModuleReference ||
10978+
isArgumentOfElementAccessExpression(node) ||
10979+
IsLiteralComputedPropertyDeclarationName(node)
10980+
}
10981+
10982+
var walk func(node *Node) bool
10983+
walk = func(node *Node) bool {
10984+
if IsIdentifier(node) && !isTagName(node) && node.Text() != "" ||
10985+
IsStringOrNumericLiteralLike(node) && literalIsName(node) ||
10986+
IsPrivateIdentifier(node) {
10987+
text := node.Text()
10988+
if _, ok := nameTable[text]; ok {
10989+
nameTable[text] = -1
10990+
} else {
10991+
nameTable[text] = node.Pos()
10992+
}
10993+
}
10994+
10995+
node.ForEachChild(walk)
10996+
jsdocNodes := node.JSDoc(file)
10997+
for _, jsdoc := range jsdocNodes {
10998+
jsdoc.ForEachChild(walk)
10999+
}
11000+
return false
11001+
}
11002+
11003+
file.AsNode().ForEachChild(walk)
11004+
return nameTable
11005+
}
11006+
1094711007
func (node *SourceFile) IsBound() bool {
1094811008
return node.isBound.Load()
1094911009
}

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 & 45 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 {
@@ -582,10 +541,6 @@ func findReferenceInPosition(refs []*ast.FileReference, pos int) *ast.FileRefere
582541
return core.Find(refs, func(ref *ast.FileReference) bool { return ref.TextRange.ContainsInclusive(pos) })
583542
}
584543

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

0 commit comments

Comments
 (0)