UNPKG

@coffeelint/cli

Version:
115 lines (104 loc) 3.66 kB
(function() { var EnsureComprehensions, indexOf = [].indexOf; module.exports = EnsureComprehensions = (function() { class EnsureComprehensions { lintToken(token, tokenApi) { var atEqual, idents, numCallEnds, numCallStarts, numParenEnds, numParenStarts, peeker, prevIdents, prevToken, ref, ref1; // Rules // Ignore if normal for-loop with a block // If LHS of operation contains either the key or value variable of // the loop, assume that it is not a comprehension. // Find all identifiers (including lhs values and parts of for loop) idents = this.findIdents(tokenApi); // if it looks like a for block, don't bother checking if (this.forBlock) { this.forBlock = false; return; } peeker = -1; atEqual = false; numCallEnds = 0; numCallStarts = 0; numParenStarts = 0; numParenEnds = 0; prevIdents = []; while ((prevToken = tokenApi.peek(peeker))) { if (prevToken[0] === 'CALL_END') { numCallEnds++; } if (prevToken[0] === 'CALL_START') { numCallStarts++; } if (prevToken[0] === '(') { numParenStarts++; } if (prevToken[0] === ')') { numParenEnds++; } if (prevToken[0] === 'IDENTIFIER') { if (!atEqual) { prevIdents.push(prevToken[1]); } else if (ref = prevToken[1], indexOf.call(idents, ref) >= 0) { return; } } if (((ref1 = prevToken[0]) === '(' || ref1 === '->' || ref1 === 'TERMINATOR') || (prevToken.newLine != null)) { break; } if (prevToken[0] === '=' && numParenEnds === numParenStarts) { atEqual = {token}; } peeker--; } // If we hit a terminal node (TERMINATOR token or w/ property newLine) // or if we hit the top of the file and we've seen an '=' sign without // any identifiers that are part of the for-loop, and there is an equal // amount of CALL_START/CALL_END tokens. An unequal number means the list // comprehension is inside of a function call if (atEqual && numCallStarts === numCallEnds) { return { token, context: '' }; } } findIdents(tokenApi) { var idents, nextToken, peeker, ref; peeker = 1; idents = []; while ((nextToken = tokenApi.peek(peeker))) { if (nextToken[0] === 'IDENTIFIER') { idents.push(nextToken[1]); } if ((ref = nextToken[0]) === 'FORIN' || ref === 'FOROF') { break; } peeker++; } // now search ahead to see if this becomes a FOR block while ((nextToken = tokenApi.peek(peeker))) { if (nextToken[0] === 'TERMINATOR') { break; } if (nextToken[0] === 'INDENT') { this.forBlock = true; break; } peeker++; } return idents; } }; EnsureComprehensions.prototype.rule = { type: 'style', name: 'ensure_comprehensions', level: 'warn', message: 'Comprehensions must have parentheses around them', description: `This rule makes sure that parentheses are around comprehensions.` }; EnsureComprehensions.prototype.tokens = ['FOR']; EnsureComprehensions.prototype.forBlock = false; return EnsureComprehensions; }).call(this); }).call(this);