vendor tsgo
This commit is contained in:
501
tools/tsgo/internal/ls/autoimport/import_adder.go
Normal file
501
tools/tsgo/internal/ls/autoimport/import_adder.go
Normal file
@@ -0,0 +1,501 @@
|
||||
package autoimport
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/checker"
|
||||
"github.com/microsoft/typescript-go/internal/compiler"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/debug"
|
||||
"github.com/microsoft/typescript-go/internal/locale"
|
||||
|
||||
// "github.com/microsoft/typescript-go/internal/ls"
|
||||
"github.com/microsoft/typescript-go/internal/ls/change"
|
||||
"github.com/microsoft/typescript-go/internal/ls/lsconv"
|
||||
"github.com/microsoft/typescript-go/internal/ls/lsutil"
|
||||
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
|
||||
"github.com/microsoft/typescript-go/internal/nodebuilder"
|
||||
)
|
||||
|
||||
type ImportAdder interface {
|
||||
HasFixes() bool
|
||||
AddImportFromExportedSymbol(symbol *ast.Symbol, isValidTypeOnlyUseSite bool)
|
||||
AddImportFix(fix *Fix)
|
||||
Edits() []*lsproto.TextEdit
|
||||
}
|
||||
|
||||
// addToExistingState tracks modifications to an existing import clause or binding pattern
|
||||
type addToExistingState struct {
|
||||
importClauseOrBindingPattern *ast.ImportClauseOrBindingPattern
|
||||
defaultImport *newImportBinding
|
||||
namedImports map[string]*newImportBinding
|
||||
}
|
||||
|
||||
// importsCollection tracks new imports to be created for a given module specifier
|
||||
type importsCollection struct {
|
||||
defaultImport *newImportBinding
|
||||
namedImports map[string]*newImportBinding
|
||||
namespaceLikeImport *newImportBinding
|
||||
useRequire bool
|
||||
}
|
||||
|
||||
func newImportsKey(moduleSpecifier string, topLevelTypeOnly bool) string {
|
||||
if topLevelTypeOnly {
|
||||
return "1|" + moduleSpecifier
|
||||
}
|
||||
return "0|" + moduleSpecifier
|
||||
}
|
||||
|
||||
type importAdder struct {
|
||||
// Context
|
||||
ctx context.Context
|
||||
checker *checker.Checker
|
||||
view *View
|
||||
formatOptions lsutil.FormatCodeSettings
|
||||
converters *lsconv.Converters
|
||||
preferences lsutil.UserPreferences
|
||||
|
||||
// State
|
||||
addToNamespace []*Fix // Namespace fixes don't conflict, so just build a list
|
||||
importType []*Fix // JSDoc type import fixes
|
||||
addToExisting map[*ast.ImportClauseOrBindingPattern]*addToExistingState // importClauseOrBindingPattern -> default or named bindings
|
||||
newImports map[string]*importsCollection // module specifier + type only -> imports
|
||||
// !!! removeExisting, verbatimImports?
|
||||
}
|
||||
|
||||
func NewImportAdder(
|
||||
ctx context.Context,
|
||||
program *compiler.Program,
|
||||
checker *checker.Checker,
|
||||
file *ast.SourceFile,
|
||||
view *View,
|
||||
formatOptions lsutil.FormatCodeSettings,
|
||||
converters *lsconv.Converters,
|
||||
preferences lsutil.UserPreferences,
|
||||
) ImportAdder {
|
||||
return &importAdder{
|
||||
ctx: ctx,
|
||||
checker: checker,
|
||||
view: view,
|
||||
formatOptions: formatOptions,
|
||||
converters: converters,
|
||||
preferences: preferences,
|
||||
addToNamespace: nil,
|
||||
importType: nil,
|
||||
addToExisting: make(map[*ast.Node]*addToExistingState),
|
||||
newImports: make(map[string]*importsCollection),
|
||||
}
|
||||
}
|
||||
|
||||
func (adder *importAdder) HasFixes() bool {
|
||||
return len(adder.addToNamespace) > 0 ||
|
||||
len(adder.importType) > 0 ||
|
||||
len(adder.addToExisting) > 0 ||
|
||||
len(adder.newImports) > 0
|
||||
}
|
||||
|
||||
// !!! referenceImport
|
||||
func (adder *importAdder) AddImportFromExportedSymbol(exportedSymbol *ast.Symbol, isValidTypeOnlyUseSite bool) {
|
||||
symbol := adder.checker.GetMergedSymbol(adder.checker.SkipAlias(exportedSymbol))
|
||||
exportInfos := adder.getAllExportsForSymbol(symbol)
|
||||
if len(exportInfos) == 0 {
|
||||
// If no exportInfo is found, this means export could not be resolved when we have filtered for autoImportFileExcludePatterns,
|
||||
// so we should not generate an import.
|
||||
// debug.Assert(len(adder.ls.UserPreferences().AutoImportFileExcludePatterns) > 0)
|
||||
return
|
||||
}
|
||||
fix := adder.getImportFixForSymbol(adder.view, adder.view.importingFile, exportInfos, isValidTypeOnlyUseSite)
|
||||
if fix != nil {
|
||||
// !!! referenceImport -> propertyName
|
||||
adder.AddImportFix(fix)
|
||||
}
|
||||
}
|
||||
|
||||
func (adder *importAdder) Edits() []*lsproto.TextEdit {
|
||||
// !!! organize imports?
|
||||
tracker := change.NewTracker(adder.ctx, adder.view.program.Options(), adder.formatOptions, adder.converters)
|
||||
quotePreference := lsutil.GetQuotePreference(adder.view.importingFile, adder.preferences)
|
||||
for _, fix := range adder.addToNamespace {
|
||||
addNamespaceQualifier(fix, tracker, adder.view.importingFile, locale.Default)
|
||||
}
|
||||
for _, fix := range adder.importType {
|
||||
addImportType(fix, adder.view.importingFile, adder.preferences, tracker, locale.Default)
|
||||
}
|
||||
for clauseOrPattern, entry := range adder.addToExisting {
|
||||
addToExistingImport(
|
||||
tracker,
|
||||
adder.view.importingFile,
|
||||
clauseOrPattern,
|
||||
entry.defaultImport,
|
||||
sortedNamedImports(entry.namedImports),
|
||||
adder.preferences,
|
||||
)
|
||||
}
|
||||
|
||||
var newDeclarations []*ast.AnyImportOrRequireStatement
|
||||
for key, newImport := range adder.newImports {
|
||||
moduleSpecifier := key[2:] // From `${0 | 1}|${moduleSpecifier}` format
|
||||
var declarations []*ast.AnyImportOrRequireStatement
|
||||
if newImport.useRequire {
|
||||
declarations = getNewRequires(
|
||||
tracker,
|
||||
moduleSpecifier,
|
||||
quotePreference,
|
||||
newImport.defaultImport,
|
||||
sortedNamedImports(newImport.namedImports),
|
||||
newImport.namespaceLikeImport,
|
||||
adder.view.program.Options(),
|
||||
)
|
||||
} else {
|
||||
declarations = getNewImports(
|
||||
tracker,
|
||||
moduleSpecifier,
|
||||
quotePreference,
|
||||
newImport.defaultImport,
|
||||
sortedNamedImports(newImport.namedImports),
|
||||
newImport.namespaceLikeImport,
|
||||
adder.view.program.Options(),
|
||||
adder.preferences,
|
||||
)
|
||||
}
|
||||
newDeclarations = append(newDeclarations, declarations...)
|
||||
}
|
||||
|
||||
if len(newDeclarations) > 0 {
|
||||
insertImports(tracker, adder.view.importingFile, newDeclarations, true /*blankLineBetween*/, adder.preferences)
|
||||
}
|
||||
|
||||
return tracker.GetChanges()[adder.view.importingFile.FileName()]
|
||||
}
|
||||
|
||||
func sortedNamedImports(m map[string]*newImportBinding) []*newImportBinding {
|
||||
keys := slices.Sorted(maps.Keys(m))
|
||||
result := make([]*newImportBinding, 0, len(keys))
|
||||
for _, k := range keys {
|
||||
result = append(result, m[k])
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// AddImportFix adds a fix to the import adder, accumulating it with other fixes
|
||||
// so that multiple imports from the same module are coalesced into a single import statement.
|
||||
func (adder *importAdder) AddImportFix(fix *Fix) {
|
||||
symbolName := fix.Name
|
||||
compilerOptions := adder.view.program.Options()
|
||||
|
||||
switch fix.Kind {
|
||||
case lsproto.AutoImportFixKindUseNamespace:
|
||||
adder.addToNamespace = append(adder.addToNamespace, fix)
|
||||
case lsproto.AutoImportFixKindJsdocTypeImport:
|
||||
adder.importType = append(adder.importType, fix)
|
||||
case lsproto.AutoImportFixKindAddToExisting:
|
||||
existingFix := getAddToExistingImportFix(adder.view.importingFile, fix)
|
||||
entry := adder.addToExisting[existingFix.importClauseOrBindingPattern]
|
||||
if entry == nil {
|
||||
entry = &addToExistingState{
|
||||
importClauseOrBindingPattern: existingFix.importClauseOrBindingPattern,
|
||||
namedImports: make(map[string]*newImportBinding),
|
||||
}
|
||||
adder.addToExisting[existingFix.importClauseOrBindingPattern] = entry
|
||||
}
|
||||
|
||||
if fix.ImportKind == lsproto.ImportKindNamed {
|
||||
prevImport := entry.namedImports[symbolName]
|
||||
var prevTypeOnly lsproto.AddAsTypeOnly
|
||||
if prevImport != nil {
|
||||
prevTypeOnly = prevImport.addAsTypeOnly
|
||||
}
|
||||
entry.namedImports[symbolName] = &newImportBinding{
|
||||
kind: lsproto.ImportKindNamed,
|
||||
name: symbolName,
|
||||
addAsTypeOnly: reduceAddAsTypeOnlyValues(prevTypeOnly, fix.AddAsTypeOnly),
|
||||
propertyName: existingFix.namedImport.propertyName,
|
||||
}
|
||||
} else {
|
||||
// Default import
|
||||
debug.Assert(
|
||||
entry.defaultImport == nil || entry.defaultImport.name == symbolName,
|
||||
"(Add to Existing) Default import should be missing or match symbolName",
|
||||
)
|
||||
var prevTypeOnly lsproto.AddAsTypeOnly
|
||||
if entry.defaultImport != nil {
|
||||
prevTypeOnly = entry.defaultImport.addAsTypeOnly
|
||||
}
|
||||
entry.defaultImport = &newImportBinding{
|
||||
kind: lsproto.ImportKindDefault,
|
||||
name: symbolName,
|
||||
addAsTypeOnly: reduceAddAsTypeOnlyValues(prevTypeOnly, fix.AddAsTypeOnly),
|
||||
}
|
||||
}
|
||||
|
||||
case lsproto.AutoImportFixKindAddNew:
|
||||
entry := adder.getNewImportEntry(fix.ModuleSpecifier, fix.ImportKind, fix.UseRequire, fix.AddAsTypeOnly)
|
||||
debug.Assert(
|
||||
entry.useRequire == fix.UseRequire,
|
||||
"(Add new) Tried to add an `import` and a `require` for the same module",
|
||||
)
|
||||
|
||||
switch fix.ImportKind {
|
||||
case lsproto.ImportKindDefault:
|
||||
debug.Assert(
|
||||
entry.defaultImport == nil || entry.defaultImport.name == symbolName,
|
||||
"(Add new) Default import should be missing or match symbolName",
|
||||
)
|
||||
var prevTypeOnly lsproto.AddAsTypeOnly
|
||||
if entry.defaultImport != nil {
|
||||
prevTypeOnly = entry.defaultImport.addAsTypeOnly
|
||||
}
|
||||
entry.defaultImport = &newImportBinding{
|
||||
kind: lsproto.ImportKindDefault,
|
||||
name: symbolName,
|
||||
addAsTypeOnly: reduceAddAsTypeOnlyValues(prevTypeOnly, fix.AddAsTypeOnly),
|
||||
}
|
||||
|
||||
case lsproto.ImportKindNamed:
|
||||
if entry.namedImports == nil {
|
||||
entry.namedImports = make(map[string]*newImportBinding)
|
||||
}
|
||||
prevImport := entry.namedImports[symbolName]
|
||||
var prevTypeOnly lsproto.AddAsTypeOnly
|
||||
if prevImport != nil {
|
||||
prevTypeOnly = prevImport.addAsTypeOnly
|
||||
}
|
||||
entry.namedImports[symbolName] = &newImportBinding{
|
||||
kind: lsproto.ImportKindNamed,
|
||||
name: symbolName,
|
||||
addAsTypeOnly: reduceAddAsTypeOnlyValues(prevTypeOnly, fix.AddAsTypeOnly),
|
||||
// !!! propertyName
|
||||
}
|
||||
|
||||
case lsproto.ImportKindCommonJS:
|
||||
if compilerOptions.VerbatimModuleSyntax == core.TSTrue {
|
||||
if entry.namedImports == nil {
|
||||
entry.namedImports = make(map[string]*newImportBinding)
|
||||
}
|
||||
prevImport := entry.namedImports[symbolName]
|
||||
var prevTypeOnly lsproto.AddAsTypeOnly
|
||||
if prevImport != nil {
|
||||
prevTypeOnly = prevImport.addAsTypeOnly
|
||||
}
|
||||
entry.namedImports[symbolName] = &newImportBinding{
|
||||
kind: lsproto.ImportKindCommonJS,
|
||||
name: symbolName,
|
||||
addAsTypeOnly: reduceAddAsTypeOnlyValues(prevTypeOnly, fix.AddAsTypeOnly),
|
||||
// !!! propertyName
|
||||
}
|
||||
} else {
|
||||
debug.Assert(
|
||||
entry.namespaceLikeImport == nil || entry.namespaceLikeImport.name == symbolName,
|
||||
"Namespacelike import should be missing or match symbolName",
|
||||
)
|
||||
entry.namespaceLikeImport = &newImportBinding{
|
||||
kind: lsproto.ImportKindCommonJS,
|
||||
name: symbolName,
|
||||
addAsTypeOnly: fix.AddAsTypeOnly,
|
||||
}
|
||||
}
|
||||
|
||||
case lsproto.ImportKindNamespace:
|
||||
debug.Assert(
|
||||
entry.namespaceLikeImport == nil || entry.namespaceLikeImport.name == symbolName,
|
||||
"Namespacelike import should be missing or match symbolName",
|
||||
)
|
||||
entry.namespaceLikeImport = &newImportBinding{
|
||||
kind: lsproto.ImportKindNamespace,
|
||||
name: symbolName,
|
||||
addAsTypeOnly: fix.AddAsTypeOnly,
|
||||
}
|
||||
}
|
||||
|
||||
case lsproto.AutoImportFixKindPromoteTypeOnly:
|
||||
// Excluding from fix-all
|
||||
default:
|
||||
debug.Fail(fmt.Sprintf("Unexpected fix kind: %v", fix.Kind))
|
||||
}
|
||||
}
|
||||
|
||||
// `NotAllowed` overrides `Required` because one addition of a new import might be required to be type-only
|
||||
// because of `--importsNotUsedAsValues=error`, but if a second addition of the same import is `NotAllowed`
|
||||
// to be type-only, the reason the first one was `Required` - the unused runtime dependency - is now moot.
|
||||
// Alternatively, if one addition is `Required` because it has no value meaning under `--preserveValueImports`
|
||||
// and `--isolatedModules`, it should be impossible for another addition to be `NotAllowed` since that would
|
||||
// mean a type is being referenced in a value location.
|
||||
func reduceAddAsTypeOnlyValues(prevValue, newValue lsproto.AddAsTypeOnly) lsproto.AddAsTypeOnly {
|
||||
if newValue > prevValue {
|
||||
return newValue
|
||||
}
|
||||
return prevValue
|
||||
}
|
||||
|
||||
func (adder *importAdder) getNewImportEntry(moduleSpecifier string, importKind lsproto.ImportKind, useRequire bool, addAsTypeOnly lsproto.AddAsTypeOnly) *importsCollection {
|
||||
// A default import that requires type-only makes the whole import type-only.
|
||||
// (We could add `default` as a named import, but that style seems undesirable.)
|
||||
// Under `--preserveValueImports` and `--importsNotUsedAsValues=error`, if a
|
||||
// module default-exports a type but named-exports some values (weird), you would
|
||||
// have to use a type-only default import and non-type-only named imports. These
|
||||
// require two separate import declarations, so we build this into the map key.
|
||||
typeOnlyKey := newImportsKey(moduleSpecifier, true /*topLevelTypeOnly*/)
|
||||
nonTypeOnlyKey := newImportsKey(moduleSpecifier, false /*topLevelTypeOnly*/)
|
||||
typeOnlyEntry := adder.newImports[typeOnlyKey]
|
||||
nonTypeOnlyEntry := adder.newImports[nonTypeOnlyKey]
|
||||
newEntry := &importsCollection{
|
||||
useRequire: useRequire,
|
||||
}
|
||||
|
||||
if importKind == lsproto.ImportKindDefault && addAsTypeOnly == lsproto.AddAsTypeOnlyRequired {
|
||||
if typeOnlyEntry != nil {
|
||||
return typeOnlyEntry
|
||||
}
|
||||
adder.newImports[typeOnlyKey] = newEntry
|
||||
return newEntry
|
||||
}
|
||||
|
||||
if addAsTypeOnly == lsproto.AddAsTypeOnlyAllowed && (typeOnlyEntry != nil || nonTypeOnlyEntry != nil) {
|
||||
if typeOnlyEntry != nil {
|
||||
return typeOnlyEntry
|
||||
}
|
||||
return nonTypeOnlyEntry
|
||||
}
|
||||
|
||||
if nonTypeOnlyEntry != nil {
|
||||
return nonTypeOnlyEntry
|
||||
}
|
||||
|
||||
adder.newImports[nonTypeOnlyKey] = newEntry
|
||||
return newEntry
|
||||
}
|
||||
|
||||
func (adder *importAdder) getAllExportsForSymbol(
|
||||
symbol *ast.Symbol,
|
||||
) []*Export {
|
||||
if export := SymbolToExport(symbol, adder.checker); export != nil {
|
||||
return adder.view.SearchByExportID(export.ExportID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TypeToAutoImportableTypeNode(
|
||||
c *checker.Checker,
|
||||
importAdder ImportAdder,
|
||||
t *checker.Type,
|
||||
contextNode *ast.Node, // !!! flags
|
||||
) *ast.TypeNode {
|
||||
idToSymbol := make(map[*ast.IdentifierNode]*ast.Symbol)
|
||||
typeNode := c.TypeToTypeNode(t, contextNode, nodebuilder.FlagsNone, idToSymbol)
|
||||
if typeNode == nil {
|
||||
return nil
|
||||
}
|
||||
return TypeNodeToAutoImportableTypeNode(typeNode, importAdder, idToSymbol)
|
||||
}
|
||||
|
||||
// TypeNodeToAutoImportableTypeNode converts import type references in a type node to
|
||||
// simple type references and registers needed imports with the import adder.
|
||||
func TypeNodeToAutoImportableTypeNode(
|
||||
typeNode *ast.TypeNode,
|
||||
importAdder ImportAdder,
|
||||
idToSymbol map[*ast.IdentifierNode]*ast.Symbol,
|
||||
) *ast.TypeNode {
|
||||
referenceTypeNode, importableSymbols := TryGetAutoImportableReferenceFromTypeNode(typeNode, idToSymbol)
|
||||
if referenceTypeNode != nil {
|
||||
if importAdder != nil {
|
||||
importSymbols(importAdder, importableSymbols)
|
||||
}
|
||||
typeNode = referenceTypeNode
|
||||
}
|
||||
|
||||
// !!! handle type node reuse: nodes needs to be fresh here but also preserve symbols
|
||||
return typeNode
|
||||
}
|
||||
|
||||
func importSymbols(importAdder ImportAdder, symbols []*ast.Symbol) {
|
||||
for _, symbol := range symbols {
|
||||
importAdder.AddImportFromExportedSymbol(symbol, true /*isValidTypeOnlyUseSite*/)
|
||||
}
|
||||
}
|
||||
|
||||
// Given a type node containing 'import("./a").SomeType<import("./b").OtherType<...>>',
|
||||
// returns an equivalent type reference node with any nested ImportTypeNodes also replaced
|
||||
// with type references, and a list of symbols that must be imported to use the type reference.
|
||||
// TryGetAutoImportableReferenceFromTypeNode converts import type references in a type node
|
||||
// to simple type references and returns the transformed type node and the symbols that need
|
||||
// to be imported.
|
||||
func TryGetAutoImportableReferenceFromTypeNode(importTypeNode *ast.TypeNode, idToSymbol map[*ast.IdentifierNode]*ast.Symbol) (*ast.TypeNode, []*ast.Symbol) {
|
||||
var symbols []*ast.Symbol
|
||||
var visitor *ast.NodeVisitor
|
||||
factory := ast.NewNodeFactory(ast.NodeFactoryHooks{})
|
||||
visit := func(node *ast.Node) *ast.Node {
|
||||
if ast.IsLiteralImportTypeNode(node) && node.AsImportTypeNode().Qualifier != nil {
|
||||
importTypeNode := node.AsImportTypeNode()
|
||||
// Symbol for the left-most thing after the dot
|
||||
firstIdentifier := ast.GetFirstIdentifier(importTypeNode.Qualifier)
|
||||
symbol := idToSymbol[firstIdentifier]
|
||||
if symbol == nil {
|
||||
// if symbol is missing then this doesn't come from a synthesized import type node
|
||||
// it has to be an import type node authored by the user and thus it has to be valid
|
||||
// it can't refer to reserved internal symbol names and such
|
||||
return node.VisitEachChild(visitor)
|
||||
}
|
||||
name := getNameForExportedSymbol(symbol, false /*preferCapitalized*/)
|
||||
var qualifier *ast.EntityName
|
||||
if name != firstIdentifier.Text() {
|
||||
qualifier = replaceFirstIdentifierOfEntityName(factory, importTypeNode.Qualifier, factory.NewIdentifier(name))
|
||||
} else {
|
||||
qualifier = importTypeNode.Qualifier
|
||||
}
|
||||
symbols = append(symbols, symbol)
|
||||
typeArguments := visitor.VisitNodes(importTypeNode.TypeArguments)
|
||||
return factory.NewTypeReferenceNode(qualifier, typeArguments)
|
||||
}
|
||||
return visitor.VisitEachChild(node)
|
||||
}
|
||||
visitor = ast.NewNodeVisitor(visit, factory, ast.NodeVisitorHooks{})
|
||||
|
||||
typeNode := visitor.VisitNode(importTypeNode)
|
||||
debug.Assert(typeNode == nil || ast.IsTypeNode(typeNode), "expected a type node")
|
||||
return typeNode, symbols
|
||||
}
|
||||
|
||||
// If a type checker and multiple files are available, consider using `forEachNameOfDefaultExport`
|
||||
// instead, which searches for names of re-exported defaults/namespaces in target files.
|
||||
func getNameForExportedSymbol(symbol *ast.Symbol, preferCapitalized bool) string {
|
||||
if symbol.Name == ast.InternalSymbolNameExportEquals || symbol.Name == ast.InternalSymbolNameDefault {
|
||||
// Names for default exports:
|
||||
// - export default foo => foo
|
||||
// - export { foo as default } => foo
|
||||
// - export default 0 => filename converted to camelCase
|
||||
name := getDefaultLikeExportNameFromDeclaration(symbol)
|
||||
if name != "" {
|
||||
return name
|
||||
}
|
||||
debug.Assert(symbol.Parent != nil, "Expected exported symbol to have module symbol as parent")
|
||||
return lsutil.ModuleSymbolToValidIdentifier(symbol.Parent, preferCapitalized)
|
||||
}
|
||||
return symbol.Name
|
||||
}
|
||||
|
||||
func replaceFirstIdentifierOfEntityName(factory *ast.NodeFactory, name *ast.EntityName, newIdentifier *ast.IdentifierNode) *ast.EntityName {
|
||||
if name.Kind == ast.KindIdentifier {
|
||||
return newIdentifier
|
||||
}
|
||||
return factory.NewQualifiedName(
|
||||
replaceFirstIdentifierOfEntityName(factory, name.AsQualifiedName().Left, newIdentifier),
|
||||
name.AsQualifiedName().Right,
|
||||
)
|
||||
}
|
||||
|
||||
func (adder *importAdder) getImportFixForSymbol(view *View, file *ast.SourceFile, exports []*Export, isValidTypeOnlyUseSite bool) *Fix {
|
||||
fixes := core.FlatMap(exports, func(export *Export) []*Fix {
|
||||
return view.GetFixes(adder.ctx, export, false /*forJSX*/, isValidTypeOnlyUseSite, nil /*usagePosition*/)
|
||||
})
|
||||
slices.SortFunc(fixes, func(a, b *Fix) int {
|
||||
return view.CompareFixesForRanking(a, b)
|
||||
})
|
||||
if len(fixes) > 0 {
|
||||
return fixes[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user