Skip to content

Commit e68e226

Browse files
Port over some baselined hover/quick info tests (#1476)
1 parent ca3a056 commit e68e226

File tree

242 files changed

+29182
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

242 files changed

+29182
-1
lines changed

internal/fourslash/_scripts/convertFourslash.mts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ function parseFourslashStatement(statement: ts.Statement): Cmd[] | undefined {
157157
case "baselineFindAllReferences":
158158
// `verify.baselineFindAllReferences(...)`
159159
return [parseBaselineFindAllReferencesArgs(callExpression.arguments)];
160+
case "baselineQuickInfo":
161+
return [parseBaselineQuickInfo(callExpression.arguments)];
160162
case "baselineGoToDefinition":
161163
case "baselineGetDefinitionAtPosition":
162164
// Both of these take the same arguments, but differ in that...
@@ -709,6 +711,16 @@ function parseBaselineGoToDefinitionArgs(args: readonly ts.Expression[]): Verify
709711
};
710712
}
711713

714+
function parseBaselineQuickInfo(args: ts.NodeArray<ts.Expression>): Cmd {
715+
if (args.length !== 0) {
716+
// All calls are currently empty!
717+
throw new Error("Expected no arguments in verify.baselineQuickInfo");
718+
}
719+
return {
720+
kind: "verifyBaselineQuickInfo",
721+
};
722+
}
723+
712724
function parseKind(expr: ts.Expression): string | undefined {
713725
if (!ts.isStringLiteral(expr)) {
714726
console.error(`Expected string literal for kind, got ${expr.getText()}`);
@@ -856,6 +868,10 @@ interface VerifyBaselineGoToDefinitionCmd {
856868
ranges?: boolean;
857869
}
858870

871+
interface VerifyBaselineQuickInfoCmd {
872+
kind: "verifyBaselineQuickInfo";
873+
}
874+
859875
interface GoToCmd {
860876
kind: "goTo";
861877
// !!! `selectRange` and `rangeStart` require parsing variables and `test.ranges()[n]`
@@ -868,7 +884,13 @@ interface EditCmd {
868884
goStatement: string;
869885
}
870886

871-
type Cmd = VerifyCompletionsCmd | VerifyBaselineFindAllReferencesCmd | VerifyBaselineGoToDefinitionCmd | GoToCmd | EditCmd;
887+
type Cmd =
888+
| VerifyCompletionsCmd
889+
| VerifyBaselineFindAllReferencesCmd
890+
| VerifyBaselineGoToDefinitionCmd
891+
| VerifyBaselineQuickInfoCmd
892+
| GoToCmd
893+
| EditCmd;
872894

873895
function generateVerifyCompletions({ marker, args, isNewIdentifierLocation }: VerifyCompletionsCmd): string {
874896
let expectedList: string;
@@ -924,6 +946,9 @@ function generateCmd(cmd: Cmd): string {
924946
return generateBaselineFindAllReferences(cmd as VerifyBaselineFindAllReferencesCmd);
925947
case "verifyBaselineGoToDefinition":
926948
return generateBaselineGoToDefinition(cmd as VerifyBaselineGoToDefinitionCmd);
949+
case "verifyBaselineQuickInfo":
950+
// Quick Info -> Hover
951+
return `f.VerifyBaselineHover(t)`;
927952
case "goTo":
928953
return generateGoToCommand(cmd as GoToCmd);
929954
case "edit":

internal/fourslash/_scripts/failingTests.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,9 @@ TestJsDocFunctionTypeCompletionsNoCrash
230230
TestJsdocExtendsTagCompletion
231231
TestJsdocImplementsTagCompletion
232232
TestJsdocImportTagCompletion1
233+
TestJsdocLink2
234+
TestJsdocLink3
235+
TestJsdocLink6
233236
TestJsdocLink_findAllReferences1
234237
TestJsdocOverloadTagCompletion
235238
TestJsdocParameterNameCompletion
@@ -301,6 +304,7 @@ TestPathCompletionsTypesVersionsWildcard4
301304
TestPathCompletionsTypesVersionsWildcard5
302305
TestPathCompletionsTypesVersionsWildcard6
303306
TestProtoVarVisibleWithOuterScopeUnderscoreProto
307+
TestQuickInfoAlias
304308
TestReferencesForExportedValues
305309
TestReferencesForStatementKeywords
306310
TestReferencesInComment

internal/fourslash/baselineutil.go

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package fourslash
22

33
import (
4+
"cmp"
45
"errors"
56
"fmt"
67
"io/fs"
78
"regexp"
89
"slices"
910
"strings"
11+
"testing"
1012

1113
"github.com/microsoft/typescript-go/internal/collections"
1214
"github.com/microsoft/typescript-go/internal/core"
@@ -443,6 +445,104 @@ func (t *textWithContext) readableJsoncBaseline(text string) {
443445
}
444446
}
445447

448+
type markerAndItem[T any] struct {
449+
Marker *Marker `json:"marker"`
450+
Item T `json:"item"`
451+
}
452+
453+
func annotateContentWithTooltips[T comparable](
454+
t *testing.T,
455+
f *FourslashTest,
456+
markersAndItems []markerAndItem[T],
457+
opName string,
458+
getRange func(item T) *lsproto.Range,
459+
getTooltipLines func(item T, prev T) []string,
460+
) string {
461+
barWithGutter := "| " + strings.Repeat("-", 70)
462+
463+
// sort by file, then *backwards* by position in the file
464+
// so we can insert multiple times on a line without counting
465+
sorted := slices.Clone(markersAndItems)
466+
slices.SortFunc(sorted, func(a, b markerAndItem[T]) int {
467+
if c := cmp.Compare(a.Marker.FileName(), b.Marker.FileName()); c != 0 {
468+
return c
469+
}
470+
return -cmp.Compare(a.Marker.Position, b.Marker.Position)
471+
})
472+
473+
filesToLines := collections.NewOrderedMapWithSizeHint[string, []string](1)
474+
var previous T
475+
for _, itemAndMarker := range sorted {
476+
marker := itemAndMarker.Marker
477+
item := itemAndMarker.Item
478+
479+
textRange := getRange(item)
480+
if textRange == nil {
481+
start := marker.LSPosition
482+
end := start
483+
end.Character = end.Character + 1
484+
textRange = &lsproto.Range{Start: start, End: end}
485+
}
486+
487+
if textRange.Start.Line != textRange.End.Line {
488+
t.Fatalf("Expected text range to be on a single line, got %v", textRange)
489+
}
490+
underline := strings.Repeat(" ", int(textRange.Start.Character)) +
491+
strings.Repeat("^", int(textRange.End.Character-textRange.Start.Character))
492+
493+
fileName := marker.FileName()
494+
lines, ok := filesToLines.Get(fileName)
495+
if !ok {
496+
lines = lineSplitter.Split(f.getScriptInfo(fileName).content, -1)
497+
}
498+
499+
var tooltipLines []string
500+
if item != *new(T) {
501+
tooltipLines = getTooltipLines(item, previous)
502+
}
503+
if len(tooltipLines) == 0 {
504+
tooltipLines = []string{fmt.Sprintf("No %s at /*%s*/.", opName, *marker.Name)}
505+
}
506+
tooltipLines = core.Map(tooltipLines, func(line string) string {
507+
return "| " + line
508+
})
509+
510+
linesToInsert := make([]string, len(tooltipLines)+3)
511+
linesToInsert[0] = underline
512+
linesToInsert[1] = barWithGutter
513+
copy(linesToInsert[2:], tooltipLines)
514+
linesToInsert[len(linesToInsert)-1] = barWithGutter
515+
516+
lines = slices.Insert(
517+
lines,
518+
int(textRange.Start.Line+1),
519+
linesToInsert...,
520+
)
521+
filesToLines.Set(fileName, lines)
522+
523+
previous = item
524+
}
525+
526+
builder := strings.Builder{}
527+
seenFirst := false
528+
for fileName, lines := range filesToLines.Entries() {
529+
builder.WriteString(fmt.Sprintf("=== %s ===\n", fileName))
530+
for _, line := range lines {
531+
builder.WriteString("// ")
532+
builder.WriteString(line)
533+
builder.WriteByte('\n')
534+
}
535+
536+
if seenFirst {
537+
builder.WriteString("\n\n")
538+
} else {
539+
seenFirst = true
540+
}
541+
}
542+
543+
return builder.String()
544+
}
545+
446546
func (t *textWithContext) sliceOfContent(start *int, end *int) string {
447547
if start == nil || *start < 0 {
448548
start = ptrTo(0)

internal/fourslash/fourslash.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,99 @@ func (f *FourslashTest) VerifyBaselineGoToDefinition(
903903
baseline.Run(t, f.baseline.getBaselineFileName(), f.baseline.content.String(), baseline.Options{})
904904
}
905905

906+
func (f *FourslashTest) VerifyBaselineHover(t *testing.T) {
907+
if f.baseline != nil {
908+
t.Fatalf("Error during test '%s': Another baseline is already in progress", t.Name())
909+
} else {
910+
f.baseline = &baselineFromTest{
911+
content: &strings.Builder{},
912+
baselineName: "hover/" + strings.TrimPrefix(t.Name(), "Test"),
913+
ext: ".baseline",
914+
}
915+
}
916+
917+
// empty baseline after test completes
918+
defer func() {
919+
f.baseline = nil
920+
}()
921+
922+
markersAndItems := core.MapFiltered(f.Markers(), func(marker *Marker) (markerAndItem[*lsproto.Hover], bool) {
923+
if marker.Name == nil {
924+
return markerAndItem[*lsproto.Hover]{}, false
925+
}
926+
927+
params := &lsproto.HoverParams{
928+
TextDocument: lsproto.TextDocumentIdentifier{
929+
Uri: ls.FileNameToDocumentURI(f.activeFilename),
930+
},
931+
Position: marker.LSPosition,
932+
}
933+
934+
resMsg, result, resultOk := sendRequest(t, f, lsproto.TextDocumentHoverInfo, params)
935+
var prefix string
936+
if f.lastKnownMarkerName != nil {
937+
prefix = fmt.Sprintf("At marker '%s': ", *f.lastKnownMarkerName)
938+
} else {
939+
prefix = fmt.Sprintf("At position (Ln %d, Col %d): ", f.currentCaretPosition.Line, f.currentCaretPosition.Character)
940+
}
941+
if resMsg == nil {
942+
t.Fatalf(prefix+"Nil response received for quick info request", f.lastKnownMarkerName)
943+
}
944+
if !resultOk {
945+
t.Fatalf(prefix+"Unexpected response type for quick info request: %T", resMsg.AsResponse().Result)
946+
}
947+
948+
return markerAndItem[*lsproto.Hover]{Marker: marker, Item: result.Hover}, true
949+
})
950+
951+
getRange := func(item *lsproto.Hover) *lsproto.Range {
952+
if item == nil || item.Range == nil {
953+
return nil
954+
}
955+
return item.Range
956+
}
957+
958+
getTooltipLines := func(item, _prev *lsproto.Hover) []string {
959+
var result []string
960+
961+
if item.Contents.MarkupContent != nil {
962+
result = strings.Split(item.Contents.MarkupContent.Value, "\n")
963+
}
964+
if item.Contents.String != nil {
965+
result = strings.Split(*item.Contents.String, "\n")
966+
}
967+
if item.Contents.MarkedStringWithLanguage != nil {
968+
result = appendLinesForMarkedStringWithLanguage(result, item.Contents.MarkedStringWithLanguage)
969+
}
970+
if item.Contents.MarkedStrings != nil {
971+
for _, ms := range *item.Contents.MarkedStrings {
972+
if ms.MarkedStringWithLanguage != nil {
973+
result = appendLinesForMarkedStringWithLanguage(result, ms.MarkedStringWithLanguage)
974+
} else {
975+
result = append(result, *ms.String)
976+
}
977+
}
978+
}
979+
980+
return result
981+
}
982+
983+
f.baseline.addResult("QuickInfo", annotateContentWithTooltips(t, f, markersAndItems, "quickinfo", getRange, getTooltipLines))
984+
if jsonStr, err := core.StringifyJson(markersAndItems, "", " "); err == nil {
985+
f.baseline.content.WriteString(jsonStr)
986+
} else {
987+
t.Fatalf("Failed to stringify markers and items for baseline: %v", err)
988+
}
989+
baseline.Run(t, f.baseline.getBaselineFileName(), f.baseline.content.String(), baseline.Options{})
990+
}
991+
992+
func appendLinesForMarkedStringWithLanguage(result []string, ms *lsproto.MarkedStringWithLanguage) []string {
993+
result = append(result, "```"+ms.Language)
994+
result = append(result, ms.Value)
995+
result = append(result, "```")
996+
return result
997+
}
998+
906999
// Collects all named markers if provided, or defaults to anonymous ranges
9071000
func (f *FourslashTest) lookupMarkersOrGetRanges(t *testing.T, markers []string) []MarkerOrRange {
9081001
var referenceLocations []MarkerOrRange

0 commit comments

Comments
 (0)