Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for tabs in indentation. #7867

Merged
merged 7 commits into from Apr 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Expand Up @@ -4,6 +4,7 @@

### New features

* [#7867](https://github.com/rubocop-hq/rubocop/pull/7867): Add support for tabs in indentation. ([@DracoAter][])
* [#7863](https://github.com/rubocop-hq/rubocop/issues/7863): Corrector now accepts nodes in addition to ranges. ([@marcandre][])
* [#7862](https://github.com/rubocop-hq/rubocop/issues/7862): Corrector now has a `wrap` method. ([@marcandre][])
* [#7850](https://github.com/rubocop-hq/rubocop/issues/7850): Make it possible to enable/disable pending cops. ([@koic][])
Expand All @@ -12,6 +13,10 @@
* [#7384](https://github.com/rubocop-hq/rubocop/pull/7384): Add new `Style/DisableCopsWithinSourceCodeDirective` cop. ([@egze][])
* [#7826](https://github.com/rubocop-hq/rubocop/issues/7826): Add new `Layout/SpaceAroundMethodCallOperator` cop. ([@saurabhmaurya15][])

### Changes

* **(Breaking)** Renamed `Layout/Tab` cop to `Layout/IndentationStyle`. ([@DracoAter][])

### Bug fixes

* [#7871](https://github.com/rubocop-hq/rubocop/pull/7871): Fix an auto-correction bug in `Lint/BooleanSymbol`. ([@knu][])
Expand Down Expand Up @@ -4452,3 +4457,4 @@
[@rafaelfranca]: https://github.com/rafaelfranca
[@knu]: https://github.com/knu
[@saurabhmaurya15]: https://github.com/saurabhmaurya15
[@DracoAter]: https:/github.com/DracoAter
28 changes: 16 additions & 12 deletions config/default.yml
Expand Up @@ -801,6 +801,22 @@ Layout/IndentationConsistency:
# A reference to `EnforcedStyle: indented_internal_methods`.
- https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#follow-the-coding-conventions

Layout/IndentationStyle:
Description: 'Consistent indentation either with tabs only or spaces only.'
StyleGuide: '#spaces-indentation'
Enabled: true
VersionAdded: '0.49'
VersionChanged: '0.82'
# By default, the indentation width from Layout/IndentationWidth is used
# But it can be overridden by setting this parameter
# It is used during auto-correction to determine how many spaces should
# replace each tab.
IndentationWidth: ~
EnforcedStyle: spaces
SupportedStyles:
- spaces
- tabs

Layout/IndentationWidth:
Description: 'Use 2 spaces for indentation.'
StyleGuide: '#spaces-indentation'
Expand Down Expand Up @@ -1263,18 +1279,6 @@ Layout/SpaceInsideStringInterpolation:
- space
- no_space

Layout/Tab:
Description: 'No hard tabs.'
StyleGuide: '#spaces-indentation'
Enabled: true
VersionAdded: '0.49'
VersionChanged: '0.51'
# By default, the indentation width from Layout/IndentationWidth is used
# But it can be overridden by setting this parameter
# It is used during auto-correction to determine how many spaces should
# replace each tab.
IndentationWidth: ~

Layout/TrailingEmptyLines:
Description: 'Checks trailing blank lines and final newline.'
StyleGuide: '#newline-eof'
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop.rb
Expand Up @@ -235,6 +235,7 @@
require_relative 'rubocop/cop/layout/heredoc_argument_closing_parenthesis'
require_relative 'rubocop/cop/layout/heredoc_indentation'
require_relative 'rubocop/cop/layout/indentation_consistency'
require_relative 'rubocop/cop/layout/indentation_style'
require_relative 'rubocop/cop/layout/indentation_width'
require_relative 'rubocop/cop/layout/initial_indentation'
require_relative 'rubocop/cop/layout/leading_comment_space'
Expand Down Expand Up @@ -278,7 +279,6 @@
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_empty_lines'
require_relative 'rubocop/cop/layout/trailing_whitespace'

Expand Down
1 change: 1 addition & 0 deletions lib/rubocop/config_obsoletion.rb
Expand Up @@ -17,6 +17,7 @@ class ConfigObsoletion
'Layout/IndentHash' => 'Layout/FirstHashElementIndentation',
'Layout/IndentHeredoc' => 'Layout/HeredocIndentation',
'Layout/LeadingBlankLines' => 'Layout/LeadingEmptyLines',
'Layout/Tab' => 'Layout/IndentationStyle',
'Layout/TrailingBlankLines' => 'Layout/TrailingEmptyLines',
'Lint/DuplicatedKey' => 'Lint/DuplicateHashKey',
'Lint/EndInMethod' => 'Style/EndBlock',
Expand Down
10 changes: 5 additions & 5 deletions lib/rubocop/cop/badge.rb
Expand Up @@ -4,11 +4,11 @@ module RuboCop
module Cop
# Identifier of all cops containing a department and cop name.
#
# All cops are identified by their badge. For example, the badge
# for `RuboCop::Cop::Layout::Tab` is `Layout/Tab`. Badges can be
# parsed as either `Department/CopName` or just `CopName` to allow
# for badge references in source files that omit the department
# for RuboCop to infer.
# All cops are identified by their badge. For example, the badge for
# `RuboCop::Cop::Layout::IndentationStyle` is `Layout/IndentationStyle`.
# Badges can be parsed as either `Department/CopName` or just `CopName` to
# allow for badge references in source files that omit the department for
# RuboCop to infer.
class Badge
# Error raised when a badge parse fails.
class InvalidBadge < Error
Expand Down
Expand Up @@ -5,9 +5,10 @@
module RuboCop
module Cop
module Layout
# This cop checks for tabs inside the source code.
# This cop checks that the indentation method is consistent.
# Either tabs only or spaces only are used for indentation.
#
# @example
# @example EnforcedStyle: spaces (default)
# # bad
# # This example uses a tab to indent bar.
# def foo
Expand All @@ -20,17 +21,30 @@ module Layout
# bar
# end
#
class Tab < Cop
# @example EnforcedStyle: tabs
# # bad
# # This example uses spaces to indent bar.
# def foo
# bar
# end
#
# # good
# # This example uses a tab to indent bar.
# def foo
# bar
# end
class IndentationStyle < Cop
include Alignment
include ConfigurableEnforcedStyle
include RangeHelp

MSG = 'Tab detected.'
MSG = '%<type>s detected in indentation.'

def investigate(processed_source)
str_ranges = string_literal_ranges(processed_source.ast)

processed_source.lines.each.with_index(1) do |line, lineno|
match = line.match(/\t+/)
match = find_offence(line)
next unless match

range = source_range(processed_source.buffer,
Expand All @@ -43,13 +57,37 @@ def investigate(processed_source)
end

def autocorrect(range)
if range.source.include?("\t")
autocorrect_lambda_for_tabs(range)
else
autocorrect_lambda_for_spaces(range)
end
end

private

def find_offence(line)
if style == :spaces
line.match(/\A\s*\t+/)
else
line.match(/\A\s* +/)
end
end

def autocorrect_lambda_for_tabs(range)
lambda do |corrector|
spaces = ' ' * configured_indentation_width
corrector.replace(range, range.source.gsub(/\t/, spaces))
end
end

private
def autocorrect_lambda_for_spaces(range)
lambda do |corrector|
corrector.replace(range, range.source.gsub(/\A\s+/) do |match|
"\t" * (match.size / configured_indentation_width)
end)
end
end

def in_string_literal?(ranges, tabs_range)
ranges.any? { |range| range.contains?(tabs_range) }
Expand All @@ -69,6 +107,10 @@ def string_literal_ranges(ast)
end
end
end

def message(_node)
format(MSG, type: style == :spaces ? 'Tab' : 'Space')
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/layout/line_length.rb
Expand Up @@ -8,7 +8,7 @@ module Layout
# This cop checks the length of lines in the source code.
# The maximum length is configurable.
# The tab size is configured in the `IndentationWidth`
# of the `Layout/Tab` cop.
# of the `Layout/IndentationStyle` cop.
# It also ignores a shebang line by default.
#
# This cop has some autocorrection capabilities.
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/mixin/line_length_help.rb
Expand Up @@ -63,7 +63,7 @@ def indentation_difference(line)
end

def tab_indentation_width
config.for_cop('Layout/Tab')['IndentationWidth']
config.for_cop('Layout/IndentationStyle')['IndentationWidth']
end

def uri_regexp
Expand Down
7 changes: 4 additions & 3 deletions lib/rubocop/cop/mixin/statement_modifier.rb
Expand Up @@ -58,12 +58,13 @@ def max_line_length
end

def indentation_multiplier
return 1 if config.for_cop('Layout/Tab')['Enabled']
return 1 if config.for_cop('Layout/IndentationStyle')['Enabled']

default_configuration = RuboCop::ConfigLoader.default_configuration
config.for_cop('Layout/Tab')['IndentationWidth'] ||
config.for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
config.for_cop('Layout/IndentationWidth')['Width'] ||
default_configuration.for_cop('Layout/Tab')['IndentationWidth'] ||
default_configuration
.for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
default_configuration.for_cop('Layout/IndentationWidth')['Width']
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/style/if_unless_modifier.rb
Expand Up @@ -9,7 +9,7 @@ module Style
#
# The maximum line length is configured in the `Layout/LineLength`
# cop. The tab size is configured in the `IndentationWidth` of the
# `Layout/Tab` cop.
# `Layout/IndentationStyle` cop.
#
# @example
# # bad
Expand Down
2 changes: 1 addition & 1 deletion manual/cops.md
Expand Up @@ -128,6 +128,7 @@ In the following section you find all available cops:
* [Layout/HeredocArgumentClosingParenthesis](cops_layout.md#layoutheredocargumentclosingparenthesis)
* [Layout/HeredocIndentation](cops_layout.md#layoutheredocindentation)
* [Layout/IndentationConsistency](cops_layout.md#layoutindentationconsistency)
* [Layout/IndentationStyle](cops_layout.md#layoutindentationstyle)
* [Layout/IndentationWidth](cops_layout.md#layoutindentationwidth)
* [Layout/InitialIndentation](cops_layout.md#layoutinitialindentation)
* [Layout/LeadingCommentSpace](cops_layout.md#layoutleadingcommentspace)
Expand Down Expand Up @@ -171,7 +172,6 @@ In the following section you find all available cops:
* [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/TrailingEmptyLines](cops_layout.md#layouttrailingemptylines)
* [Layout/TrailingWhitespace](cops_layout.md#layouttrailingwhitespace)

Expand Down
89 changes: 54 additions & 35 deletions manual/cops_layout.md
Expand Up @@ -2720,6 +2720,59 @@ EnforcedStyle | `normal` | `normal`, `indented_internal_methods`
* [https://rubystyle.guide#spaces-indentation](https://rubystyle.guide#spaces-indentation)
* [https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#follow-the-coding-conventions](https://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#follow-the-coding-conventions)

## Layout/IndentationStyle

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Enabled | Yes | Yes | 0.49 | 0.82

This cop checks that the indentation method is consistent.
Either tabs only or spaces only are used for indentation.

### Examples

#### EnforcedStyle: spaces (default)

```ruby
# bad
# This example uses a tab to indent bar.
def foo
bar
end

# good
# This example uses spaces to indent bar.
def foo
bar
end
```
#### EnforcedStyle: tabs

```ruby
# bad
# This example uses spaces to indent bar.
def foo
bar
end

# good
# This example uses a tab to indent bar.
def foo
bar
end
```

### Configurable attributes

Name | Default value | Configurable values
--- | --- | ---
IndentationWidth | `<none>` | Integer
EnforcedStyle | `spaces` | `spaces`, `tabs`

### References

* [https://rubystyle.guide#spaces-indentation](https://rubystyle.guide#spaces-indentation)

## Layout/IndentationWidth

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down Expand Up @@ -2916,7 +2969,7 @@ Enabled | Yes | Yes | 0.25 | 0.78
This cop checks the length of lines in the source code.
The maximum length is configurable.
The tab size is configured in the `IndentationWidth`
of the `Layout/Tab` cop.
of the `Layout/IndentationStyle` cop.
It also ignores a shebang line by default.

This cop has some autocorrection capabilities.
Expand Down Expand Up @@ -4873,40 +4926,6 @@ EnforcedStyle | `no_space` | `space`, `no_space`

* [https://rubystyle.guide#string-interpolation](https://rubystyle.guide#string-interpolation)

## Layout/Tab

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Enabled | Yes | Yes | 0.49 | 0.51

This cop checks for tabs inside the source code.

### Examples

```ruby
# bad
# This example uses a tab to indent bar.
def foo
bar
end

# good
# This example uses spaces to indent bar.
def foo
bar
end
```

### Configurable attributes

Name | Default value | Configurable values
--- | --- | ---
IndentationWidth | `<none>` | Integer

### References

* [https://rubystyle.guide#spaces-indentation](https://rubystyle.guide#spaces-indentation)

## Layout/TrailingEmptyLines

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down
2 changes: 1 addition & 1 deletion manual/cops_style.md
Expand Up @@ -2925,7 +2925,7 @@ written as modifier `if`/`unless`. The cop also checks for modifier

The maximum line length is configured in the `Layout/LineLength`
cop. The tab size is configured in the `IndentationWidth` of the
`Layout/Tab` cop.
`Layout/IndentationStyle` cop.

### Examples

Expand Down