From 1488e2ca88c86eee98302e545f937e9a76db2a58 Mon Sep 17 00:00:00 2001 From: Owen Stephens Date: Sun, 19 Jun 2016 14:28:44 +0100 Subject: [PATCH] [Fix #3058] Add space inside percent literal style cops (#3214) Add `Style/SpaceInsideArrayPercentLiteral` and `Style/SpaceInsidePercentLiteralDelimiters` --- CHANGELOG.md | 2 + config/enabled.yml | 8 ++ lib/rubocop.rb | 3 + lib/rubocop/cop/mixin/match_range.rb | 26 +++++ lib/rubocop/cop/mixin/percent_literal.rb | 10 ++ .../space_inside_array_percent_literal.rb | 53 ++++++++++ ...space_inside_percent_literal_delimiters.rb | 64 +++++++++++ ...space_inside_array_percent_literal_spec.rb | 86 +++++++++++++++ ..._inside_percent_literal_delimiters_spec.rb | 100 ++++++++++++++++++ 9 files changed, 352 insertions(+) create mode 100644 lib/rubocop/cop/mixin/match_range.rb create mode 100644 lib/rubocop/cop/style/space_inside_array_percent_literal.rb create mode 100644 lib/rubocop/cop/style/space_inside_percent_literal_delimiters.rb create mode 100644 spec/rubocop/cop/style/space_inside_array_percent_literal_spec.rb create mode 100644 spec/rubocop/cop/style/space_inside_percent_literal_delimiters_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e8c11a83c19..1ca8596d6719 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ * [#3173](https://github.com/bbatsov/rubocop/pull/3173): Make `Style/ModuleFunction` configurable with `module_function` and `extend_self` styles. ([@tjwp][]) * [#3105](https://github.com/bbatsov/rubocop/issues/3105): Add new `Style/RequestReferer` cop. ([@giannileggio][]) * [#3200](https://github.com/bbatsov/rubocop/pull/3200): Add autocorrect for `Style/EachForSimpleLoop` cop. ([@tejasbubane][]) +* [#3058](https://github.com/bbatsov/rubocop/issues/3058): Add new `Style/SpaceInsideArrayPercentLiteral` cop. ([@owst][]) +* [#3058](https://github.com/bbatsov/rubocop/issues/3058): Add new `Style/SpaceInsidePercentLiteralDelimiters` cop. ([@owst][]) * [#3179](https://github.com/bbatsov/rubocop/pull/3179): Expose files to support testings Cops using RSpec. ([@tjwp][]) * [#3191](https://github.com/bbatsov/rubocop/issues/3191): Allow arbitrary comments after cop names in CommentConfig lines (e.g. rubocop:enable). ([@owst][]) * [#3177](https://github.com/bbatsov/rubocop/pull/3177): Add new `Style/NumericLiteralPrefix` cop. ([@tejasbubane][]) diff --git a/config/enabled.yml b/config/enabled.yml index e990f287dea0..6abf98cd6b63 100644 --- a/config/enabled.yml +++ b/config/enabled.yml @@ -760,6 +760,14 @@ Style/SpaceAroundOperators: StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' Enabled: true +Style/SpaceInsideArrayPercentLiteral: + Description: 'No unnecessary additional spaces between elements in %i/%w literals.' + Enabled: true + +Style/SpaceInsidePercentLiteralDelimiters: + Description: 'No unnecessary spaces inside delimiters of %i/%w/%x literals.' + Enabled: true + Style/SpaceInsideBrackets: Description: 'No spaces after [ or before ].' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 4167967c78ae..d549385a86b7 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -64,6 +64,7 @@ require 'rubocop/cop/mixin/if_node' require 'rubocop/cop/mixin/integer_node' require 'rubocop/cop/mixin/on_method_def' +require 'rubocop/cop/mixin/match_range' require 'rubocop/cop/mixin/method_complexity' # relies on on_method_def require 'rubocop/cop/mixin/method_preference' require 'rubocop/cop/mixin/min_body_length' @@ -329,10 +330,12 @@ require 'rubocop/cop/style/space_before_comment' require 'rubocop/cop/style/space_before_first_arg' require 'rubocop/cop/style/space_before_semicolon' +require 'rubocop/cop/style/space_inside_array_percent_literal' require 'rubocop/cop/style/space_inside_block_braces' require 'rubocop/cop/style/space_inside_brackets' require 'rubocop/cop/style/space_inside_hash_literal_braces' require 'rubocop/cop/style/space_inside_parens' +require 'rubocop/cop/style/space_inside_percent_literal_delimiters' require 'rubocop/cop/style/space_inside_range_literal' require 'rubocop/cop/style/space_inside_string_interpolation' require 'rubocop/cop/style/special_global_vars' diff --git a/lib/rubocop/cop/mixin/match_range.rb b/lib/rubocop/cop/mixin/match_range.rb new file mode 100644 index 000000000000..cf3b89da0a52 --- /dev/null +++ b/lib/rubocop/cop/mixin/match_range.rb @@ -0,0 +1,26 @@ +# encoding: utf-8 +# frozen_string_literal: true + +module RuboCop + module Cop + # Common functionality for obtaining source ranges from regexp matches + module MatchRange + # Return a new `Range` covering the first matching group number for each + # match of `regex` inside `range` + def each_match_range(range, regex) + range.source.scan(regex) do + yield match_range(range, Regexp.last_match) + end + end + + # For a `match` inside `range`, return a new `Range` covering the match + def match_range(range, match) + Parser::Source::Range.new( + range.source_buffer, + range.begin_pos + match.begin(1), + range.begin_pos + match.end(1) + ) + end + end + end +end diff --git a/lib/rubocop/cop/mixin/percent_literal.rb b/lib/rubocop/cop/mixin/percent_literal.rb index ac0a4b6c416d..fe45e8742017 100644 --- a/lib/rubocop/cop/mixin/percent_literal.rb +++ b/lib/rubocop/cop/mixin/percent_literal.rb @@ -22,6 +22,16 @@ def begin_source(node) def type(node) node.loc.begin.source[0..-2] end + + # A range containing only the contents of the percent literal (e.g. in + # %i{1 2 3} this will be the range covering '1 2 3' only) + def contents_range(node) + Parser::Source::Range.new( + node.loc.expression.source_buffer, + node.loc.begin.end_pos, + node.loc.end.begin_pos + ) + end end end end diff --git a/lib/rubocop/cop/style/space_inside_array_percent_literal.rb b/lib/rubocop/cop/style/space_inside_array_percent_literal.rb new file mode 100644 index 000000000000..24f5f99c43b0 --- /dev/null +++ b/lib/rubocop/cop/style/space_inside_array_percent_literal.rb @@ -0,0 +1,53 @@ +# encoding: utf-8 +# frozen_string_literal: true + +module RuboCop + module Cop + module Style + # Checks for unnecessary additional spaces inside array percent literals + # (i.e. %i/%w). + # + # @good + # %i(foo bar baz) + # + # @bad + # %w(foo bar baz) + class SpaceInsideArrayPercentLiteral < Cop + include MatchRange + include PercentLiteral + + MSG = 'Use only a single space inside array percent literal.'.freeze + MULTIPLE_SPACES_BETWEEN_ITEMS_REGEX = + /(?:[\S&&[^\\]](?:\\ )*)( {2,})(?=\S)/ + + def on_array(node) + process(node, *%w(%i %I %w %W)) + end + + def on_percent_literal(node) + each_unnecessary_space_match(node) do |range| + add_offense(node, range, MSG) + end + end + + def autocorrect(node) + lambda do |corrector| + each_unnecessary_space_match(node) do |range| + corrector.replace(range, ' ') + end + end + end + + private + + def each_unnecessary_space_match(node, &blk) + each_match_range( + contents_range(node), + MULTIPLE_SPACES_BETWEEN_ITEMS_REGEX, + &blk + ) + end + end + end + end +end diff --git a/lib/rubocop/cop/style/space_inside_percent_literal_delimiters.rb b/lib/rubocop/cop/style/space_inside_percent_literal_delimiters.rb new file mode 100644 index 000000000000..ffe992b4f867 --- /dev/null +++ b/lib/rubocop/cop/style/space_inside_percent_literal_delimiters.rb @@ -0,0 +1,64 @@ +# encoding: utf-8 +# frozen_string_literal: true + +module RuboCop + module Cop + module Style + # Checks for unnecessary additional spaces inside the delimiters of + # %i/%w/%x literals. + # + # @good + # %i(foo bar baz) + # + # @bad + # %w( foo bar baz ) + # + # @bad + # %x( ls -l ) + class SpaceInsidePercentLiteralDelimiters < Cop + include MatchRange + include PercentLiteral + + MSG = 'Do not use spaces inside percent literal delimiters.'.freeze + BEGIN_REGEX = /\A( +)/ + END_REGEX = /(?