Skip to content

Commit dc6ce07

Browse files
Folding Ranges implementation (#1326)
Co-authored-by: Jake Bailey <[email protected]>
1 parent db76964 commit dc6ce07

37 files changed

+2449
-7
lines changed

internal/fourslash/_scripts/convertFourslash.mts

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ function parseFourslashStatement(statement: ts.Statement): Cmd[] | undefined {
234234
return [{ kind: "verifyBaselineDiagnostics" }];
235235
case "navigateTo":
236236
return parseVerifyNavigateTo(callExpression.arguments);
237+
case "outliningSpansInCurrentFile":
238+
case "outliningHintSpansInCurrentFile":
239+
return parseOutliningSpansArgs(callExpression.arguments);
237240
}
238241
}
239242
// `goTo....`
@@ -2036,6 +2039,46 @@ function parseBaselineCallHierarchy(args: ts.NodeArray<ts.Expression>): Cmd {
20362039
};
20372040
}
20382041

2042+
function parseOutliningSpansArgs(args: readonly ts.Expression[]): [VerifyOutliningSpansCmd] | undefined {
2043+
if (args.length === 0) {
2044+
console.error("Expected at least one argument in verify.outliningSpansInCurrentFile");
2045+
return undefined;
2046+
}
2047+
2048+
let spans: string = "";
2049+
// Optional second argument for kind filter
2050+
let foldingRangeKind: string | undefined;
2051+
if (args.length > 1) {
2052+
const kindArg = getStringLiteralLike(args[1]);
2053+
if (!kindArg) {
2054+
console.error(`Expected string literal for outlining kind, got ${args[1].getText()}`);
2055+
return undefined;
2056+
}
2057+
switch (kindArg.text) {
2058+
case "comment":
2059+
foldingRangeKind = "lsproto.FoldingRangeKindComment";
2060+
break;
2061+
case "region":
2062+
foldingRangeKind = "lsproto.FoldingRangeKindRegion";
2063+
break;
2064+
case "imports":
2065+
foldingRangeKind = "lsproto.FoldingRangeKindImports";
2066+
break;
2067+
case "code":
2068+
break;
2069+
default:
2070+
console.error(`Unknown folding range kind: ${kindArg.text}`);
2071+
return undefined;
2072+
}
2073+
}
2074+
2075+
return [{
2076+
kind: "verifyOutliningSpans",
2077+
spans,
2078+
foldingRangeKind,
2079+
}];
2080+
}
2081+
20392082
function parseKind(expr: ts.Expression): string | undefined {
20402083
if (!ts.isStringLiteral(expr)) {
20412084
console.error(`Expected string literal for kind, got ${expr.getText()}`);
@@ -2513,6 +2556,12 @@ interface VerifyNoSignatureHelpForTriggerReasonCmd {
25132556
markers: string[];
25142557
}
25152558

2559+
interface VerifyOutliningSpansCmd {
2560+
kind: "verifyOutliningSpans";
2561+
spans: string;
2562+
foldingRangeKind?: string;
2563+
}
2564+
25162565
type Cmd =
25172566
| VerifyCompletionsCmd
25182567
| VerifyApplyCodeActionFromCompletionCmd
@@ -2536,7 +2585,15 @@ type Cmd =
25362585
| VerifyBaselineInlayHintsCmd
25372586
| VerifyImportFixAtPositionCmd
25382587
| VerifyDiagnosticsCmd
2539-
| VerifyBaselineDiagnosticsCmd;
2588+
| VerifyBaselineDiagnosticsCmd
2589+
| VerifyOutliningSpansCmd;
2590+
2591+
function generateVerifyOutliningSpans({ foldingRangeKind }: VerifyOutliningSpansCmd): string {
2592+
if (foldingRangeKind) {
2593+
return `f.VerifyOutliningSpans(t, ${foldingRangeKind})`;
2594+
}
2595+
return `f.VerifyOutliningSpans(t)`;
2596+
}
25402597

25412598
function generateVerifyCompletions({ marker, args, isNewIdentifierLocation, andApplyCodeActionArgs }: VerifyCompletionsCmd): string {
25422599
let expectedList: string;
@@ -2830,6 +2887,8 @@ function generateCmd(cmd: Cmd): string {
28302887
return generateSignatureHelpPresent(cmd);
28312888
case "verifyNoSignatureHelpForTriggerReason":
28322889
return generateNoSignatureHelpForTriggerReason(cmd);
2890+
case "verifyOutliningSpans":
2891+
return generateVerifyOutliningSpans(cmd);
28332892
default:
28342893
let neverCommand: never = cmd;
28352894
throw new Error(`Unknown command kind: ${neverCommand as Cmd["kind"]}`);

internal/fourslash/_scripts/manualTests.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@ renameForDefaultExport01
1515
tsxCompletion12
1616
jsDocFunctionSignatures2
1717
jsDocFunctionSignatures12
18+
outliningHintSpansForFunction
19+
getOutliningSpans
20+
outliningForNonCompleteInterfaceDeclaration
21+
incrementalParsingWithJsDoc

internal/fourslash/fourslash.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,55 @@ func (f *FourslashTest) VerifyBaselineWorkspaceSymbol(t *testing.T, query string
15211521
))
15221522
}
15231523

1524+
func (f *FourslashTest) VerifyOutliningSpans(t *testing.T, foldingRangeKind ...lsproto.FoldingRangeKind) {
1525+
params := &lsproto.FoldingRangeParams{
1526+
TextDocument: lsproto.TextDocumentIdentifier{
1527+
Uri: lsconv.FileNameToDocumentURI(f.activeFilename),
1528+
},
1529+
}
1530+
result := sendRequest(t, f, lsproto.TextDocumentFoldingRangeInfo, params)
1531+
if result.FoldingRanges == nil {
1532+
t.Fatalf("Nil response received for folding range request")
1533+
}
1534+
1535+
// Extract actual folding ranges from the result and filter by kind if specified
1536+
var actualRanges []*lsproto.FoldingRange
1537+
actualRanges = *result.FoldingRanges
1538+
if len(foldingRangeKind) > 0 {
1539+
targetKind := foldingRangeKind[0]
1540+
var filtered []*lsproto.FoldingRange
1541+
for _, r := range actualRanges {
1542+
if r.Kind != nil && *r.Kind == targetKind {
1543+
filtered = append(filtered, r)
1544+
}
1545+
}
1546+
actualRanges = filtered
1547+
}
1548+
1549+
if len(actualRanges) != len(f.Ranges()) {
1550+
t.Fatalf("verifyOutliningSpans failed - expected total spans to be %d, but was %d",
1551+
len(f.Ranges()), len(actualRanges))
1552+
}
1553+
1554+
slices.SortFunc(f.Ranges(), func(a, b *RangeMarker) int {
1555+
return lsproto.ComparePositions(a.LSPos(), b.LSPos())
1556+
})
1557+
1558+
for i, expectedRange := range f.Ranges() {
1559+
actualRange := actualRanges[i]
1560+
startPos := lsproto.Position{Line: actualRange.StartLine, Character: *actualRange.StartCharacter}
1561+
endPos := lsproto.Position{Line: actualRange.EndLine, Character: *actualRange.EndCharacter}
1562+
1563+
if lsproto.ComparePositions(startPos, expectedRange.LSRange.Start) != 0 ||
1564+
lsproto.ComparePositions(endPos, expectedRange.LSRange.End) != 0 {
1565+
t.Fatalf("verifyOutliningSpans failed - span %d has invalid positions:\n actual: start (%d,%d), end (%d,%d)\n expected: start (%d,%d), end (%d,%d)",
1566+
i+1,
1567+
actualRange.StartLine, *actualRange.StartCharacter, actualRange.EndLine, *actualRange.EndCharacter,
1568+
expectedRange.LSRange.Start.Line, expectedRange.LSRange.Start.Character, expectedRange.LSRange.End.Line, expectedRange.LSRange.End.Character)
1569+
}
1570+
}
1571+
}
1572+
15241573
func (f *FourslashTest) VerifyBaselineHover(t *testing.T) {
15251574
markersAndItems := core.MapFiltered(f.Markers(), func(marker *Marker) (markerAndItem[*lsproto.Hover], bool) {
15261575
if marker.Name == nil {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package fourslash_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/microsoft/typescript-go/internal/fourslash"
7+
"github.com/microsoft/typescript-go/internal/testutil"
8+
)
9+
10+
func TestCorreuptedTryExpressionsDontCrashGettingOutlineSpans(t *testing.T) {
11+
t.Parallel()
12+
13+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
14+
const content = `try[| {
15+
var x = [
16+
{% try[||] %}|][|{% except %}|]
17+
]
18+
} catch (e)[| {
19+
20+
}|]`
21+
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
22+
f.VerifyOutliningSpans(t)
23+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package fourslash_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/microsoft/typescript-go/internal/fourslash"
7+
"github.com/microsoft/typescript-go/internal/testutil"
8+
)
9+
10+
func TestGetOutliningForArrayDestructuring(t *testing.T) {
11+
t.Parallel()
12+
13+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
14+
const content = `const[| [
15+
a,
16+
b,
17+
c
18+
]|] =[| [
19+
1,
20+
2,
21+
3
22+
]|];
23+
const[| [
24+
[|[
25+
[|[
26+
[|[
27+
a,
28+
b,
29+
c
30+
]|]
31+
]|]
32+
]|],
33+
[|[
34+
a1,
35+
b1,
36+
c1
37+
]|]
38+
]|] =[| [
39+
[|[
40+
[|[
41+
[|[
42+
1,
43+
2,
44+
3
45+
]|]
46+
]|]
47+
]|],
48+
[|[
49+
1,
50+
2,
51+
3
52+
]|]
53+
]|]`
54+
f := fourslash.NewFourslash(t, nil /*capabilities*/, content)
55+
f.VerifyOutliningSpans(t)
56+
}

0 commit comments

Comments
 (0)