@coffeelint/cli
Version:
Lint your CoffeeScript
152 lines (136 loc) • 4.91 kB
JavaScript
(function() {
var SpaceOperators,
indexOf = [].indexOf;
module.exports = SpaceOperators = (function() {
class SpaceOperators {
constructor() {
this.callTokens = []; // A stack tracking the call token pairs.
this.parenTokens = []; // A stack tracking the parens token pairs.
this.interpolationLevel = 0;
this.isParam = 0;
}
lintToken(token, tokenApi) {
var rest, type;
[type, ...rest] = token;
// These just keep track of state
if (type === 'CALL_START' || type === 'CALL_END') {
this.trackCall(token, tokenApi);
return;
}
if (type === 'PARAM_START' || type === 'PARAM_END') {
this.trackParams(token, tokenApi);
return;
}
if (type === 'STRING_START' || type === 'STRING_END') {
this.trackParens(token, tokenApi);
return;
}
// These may return errors
if (type === '+' || type === '-') {
return this.lintPlus(token, tokenApi);
} else {
return this.lintMath(token, tokenApi);
}
}
lintPlus(token, tokenApi) {
var isUnary, notFirstToken, p, ref, unaries;
// We can't check this inside of interpolations right now, because the
// plusses used for the string type co-ercion are marked not spaced.
if (this.isInInterpolation() || this.isInExtendedRegex()) {
return null;
}
p = tokenApi.peek(-1);
unaries = ['TERMINATOR', '(', '=', '-', '+', ',', 'CALL_START', 'INDEX_START', '..', '...', 'COMPARE', 'IF', 'THROW', '&', '^', '|', '&&', '||', 'POST_IF', ':', '[', 'INDENT', 'COMPOUND_ASSIGN', 'RETURN', 'MATH', 'BY', 'LEADING_WHEN'];
isUnary = !p ? false : (ref = p[0], indexOf.call(unaries, ref) >= 0);
notFirstToken = p || (token.spaced != null) || token.newLine;
if (notFirstToken && ((isUnary && (token.spaced != null)) || (!isUnary && !token.newLine && (!token.spaced || (p && !p.spaced))))) {
return {
token,
context: token[1]
};
} else {
return null;
}
}
lintMath(token, tokenApi) {
var default_parameters, p;
default_parameters = tokenApi.config[this.rule.name].default_parameters;
p = tokenApi.peek(-1);
if (!default_parameters && this.isParam > 0 && token[0] === '=') {
if (token.spaced || (p && p.spaced)) {
return {
token,
context: token[1]
};
} else {
return null;
}
} else if (!token.newLine && (!token.spaced || (p && !p.spaced))) {
return {
token,
context: token[1]
};
} else {
return null;
}
}
isInExtendedRegex() {
var i, len, ref, t;
ref = this.callTokens;
for (i = 0, len = ref.length; i < len; i++) {
t = ref[i];
if (t.isRegex) {
return true;
}
}
return false;
}
isInInterpolation() {
return this.interpolationLevel > 0;
}
trackCall(token, tokenApi) {
var p;
if (token[0] === 'CALL_START') {
p = tokenApi.peek(-1);
// Track regex calls, to know (approximately) if we're in an
// extended regex.
token.isRegex = p && p[0] === 'IDENTIFIER' && p[1] === 'RegExp';
this.callTokens.push(token);
} else {
this.callTokens.pop();
}
return null;
}
trackParens(token, tokenApi) {
if (token[0] === 'STRING_START') {
this.interpolationLevel += 1;
} else if (token[0] === 'STRING_END') {
this.interpolationLevel -= 1;
}
// We're not linting, just tracking interpolations.
return null;
}
trackParams(token, tokenApi) {
if (token[0] === 'PARAM_START') {
this.isParam++;
} else if (token[0] === 'PARAM_END') {
this.isParam--;
}
// We're not linting, just tracking function params.
return null;
}
};
SpaceOperators.prototype.rule = {
type: 'style',
name: 'space_operators',
level: 'ignore',
message: 'Operators must be spaced properly',
description: `This rule enforces that operators have space around them.
Optionally, you can set \`default_parameters\` to \`false\` to
require no space around \`=\` when used to define default paramaters.`,
default_parameters: true
};
SpaceOperators.prototype.tokens = ['+', '-', '=', '**', 'MATH', 'COMPARE', '&', '^', '|', '&&', '||', 'COMPOUND_ASSIGN', 'STRING_START', 'STRING_END', 'CALL_START', 'CALL_END', 'PARAM_START', 'PARAM_END'];
return SpaceOperators;
}).call(this);
}).call(this);