kittenipc/kitcom/internal/tsgo/ls/autoimportsexportinfo.go
2025-10-15 10:12:44 +03:00

182 lines
5.7 KiB
Go

package ls
import (
"context"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/checker"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner"
)
func (l *LanguageService) getExportInfos(
ctx context.Context,
ch *checker.Checker,
importingFile *ast.SourceFile,
preferences *UserPreferences,
exportMapKey ExportInfoMapKey,
) []*SymbolExportInfo {
expInfoMap := NewExportInfoMap(l.GetProgram().GetGlobalTypingsCacheLocation())
moduleCount := 0
symbolNameMatch := func(symbolName string) bool {
return symbolName == exportMapKey.SymbolName
}
forEachExternalModuleToImportFrom(
ch,
l.GetProgram(),
preferences,
// /*useAutoImportProvider*/ true,
func(moduleSymbol *ast.Symbol, moduleFile *ast.SourceFile, ch *checker.Checker, isFromPackageJson bool) {
if moduleCount = moduleCount + 1; moduleCount%100 == 0 && ctx.Err() != nil {
return
}
if moduleFile == nil && moduleSymbol.Name != exportMapKey.AmbientModuleName {
return
}
seenExports := collections.Set[string]{}
defaultInfo := getDefaultLikeExportInfo(moduleSymbol, ch)
var exportingModuleSymbol *ast.Symbol
if defaultInfo != nil {
exportingModuleSymbol = defaultInfo.exportingModuleSymbol
// Note: I think we shouldn't actually see resolved module symbols here, but weird merges
// can cause it to happen: see 'completionsImport_mergedReExport.ts'
if isImportableSymbol(exportingModuleSymbol, ch) {
expInfoMap.add(
importingFile.Path(),
exportingModuleSymbol,
core.IfElse(defaultInfo.exportKind == ExportKindDefault, ast.InternalSymbolNameDefault, ast.InternalSymbolNameExportEquals),
moduleSymbol,
moduleFile,
defaultInfo.exportKind,
isFromPackageJson,
ch,
symbolNameMatch,
nil,
)
}
}
ch.ForEachExportAndPropertyOfModule(moduleSymbol, func(exported *ast.Symbol, key string) {
if exported != exportingModuleSymbol && isImportableSymbol(exported, ch) && seenExports.AddIfAbsent(key) {
expInfoMap.add(
importingFile.Path(),
exported,
key,
moduleSymbol,
moduleFile,
ExportKindNamed,
isFromPackageJson,
ch,
symbolNameMatch,
nil,
)
}
})
})
return expInfoMap.get(importingFile.Path(), ch, exportMapKey)
}
func (l *LanguageService) searchExportInfosForCompletions(
ctx context.Context,
ch *checker.Checker,
importingFile *ast.SourceFile,
preferences *UserPreferences,
isForImportStatementCompletion bool,
isRightOfOpenTag bool,
isTypeOnlyLocation bool,
lowerCaseTokenText string,
action func([]*SymbolExportInfo, string, bool, ExportInfoMapKey) []*SymbolExportInfo,
) {
symbolNameMatches := map[string]bool{}
symbolNameMatch := func(symbolName string) bool {
if !scanner.IsIdentifierText(symbolName, importingFile.LanguageVariant) {
return false
}
if b, ok := symbolNameMatches[symbolName]; ok {
return b
}
if isNonContextualKeyword(scanner.StringToToken(symbolName)) {
symbolNameMatches[symbolName] = false
return false
}
// Do not try to auto-import something with a lowercase first letter for a JSX tag
firstChar := rune(symbolName[0])
if isRightOfOpenTag && (firstChar < 'A' || firstChar > 'Z') {
symbolNameMatches[symbolName] = false
return false
}
symbolNameMatches[symbolName] = charactersFuzzyMatchInString(symbolName, lowerCaseTokenText)
return symbolNameMatches[symbolName]
}
flagMatch := func(targetFlags ast.SymbolFlags) bool {
if !isTypeOnlyLocation && !isForImportStatementCompletion && (targetFlags&ast.SymbolFlagsValue) == 0 {
return false
}
if isTypeOnlyLocation && (targetFlags&(ast.SymbolFlagsModule|ast.SymbolFlagsType) == 0) {
return false
}
return true
}
expInfoMap := NewExportInfoMap(l.GetProgram().GetGlobalTypingsCacheLocation())
moduleCount := 0
forEachExternalModuleToImportFrom(
ch,
l.GetProgram(),
preferences,
// /*useAutoImportProvider*/ true,
func(moduleSymbol *ast.Symbol, moduleFile *ast.SourceFile, ch *checker.Checker, isFromPackageJson bool) {
if moduleCount = moduleCount + 1; moduleCount%100 == 0 && ctx.Err() != nil {
return
}
seenExports := collections.Set[string]{}
defaultInfo := getDefaultLikeExportInfo(moduleSymbol, ch)
// Note: I think we shouldn't actually see resolved module symbols here, but weird merges
// can cause it to happen: see 'completionsImport_mergedReExport.ts'
if defaultInfo != nil && isImportableSymbol(defaultInfo.exportingModuleSymbol, ch) {
expInfoMap.add(
importingFile.Path(),
defaultInfo.exportingModuleSymbol,
core.IfElse(defaultInfo.exportKind == ExportKindDefault, ast.InternalSymbolNameDefault, ast.InternalSymbolNameExportEquals),
moduleSymbol,
moduleFile,
defaultInfo.exportKind,
isFromPackageJson,
ch,
symbolNameMatch,
flagMatch,
)
}
var exportingModuleSymbol *ast.Symbol
if defaultInfo != nil {
exportingModuleSymbol = defaultInfo.exportingModuleSymbol
}
ch.ForEachExportAndPropertyOfModule(moduleSymbol, func(exported *ast.Symbol, key string) {
if exported != exportingModuleSymbol && isImportableSymbol(exported, ch) && seenExports.AddIfAbsent(key) {
expInfoMap.add(
importingFile.Path(),
exported,
key,
moduleSymbol,
moduleFile,
ExportKindNamed,
isFromPackageJson,
ch,
symbolNameMatch,
flagMatch,
)
}
})
})
expInfoMap.search(
ch,
importingFile.Path(),
/*preferCapitalized*/ isRightOfOpenTag,
func(symbolName string, targetFlags ast.SymbolFlags) bool {
return symbolNameMatch(symbolName) && flagMatch(targetFlags)
},
action,
)
}