diff --git a/src/rules/whitespaceRule.ts b/src/rules/whitespaceRule.ts index 775c5893a92..2dd286ceeb1 100644 --- a/src/rules/whitespaceRule.ts +++ b/src/rules/whitespaceRule.ts @@ -26,6 +26,7 @@ const OPTION_MODULE = "check-module"; const OPTION_SEPARATOR = "check-separator"; const OPTION_TYPE = "check-type"; const OPTION_TYPECAST = "check-typecast"; +const OPTION_PREBLOCK = "check-preblock"; export class Rule extends Lint.Rules.AbstractRule { /* tslint:disable:object-literal-sort-keys */ @@ -42,13 +43,14 @@ export class Rule extends Lint.Rules.AbstractRule { * \`"check-module"\` checks for whitespace in import & export statements. * \`"check-separator"\` checks for whitespace after separator tokens (\`,\`/\`;\`). * \`"check-type"\` checks for whitespace before a variable type specification. - * \`"check-typecast"\` checks for whitespace between a typecast and its target.`, + * \`"check-typecast"\` checks for whitespace between a typecast and its target. + * \`"check-preblock"\` checks for whitespace before the opening brace of a block`, options: { type: "array", items: { type: "string", enum: ["check-branch", "check-decl", "check-operator", "check-module", - "check-separator", "check-type", "check-typecast"], + "check-separator", "check-type", "check-typecast", "check-preblock"], }, minLength: 0, maxLength: 7, @@ -87,7 +89,7 @@ class WhitespaceWalker extends Lint.SkippableTokenAwareRuleWalker { if (tokenKind === ts.SyntaxKind.WhitespaceTrivia || tokenKind === ts.SyntaxKind.NewLineTrivia) { prevTokenShouldBeFollowedByWhitespace = false; } else if (prevTokenShouldBeFollowedByWhitespace) { - this.addFailureAt(startPos, 1, Rule.FAILURE_STRING); + this.addMissingWhitespaceErrorAt(startPos); prevTokenShouldBeFollowedByWhitespace = false; } @@ -154,6 +156,13 @@ class WhitespaceWalker extends Lint.SkippableTokenAwareRuleWalker { super.visitBinaryExpression(node); } + protected visitBlock(block: ts.Block) { + if (this.hasOption(OPTION_PREBLOCK)) { + this.checkForTrailingWhitespace(block.getFullStart()); + } + super.visitBlock(block); + } + // check for spaces between ternary operator symbols public visitConditionalExpression(node: ts.ConditionalExpression) { if (this.hasOption(OPTION_OPERATOR)) { @@ -261,7 +270,12 @@ class WhitespaceWalker extends Lint.SkippableTokenAwareRuleWalker { if (nextTokenType !== ts.SyntaxKind.WhitespaceTrivia && nextTokenType !== ts.SyntaxKind.NewLineTrivia && nextTokenType !== ts.SyntaxKind.EndOfFileToken) { - this.addFailureAt(position, 1, Rule.FAILURE_STRING); + this.addMissingWhitespaceErrorAt(position); } } + + private addMissingWhitespaceErrorAt(position: number) { + const fix = this.createFix(this.appendText(position, " ")); + this.addFailureAt(position, 1, Rule.FAILURE_STRING, fix); + } } diff --git a/test/rules/whitespace/all/test.js.lint b/test/rules/whitespace/all/test.js.lint index 314a6cd2fe8..c4cc76957c6 100644 --- a/test/rules/whitespace/all/test.js.lint +++ b/test/rules/whitespace/all/test.js.lint @@ -86,3 +86,29 @@ export function each(obj, iterator, context) { export {each as forEach}; import "libE"; + +function foobar(){} + ~ [missing whitespace] + +function foorbar() +{} + +if (){ + ~ [missing whitespace] + // +} else{} + ~ [missing whitespace] + +if () +{} +else +{} + +/* some comment */{ + // some code with a preceding comment +} + +{ + const foo = 123; + // code that just wants to be encapsulated in a block scope +} diff --git a/test/rules/whitespace/all/test.ts.fix b/test/rules/whitespace/all/test.ts.fix new file mode 100644 index 00000000000..091a21af295 --- /dev/null +++ b/test/rules/whitespace/all/test.ts.fix @@ -0,0 +1,94 @@ +import ast = AST; +module M { + export var ast = AST; + + var x: number; + + var y = (x === 10) ? 1 : 2; + + var zz = (y === 4); + + var z = y; + + var a, b; + + switch (x) { + case 1: break; + default: break; + } + + for (x = 1; x < 2; ++x) { + goto: console.log("hi"); + } + + while (i < 1) { + ++i; + } + + var q; + q.forEach(() => 3); + q.forEach(() => { + return 3; + }); + + var r: () => string; + var s: new () => string; + var a = "10"; + var a = "10"; +} + +var a; + +export = a; + +a.then(() => { + return 1; +}).if(() => { + return 1; +}); + +var name = "something"; +var test = ` + +