diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts
index e0d99d1d27165..03ae0529ca9d9 100644
--- a/src/services/outliningElementsCollector.ts
+++ b/src/services/outliningElementsCollector.ts
@@ -2,13 +2,17 @@
namespace ts.OutliningElementsCollector {
const collapseText = "...";
const maxDepth = 20;
+ const defaultLabel = "#region";
+ const regionMatch = new RegExp("^\\s*//\\s*#(end)?region(?:\\s+(.*))?$");
export function collectElements(sourceFile: SourceFile, cancellationToken: CancellationToken): OutliningSpan[] {
const elements: OutliningSpan[] = [];
let depth = 0;
+ const regions: OutliningSpan[] = [];
walk(sourceFile);
- return elements;
+ gatherRegions();
+ return elements.sort((span1, span2) => span1.textSpan.start - span2.textSpan.start);
/** If useFullStart is true, then the collapsing span includes leading whitespace, including linebreaks. */
function addOutliningSpan(hintSpanNode: Node, startElement: Node, endElement: Node, autoCollapse: boolean, useFullStart: boolean) {
@@ -89,6 +93,39 @@ namespace ts.OutliningElementsCollector {
return isFunctionBlock(node) && node.parent.kind !== SyntaxKind.ArrowFunction;
}
+ function gatherRegions(): void {
+ const lineStarts = sourceFile.getLineStarts();
+
+ for (let i = 0; i < lineStarts.length; i++) {
+ const currentLineStart = lineStarts[i];
+ const lineEnd = lineStarts[i + 1] - 1 || sourceFile.getEnd();
+ const comment = sourceFile.text.substring(currentLineStart, lineEnd);
+ const result = comment.match(regionMatch);
+
+ if (result && !isInComment(sourceFile, currentLineStart)) {
+ if (!result[1]) {
+ const start = sourceFile.getFullText().indexOf("//", currentLineStart);
+ const textSpan = createTextSpanFromBounds(start, lineEnd);
+ const region: OutliningSpan = {
+ textSpan,
+ hintSpan: textSpan,
+ bannerText: result[2] || defaultLabel,
+ autoCollapse: false
+ };
+ regions.push(region);
+ }
+ else {
+ const region = regions.pop();
+ if (region) {
+ region.textSpan.length = lineEnd - region.textSpan.start;
+ region.hintSpan.length = lineEnd - region.textSpan.start;
+ elements.push(region);
+ }
+ }
+ }
+ }
+ }
+
function walk(n: Node): void {
cancellationToken.throwIfCancellationRequested();
if (depth > maxDepth) {
diff --git a/tests/cases/fourslash/getOutliningSpansForRegions.ts b/tests/cases/fourslash/getOutliningSpansForRegions.ts
new file mode 100644
index 0000000000000..fcd71e29ef13f
--- /dev/null
+++ b/tests/cases/fourslash/getOutliningSpansForRegions.ts
@@ -0,0 +1,51 @@
+///
+
+////// region without label
+////[|// #region
+////
+////// #endregion|]
+////
+////// region without label with trailing spaces
+////[|// #region
+////
+////// #endregion|]
+////
+////// region with label
+////[|// #region label1
+////
+////// #endregion|]
+////
+////// region with extra whitespace in all valid locations
+//// [|// #region label2 label3
+////
+//// // #endregion|]
+////
+////// No space before directive
+////[|//#region label4
+////
+//////#endregion|]
+////
+////// Nested regions
+////[|// #region outer
+////
+////[|// #region inner
+////
+////// #endregion inner|]
+////
+////// #endregion outer|]
+////
+////// region delimiters not valid when there is preceding text on line
+//// test // #region invalid1
+////
+////test // #endregion
+////
+////// region delimiters not valid when in multiline comment
+/////*
+////// #region invalid2
+////*/
+////
+/////*
+////// #endregion
+////*/
+
+verify.outliningSpansInCurrentFile(test.ranges());
\ No newline at end of file
diff --git a/tests/cases/fourslash/getOutliningSpansForUnbalancedEndRegion.ts b/tests/cases/fourslash/getOutliningSpansForUnbalancedEndRegion.ts
new file mode 100644
index 0000000000000..c15e23eba7576
--- /dev/null
+++ b/tests/cases/fourslash/getOutliningSpansForUnbalancedEndRegion.ts
@@ -0,0 +1,10 @@
+///
+
+////// bottom-heavy region balance
+////[|// #region matched
+////
+////// #endregion matched|]
+////
+////// #endregion unmatched
+
+verify.outliningSpansInCurrentFile(test.ranges());
\ No newline at end of file
diff --git a/tests/cases/fourslash/getOutliningSpansForUnbalancedRegion.ts b/tests/cases/fourslash/getOutliningSpansForUnbalancedRegion.ts
new file mode 100644
index 0000000000000..f50aae713a52b
--- /dev/null
+++ b/tests/cases/fourslash/getOutliningSpansForUnbalancedRegion.ts
@@ -0,0 +1,11 @@
+///
+
+////// top-heavy region balance
+////// #region unmatched
+////
+////[|// #region matched
+////
+////// #endregion matched|]
+
+debugger;
+verify.outliningSpansInCurrentFile(test.ranges());
\ No newline at end of file