Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
da0e338
WIP
andrewbranch Oct 15, 2025
ea7ec1b
WIP
andrewbranch Oct 16, 2025
5d88696
Merge branch 'main' into autoimport
andrewbranch Oct 27, 2025
3482314
WIP add to existing, resolve fix
andrewbranch Oct 29, 2025
654a104
Move change tracker, converters, utils to separate packages
andrewbranch Oct 29, 2025
50be94c
WIP fix edits
andrewbranch Oct 29, 2025
4e1e5ea
Move organizeimports
andrewbranch Oct 29, 2025
76a99d7
Get hand-written tests working
andrewbranch Oct 29, 2025
c357794
Remove trie and use word indices instead
andrewbranch Oct 29, 2025
56e22dd
Merge branch 'main' into autoimport
andrewbranch Oct 30, 2025
df73b49
WIP snapshot integration
andrewbranch Oct 31, 2025
a185c2b
node_modules index building, WIP snapshot host
andrewbranch Nov 4, 2025
8bcce85
Wire up to snapshot
andrewbranch Nov 4, 2025
0f47ffb
System end to end
andrewbranch Nov 5, 2025
7fb4085
Merge branch 'main' into autoimport
andrewbranch Nov 7, 2025
7877d4e
Specifier cache, small fixes
andrewbranch Nov 7, 2025
fb3318e
Optimize a bit
andrewbranch Nov 10, 2025
1516a80
Start deleting stuff, convert fourslash tests
andrewbranch Nov 11, 2025
13e1c46
Move ScriptElementKind to lsutil
andrewbranch Nov 12, 2025
207c7e2
Merge branch 'main' into autoimport
andrewbranch Nov 12, 2025
01ade0f
Revert submodule change
andrewbranch Nov 12, 2025
04bbed1
Minimal checker-based alias resolution
andrewbranch Nov 12, 2025
a34e2c1
Second resolution pass for ambient modules
andrewbranch Nov 16, 2025
86506fc
Chip away at test failures
andrewbranch Nov 17, 2025
8053a4f
Try searching dependencies for more files
andrewbranch Nov 18, 2025
8b43d2b
Handle package shadowing
andrewbranch Nov 19, 2025
9587948
WIP sort fixes
andrewbranch Nov 19, 2025
33f3282
Chip away at tests more
andrewbranch Nov 20, 2025
9392b86
Clean up export extracting, implement useRequire
andrewbranch Nov 20, 2025
4bc5e2d
Fix more tests
andrewbranch Nov 20, 2025
4ce391f
Finish going through existing enabled tests
andrewbranch Nov 21, 2025
d0f1fe5
Enable other passing tests
andrewbranch Nov 21, 2025
91d910d
Log more stats, try local name resolution before checker usage
andrewbranch Nov 21, 2025
2b8b2a9
Merge branch 'main' into autoimport
andrewbranch Nov 21, 2025
8b006b2
WIP codefixes
andrewbranch Nov 22, 2025
3c7e27b
WIP code fix
andrewbranch Nov 24, 2025
732793f
Initial plan
Copilot Nov 22, 2025
87b670a
Port getFixesInfoForNonUMDImport to use new autoimport.View
Copilot Nov 22, 2025
7f2d093
Apply formatting
Copilot Nov 22, 2025
1bcf069
Convert UMD and type-only fixes to use autoimport.Fix
Copilot Nov 24, 2025
1e0ca2b
Fix panics and add type-only import support
Copilot Nov 25, 2025
46f3e40
Add JSDoc import type fix support
Copilot Nov 25, 2025
96af867
Improve error handling for JSDoc import type fix
Copilot Nov 25, 2025
a7e6432
Continue fixing tests
andrewbranch Dec 2, 2025
6af6a81
Continue fixing tests
andrewbranch Dec 2, 2025
4a30356
Handle node_modules that are excluded by package.json but imported by…
andrewbranch Dec 3, 2025
bd11dde
Update failing tests
andrewbranch Dec 3, 2025
3c4ef31
Delete old code
andrewbranch Dec 3, 2025
c4e0fb7
Add basic benchmark
andrewbranch Dec 3, 2025
f7719d9
Use one alias resolver + checker per node_modules package so intra-pa…
andrewbranch Dec 4, 2025
4f6ff35
Start hammering out lifecycle bugs
andrewbranch Dec 4, 2025
98ee737
Add more lifecycle tests
andrewbranch Dec 5, 2025
e9ba700
Merge branch 'main' into autoimport
andrewbranch Dec 5, 2025
a003bb6
Fix test
andrewbranch Dec 6, 2025
34f1f4d
Clean up dirty state handling some
andrewbranch Dec 8, 2025
873b1b5
Comment
andrewbranch Dec 8, 2025
cbb1b88
Keep testing
andrewbranch Dec 9, 2025
bf709e8
Merge branch 'main' into autoimport
andrewbranch Dec 10, 2025
89632e7
WIP respecting new preferences and fixing tests
andrewbranch Dec 10, 2025
67d47ac
Copy missing promoteFromTypeOnly logic
andrewbranch Dec 10, 2025
c03e965
Handle endings
andrewbranch Dec 10, 2025
538408c
Better panic message
andrewbranch Dec 10, 2025
3a2f182
Disable JavaScript entrypoints for now
andrewbranch Dec 11, 2025
6ba0adf
Add logging for skipped files
andrewbranch Dec 11, 2025
7441386
Don’t retain any files
andrewbranch Dec 11, 2025
c37ee83
Delete commented out code
andrewbranch Dec 11, 2025
97564ac
Implement watch
andrewbranch Dec 11, 2025
8fd03b9
Warm auto-import registry on file edit
andrewbranch Dec 11, 2025
0c1db12
Fix some tests, delete some dead code
andrewbranch Dec 11, 2025
9b84b1c
Sort only as much as needed in the server, and then deterministically…
andrewbranch Dec 12, 2025
ce4a9bf
Optimize isFixPossiblyReExportingImportingFile
andrewbranch Dec 12, 2025
390882d
Bail out of expensive error checking during alias resolution
andrewbranch Dec 12, 2025
723a9d0
Update failing tests
andrewbranch Dec 12, 2025
b13b974
Finish wildcard exports TODO
andrewbranch Dec 12, 2025
4b87128
Finish more TODOs
andrewbranch Dec 13, 2025
c140faf
Fix module augmentation TODO
andrewbranch Dec 13, 2025
eb6cc62
Merge branch 'main' into autoimport
andrewbranch Dec 15, 2025
4d49179
Delete zero value
andrewbranch Dec 15, 2025
243d025
Only scrape dependencies and peerDependencies
andrewbranch Dec 16, 2025
30d24e8
Fix tests, fix type/value filtering
andrewbranch Dec 16, 2025
3b0ff4a
Lint/generate
andrewbranch Dec 16, 2025
639d047
Fix target grouping for export *
andrewbranch Dec 16, 2025
32f401a
Delete unused properties
andrewbranch Dec 16, 2025
24f9c97
Fix crashing tests
andrewbranch Dec 16, 2025
6b0992a
Fix race
andrewbranch Dec 16, 2025
44e5546
Fix other race
andrewbranch Dec 16, 2025
296b888
Address project update to-do
andrewbranch Dec 17, 2025
2e3bada
Tolerate erroneously mixed export kinds
andrewbranch Dec 17, 2025
36cc710
PR feedback
andrewbranch Dec 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/tsgo/lsp.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func runLSP(args []string) int {
defer stop()

if err := s.Run(ctx); err != nil {
fmt.Fprintln(os.Stderr, err)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven’t looked at #2336 yet, but this could be what’s missing to show a stack trace.

return 1
}
return 0
Expand Down
6 changes: 3 additions & 3 deletions internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ func (api *API) GetSymbolAtPosition(ctx context.Context, projectId Handle[projec
return nil, errors.New("project not found")
}

languageService := ls.NewLanguageService(project.GetProgram(), snapshot)
languageService := ls.NewLanguageService(project.ConfigFilePath(), project.GetProgram(), snapshot)
symbol, err := languageService.GetSymbolAtPosition(ctx, fileName, position)
if err != nil || symbol == nil {
return nil, err
Expand Down Expand Up @@ -203,7 +203,7 @@ func (api *API) GetSymbolAtLocation(ctx context.Context, projectId Handle[projec
if node == nil {
return nil, fmt.Errorf("node of kind %s not found at position %d in file %q", kind.String(), pos, sourceFile.FileName())
}
languageService := ls.NewLanguageService(project.GetProgram(), snapshot)
languageService := ls.NewLanguageService(project.ConfigFilePath(), project.GetProgram(), snapshot)
symbol := languageService.GetSymbolAtLocation(ctx, node)
if symbol == nil {
return nil, nil
Expand Down Expand Up @@ -233,7 +233,7 @@ func (api *API) GetTypeOfSymbol(ctx context.Context, projectId Handle[project.Pr
if !ok {
return nil, fmt.Errorf("symbol %q not found", symbolHandle)
}
languageService := ls.NewLanguageService(project.GetProgram(), snapshot)
languageService := ls.NewLanguageService(project.ConfigFilePath(), project.GetProgram(), snapshot)
t := languageService.GetTypeOfSymbol(ctx, symbol)
if t == nil {
return nil, nil
Expand Down
8 changes: 8 additions & 0 deletions internal/ast/symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ func (s *Symbol) IsStatic() bool {
return modifierFlags&ModifierFlagsStatic != 0
}

// See comment on `declareModuleMember` in `binder.go`.
func (s *Symbol) CombinedLocalAndExportSymbolFlags() SymbolFlags {
if s.ExportSymbol != nil {
return s.Flags | s.ExportSymbol.Flags
}
return s.Flags
}

// SymbolTable

type SymbolTable map[string]*Symbol
Expand Down
48 changes: 35 additions & 13 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1870,11 +1870,11 @@ func IsExpressionNode(node *Node) bool {
for node.Parent.Kind == KindQualifiedName {
node = node.Parent
}
return IsTypeQueryNode(node.Parent) || IsJSDocLinkLike(node.Parent) || IsJSDocNameReference(node.Parent) || isJSXTagName(node)
return IsTypeQueryNode(node.Parent) || IsJSDocLinkLike(node.Parent) || IsJSDocNameReference(node.Parent) || IsJsxTagName(node)
case KindPrivateIdentifier:
return IsBinaryExpression(node.Parent) && node.Parent.AsBinaryExpression().Left == node && node.Parent.AsBinaryExpression().OperatorToken.Kind == KindInKeyword
case KindIdentifier:
if IsTypeQueryNode(node.Parent) || IsJSDocLinkLike(node.Parent) || IsJSDocNameReference(node.Parent) || isJSXTagName(node) {
if IsTypeQueryNode(node.Parent) || IsJSDocLinkLike(node.Parent) || IsJSDocNameReference(node.Parent) || IsJsxTagName(node) {
return true
}
fallthrough
Expand Down Expand Up @@ -1991,15 +1991,6 @@ func IsJSDocTag(node *Node) bool {
return node.Kind >= KindFirstJSDocTagNode && node.Kind <= KindLastJSDocTagNode
}

func isJSXTagName(node *Node) bool {
parent := node.Parent
switch parent.Kind {
case KindJsxOpeningElement, KindJsxSelfClosingElement, KindJsxClosingElement:
return parent.TagName() == node
}
return false
}

func IsSuperCall(node *Node) bool {
return IsCallExpression(node) && node.Expression().Kind == KindSuperKeyword
}
Expand Down Expand Up @@ -3407,12 +3398,12 @@ func IsExternalModuleAugmentation(node *Node) bool {
func GetSourceFileOfModule(module *Symbol) *SourceFile {
declaration := module.ValueDeclaration
if declaration == nil {
declaration = getNonAugmentationDeclaration(module)
declaration = GetNonAugmentationDeclaration(module)
}
return GetSourceFileOfNode(declaration)
}

func getNonAugmentationDeclaration(symbol *Symbol) *Node {
func GetNonAugmentationDeclaration(symbol *Symbol) *Node {
return core.Find(symbol.Declarations, func(d *Node) bool {
return !IsExternalModuleAugmentation(d) && !IsGlobalScopeAugmentation(d)
})
Expand Down Expand Up @@ -3888,6 +3879,37 @@ func GetContainingFunction(node *Node) *Node {
return FindAncestor(node.Parent, IsFunctionLike)
}

func ImportFromModuleSpecifier(node *Node) *Node {
if result := TryGetImportFromModuleSpecifier(node); result != nil {
return result
}
debug.FailBadSyntaxKind(node.Parent)
return nil
}

func TryGetImportFromModuleSpecifier(node *StringLiteralLike) *Node {
switch node.Parent.Kind {
case KindImportDeclaration, KindJSImportDeclaration, KindExportDeclaration:
return node.Parent
case KindExternalModuleReference:
return node.Parent.Parent
case KindCallExpression:
if IsImportCall(node.Parent) || IsRequireCall(node.Parent, false /*requireStringLiteralLikeArgument*/) {
return node.Parent
}
return nil
case KindLiteralType:
if !IsStringLiteral(node) {
return nil
}
if IsImportTypeNode(node.Parent.Parent) {
return node.Parent.Parent
}
return nil
}
return nil
}

func IsImplicitlyExportedJSTypeAlias(node *Node) bool {
return IsJSTypeAliasDeclaration(node) && IsSourceFile(node.Parent) && IsExternalOrCommonJSModule(node.Parent.AsSourceFile())
}
Expand Down
4 changes: 4 additions & 0 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -14430,6 +14430,9 @@ func (c *Checker) getEmitSyntaxForModuleSpecifierExpression(usage *ast.Node) cor
}

func (c *Checker) errorNoModuleMemberSymbol(moduleSymbol *ast.Symbol, targetSymbol *ast.Symbol, node *ast.Node, name *ast.Node) {
if c.compilerOptions.NoCheck.IsTrue() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto-imports uses checkers for minimal alias resolution, and these code paths go way down a rabbit hole hitting the file system and using the node builder to generate errors. Added some targeted short circuits.

return
}
moduleName := c.getFullyQualifiedName(moduleSymbol, node)
declarationName := scanner.DeclarationNameToString(name)
var suggestion *ast.Symbol
Expand Down Expand Up @@ -14641,6 +14644,7 @@ func (c *Checker) markSymbolOfAliasDeclarationIfTypeOnly(aliasDeclaration *ast.N

func (c *Checker) resolveExternalModuleName(location *ast.Node, moduleReferenceExpression *ast.Node, ignoreErrors bool) *ast.Symbol {
errorMessage := diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations
ignoreErrors = ignoreErrors || c.compilerOptions.NoCheck.IsTrue()
return c.resolveExternalModuleNameWorker(location, moduleReferenceExpression, core.IfElse(ignoreErrors, nil, errorMessage), ignoreErrors, false /*isForAugmentation*/)
}

Expand Down
9 changes: 8 additions & 1 deletion internal/checker/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func (c *Checker) getSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags)
// Copy the given symbol into symbol tables if the symbol has the given meaning
// and it doesn't already exists in the symbol table.
copySymbol := func(symbol *ast.Symbol, meaning ast.SymbolFlags) {
if GetCombinedLocalAndExportSymbolFlags(symbol)&meaning != 0 {
if symbol.CombinedLocalAndExportSymbolFlags()&meaning != 0 {
id := symbol.Name
// We will copy all symbol regardless of its reserved name because
// symbolsToArray will check whether the key is a reserved name and
Expand Down Expand Up @@ -393,6 +393,13 @@ func (c *Checker) GetRootSymbols(symbol *ast.Symbol) []*ast.Symbol {
return result
}

func (c *Checker) GetMappedTypeSymbolOfProperty(symbol *ast.Symbol) *ast.Symbol {
if valueLinks := c.valueSymbolLinks.TryGet(symbol); valueLinks != nil {
return valueLinks.containingType.symbol
}
return nil
}

func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol {
if symbol.CheckFlags&ast.CheckFlagsSynthetic != 0 {
return core.MapNonNil(
Expand Down
8 changes: 0 additions & 8 deletions internal/checker/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1622,14 +1622,6 @@ func symbolsToArray(symbols ast.SymbolTable) []*ast.Symbol {
return result
}

// See comment on `declareModuleMember` in `binder.go`.
func GetCombinedLocalAndExportSymbolFlags(symbol *ast.Symbol) ast.SymbolFlags {
if symbol.ExportSymbol != nil {
return symbol.Flags | symbol.ExportSymbol.Flags
}
return symbol.Flags
}

func SkipAlias(symbol *ast.Symbol, checker *Checker) *ast.Symbol {
if symbol.Flags&ast.SymbolFlagsAlias != 0 {
return checker.GetAliasedSymbol(symbol)
Expand Down
6 changes: 6 additions & 0 deletions internal/collections/multimap.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ type MultiMap[K comparable, V comparable] struct {
M map[K][]V
}

func NewMultiMapWithSizeHint[K comparable, V comparable](hint int) *MultiMap[K, V] {
return &MultiMap[K, V]{
M: make(map[K][]V, hint),
}
}

func GroupBy[K comparable, V comparable](items []V, groupId func(V) K) *MultiMap[K, V] {
m := &MultiMap[K, V]{}
for _, item := range items {
Expand Down
70 changes: 70 additions & 0 deletions internal/collections/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ func NewSetWithSizeHint[T comparable](hint int) *Set[T] {
}

func (s *Set[T]) Has(key T) bool {
if s == nil {
return false
}
_, ok := s.M[key]
return ok
}
Expand All @@ -30,14 +33,23 @@ func (s *Set[T]) Delete(key T) {
}

func (s *Set[T]) Len() int {
if s == nil {
return 0
}
return len(s.M)
}

func (s *Set[T]) Keys() map[T]struct{} {
if s == nil {
return nil
}
return s.M
}

func (s *Set[T]) Clear() {
if s == nil {
return
}
clear(s.M)
}

Expand All @@ -58,6 +70,37 @@ func (s *Set[T]) Clone() *Set[T] {
return clone
}

func (s *Set[T]) Union(other *Set[T]) {
if s.Len() == 0 && other.Len() == 0 {
return
}
if s == nil {
panic("cannot modify nil Set")
}
if s.M == nil {
s.M = maps.Clone(other.M)
return
}
maps.Copy(s.M, other.M)
}

func (s *Set[T]) UnionedWith(other *Set[T]) *Set[T] {
if s == nil && other == nil {
return nil
}
result := s.Clone()
if other != nil {
if result == nil {
result = &Set[T]{}
}
if result.M == nil {
result.M = make(map[T]struct{}, len(other.M))
}
maps.Copy(result.M, other.M)
}
return result
}

func (s *Set[T]) Equals(other *Set[T]) bool {
if s == other {
return true
Expand All @@ -68,6 +111,33 @@ func (s *Set[T]) Equals(other *Set[T]) bool {
return maps.Equal(s.M, other.M)
}

func (s *Set[T]) IsSubsetOf(other *Set[T]) bool {
if s == nil {
return true
}
if other == nil {
return false
}
for key := range s.M {
if !other.Has(key) {
return false
}
}
return true
}

func (s *Set[T]) Intersects(other *Set[T]) bool {
if s == nil || other == nil {
return false
}
for key := range s.M {
if other.Has(key) {
return true
}
}
return false
}

func NewSetFromItems[T comparable](items ...T) *Set[T] {
s := &Set[T]{}
for _, item := range items {
Expand Down
Loading