Skip to content

Commit

Permalink
[rubocop#4811] Add new Layout/SpaceInsideReferenceBrackets cop
Browse files Browse the repository at this point in the history
  • Loading branch information
garettarrowood committed Dec 8, 2017
1 parent 3e55914 commit eb5ad0c
Show file tree
Hide file tree
Showing 10 changed files with 490 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* [#5101](https://github.com/bbatsov/rubocop/pull/5101): Allow to specify `TargetRubyVersion` 2.5. ([@walf443][])
* [#1575](https://github.com/bbatsov/rubocop/issues/1575): Add new `Layout/ClassStructure` cop that checks whether definitions in a class are in the configured order. This cop is disabled by default. ([@jonatas][])
* New cop `Rails/InverseOf` checks for association arguments that require setting the `inverse_of` option manually. ([@bdewater][])
* [#4811](https://github.com/bbatsov/rubocop/issues/4811): Add new `Layout/SpaceInsideReferenceBrackets` cop. ([@garettarrowood][])
* [#4252](https://github.com/bbatsov/rubocop/issues/4252): Add new `Style/TrailingBodyOnMethodDefinition` cop. ([@garettarrowood][])
* Add new `Style/TrailingMethodEndStatment` cop. ([@garettarrowood][])
* [#4650](https://github.com/bbatsov/rubocop/issues/4650): Add new `Style/StringHashKeys` cop. ([@donjar][])
Expand Down
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -526,6 +526,12 @@ Layout/SpaceInsideHashLiteralBraces:
- space
- no_space

Layout/SpaceInsideReferenceBrackets:
EnforcedStyle: no_space
SupportedStyles:
- space
- no_space

Layout/SpaceInsideStringInterpolation:
EnforcedStyle: no_space
SupportedStyles:
Expand Down
4 changes: 4 additions & 0 deletions config/enabled.yml
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,10 @@ Layout/SpaceInsideRangeLiteral:
StyleGuide: '#no-space-inside-range-literals'
Enabled: true

Layout/SpaceInsideReferenceBrackets:
Description: 'Checks the spacing inside referential brackets.'
Enabled: true

Layout/SpaceInsideStringInterpolation:
Description: 'Checks for padding/surrounding spaces inside string interpolation.'
StyleGuide: '#string-interpolation'
Expand Down
1 change: 1 addition & 0 deletions lib/rubocop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@
require_relative 'rubocop/cop/layout/space_inside_parens'
require_relative 'rubocop/cop/layout/space_inside_percent_literal_delimiters'
require_relative 'rubocop/cop/layout/space_inside_range_literal'
require_relative 'rubocop/cop/layout/space_inside_reference_brackets'
require_relative 'rubocop/cop/layout/space_inside_string_interpolation'
require_relative 'rubocop/cop/layout/tab'
require_relative 'rubocop/cop/layout/trailing_blank_lines'
Expand Down
136 changes: 136 additions & 0 deletions lib/rubocop/cop/layout/space_inside_reference_brackets.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Layout
# Checks that reference brackets have or don't have
# surrounding space depending on configuration.
#
# @example EnforcedStyle: no_space (default)
# # The `no_space` style enforces that reference brackets have
# # no surrounding space.
#
# # bad
# hash[ :key ]
# array[ index ]
#
# # good
# hash[:key]
# array[index]
#
# @example EnforcedStyle: space
# # The `space` style enforces that reference brackets have
# # surrounding space.
#
# # bad
# hash[:key]
# array[index]
#
# # good
# hash[ :key ]
# array[ index ]
class SpaceInsideReferenceBrackets < Cop
include SurroundingSpace
include ConfigurableEnforcedStyle

MSG = '%<command>s space inside reference brackets.'.freeze

def on_send(node)
return if node.multiline?
return unless left_ref_bracket(node)
left_token = left_ref_bracket(node)
right_token = right_ref_bracket(node, left_token)

if style == :no_space
no_space_offenses(node, left_token, right_token)
else
space_offenses(node, left_token, right_token)
end
end

private

def autocorrect(node)
lambda do |corrector|
left_token = left_ref_bracket(node)
right_token = right_ref_bracket(node, left_token)

if style == :no_space
no_space_corrector(corrector, left_token, right_token)
else
space_corrector(corrector, left_token, right_token)
end
end
end

def left_ref_bracket(node)
line_tokens(node).reverse.find { |t| t.type == :tLBRACK2 }
end

def line_tokens(node)
tokens = processed_source.tokens.select do |t|
t.pos.line == send_line(node)
end
tokens.select { |t| t.pos.end_pos <= node.source_range.end_pos }
end

def send_line(node)
node.source_range.first_line
end

def right_ref_bracket(node, token)
i = line_tokens(node).index(token)
line_tokens(node).slice(i..-1).find { |t| t.type == :tRBRACK }
end

def no_space_offenses(node, left_token, right_token)
if extra_space?(left_token, :left)
range = side_space_range(range: left_token.pos, side: :right)
add_offense(node, location: range,
message: format(MSG, command: 'Do not use'))
end
return unless extra_space?(right_token, :right)
range = side_space_range(range: right_token.pos, side: :left)
add_offense(node, location: range,
message: format(MSG, command: 'Do not use'))
end

def no_space_corrector(corrector, left_token, right_token)
if space_after?(left_token)
range = side_space_range(range: left_token.pos, side: :right)
corrector.remove(range)
end
return unless space_before?(right_token)
range = side_space_range(range: right_token.pos, side: :left)
corrector.remove(range)
end

def space_offenses(node, left_token, right_token)
unless extra_space?(left_token, :left)
add_offense(node, location: left_token.pos,
message: format(MSG, command: 'Use'))
end
return if extra_space?(right_token, :right) || right_token.nil?
add_offense(node, location: right_token.pos,
message: format(MSG, command: 'Use'))
end

def space_corrector(corrector, left_token, right_token)
unless space_after?(left_token)
corrector.insert_after(left_token.pos, ' ')
end
return if space_before?(right_token)
corrector.insert_before(right_token.pos, ' ')
end

def extra_space?(token, side)
if side == :left
token && space_after?(token)
else
token && space_before?(token)
end
end
end
end
end
end
25 changes: 25 additions & 0 deletions lib/rubocop/cop/mixin/surrounding_space.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ def space_before?(token)
token.pos.source_buffer.source.match(/\G\s/, token.pos.begin_pos - 1)
end

def side_space_range(range:, side:)
buffer = @processed_source.buffer
src = buffer.source

begin_pos = range.begin_pos
end_pos = range.end_pos
if side == :left
begin_pos = reposition(src, begin_pos, -1)
end_pos -= 1
end
if side == :right
begin_pos += 1
end_pos = reposition(src, end_pos, 1)
end
Parser::Source::Range.new(buffer, begin_pos, end_pos)
end

def index_of_first_token(node)
range = node.source_range
token_table[range.line][range.column]
Expand All @@ -38,6 +55,14 @@ def token_table
table
end
end

private

def reposition(src, pos, step)
offset = step == -1 ? -1 : 0
pos += step while src[pos + offset] =~ /[ \t]/
pos < 0 ? 0 : pos
end
end
end
end
1 change: 1 addition & 0 deletions manual/cops.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ In the following section you find all available cops:
* [Layout/SpaceInsideParens](cops_layout.md#layoutspaceinsideparens)
* [Layout/SpaceInsidePercentLiteralDelimiters](cops_layout.md#layoutspaceinsidepercentliteraldelimiters)
* [Layout/SpaceInsideRangeLiteral](cops_layout.md#layoutspaceinsiderangeliteral)
* [Layout/SpaceInsideReferenceBrackets](cops_layout.md#layoutspaceinsidereferencebrackets)
* [Layout/SpaceInsideStringInterpolation](cops_layout.md#layoutspaceinsidestringinterpolation)
* [Layout/Tab](cops_layout.md#layouttab)
* [Layout/TrailingBlankLines](cops_layout.md#layouttrailingblanklines)
Expand Down
46 changes: 46 additions & 0 deletions manual/cops_layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -2894,6 +2894,52 @@ Checks for spaces inside range literals.

* [https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals](https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals)

## Layout/SpaceInsideReferenceBrackets

Enabled by default | Supports autocorrection
--- | ---
Enabled | Yes

Checks that reference brackets have or don't have
surrounding space depending on configuration.

### Examples

#### EnforcedStyle: no_space (default)

```ruby
# The `no_space` style enforces that reference brackets have
# no surrounding space.

# bad
hash[ :key ]
array[ index ]

# good
hash[:key]
array[index]
```
#### EnforcedStyle: space

```ruby
# The `space` style enforces that reference brackets have
# surrounding space.

# bad
hash[:key]
array[index]

# good
hash[ :key ]
array[ index ]
```

### Configurable attributes

Name | Default value | Configurable values
--- | --- | ---
EnforcedStyle | `no_space` | `space`, `no_space`

## Layout/SpaceInsideStringInterpolation

Enabled by default | Supports autocorrection
Expand Down
3 changes: 3 additions & 0 deletions spec/rubocop/cop/layout/space_around_keyword_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@
# Layout/SpaceInsideHashLiteralBraces
it_behaves_like 'accept around', '{}', '{a: begin end}'

# Layout/SpaceInsideReferenceBrackets
it_behaves_like 'accept around', '[]', 'a[begin end]'

# Layout/SpaceInsideStringInterpolation
it_behaves_like 'accept around', '{}', '"#{begin end}"'
end
Loading

0 comments on commit eb5ad0c

Please sign in to comment.