From 41a2a0371251937b77f80db0d0f407f93d880f55 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 28 Oct 2014 15:08:36 -0700 Subject: [PATCH] initial version of list item formatting --- src/services/formatting/format.ts | 107 +++++++++++++++++++----------- src/services/utilities.ts | 10 ++- 2 files changed, 75 insertions(+), 42 deletions(-) diff --git a/src/services/formatting/format.ts b/src/services/formatting/format.ts index 607b5c148e6..cec141bd875 100644 --- a/src/services/formatting/format.ts +++ b/src/services/formatting/format.ts @@ -123,16 +123,20 @@ module ts.formatting { return r => false; } else { - var sorted = errors.slice(0).filter(d => d.isParseError).sort((e1, e2) => e1.start - e2.start); - var index = 0; // TODO: set based on the range - var endIndex = sorted.length; // TODO: set based on the range - if (endIndex === 0 || index === sorted.length) { - // errors are outside the interesting span - always return false + // pick only errors that fall in range + var sorted = errors + .filter(d => d.isParseError && (d.start >= originalRange.pos && d.start + d.length < originalRange.end)) + .sort((e1, e2) => e1.start - e2.start); + + if (!sorted.length) { + // no errors in interesting span - always return false return r => false; } + + var index = 0; return r => { while (true) { - if (index >= endIndex) { + if (index >= sorted.length) { return false; } else { @@ -196,14 +200,24 @@ module ts.formatting { return 0; } + function getListItemIndentation(nodes: Node[], index: number, parentStartLine: number, options: EditorOptions): number { + Debug.assert(index >= 0 && index < nodes.length); + var node = nodes[index]; + var start = node.getStart(sourceFile); + var nodeStartLine = sourceFile.getLineAndCharacterFromPosition(start).line; + var startLinePosition = getStartPositionOfLine(nodeStartLine, sourceFile); + var shareLine = nodeStartLine === parentStartLine; + var column = SmartIndenter.findFirstNonWhitespaceColumn(startLinePosition, start, sourceFile, options); // ? + return shareLine && start !== column? -1 : column; + } + function processNode(node: Node, contextNode: Node, nodeStartLine: number, indentation: number) { - // TODO: skip nodes that has skipped or missing tokens - if (!rangeOverlapsWithRange(originalRange, node)) { + if (!rangeOverlapsWithStartEnd(originalRange, node.getStart(sourceFile), node.getEnd())) { return; } var commentIndentation = indentation; - var parentIndentation: DynamicIndentation = { + var nodeIndentation: DynamicIndentation = { getCommentIndentation: () => commentIndentation, getIndentation: () => indentation, recomputeIndentation: (lineAdded) => { @@ -218,10 +232,13 @@ module ts.formatting { var childContextNode = contextNode; forEachChild( node, - child => processChildNode(child, /*containingList*/ undefined, /*listElementIndex*/ -1), + child => { + processChildNode(child, undefined, /*containingList*/ undefined, /*listElementIndex*/ -1) + }, nodes => { + var inheritedIndentation: number = undefined; for (var i = 0, len = nodes.length; i < len; ++i) { - processChildNode(nodes[i], /*containingList*/ nodes, /*listElementIndex*/ i) + inheritedIndentation = processChildNode(nodes[i], inheritedIndentation, /*containingList*/ nodes, /*listElementIndex*/ i) } } ); @@ -238,14 +255,14 @@ module ts.formatting { ? indentation + options.IndentSize : indentation; - doConsumeTokenAndAdvanceScanner(tokenInfo, node, parentIndentation); + doConsumeTokenAndAdvanceScanner(tokenInfo, node, nodeIndentation); } /// Local functions - function processChildNode(child: Node, containingList: Node[], listElementIndex: number): void { + function processChildNode(child: Node, inheritedIndentation: number, containingList: Node[], listElementIndex: number): number { if (child.kind === SyntaxKind.Missing) { - return; + return inheritedIndentation; } var start = child.getStart(sourceFile); @@ -255,54 +272,66 @@ module ts.formatting { break; } - doConsumeTokenAndAdvanceScanner(tokenInfo, node, parentIndentation); + doConsumeTokenAndAdvanceScanner(tokenInfo, node, nodeIndentation); } if (!formattingScanner.isOnToken()) { - return; + return inheritedIndentation; } - var childStartLine = sourceFile.getLineAndCharacterFromPosition(start).line; - // determine child indentation - // TODO: share this code with SmartIndenter - var increaseIndentation = - childStartLine !== nodeStartLine && - !SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(node, child, childStartLine, sourceFile) && - SmartIndenter.shouldIndentChildNode(node, child); - + var childStart = sourceFile.getLineAndCharacterFromPosition(start); + var actualIndentation = inheritedIndentation; + + var isChildInRange = rangeOverlapsWithStartEnd(originalRange, start, child.getEnd()); + + var childIndentationValue: number; + if (containingList && !isChildInRange) { + // child is a list item that is not in span being formatted + // fetch actual indentation for the child item to push it downstream + // TODO: ensure that indentation is picked correctly + var actualIndentation = getListItemIndentation(containingList, listElementIndex, nodeStartLine, options); + if (actualIndentation !== -1) { + inheritedIndentation = actualIndentation; + } + var childIndentationValue = increaseIndentation || indentation; + } + else { + if (inheritedIndentation !== undefined) { + var childIndentationValue = inheritedIndentation; + } + else { + var shareLine = nodeStartLine === childStart.line; + var increaseIndentation = + !shareLine && + !SmartIndenter.childStartsOnTheSameLineWithElseInIfStatement(node, child, childStart.line, sourceFile) && + SmartIndenter.shouldIndentChildNode(node, child); + var childIndentationValue = increaseIndentation ? indentation + options.IndentSize : indentation; + } + } - var childIndentationValue = increaseIndentation ? indentation + options.IndentSize : indentation; var childIndentation: DynamicIndentation = { getIndentation: () => childIndentationValue, getCommentIndentation: () => childIndentationValue, recomputeIndentation: lineAdded => { - parentIndentation.recomputeIndentation(lineAdded); - var delta = getIndentationDelta(node, lineAdded); //? + nodeIndentation.recomputeIndentation(lineAdded); + var delta = getIndentationDelta(node, lineAdded); if (delta) { childIndentationValue += delta; } }, }; + // ensure that current token is inside child node if (isToken(child)) { var tokenInfo = formattingScanner.readTokenInfo(node); Debug.assert(tokenInfo.token.end === child.end); doConsumeTokenAndAdvanceScanner(tokenInfo, node, childIndentation); - return; - } - - - var newIndentation: number; - if (listElementIndex === -1) { - // child is not list element } else { - // child is a list element + processNode(child, childContextNode, childStart.line, childIndentationValue); + childContextNode = node; } - - - processNode(child, childContextNode, childStartLine, childIndentationValue); - childContextNode = node; + return inheritedIndentation; } function doConsumeTokenAndAdvanceScanner(currentTokenInfo: TokenInfo, parent: Node, indentation: DynamicIndentation): void { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 58f7f6f936a..8693b7ae230 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -13,9 +13,13 @@ module ts { return start <= range.pos && end >= range.end; } - export function rangeOverlapsWithRange(r1: TextRange, r2: TextRange): boolean { - var start = Math.max(r1.pos, r2.pos); - var end = Math.min(r1.end, r2.end); + export function rangeContainsStartEnd(range: TextRange, start: number, end: number): boolean { + return range.pos <= start && range.end >= end; + } + + export function rangeOverlapsWithStartEnd(r1: TextRange, start: number, end: number) { + var start = Math.max(r1.pos, start); + var end = Math.min(r1.end, end); return start < end; }