From 082573066e97b1b5f30d6055d74af636a7debc8d Mon Sep 17 00:00:00 2001 From: Orta Therox Date: Wed, 19 Feb 2020 15:49:54 -0500 Subject: [PATCH 1/2] Adds floating block comments to the outlining spans response --- src/services/outliningElementsCollector.ts | 18 ++++++++++++++++++ ...tlineSpansBlockCommentsWithoutStatements.ts | 9 +++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/cases/fourslash/outlineSpansBlockCommentsWithoutStatements.ts diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index b6212e2a2d285..5b2eaeac8edb3 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -4,6 +4,7 @@ namespace ts.OutliningElementsCollector { const res: OutliningSpan[] = []; addNodeOutliningSpans(sourceFile, cancellationToken, res); addRegionOutliningSpans(sourceFile, res); + addTopLevelUnattachedCommentSpans(sourceFile, res); return res.sort((span1, span2) => span1.textSpan.start - span2.textSpan.start); } @@ -106,6 +107,23 @@ namespace ts.OutliningElementsCollector { return regionDelimiterRegExp.exec(lineText); } + function addTopLevelUnattachedCommentSpans(sourceFile: SourceFile, out: Push): void { + // Comments which are attached to statements would be included in addNodeOutliningSpans + if (sourceFile.statements.length > 0) return; + + // This will instead set up spans for the missing ones + forEach(getLeadingCommentRangesOfNode(sourceFile, sourceFile), (range) => { + // To not mess with // #region support + const isMultiline = sourceFile.text.substring(range.pos, 2) === "/*"; + if (!isMultiline) return; + + const span = createTextSpanFromBounds(range.pos, range.end); + const comment = sourceFile.text.substring(range.pos, range.end); + const outline = createOutliningSpan(span, OutliningSpanKind.Comment, span, /*autoCollapse*/ false, comment); + out.push(outline); + }); + } + function addOutliningForLeadingCommentsForNode(n: Node, sourceFile: SourceFile, cancellationToken: CancellationToken, out: Push): void { const comments = getLeadingCommentRangesOfNode(n, sourceFile); if (!comments) return; diff --git a/tests/cases/fourslash/outlineSpansBlockCommentsWithoutStatements.ts b/tests/cases/fourslash/outlineSpansBlockCommentsWithoutStatements.ts new file mode 100644 index 0000000000000..1d0fd91e78327 --- /dev/null +++ b/tests/cases/fourslash/outlineSpansBlockCommentsWithoutStatements.ts @@ -0,0 +1,9 @@ +/// + +// #22732 + +////[|/* +///// * Some text +//// */|] + +verify.outliningHintSpansInCurrentFile(test.ranges()); From 39b0cf199ada9656619842c056fd7591a7dafb2b Mon Sep 17 00:00:00 2001 From: orta therox Date: Mon, 24 Feb 2020 14:53:29 -0500 Subject: [PATCH 2/2] Only use one route for grabbing outline nodes, which now includes special casing the EOF token --- src/harness/fourslashImpl.ts | 25 ++++++++++++++++--- src/services/outliningElementsCollector.ts | 23 +++-------------- tests/cases/fourslash/fourslash.ts | 2 +- ...ansTrailingBlockCommentsAfterStatements.ts | 10 ++++++++ .../server/getOutliningSpansForRegions.ts | 2 +- 5 files changed, 36 insertions(+), 26 deletions(-) create mode 100644 tests/cases/fourslash/outlineSpansTrailingBlockCommentsAfterStatements.ts diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 3be657d4cd02b..84d4e753ad02f 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -2500,18 +2500,35 @@ namespace FourSlash { public printOutliningSpans() { const spans = this.languageService.getOutliningSpans(this.activeFile.fileName); - Harness.IO.log(`Outlining spans (${spans.length} items)`); + Harness.IO.log(`Outlining spans (${spans.length} items)\nResults:`); Harness.IO.log(stringify(spans)); + this.printOutliningSpansInline(spans); + } + + private printOutliningSpansInline(spans: ts.OutliningSpan[]) { + const allSpanInsets = [] as { text: string, pos: number }[]; + let annotated = this.activeFile.content; + ts.forEach(spans, span => { + allSpanInsets.push({ text: "[|", pos: span.textSpan.start }); + allSpanInsets.push({ text: "|]", pos: span.textSpan.start + span.textSpan.length }); + }); + + const reverseSpans = allSpanInsets.sort((l, r) => r.pos - l.pos); + ts.forEach(reverseSpans, span => { + annotated = annotated.slice(0, span.pos) + span.text + annotated.slice(span.pos); + }); + Harness.IO.log(`\nMockup:\n${annotated}`); } public verifyOutliningSpans(spans: Range[], kind?: "comment" | "region" | "code" | "imports") { const actual = this.languageService.getOutliningSpans(this.activeFile.fileName); - if (actual.length !== spans.length) { - this.raiseError(`verifyOutliningSpans failed - expected total spans to be ${spans.length}, but was ${actual.length}`); + const filterActual = ts.filter(actual, f => kind === undefined ? true : f.kind === kind); + if (filterActual.length !== spans.length) { + this.raiseError(`verifyOutliningSpans failed - expected total spans to be ${spans.length}, but was ${actual.length}\n\nFound Spans:\n\n${this.printOutliningSpansInline(actual)}`); } - ts.zipWith(spans, actual, (expectedSpan, actualSpan, i) => { + ts.zipWith(spans, filterActual, (expectedSpan, actualSpan, i) => { if (expectedSpan.pos !== actualSpan.textSpan.start || expectedSpan.end !== ts.textSpanEnd(actualSpan.textSpan)) { return this.raiseError(`verifyOutliningSpans failed - span ${(i + 1)} expected: (${expectedSpan.pos},${expectedSpan.end}), actual: (${actualSpan.textSpan.start},${ts.textSpanEnd(actualSpan.textSpan)})`); } diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index 5b2eaeac8edb3..ab8cfb3f6c33e 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -4,14 +4,14 @@ namespace ts.OutliningElementsCollector { const res: OutliningSpan[] = []; addNodeOutliningSpans(sourceFile, cancellationToken, res); addRegionOutliningSpans(sourceFile, res); - addTopLevelUnattachedCommentSpans(sourceFile, res); return res.sort((span1, span2) => span1.textSpan.start - span2.textSpan.start); } function addNodeOutliningSpans(sourceFile: SourceFile, cancellationToken: CancellationToken, out: Push): void { let depthRemaining = 40; let current = 0; - const statements = sourceFile.statements; + // Includes the EOF Token so that comments which aren't attached to statements are included + const statements = [...sourceFile.statements, sourceFile.endOfFileToken]; const n = statements.length; while (current < n) { while (current < n && !isAnyImportSyntax(statements[current])) { @@ -34,7 +34,7 @@ namespace ts.OutliningElementsCollector { if (depthRemaining === 0) return; cancellationToken.throwIfCancellationRequested(); - if (isDeclaration(n)) { + if (isDeclaration(n) || n.kind === SyntaxKind.EndOfFileToken) { addOutliningForLeadingCommentsForNode(n, sourceFile, cancellationToken, out); } @@ -107,23 +107,6 @@ namespace ts.OutliningElementsCollector { return regionDelimiterRegExp.exec(lineText); } - function addTopLevelUnattachedCommentSpans(sourceFile: SourceFile, out: Push): void { - // Comments which are attached to statements would be included in addNodeOutliningSpans - if (sourceFile.statements.length > 0) return; - - // This will instead set up spans for the missing ones - forEach(getLeadingCommentRangesOfNode(sourceFile, sourceFile), (range) => { - // To not mess with // #region support - const isMultiline = sourceFile.text.substring(range.pos, 2) === "/*"; - if (!isMultiline) return; - - const span = createTextSpanFromBounds(range.pos, range.end); - const comment = sourceFile.text.substring(range.pos, range.end); - const outline = createOutliningSpan(span, OutliningSpanKind.Comment, span, /*autoCollapse*/ false, comment); - out.push(outline); - }); - } - function addOutliningForLeadingCommentsForNode(n: Node, sourceFile: SourceFile, cancellationToken: CancellationToken, out: Push): void { const comments = getLeadingCommentRangesOfNode(n, sourceFile); if (!comments) return; diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index d44148972fbf9..beb9d0c4d841f 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -317,7 +317,7 @@ declare namespace FourSlashInterface { baselineQuickInfo(): void; baselineSmartSelection(): void; nameOrDottedNameSpanTextIs(text: string): void; - outliningSpansInCurrentFile(spans: Range[]): void; + outliningSpansInCurrentFile(spans: Range[], kind?: "comment" | "region" | "code" | "imports"): void; outliningHintSpansInCurrentFile(spans: Range[]): void; todoCommentsInCurrentFile(descriptors: string[]): void; matchingBracePositionInCurrentFile(bracePosition: number, expectedMatchPosition: number): void; diff --git a/tests/cases/fourslash/outlineSpansTrailingBlockCommentsAfterStatements.ts b/tests/cases/fourslash/outlineSpansTrailingBlockCommentsAfterStatements.ts new file mode 100644 index 0000000000000..2397be94b56f1 --- /dev/null +++ b/tests/cases/fourslash/outlineSpansTrailingBlockCommentsAfterStatements.ts @@ -0,0 +1,10 @@ +/// + +// #22732 + +////console.log(0); +////[|/* +///// * Some text +//// */|] + +verify.outliningHintSpansInCurrentFile(test.ranges()); diff --git a/tests/cases/fourslash/server/getOutliningSpansForRegions.ts b/tests/cases/fourslash/server/getOutliningSpansForRegions.ts index 81d2409cf9db7..dbfe68204dc75 100644 --- a/tests/cases/fourslash/server/getOutliningSpansForRegions.ts +++ b/tests/cases/fourslash/server/getOutliningSpansForRegions.ts @@ -48,4 +48,4 @@ ////// #endregion ////*/ -verify.outliningSpansInCurrentFile(test.ranges(), "region"); +verify.outliningSpansInCurrentFile(test.ranges(), "region"); \ No newline at end of file