Merge pull request #5983 from SaschaNaz/formatWhitespaces
Trim empty line whitespaces
This commit is contained in:
commit
a444be84c4
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
51
tests/cases/fourslash/formatDocumentWithTrivia.ts
Normal file
51
tests/cases/fourslash/formatDocumentWithTrivia.ts
Normal 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
|
||||
|
||||
`);
|
10
tests/cases/fourslash/formatSelectionWithTrivia2.ts
Normal file
10
tests/cases/fourslash/formatSelectionWithTrivia2.ts
Normal file
|
@ -0,0 +1,10 @@
|
|||
/// <reference path="fourslash.ts" />
|
||||
|
||||
/////*begin*/;
|
||||
////
|
||||
/////*end*/
|
||||
////
|
||||
|
||||
format.selection('begin', 'end');
|
||||
|
||||
verify.currentFileContentIs(";\n\n\n ");
|
Loading…
Reference in a new issue