Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Refactor some rules #2359

Merged
merged 9 commits into from
Mar 27, 2017
Merged

Refactor some rules #2359

merged 9 commits into from
Mar 27, 2017

Conversation

ajafff
Copy link
Contributor

@ajafff ajafff commented Mar 16, 2017

PR checklist

  • Addresses an existing issue: #0000
  • New feature, bugfix, or enhancement
    • Includes tests
  • Documentation update

Overview of change:

Refactored some of the rather simple rules to WalkContext or AbstractWalker.
no-trailing-whitespace is the only rule where the logic changes: now it looks for trailing whitespace in the source text and iff there are lines found, it searches the AST for template spans and comments.
[rule-change] no-trailing-whitespace now checks template strings by default. Use the new options ignore-template-strings to restore the old behavior.
[new-rule-option] added ignore-template-strings to no-trailing-whitespace
[enhancement] fixer of quotemark unescapes original quotemark (e.g. '\'' -> "'")

Refactoring is not done yet, but the perf improvement already looks pretty good: (linting this project with itself):
master: 7.7s
this PR: 6.7s

@jmlopez-rod
Copy link
Contributor

@ajafff how do you get the timings? Are you taking averages of runs or just a sample? I'm trying to see what's the best way of measuring performance on my rules so that I can change them to using the abstract walker but I can't really get a hold of a number.

@@ -36,35 +37,41 @@ export class Rule extends Lint.Rules.AbstractRule {
public static FAILURE_STRING = "The internal 'module' syntax is deprecated, use the 'namespace' keyword instead.";

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithWalker(new NoInternalModuleWalker(sourceFile, this.getOptions()));
return this.applyWithWalker(new NoInternalModuleWalker(sourceFile, this.ruleName, undefined));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make options an optional parameter instead of using undefined

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never mind, it doesn't seem like there's any way to do this in a type-safe way.

const possibleFailures: ts.TextRange[] = [];
const sourceFile = ctx.sourceFile;
const text = sourceFile.text;
for (const range of getLineRanges(sourceFile)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do it with just a regexp:

const rgx = /[^\S\n]+(\r?\n|$)/g; // Non-newline spaces, then a newline or EOF
let match: RegExpExecArray | null;
while (match = rgx.exec(text)) {
    const end = text.indexOf("\n", match.index);
    possibleFailures.push({
        pos: match.index,
        end: end === -1 ? text.length : end
    });
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your solution looks nicer at first glance. It seems to include \r in the range of the failure, though.

I just added a new property contentLength to getLineRanges which has already stripped \r?\n. The regex is now even simpler.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be done without indexOf because enough information is returned by exec. Then it's easier to use len instead of end, so you end up with:

function walk(ctx: Lint.WalkContext<IgnoreOption>) {
    const { options, sourceFile } = ctx;
    const possibleFailures = getPossibleFailures(sourceFile.text);
    if (possibleFailures.length === 0) {
        return;
    }

    const excludedRanges = options === IgnoreOption.None
                           ? getTemplateRanges(sourceFile)
                           : getExcludedRanges(sourceFile, options);
    for (const { pos, len } of possibleFailures) {
        if (!excludedRanges.some((range) => range.pos < pos && pos < range.end)) {
            ctx.addFailureAt(pos, len, Rule.FAILURE_STRING, ctx.createFix(Lint.Replacement.deleteText(pos, len)));
        }
    }
}

function getPossibleFailures(text: string): Array<{ pos: number, len: number }> {
    const result = [];
    const rgx = /[^\S\n]+(\r?\n|$)/g;
    let match: RegExpExecArray | null;
    while (match = rgx.exec(text)) {
        const matched = match[0];
        result.push({ pos: match.index, len: matched.endsWith("\n") ? matched.length - 1 : matched.length });
    }
    return result;
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, could this rule have an option to not ignore template strings?

@ajafff
Copy link
Contributor Author

ajafff commented Mar 17, 2017

@jmlopez-rod The easy way is to execute yarn lint:from-bin (or something similar like time bin/tslint ...) multiple times the get the average. Most of the time the measured times don't differ much. Then build the revision you want to compare to and execute it again.

When you want to measure the time without I/O, you need to use the profiler in node inspector: node --debug-brk --inspect path/to/tslint

const match = text.substr(line.pos, line.contentLength).match(/\s+$/);
if (match !== null) {
possibleFailures.push({
end: line.pos + line.contentLength,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is why I don't like object-literal-sort-keys: It doesn't make sense to have the keys in alphabetical order here.

[rule-change] `no-trailing-whitespace` now checks template strings by default. Use the new options `ignore-template-strings` to restore the old behavior.
[new-rule-option] added `ignore-template-strings` to `no-trailing-whitespace`
@ajafff
Copy link
Contributor Author

ajafff commented Mar 23, 2017

@andy-hanson I changed no-trailing-whitespace to check template strings by default and added an option to ignore them.

Copy link
Contributor

@adidahiya adidahiya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for trailing whitespace behavior change

yarn.lock Outdated
version "1.2.2"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-1.2.2.tgz#7e165c601367b9f89200b97ff47d9e38d1a6e4c8"

tsutils@^1.4.0:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

multiple tsutils again :(

I've noticed yarn do this often lately when I try to upgrade a package (seems like a bug). I've usually fixed it by deleting all of the entries for a particular package (tsutils here) and running yarn again; it then resolves them to a single version.

@adidahiya
Copy link
Contributor

oh, and yarn.lock has a conflict anyway.

otherwise the PR looks good to go

@ajafff
Copy link
Contributor Author

ajafff commented Mar 27, 2017

@adidahiya conflicts in yarn.lock are resolved now

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants