From 9726ba1198dddce9dc2d8a5508d1dc8904185444 Mon Sep 17 00:00:00 2001 From: uniqueiniquity Date: Tue, 8 Aug 2017 09:50:07 -0700 Subject: [PATCH] Add support for custom outlining regions --- src/compiler/types.ts | 4 ++ src/services/outliningElementsCollector.ts | 69 ++++++++++++++++++++++ 2 files changed, 73 insertions(+) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 608bc779042..4bf180c595a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2037,6 +2037,10 @@ namespace ts { end: -1; } + export interface RegionRange extends TextRange { + name?: string; + } + // represents a top level: { type } expression in a JSDoc comment. export interface JSDocTypeExpression extends TypeNode { kind: SyntaxKind.JSDocTypeExpression; diff --git a/src/services/outliningElementsCollector.ts b/src/services/outliningElementsCollector.ts index e0d99d1d271..3bd53f86301 100644 --- a/src/services/outliningElementsCollector.ts +++ b/src/services/outliningElementsCollector.ts @@ -6,6 +6,10 @@ namespace ts.OutliningElementsCollector { export function collectElements(sourceFile: SourceFile, cancellationToken: CancellationToken): OutliningSpan[] { const elements: OutliningSpan[] = []; let depth = 0; + const regions: RegionRange[] = []; + const regionText = "#region"; + const regionStart = new RegExp("// #region( .+| *)", "g"); + const regionEnd = new RegExp("// #endregion *"); walk(sourceFile); return elements; @@ -35,6 +39,18 @@ namespace ts.OutliningElementsCollector { } } + function addOutliningSpanRegions(regionSpan: RegionRange) { + if (regionSpan) { + const span: OutliningSpan = { + textSpan: createTextSpanFromBounds(regionSpan.pos, regionSpan.end), + hintSpan: createTextSpanFromBounds(regionSpan.pos, regionSpan.end), + bannerText: regionSpan.name, + autoCollapse: false, + }; + elements.push(span); + } + } + function addOutliningForLeadingCommentsForNode(n: Node) { const comments = ts.getLeadingCommentRangesOfNode(n, sourceFile); @@ -89,12 +105,65 @@ namespace ts.OutliningElementsCollector { return isFunctionBlock(node) && node.parent.kind !== SyntaxKind.ArrowFunction; } + function isRegionStart(range: CommentRange) { + const comment = sourceFile.text.substring(range.pos, range.end); + const result = comment.match(regionStart); + + if (result && result.length > 0) { + const name = result[0].substring(10).trim(); + if (name) { + return name; + } + else { + return regionText; + } + } + return ""; + } + + function isRegionEnd(range: CommentRange) { + const comment = sourceFile.text.substring(range.pos, range.end); + return comment.match(regionEnd); + } + + function addRegionsNearNode(n: Node) { + const comments = ts.getLeadingCommentRangesOfNode(n, sourceFile); + + if (n.kind !== SyntaxKind.SourceFile && comments) { + for (const currentComment of comments) { + cancellationToken.throwIfCancellationRequested(); + + if (currentComment.kind === SyntaxKind.SingleLineCommentTrivia) { + const name = isRegionStart(currentComment); + if (name) { + const region: RegionRange = { + pos: currentComment.pos, + end: currentComment.end, + name, + }; + regions.push(region); + } + else if (isRegionEnd(currentComment)) { + const region = regions.pop(); + + if (region) { + region.end = currentComment.end; + addOutliningSpanRegions(region); + } + } + } + } + } + } + function walk(n: Node): void { cancellationToken.throwIfCancellationRequested(); if (depth > maxDepth) { return; } + addRegionsNearNode(n); + if (isDeclaration(n)) { addOutliningForLeadingCommentsForNode(n); }