Skip to content

Commit f1713c9

Browse files
committed
Add outlining spans for import declarations
1 parent 0b902bf commit f1713c9

File tree

4 files changed

+61
-9
lines changed

4 files changed

+61
-9
lines changed

src/harness/fourslash.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2467,7 +2467,7 @@ Actual: ${stringify(fullActual)}`);
24672467
Harness.IO.log(stringify(spans));
24682468
}
24692469

2470-
public verifyOutliningSpans(spans: Range[], kind?: "comment" | "region" | "code") {
2470+
public verifyOutliningSpans(spans: Range[], kind?: "comment" | "region" | "code" | "import") {
24712471
const actual = this.languageService.getOutliningSpans(this.activeFile.fileName);
24722472

24732473
if (actual.length !== spans.length) {
@@ -4299,7 +4299,7 @@ namespace FourSlashInterface {
42994299
this.state.verifyCurrentNameOrDottedNameSpanText(text);
43004300
}
43014301

4302-
public outliningSpansInCurrentFile(spans: FourSlash.Range[], kind?: "comment" | "region" | "code") {
4302+
public outliningSpansInCurrentFile(spans: FourSlash.Range[], kind?: "comment" | "region" | "code" | "import") {
43034303
this.state.verifyOutliningSpans(spans, kind);
43044304
}
43054305

src/services/outliningElementsCollector.ts

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,35 @@ namespace ts.OutliningElementsCollector {
99

1010
function addNodeOutliningSpans(sourceFile: SourceFile, cancellationToken: CancellationToken, out: Push<OutliningSpan>): void {
1111
let depthRemaining = 40;
12-
sourceFile.forEachChild(function walk(n) {
12+
let current = 0;
13+
const statements = sourceFile.statements;
14+
const n = statements.length;
15+
while (current < n) {
16+
while (current < n && !isAnyImportSyntax(statements[current])) {
17+
visitNonImportNode(statements[current]);
18+
current++;
19+
}
20+
if (current === n) break;
21+
const firstImport = statements[current];
22+
while (current < n && isAnyImportSyntax(statements[current])) {
23+
visitImportNode(statements[current] as AnyImportSyntax, sourceFile, cancellationToken, out);
24+
current++;
25+
}
26+
const lastImport = current < n ? statements[current - 1] : statements[n - 1];
27+
if (lastImport !== firstImport) {
28+
out.push(createOutliningSpanFromBounds(findChildOfKind(firstImport, SyntaxKind.ImportKeyword, sourceFile)!.getStart(sourceFile), lastImport.getEnd(), OutliningSpanKind.Import));
29+
}
30+
}
31+
32+
function visitImportNode(node: AnyImportSyntax, sourceFile: SourceFile, cancellationToken: CancellationToken, out: Push<OutliningSpan>) {
33+
// Add outlining spans for comments if they exist
34+
addOutliningForLeadingCommentsForNode(node, sourceFile, cancellationToken, out);
35+
// Add outlining spans for the import statement itself if applicable
36+
const span = getOutliningSpanForNode(node, sourceFile);
37+
if (span) out.push(span);
38+
}
39+
40+
function visitNonImportNode(n: Node) {
1341
if (depthRemaining === 0) return;
1442
cancellationToken.throwIfCancellationRequested();
1543

@@ -23,17 +51,17 @@ namespace ts.OutliningElementsCollector {
2351
depthRemaining--;
2452
if (isIfStatement(n) && n.elseStatement && isIfStatement(n.elseStatement)) {
2553
// Consider an 'else if' to be on the same depth as the 'if'.
26-
walk(n.expression);
27-
walk(n.thenStatement);
54+
visitNonImportNode(n.expression);
55+
visitNonImportNode(n.thenStatement);
2856
depthRemaining++;
29-
walk(n.elseStatement);
57+
visitNonImportNode(n.elseStatement);
3058
depthRemaining--;
3159
}
3260
else {
33-
n.forEachChild(walk);
61+
n.forEachChild(visitNonImportNode);
3462
}
3563
depthRemaining++;
36-
});
64+
}
3765
}
3866

3967
function addRegionOutliningSpans(sourceFile: SourceFile, out: Push<OutliningSpan>): void {
@@ -149,6 +177,9 @@ namespace ts.OutliningElementsCollector {
149177
return spanForObjectOrArrayLiteral(n);
150178
case SyntaxKind.ArrayLiteralExpression:
151179
return spanForObjectOrArrayLiteral(n, SyntaxKind.OpenBracketToken);
180+
case SyntaxKind.ImportDeclaration:
181+
const importClause = (n as ImportDeclaration).importClause;
182+
return importClause && importClause.namedBindings && importClause.namedBindings.kind !== SyntaxKind.NamespaceImport ? spanForNode(importClause.namedBindings) : undefined;
152183
}
153184

154185
function spanForObjectOrArrayLiteral(node: Node, open: SyntaxKind.OpenBraceToken | SyntaxKind.OpenBracketToken = SyntaxKind.OpenBraceToken): OutliningSpan | undefined {

src/services/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -826,7 +826,8 @@ namespace ts {
826826
export const enum OutliningSpanKind {
827827
Comment = "comment",
828828
Region = "region",
829-
Code = "code"
829+
Code = "code",
830+
Import = "import"
830831
}
831832

832833
export const enum OutputFileType {
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/// <reference path="fourslash.ts"/>
2+
3+
4+
////[|import * as ns from "mod";
5+
////
6+
////import d from "mod";
7+
////import { a, b, c } from "mod";
8+
////
9+
////import r = require("mod");|]
10+
////
11+
////// statement
12+
////var x = 0;
13+
////
14+
////// another set of imports
15+
////[|import * as ns from "mod";
16+
////import d from "mod";
17+
////import { a, b, c } from "mod";
18+
////import r = require("mod");|]
19+
20+
verify.outliningSpansInCurrentFile(test.ranges(), "import");

0 commit comments

Comments
 (0)