Skip to content

Commit

Permalink
Add a relax option "avoid-escape" for quotemark
Browse files Browse the repository at this point in the history
This rule allow to use "other" quote mark for escaping.
It is similar to the "escape" option of JSCS or the "avoid-escape" option of ESLint.

> Allow the "other" quote mark to be used, but only to avoid having to escape.
>
> from "JSCS - validateQuoteMarks rule"
> http://jscs.info/rule/validateQuoteMarks.html

> The third parameter enables an exception to the rule to avoid escaping
> quotes.
>
> from "Rule quotes - ESLint - Pluggable JavaScript linter"
> http://eslint.org/docs/rules/quotes

Related palantir#543
  • Loading branch information
Kuniwak committed Sep 18, 2015
1 parent 28bb97f commit 95cd790
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 19 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,10 @@ A sample configuration file with all options is available [here](https://github.
* `"check-else"` checks that `else` is on the same line as the closing brace for `if`
* `"check-open-brace"` checks that an open brace falls on the same line as its preceding expression.
* `"check-whitespace"` checks preceding whitespace for the specified tokens.
* `quotemark` enforces consistent single or double quoted string literals. Rule options (one is required):
* `quotemark` enforces consistent single or double quoted string literals. Rule options (at least one of `"double"` or `"single"` is required):
* `"single"` enforces single quotes
* `"double"` enforces double quotes
* `"avoid-escape"` allows you to use the "other" quotemark in cases where escaping would normally be required. For example, `[true, "double", "avoid-escape"]` would not report a failure on the string literal `'Hello "World"'`.
* `radix` enforces the radix parameter of `parseInt`
* `semicolon` enforces semicolons at the end of every statement.
* `sort-object-literal-keys` checks that keys in object literals are declared in alphabetical order
Expand Down
2 changes: 1 addition & 1 deletion docs/sample.tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
"check-else",
"check-whitespace"
],
"quotemark": [true, "double"],
"quotemark": [true, "double", "avoid-escape"],
"radix": true,
"semicolon": true,
"sort-object-literal-keys": true,
Expand Down
33 changes: 18 additions & 15 deletions src/rules/quotemarkRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,21 @@ export class Rule extends Lint.Rules.AbstractRule {
}

class QuoteWalker extends Lint.RuleWalker {
private quoteMark: QuoteMark;
private quoteMark = QuoteMark.DOUBLE_QUOTES;
private avoidEscape: boolean;

constructor(sourceFile: ts.SourceFile, options: Lint.IOptions) {
super(sourceFile, options);

const quoteMarkString = this.getOptions()[0];
const ruleArguments = this.getOptions();
const quoteMarkString = ruleArguments[0];
if (quoteMarkString === "single") {
this.quoteMark = QuoteMark.SINGLE_QUOTES;
} else {
this.quoteMark = QuoteMark.DOUBLE_QUOTES;
}

this.avoidEscape = ruleArguments.indexOf("avoid-escape") > 0;
}

public visitNode(node : ts.Node) {
Expand All @@ -57,8 +61,6 @@ class QuoteWalker extends Lint.RuleWalker {
}

private handleNode(node: ts.Node) {
let failure: Lint.RuleFailure;

if (node.kind === ts.SyntaxKind.StringLiteral) {
const text = node.getText();
const width = node.getWidth();
Expand All @@ -67,19 +69,20 @@ class QuoteWalker extends Lint.RuleWalker {
const firstCharacter = text.charAt(0);
const lastCharacter = text.charAt(text.length - 1);

if (this.quoteMark === QuoteMark.SINGLE_QUOTES) {
if (firstCharacter !== "'" || lastCharacter !== "'") {
failure = this.createFailure(position, width, Rule.SINGLE_QUOTE_FAILURE);
}
} else if (this.quoteMark === QuoteMark.DOUBLE_QUOTES) {
if (firstCharacter !== "\"" || lastCharacter !== "\"") {
failure = this.createFailure(position, width, Rule.DOUBLE_QUOTE_FAILURE);
const expectedQuoteMark = (this.quoteMark === QuoteMark.SINGLE_QUOTES) ? "'" : "\"";

if (firstCharacter !== expectedQuoteMark || lastCharacter !== expectedQuoteMark) {
// allow the "other" quote mark to be used, but only to avoid having to escape
const includesOtherQuoteMark = text.slice(1, -1).indexOf(expectedQuoteMark) !== -1;

if (!(this.avoidEscape && includesOtherQuoteMark)) {
const failureMessage = (this.quoteMark === QuoteMark.SINGLE_QUOTES)
? Rule.SINGLE_QUOTE_FAILURE
: Rule.DOUBLE_QUOTE_FAILURE;

this.addFailure(this.createFailure(position, width, failureMessage));
}
}
}

if (failure != null) {
this.addFailure(failure);
}
}
}
2 changes: 2 additions & 0 deletions test/files/rules/quotemark.test.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
var single = 'single';
var doublee = "married";
var singleWithinDouble = "'singleWithinDouble'";
var doubleWithinSingle = '"doubleWithinSingle"';
28 changes: 26 additions & 2 deletions test/rules/quotemarkRuleTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,38 @@ describe("<quotemark>", () => {

it("enforces single quotes", () => {
const actualFailures = Lint.Test.applyRuleOnFile(fileName, QuoteMarkRule, [true, "single"]);
const expectedFailures = [
Lint.Test.createFailure(fileName, [2, 19], [2, 28], singleFailureString),
Lint.Test.createFailure(fileName, [3, 26], [3, 48], singleFailureString)
];

assert.equal(actualFailures.length, 2);
assert.isTrue(actualFailures[0].equals(expectedFailures[0]));
assert.isTrue(actualFailures[1].equals(expectedFailures[1]));
});

it("enforces double quotes", () => {
const actualFailures = Lint.Test.applyRuleOnFile(fileName, QuoteMarkRule, [true, "double"]);
const expectedFailures = [
Lint.Test.createFailure(fileName, [1, 14], [1, 22], doubleFailureString),
Lint.Test.createFailure(fileName, [4, 26], [4, 48], doubleFailureString)
];

assert.equal(actualFailures.length, 2);
assert.isTrue(actualFailures[0].equals(expectedFailures[0]));
assert.isTrue(actualFailures[1].equals(expectedFailures[1]));
});

it("enforces single quotes but allow other quote marks to avoid having to escape", () => {
const actualFailures = Lint.Test.applyRuleOnFile(fileName, QuoteMarkRule, [true, "single", "avoid-escape"]);
const expectedFailure = Lint.Test.createFailure(fileName, [2, 19], [2, 28], singleFailureString);

assert.equal(actualFailures.length, 1);
assert.isTrue(actualFailures[0].equals(expectedFailure));
});

it("enforces double quotes", () => {
const actualFailures = Lint.Test.applyRuleOnFile(fileName, QuoteMarkRule, [true, "double"]);
it("enforces double quotes but allow other quote marks to avoid having to escape", () => {
const actualFailures = Lint.Test.applyRuleOnFile(fileName, QuoteMarkRule, [true, "double", "avoid-escape"]);
const expectedFailure = Lint.Test.createFailure(fileName, [1, 14], [1, 22], doubleFailureString);

assert.equal(actualFailures.length, 1);
Expand Down

0 comments on commit 95cd790

Please sign in to comment.