From 70caf7d14d3f7df14059b0345b06c170ac18be7c Mon Sep 17 00:00:00 2001 From: Radu Dan Date: Fri, 4 Mar 2016 11:25:12 +0200 Subject: [PATCH 1/4] Saner semantics of `coffeelint: enable` (always reverts to post-config state) --- src/coffeelint.coffee | 25 ++++++++++++++++--------- test/test_comment_config.coffee | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/coffeelint.coffee b/src/coffeelint.coffee index ba7c2633..017b7952 100644 --- a/src/coffeelint.coffee +++ b/src/coffeelint.coffee @@ -52,14 +52,19 @@ extend = (destination, sources...) -> defaults = (source, defaults) -> extend({}, defaults, source) +# Helper to add rules to disabled list +union = (a, b) -> + c = {} + for x in a + c[x] = true + for x in b + c[x] = true + + x for x of c + # Helper to remove rules from disabled list difference = (a, b) -> - j = 0 - while j < a.length - if a[j] in b - a.splice(j, 1) - else - j++ + x for x in a when x not in b LineLinter = require './line_linter.coffee' LexicalLinter = require './lexical_linter.coffee' @@ -336,10 +341,12 @@ coffeelint.lint = (source, userConfig = {}, literate = false) -> rules = inlineConfig[cmd][i] { 'disable': -> - disabled = disabled.concat(rules) + disabled = union(disabled, rules) 'enable': -> - difference(disabled, rules) - disabled = disabledInitially if rules.length is 0 + if rules.length + disabled = difference(disabled, rules) + else + disabled = disabledInitially }[cmd]() if rules? # advance line and append relevant messages while nextLine is i and allErrors.length > 0 diff --git a/test/test_comment_config.coffee b/test/test_comment_config.coffee index c39adeb3..dd1ea8f1 100644 --- a/test/test_comment_config.coffee +++ b/test/test_comment_config.coffee @@ -49,7 +49,7 @@ vows.describe('comment_config').addBatch({ assert.equal(errors[1].lineNumber, 3) assert.ok(errors[1].message) - 'Enable all statements': + 'Revert to post-config state': topic: () -> ''' # coffeelint: disable=no_trailing_semicolons,no_implicit_parens From a347996387d109bf750cb28f81bbfc56dff2b355 Mon Sep 17 00:00:00 2001 From: Radu Dan Date: Fri, 4 Mar 2016 08:22:34 +0200 Subject: [PATCH 2/4] Implement coffeelint-disable-line and coffelint-enable-line comment config rules --- src/coffeelint.coffee | 19 +++++++++++++++---- src/line_linter.coffee | 8 +++++--- test/test_comment_config.coffee | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/coffeelint.coffee b/src/coffeelint.coffee index 017b7952..af0fa59c 100644 --- a/src/coffeelint.coffee +++ b/src/coffeelint.coffee @@ -298,8 +298,8 @@ coffeelint.lint = (source, userConfig = {}, literate = false) -> disabledInitially = [] # Check ahead for inline enabled rules for l in source.split('\n') - [ regex, set, rule ] = LineLinter.configStatement.exec(l) or [] - if set is 'enable' and config[rule]?.level is 'ignore' + [ regex, set, ..., rule ] = LineLinter.configStatement.exec(l) or [] + if set in ['enable', 'enable-line'] and config[rule]?.level is 'ignore' disabledInitially.push rule config[rule].level = 'error' @@ -327,6 +327,8 @@ coffeelint.lint = (source, userConfig = {}, literate = false) -> inlineConfig = enable: {} disable: {} + 'enable-line': {} + 'disable-line': {} # Sort by line number and return. errors.sort((a, b) -> a.lineNumber - b.lineNumber) @@ -337,16 +339,25 @@ coffeelint.lint = (source, userConfig = {}, literate = false) -> disabled = disabledInitially nextLine = 0 for i in [0...source.split('\n').length] + disabledLine = disabled for cmd of inlineConfig rules = inlineConfig[cmd][i] { 'disable': -> disabled = union(disabled, rules) + 'disable-line': -> + disabledLine = disabledLine.concat(rules) 'enable': -> if rules.length disabled = difference(disabled, rules) + disabledLine = difference(disabledLine, rules) else - disabled = disabledInitially + disabled = disabledLine = disabledInitially + 'enable-line': -> + if rules.length + disabledLine = difference(disabledLine, rules) + else + disabledLine = disabledInitially }[cmd]() if rules? # advance line and append relevant messages while nextLine is i and allErrors.length > 0 @@ -354,7 +365,7 @@ coffeelint.lint = (source, userConfig = {}, literate = false) -> e = allErrors[0] if e.lineNumber is i + 1 or not e.lineNumber? e = allErrors.shift() - errors.push e unless e.rule in disabled + errors.push e unless e.rule in disabledLine cache?.set source, errors diff --git a/src/line_linter.coffee b/src/line_linter.coffee index 6be02eb8..b7af7c85 100644 --- a/src/line_linter.coffee +++ b/src/line_linter.coffee @@ -69,7 +69,7 @@ class LineApi BaseLinter = require './base_linter.coffee' # Some repeatedly used regular expressions. -configStatement = /coffeelint:\s*(disable|enable)(?:=([\w\s,]*))?/ +configStatement = /coffeelint:\s*((disable|enable)(-line)?)(?:=([\w\s,]*))?/ # # A class that performs regex checks on each line of the source. @@ -88,6 +88,8 @@ module.exports = class LineLinter extends BaseLinter @inlineConfig = enable: {} disable: {} + 'enable-line': {} + 'disable-line': {} acceptRule: (rule) -> return typeof rule.lintLine is 'function' @@ -121,8 +123,8 @@ module.exports = class LineLinter extends BaseLinter if result? cmd = result[1] rules = [] - if result[2]? - for r in result[2].split(',') + if result[4]? + for r in result[4].split(',') rules.push r.replace(/^\s+|\s+$/g, '') @inlineConfig[cmd][@lineNumber] = rules return null diff --git a/test/test_comment_config.coffee b/test/test_comment_config.coffee index dd1ea8f1..298c89fe 100644 --- a/test/test_comment_config.coffee +++ b/test/test_comment_config.coffee @@ -25,6 +25,23 @@ vows.describe('comment_config').addBatch({ assert.equal(errors[0].lineNumber, 5) assert.ok(errors[0].message) + 'Disable statements per line': + topic: () -> + ''' + a 'foo'; # coffeelint: disable-line=no_trailing_semicolons + b 'bar'; + ''' + + 'can disable rules in your config': (source) -> + config = + no_trailing_semicolons: level: 'error' + errors = coffeelint.lint(source, config) + assert.equal(errors.length, 1) + assert.equal(errors[0].rule, 'no_trailing_semicolons') + assert.equal(errors[0].level, 'error') + assert.equal(errors[0].lineNumber, 2) + assert.ok(errors[0].message) + 'Enable statements': topic: () -> ''' @@ -49,6 +66,22 @@ vows.describe('comment_config').addBatch({ assert.equal(errors[1].lineNumber, 3) assert.ok(errors[1].message) + 'Enable statements per line': + topic: () -> + ''' + a 'foo' + b 'bar' # coffeelint: enable-line=no_implicit_parens + c 'baz' + ''' + + 'can enable rules not in your config': (source) -> + errors = coffeelint.lint(source) + assert.equal(errors.length, 1) + assert.equal(errors[0].rule, 'no_implicit_parens') + assert.equal(errors[0].level, 'error') + assert.equal(errors[0].lineNumber, 2) + assert.ok(errors[0].message) + 'Revert to post-config state': topic: () -> ''' From 77776c304e57b4124fd25f7eb9589b0c650dba93 Mon Sep 17 00:00:00 2001 From: Radu Dan Date: Fri, 4 Mar 2016 09:25:32 +0200 Subject: [PATCH 3/4] Implement `coffeelint: disable` and `coffeelint: disable-line` without any arguments to disable all configured rules --- src/coffeelint.coffee | 21 +++++++++++++++-- test/test_comment_config.coffee | 42 +++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/coffeelint.coffee b/src/coffeelint.coffee index af0fa59c..91999543 100644 --- a/src/coffeelint.coffee +++ b/src/coffeelint.coffee @@ -333,6 +333,16 @@ coffeelint.lint = (source, userConfig = {}, literate = false) -> # Sort by line number and return. errors.sort((a, b) -> a.lineNumber - b.lineNumber) + # Create a list of all errors + disabledEntirely = do -> + result = [] + map = {} + for { name } in errors or [] + if not map[name] + result.push(name) + map[name] = true + result + # Disable/enable rules for inline blocks allErrors = errors errors = [] @@ -344,9 +354,16 @@ coffeelint.lint = (source, userConfig = {}, literate = false) -> rules = inlineConfig[cmd][i] { 'disable': -> - disabled = union(disabled, rules) + if rules.length + disabled = union(disabled, rules) + disabledLine = union(disabledLine, rules) + else + disabled = disabledLine = disabledEntirely 'disable-line': -> - disabledLine = disabledLine.concat(rules) + if rules.length + disabledLine = union(disabledLine, rules) + else + disabledLine = disabledEntirely 'enable': -> if rules.length disabled = difference(disabled, rules) diff --git a/test/test_comment_config.coffee b/test/test_comment_config.coffee index 298c89fe..739bb73d 100644 --- a/test/test_comment_config.coffee +++ b/test/test_comment_config.coffee @@ -25,6 +25,26 @@ vows.describe('comment_config').addBatch({ assert.equal(errors[0].lineNumber, 5) assert.ok(errors[0].message) + 'Disable all statements': + topic: () -> + ''' + # coffeelint: disable + a 'you get a semi-colon'; + b 'you get a semi-colon'; + # coffeelint: enable + c 'everybody gets a semi-colon'; + ''' + + 'can disable rules in your config': (source) -> + config = + no_trailing_semicolons: level: 'error' + errors = coffeelint.lint(source, config) + assert.equal(errors.length, 1) + assert.equal(errors[0].rule, 'no_trailing_semicolons') + assert.equal(errors[0].level, 'error') + assert.equal(errors[0].lineNumber, 5) + assert.ok(errors[0].message) + 'Disable statements per line': topic: () -> ''' @@ -42,6 +62,28 @@ vows.describe('comment_config').addBatch({ assert.equal(errors[0].lineNumber, 2) assert.ok(errors[0].message) + 'Disable all statements per line': + topic: () -> + ''' + a 'foo'; # coffeelint: disable-line + b 'bar'; + ''' + + 'can disable rules in your config': (source) -> + config = + no_trailing_semicolons: level: 'error' + no_implicit_parens: level: 'error' + errors = coffeelint.lint(source, config) + assert.equal(errors.length, 2) + assert.equal(errors[0].rule, 'no_implicit_parens') + assert.equal(errors[0].level, 'error') + assert.equal(errors[0].lineNumber, 2) + assert.ok(errors[0].message) + assert.equal(errors[1].rule, 'no_trailing_semicolons') + assert.equal(errors[1].level, 'error') + assert.equal(errors[1].lineNumber, 2) + assert.ok(errors[1].message) + 'Enable statements': topic: () -> ''' From 0e68c705de252c52002f345321c929455dbade47 Mon Sep 17 00:00:00 2001 From: Radu Dan Date: Fri, 4 Mar 2016 09:42:53 +0200 Subject: [PATCH 4/4] Add support for shortcuts (noqa) --- src/coffeelint.coffee | 2 +- src/line_linter.coffee | 13 ++++++++++--- test/test_comment_config.coffee | 17 +++++++++++++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/coffeelint.coffee b/src/coffeelint.coffee index 91999543..89685f99 100644 --- a/src/coffeelint.coffee +++ b/src/coffeelint.coffee @@ -298,7 +298,7 @@ coffeelint.lint = (source, userConfig = {}, literate = false) -> disabledInitially = [] # Check ahead for inline enabled rules for l in source.split('\n') - [ regex, set, ..., rule ] = LineLinter.configStatement.exec(l) or [] + [ regex, set, ..., rule ] = LineLinter.getDirective(l) or [] if set in ['enable', 'enable-line'] and config[rule]?.level is 'ignore' disabledInitially.push rule config[rule].level = 'error' diff --git a/src/line_linter.coffee b/src/line_linter.coffee index b7af7c85..4f2492ae 100644 --- a/src/line_linter.coffee +++ b/src/line_linter.coffee @@ -70,14 +70,21 @@ BaseLinter = require './base_linter.coffee' # Some repeatedly used regular expressions. configStatement = /coffeelint:\s*((disable|enable)(-line)?)(?:=([\w\s,]*))?/ +configShortcuts = [ + # TODO: make this user (and / or api) configurable + [/\#.*noqa/, 'coffeelint: disable-line'] +] # # A class that performs regex checks on each line of the source. # module.exports = class LineLinter extends BaseLinter - # This is exposed here so coffeelint.coffee can reuse it - @configStatement: configStatement + @getDirective: (line) -> + for [shortcut, replacement] in configShortcuts + if line.match(shortcut) + return configStatement.exec(replacement) + return configStatement.exec(line) constructor: (source, config, rules, tokensByLine, literate = false) -> super source, config, rules @@ -119,7 +126,7 @@ module.exports = class LineLinter extends BaseLinter collectInlineConfig: (line) -> # Check for block config statements enable and disable - result = configStatement.exec(line) + result = @constructor.getDirective(line) if result? cmd = result[1] rules = [] diff --git a/test/test_comment_config.coffee b/test/test_comment_config.coffee index 739bb73d..fa3fea7e 100644 --- a/test/test_comment_config.coffee +++ b/test/test_comment_config.coffee @@ -62,6 +62,23 @@ vows.describe('comment_config').addBatch({ assert.equal(errors[0].lineNumber, 2) assert.ok(errors[0].message) + 'Expand shortcuts': + topic: () -> + ''' + a 'foo'; # noqa + b 'bar'; + ''' + + 'will expand and honor directive shortcuts': (source) -> + config = + no_trailing_semicolons: level: 'error' + errors = coffeelint.lint(source, config) + assert.equal(errors.length, 1) + assert.equal(errors[0].rule, 'no_trailing_semicolons') + assert.equal(errors[0].level, 'error') + assert.equal(errors[0].lineNumber, 2) + assert.ok(errors[0].message) + 'Disable all statements per line': topic: () -> '''