Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
89 changes: 87 additions & 2 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -1386,7 +1386,7 @@ func GetNameOfDeclaration(declaration *Node) *Node {
return nonAssignedName
}
if IsFunctionExpression(declaration) || IsArrowFunction(declaration) || IsClassExpression(declaration) {
return getAssignedName(declaration)
return GetAssignedName(declaration)
}
return nil
}
Expand Down Expand Up @@ -1415,7 +1415,7 @@ func GetNonAssignedNameOfDeclaration(declaration *Node) *Node {
return declaration.Name()
}

func getAssignedName(node *Node) *Node {
func GetAssignedName(node *Node) *Node {
parent := node.Parent
if parent != nil {
switch parent.Kind {
Expand Down Expand Up @@ -3441,6 +3441,91 @@ func IsRightSideOfPropertyAccess(node *Node) bool {
return node.Parent.Kind == KindPropertyAccessExpression && node.Parent.Name() == node
}

func IsArgumentExpressionOfElementAccess(node *Node) bool {
return node.Parent != nil && node.Parent.Kind == KindElementAccessExpression && node.Parent.AsElementAccessExpression().ArgumentExpression == node
}

func ClimbPastPropertyAccess(node *Node) *Node {
if IsRightSideOfPropertyAccess(node) {
return node.Parent
}
return node
}

func climbPastPropertyOrElementAccess(node *Node) *Node {
if IsRightSideOfPropertyAccess(node) || IsArgumentExpressionOfElementAccess(node) {
return node.Parent
}
return node
}

func selectExpressionOfCallOrNewExpressionOrDecorator(node *Node) *Node {
if IsCallExpression(node) || IsNewExpression(node) || IsDecorator(node) {
return node.Expression()
}
return nil
}

func selectTagOfTaggedTemplateExpression(node *Node) *Node {
if IsTaggedTemplateExpression(node) {
return node.AsTaggedTemplateExpression().Tag
}
return nil
}

func selectTagNameOfJsxOpeningLikeElement(node *Node) *Node {
if IsJsxOpeningElement(node) || IsJsxSelfClosingElement(node) {
return node.TagName()
}
return nil
}

func IsCallExpressionTarget(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool {
return isCalleeWorker(node, IsCallExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions)
}

func IsNewExpressionTarget(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool {
return isCalleeWorker(node, IsNewExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions)
}

func IsCallOrNewExpressionTarget(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool {
return isCalleeWorker(node, IsCallOrNewExpression, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions)
}

func IsTaggedTemplateTag(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool {
return isCalleeWorker(node, IsTaggedTemplateExpression, selectTagOfTaggedTemplateExpression, includeElementAccess, skipPastOuterExpressions)
}

func IsDecoratorTarget(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool {
return isCalleeWorker(node, IsDecorator, selectExpressionOfCallOrNewExpressionOrDecorator, includeElementAccess, skipPastOuterExpressions)
}

func IsJsxOpeningLikeElementTagName(node *Node, includeElementAccess bool, skipPastOuterExpressions bool) bool {
return isCalleeWorker(node, IsJsxOpeningLikeElement, selectTagNameOfJsxOpeningLikeElement, includeElementAccess, skipPastOuterExpressions)
}

func isCalleeWorker(
node *Node,
pred func(*Node) bool,
calleeSelector func(*Node) *Node,
includeElementAccess bool,
skipPastOuterExpressions bool,
) bool {
var target *Node
if includeElementAccess {
target = climbPastPropertyOrElementAccess(node)
} else {
target = ClimbPastPropertyAccess(node)
}
if skipPastOuterExpressions {
// Only skip outer expressions if the target is actually an expression node
if IsExpression(target) {
target = SkipOuterExpressions(target, OEKAll)
}
}
return target != nil && target.Parent != nil && pred(target.Parent) && calleeSelector(target.Parent) == target
}

func IsRightSideOfQualifiedNameOrPropertyAccess(node *Node) bool {
parent := node.Parent
switch parent.Kind {
Expand Down
18 changes: 18 additions & 0 deletions internal/fourslash/_scripts/convertFourslash.mts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ function parseFourslashStatement(statement: ts.Statement): Cmd[] | undefined {
return parseNoSignatureHelpForTriggerReason(callExpression.arguments);
case "baselineSmartSelection":
return [parseBaselineSmartSelection(callExpression.arguments)];
case "baselineCallHierarchy":
return [parseBaselineCallHierarchy(callExpression.arguments)];
case "baselineGoToDefinition":
case "baselineGetDefinitionAtPosition":
case "baselineGoToType":
Expand Down Expand Up @@ -2025,6 +2027,15 @@ function parseBaselineSmartSelection(args: ts.NodeArray<ts.Expression>): Cmd {
};
}

function parseBaselineCallHierarchy(args: ts.NodeArray<ts.Expression>): Cmd {
if (args.length !== 0) {
throw new Error("Expected no arguments in verify.baselineCallHierarchy");
}
return {
kind: "verifyBaselineCallHierarchy",
};
}

function parseKind(expr: ts.Expression): string | undefined {
if (!ts.isStringLiteral(expr)) {
console.error(`Expected string literal for kind, got ${expr.getText()}`);
Expand Down Expand Up @@ -2399,6 +2410,10 @@ interface VerifyBaselineSmartSelection {
kind: "verifyBaselineSmartSelection";
}

interface VerifyBaselineCallHierarchy {
kind: "verifyBaselineCallHierarchy";
}

interface VerifyBaselineRenameCmd {
kind: "verifyBaselineRename" | "verifyBaselineRenameAtRangesWithText";
args: string[];
Expand Down Expand Up @@ -2511,6 +2526,7 @@ type Cmd =
| VerifyNoSignatureHelpCmd
| VerifySignatureHelpPresentCmd
| VerifyNoSignatureHelpForTriggerReasonCmd
| VerifyBaselineCallHierarchy
| GoToCmd
| EditCmd
| VerifyQuickInfoCmd
Expand Down Expand Up @@ -2777,6 +2793,8 @@ function generateCmd(cmd: Cmd): string {
return `f.VerifyBaselineSignatureHelp(t)`;
case "verifyBaselineSmartSelection":
return `f.VerifyBaselineSelectionRanges(t)`;
case "verifyBaselineCallHierarchy":
return `f.VerifyBaselineCallHierarchy(t)`;
case "goTo":
return generateGoToCommand(cmd);
case "edit":
Expand Down
18 changes: 18 additions & 0 deletions internal/fourslash/baselineutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

const (
autoImportsCmd baselineCommand = "Auto Imports"
callHierarchyCmd baselineCommand = "Call Hierarchy"
documentHighlightsCmd baselineCommand = "documentHighlights"
findAllReferencesCmd baselineCommand = "findAllReferences"
goToDefinitionCmd baselineCommand = "goToDefinition"
Expand Down Expand Up @@ -71,6 +72,8 @@ func getBaselineExtension(command baselineCommand) string {
switch command {
case quickInfoCmd, signatureHelpCmd, smartSelectionCmd, inlayHintsCmd, nonSuggestionDiagnosticsCmd:
return "baseline"
case callHierarchyCmd:
return "callHierarchy.txt"
case autoImportsCmd:
return "baseline.md"
default:
Expand All @@ -91,6 +94,21 @@ func (f *FourslashTest) getBaselineOptions(command baselineCommand, testPath str
Subfolder: subfolder,
IsSubmodule: true,
}
case callHierarchyCmd:
return baseline.Options{
Subfolder: subfolder,
IsSubmodule: true,
DiffFixupOld: func(s string) string {
// TypeScript baselines have "/tests/cases/fourslash/" prefix in file paths
// Handle /server/ subdirectory - need to remove both prefixes
s = strings.ReplaceAll(s, "/tests/cases/fourslash/server/", "/")
s = strings.ReplaceAll(s, "/tests/cases/fourslash/", "/")
// SymbolKind enum differences between Strada and tsgo
s = strings.ReplaceAll(s, "kind: getter", "kind: property")
s = strings.ReplaceAll(s, "kind: script", "kind: file")
return s
},
}
case renameCmd:
return baseline.Options{
Subfolder: subfolder,
Expand Down
Loading