vendor tsgo
This commit is contained in:
207
tools/tsgo/internal/ls/codelens.go
Normal file
207
tools/tsgo/internal/ls/codelens.go
Normal file
@@ -0,0 +1,207 @@
|
||||
package ls
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/diagnostics"
|
||||
"github.com/microsoft/typescript-go/internal/locale"
|
||||
"github.com/microsoft/typescript-go/internal/ls/lsutil"
|
||||
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
|
||||
"github.com/microsoft/typescript-go/internal/scanner"
|
||||
)
|
||||
|
||||
func (l *LanguageService) ProvideCodeLenses(ctx context.Context, documentURI lsproto.DocumentUri) (lsproto.CodeLensResponse, error) {
|
||||
_, file := l.getProgramAndFile(documentURI)
|
||||
|
||||
userPrefs := l.UserPreferences().CodeLens
|
||||
if !userPrefs.ReferencesCodeLensEnabled.IsTrue() && !userPrefs.ImplementationsCodeLensEnabled.IsTrue() {
|
||||
return lsproto.CodeLensResponse{}, nil
|
||||
}
|
||||
|
||||
// Keeps track of the last symbol to avoid duplicating code lenses across overloads.
|
||||
var lastSymbol *ast.Symbol
|
||||
var result []*lsproto.CodeLens
|
||||
var visit func(node *ast.Node) bool
|
||||
visit = func(node *ast.Node) bool {
|
||||
if ctx.Err() != nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if currentSymbol := node.Symbol(); lastSymbol != currentSymbol {
|
||||
lastSymbol = currentSymbol
|
||||
|
||||
if userPrefs.ReferencesCodeLensEnabled.IsTrue() && isValidReferenceLensNode(node, userPrefs) {
|
||||
result = append(result, l.newCodeLensForNode(documentURI, file, node, lsproto.CodeLensKindReferences))
|
||||
}
|
||||
|
||||
if userPrefs.ImplementationsCodeLensEnabled.IsTrue() && isValidImplementationsCodeLensNode(node, userPrefs) {
|
||||
result = append(result, l.newCodeLensForNode(documentURI, file, node, lsproto.CodeLensKindImplementations))
|
||||
}
|
||||
}
|
||||
|
||||
savedLastSymbol := lastSymbol
|
||||
node.ForEachChild(visit)
|
||||
lastSymbol = savedLastSymbol
|
||||
return false
|
||||
}
|
||||
|
||||
visit(file.AsNode())
|
||||
|
||||
return lsproto.CodeLensResponse{
|
||||
CodeLenses: &result,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *LanguageService) ResolveCodeLens(ctx context.Context, codeLens *lsproto.CodeLens, showLocationsCommandName *string, orchestrator CrossProjectOrchestrator) (*lsproto.CodeLens, error) {
|
||||
uri := codeLens.Data.Uri
|
||||
textDoc := lsproto.TextDocumentIdentifier{Uri: uri}
|
||||
locale := locale.FromContext(ctx)
|
||||
var locs []lsproto.Location
|
||||
var lensTitle string
|
||||
switch codeLens.Data.Kind {
|
||||
case lsproto.CodeLensKindReferences:
|
||||
referencesResp, err := l.ProvideReferences(ctx, &lsproto.ReferenceParams{
|
||||
TextDocument: textDoc,
|
||||
Position: codeLens.Range.Start,
|
||||
Context: &lsproto.ReferenceContext{
|
||||
// Don't include the declaration in the references count.
|
||||
IncludeDeclaration: false,
|
||||
},
|
||||
}, orchestrator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if referencesResp.Locations != nil {
|
||||
locs = *referencesResp.Locations
|
||||
}
|
||||
|
||||
if len(locs) == 1 {
|
||||
lensTitle = diagnostics.X_1_reference.Localize(locale)
|
||||
} else {
|
||||
lensTitle = diagnostics.X_0_references.Localize(locale, len(locs))
|
||||
}
|
||||
case lsproto.CodeLensKindImplementations:
|
||||
|
||||
implementations, err := l.provideImplementationsEx(
|
||||
ctx,
|
||||
&lsproto.ImplementationParams{
|
||||
TextDocument: textDoc,
|
||||
Position: codeLens.Range.Start,
|
||||
},
|
||||
// "Force" link support to be false so that we only get `Locations` back,
|
||||
// and don't include the "current" node in the results.
|
||||
symbolEntryTransformOptions{
|
||||
requireLocationsResult: true,
|
||||
dropOriginNodes: true,
|
||||
},
|
||||
orchestrator,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if implementations.Locations != nil {
|
||||
locs = *implementations.Locations
|
||||
}
|
||||
|
||||
if len(locs) == 1 {
|
||||
lensTitle = diagnostics.X_1_implementation.Localize(locale)
|
||||
} else {
|
||||
lensTitle = diagnostics.X_0_implementations.Localize(locale, len(locs))
|
||||
}
|
||||
}
|
||||
|
||||
cmd := &lsproto.Command{
|
||||
Title: lensTitle,
|
||||
}
|
||||
if len(locs) > 0 && showLocationsCommandName != nil {
|
||||
cmd.Command = *showLocationsCommandName
|
||||
cmd.Arguments = &[]any{
|
||||
uri,
|
||||
codeLens.Range.Start,
|
||||
locs,
|
||||
}
|
||||
}
|
||||
|
||||
codeLens.Command = cmd
|
||||
return codeLens, nil
|
||||
}
|
||||
|
||||
func (l *LanguageService) newCodeLensForNode(fileUri lsproto.DocumentUri, file *ast.SourceFile, node *ast.Node, kind lsproto.CodeLensKind) *lsproto.CodeLens {
|
||||
nodeForRange := node
|
||||
nodeName := node.Name()
|
||||
if nodeName != nil {
|
||||
nodeForRange = nodeName
|
||||
}
|
||||
pos := scanner.SkipTrivia(file.Text(), nodeForRange.Pos())
|
||||
|
||||
return &lsproto.CodeLens{
|
||||
Range: lsproto.Range{
|
||||
Start: l.converters.PositionToLineAndCharacter(file, core.TextPos(pos)),
|
||||
End: l.converters.PositionToLineAndCharacter(file, core.TextPos(node.End())),
|
||||
},
|
||||
Data: &lsproto.CodeLensData{
|
||||
Kind: kind,
|
||||
Uri: fileUri,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func isValidImplementationsCodeLensNode(node *ast.Node, userPrefs lsutil.CodeLensUserPreferences) bool {
|
||||
switch node.Kind {
|
||||
// Always show on interfaces
|
||||
case ast.KindInterfaceDeclaration:
|
||||
// TODO: ast.KindTypeAliasDeclaration?
|
||||
return true
|
||||
|
||||
// If configured, show on interface methods
|
||||
case ast.KindMethodSignature:
|
||||
return userPrefs.ImplementationsCodeLensShowOnInterfaceMethods.IsTrue() && node.Parent.Kind == ast.KindInterfaceDeclaration
|
||||
|
||||
// If configured, show on all class methods - but not private ones.
|
||||
case ast.KindMethodDeclaration:
|
||||
if userPrefs.ImplementationsCodeLensShowOnAllClassMethods.IsTrue() && node.Parent.Kind == ast.KindClassDeclaration {
|
||||
return !ast.HasModifier(node, ast.ModifierFlagsPrivate) && node.Name().Kind != ast.KindPrivateIdentifier
|
||||
}
|
||||
fallthrough
|
||||
|
||||
// Always show on abstract classes/properties/methods
|
||||
case ast.KindClassDeclaration, ast.KindConstructor,
|
||||
ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration:
|
||||
return ast.HasModifier(node, ast.ModifierFlagsAbstract)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isValidReferenceLensNode(node *ast.Node, userPrefs lsutil.CodeLensUserPreferences) bool {
|
||||
switch node.Kind {
|
||||
case ast.KindFunctionDeclaration:
|
||||
if userPrefs.ReferencesCodeLensShowOnAllFunctions.IsTrue() {
|
||||
return true
|
||||
}
|
||||
fallthrough
|
||||
|
||||
case ast.KindVariableDeclaration:
|
||||
return ast.GetCombinedModifierFlags(node)&ast.ModifierFlagsExport != 0
|
||||
|
||||
case ast.KindClassDeclaration, ast.KindInterfaceDeclaration, ast.KindTypeAliasDeclaration, ast.KindEnumDeclaration, ast.KindEnumMember:
|
||||
return true
|
||||
|
||||
case ast.KindMethodDeclaration, ast.KindMethodSignature, ast.KindConstructor,
|
||||
ast.KindGetAccessor, ast.KindSetAccessor,
|
||||
ast.KindPropertyDeclaration, ast.KindPropertySignature:
|
||||
// Don't show if child and parent have same start
|
||||
// For https://github.com/microsoft/vscode/issues/90396
|
||||
// !!!
|
||||
|
||||
switch node.Parent.Kind {
|
||||
case ast.KindClassDeclaration, ast.KindInterfaceDeclaration, ast.KindTypeLiteral:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
Reference in New Issue
Block a user