Merge pull request #5983 from SaschaNaz/formatWhitespaces

Trim empty line whitespaces
This commit is contained in:
Mohamed Hegazy 2016-01-07 16:43:21 -08:00
commit a444be84c4
4 changed files with 125 additions and 32 deletions

View file

@ -121,7 +121,7 @@ namespace ts.formatting {
function findOutermostParent(position: number, expectedTokenKind: SyntaxKind, sourceFile: SourceFile): Node {
let precedingToken = findPrecedingToken(position, sourceFile);
// when it is claimed that trigger character was typed at given position
// we verify that there is a token with a matching kind whose end is equal to position (because the character was just typed).
// If this condition is not hold - then trigger character was typed in some other context,
@ -151,7 +151,7 @@ namespace ts.formatting {
return current;
}
// Returns true if node is a element in some list in parent
// i.e. parent is class declaration with the list of members and node is one of members.
function isListElement(parent: Node, node: Node): boolean {
@ -198,7 +198,7 @@ namespace ts.formatting {
if (!errors.length) {
return rangeHasNoErrors;
}
// pick only errors that fall in range
let sorted = errors
.filter(d => rangeOverlapsWithStartEnd(originalRange, d.start, d.start + d.length))
@ -341,6 +341,14 @@ namespace ts.formatting {
processNode(enclosingNode, enclosingNode, startLine, undecoratedStartLine, initialIndentation, delta);
}
if (!formattingScanner.isOnToken()) {
let leadingTrivia = formattingScanner.getCurrentLeadingTrivia();
if (leadingTrivia) {
processTrivia(leadingTrivia, enclosingNode, enclosingNode, undefined);
trimTrailingWhitespacesForRemainingRange();
}
}
formattingScanner.close();
return edits;
@ -828,9 +836,7 @@ namespace ts.formatting {
}
// We need to trim trailing whitespace between the tokens if they were on different lines, and no rule was applied to put them on the same line
trimTrailingWhitespaces =
(rule.Operation.Action & (RuleAction.NewLine | RuleAction.Space)) &&
rule.Flag !== RuleFlags.CanDeleteNewLines;
trimTrailingWhitespaces = !(rule.Operation.Action & RuleAction.Delete) && rule.Flag !== RuleFlags.CanDeleteNewLines;
}
else {
trimTrailingWhitespaces = true;
@ -929,17 +935,41 @@ namespace ts.formatting {
continue;
}
let pos = lineEndPosition;
while (pos >= lineStartPosition && isWhiteSpace(sourceFile.text.charCodeAt(pos))) {
pos--;
}
if (pos !== lineEndPosition) {
Debug.assert(pos === lineStartPosition || !isWhiteSpace(sourceFile.text.charCodeAt(pos)));
recordDelete(pos + 1, lineEndPosition - pos);
let whitespaceStart = getTrailingWhitespaceStartPosition(lineStartPosition, lineEndPosition);
if (whitespaceStart !== -1) {
Debug.assert(whitespaceStart === lineStartPosition || !isWhiteSpace(sourceFile.text.charCodeAt(whitespaceStart - 1)));
recordDelete(whitespaceStart, lineEndPosition + 1 - whitespaceStart);
}
}
}
/**
* @param start The position of the first character in range
* @param end The position of the last character in range
*/
function getTrailingWhitespaceStartPosition(start: number, end: number) {
let pos = end;
while (pos >= start && isWhiteSpace(sourceFile.text.charCodeAt(pos))) {
pos--;
}
if (pos !== end) {
return pos + 1;
}
return -1;
}
/**
* Trimming will be done for lines after the previous range
*/
function trimTrailingWhitespacesForRemainingRange() {
let startPosition = previousRange ? previousRange.end : originalRange.pos;
let startLine = sourceFile.getLineAndCharacterOfPosition(startPosition).line;
let endLine = sourceFile.getLineAndCharacterOfPosition(originalRange.end).line;
trimTrailingWhitespacesForLines(startLine, endLine + 1, previousRange);
}
function newTextChange(start: number, len: number, newText: string): TextChange {
return { span: createTextSpan(start, len), newText }
}

View file

@ -5,21 +5,22 @@
namespace ts.formatting {
const standardScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.Standard);
const jsxScanner = createScanner(ScriptTarget.Latest, /*skipTrivia*/ false, LanguageVariant.JSX);
/**
* Scanner that is currently used for formatting
*/
let scanner: Scanner;
export interface FormattingScanner {
advance(): void;
isOnToken(): boolean;
readTokenInfo(n: Node): TokenInfo;
getCurrentLeadingTrivia(): TextRangeWithKind[];
lastTrailingTriviaWasNewLine(): boolean;
close(): void;
}
const enum ScanAction{
const enum ScanAction {
Scan,
RescanGreaterThanToken,
RescanSlashToken,
@ -37,19 +38,20 @@ namespace ts.formatting {
let wasNewLine: boolean = true;
let leadingTrivia: TextRangeWithKind[];
let trailingTrivia: TextRangeWithKind[];
let savedPos: number;
let lastScanAction: ScanAction;
let lastTokenInfo: TokenInfo;
return {
advance: advance,
readTokenInfo: readTokenInfo,
isOnToken: isOnToken,
advance,
readTokenInfo,
isOnToken,
getCurrentLeadingTrivia: () => leadingTrivia,
lastTrailingTriviaWasNewLine: () => wasNewLine,
close: () => {
Debug.assert(scanner !== undefined);
lastTokenInfo = undefined;
scanner.setText(undefined);
scanner = undefined;
@ -58,7 +60,7 @@ namespace ts.formatting {
function advance(): void {
Debug.assert(scanner !== undefined);
lastTokenInfo = undefined;
let isStarted = scanner.getStartPos() !== startPos;
@ -81,7 +83,7 @@ namespace ts.formatting {
let t: SyntaxKind;
let pos = scanner.getStartPos();
// Read leading trivia and token
while (pos < endPos) {
let t = scanner.getToken();
@ -122,10 +124,10 @@ namespace ts.formatting {
return false;
}
function shouldRescanJsxIdentifier(node: Node): boolean {
if (node.parent) {
switch(node.parent.kind) {
switch (node.parent.kind) {
case SyntaxKind.JsxAttribute:
case SyntaxKind.JsxOpeningElement:
case SyntaxKind.JsxClosingElement:
@ -133,7 +135,7 @@ namespace ts.formatting {
return node.kind === SyntaxKind.Identifier;
}
}
return false;
}
@ -142,7 +144,7 @@ namespace ts.formatting {
}
function shouldRescanTemplateToken(container: Node): boolean {
return container.kind === SyntaxKind.TemplateMiddle ||
return container.kind === SyntaxKind.TemplateMiddle ||
container.kind === SyntaxKind.TemplateTail;
}
@ -152,11 +154,11 @@ namespace ts.formatting {
function readTokenInfo(n: Node): TokenInfo {
Debug.assert(scanner !== undefined);
if (!isOnToken()) {
// scanner is not on the token (either advance was not called yet or scanner is already past the end position)
return {
leadingTrivia: leadingTrivia,
leadingTrivia,
trailingTrivia: undefined,
token: undefined
};
@ -164,7 +166,7 @@ namespace ts.formatting {
// normally scanner returns the smallest available token
// check the kind of context node to determine if scanner should have more greedy behavior and consume more text.
let expectedScanAction =
let expectedScanAction =
shouldRescanGreaterThanToken(n)
? ScanAction.RescanGreaterThanToken
: shouldRescanSlashToken(n)
@ -226,7 +228,7 @@ namespace ts.formatting {
if (trailingTrivia) {
trailingTrivia = undefined;
}
while(scanner.getStartPos() < endPos) {
while (scanner.getStartPos() < endPos) {
currentToken = scanner.scan();
if (!isTrivia(currentToken)) {
break;
@ -261,7 +263,7 @@ namespace ts.formatting {
function isOnToken(): boolean {
Debug.assert(scanner !== undefined);
let current = (lastTokenInfo && lastTokenInfo.token.kind) || scanner.getToken();
let startPos = (lastTokenInfo && lastTokenInfo.token.pos) || scanner.getStartPos();
return startPos < endPos && current !== SyntaxKind.EndOfFileToken && !isTrivia(current);

View file

@ -0,0 +1,51 @@
/// <reference path="fourslash.ts" />
////
////// whitespace below
////
////// whitespace above
////
////let x;
////
////// abc
////
////let y;
////
////// whitespace above again
////
////while (true) {
//// while (true) {
//// }
////
//// // whitespace above
////}
////
////// whitespace above again
////
////
format.document();
verify.currentFileContentIs(`
// whitespace below
// whitespace above
let x;
// abc
let y;
// whitespace above again
while (true) {
while (true) {
}
// whitespace above
}
// whitespace above again
`);

View file

@ -0,0 +1,10 @@
/// <reference path="fourslash.ts" />
/////*begin*/;
////
/////*end*/
////
format.selection('begin', 'end');
verify.currentFileContentIs(";\n\n\n ");