rubocop-rubyfmt 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 9f586cfd1ffc7386de88b57de617856b9167ead93e62265cddeb165daefa159d
4
+ data.tar.gz: 852bed5eb157c8bebf8b48c47749923a92d510f6b96c8ca4f1faf1365b7c4b22
5
+ SHA512:
6
+ metadata.gz: 41819404358b145c83c7aab4c4beb1e911d0b00ec544e0a416856a3bd247ab8a392939a8c7440dcf71f280fd148bdcd6855df86fa14e10a70423841b4f4f83d5
7
+ data.tar.gz: 0abe8016a1adad552574f0ed43f8e42764e847b85af68c1466ee9adebb9107c25d89cabf454704ee7301eea25e7383f597dc52c4c4e1fb416e07d9e0a5c882fd
data/.rubocop.yml ADDED
@@ -0,0 +1,33 @@
1
+ plugins:
2
+ - rubocop-internal_affairs
3
+ - rubocop-rubyfmt
4
+ - rubocop-rake
5
+ - rubocop-rspec
6
+
7
+ AllCops:
8
+ NewCops: enable
9
+
10
+ Naming/FileName:
11
+ Exclude:
12
+ - lib/rubocop-rubyfmt.rb
13
+
14
+ Metrics/AbcSize:
15
+ Enabled: false
16
+ Metrics/BlockLength:
17
+ Enabled: false
18
+ Metrics/ClassLength:
19
+ Enabled: false
20
+ Metrics/CyclomaticComplexity:
21
+ Enabled: false
22
+ Metrics/MethodLength:
23
+ Enabled: false
24
+ Metrics/PerceivedComplexity:
25
+ Enabled: false
26
+
27
+ Style/IfUnlessModifier:
28
+ Enabled: false
29
+ Style/AccessModifierDeclarations:
30
+ Enabled: false
31
+
32
+ RSpec/ExampleLength:
33
+ Enabled: false
@@ -0,0 +1,7 @@
1
+ {
2
+ "[ruby]": {
3
+ "editor.defaultFormatter": "Shopify.ruby-lsp",
4
+ "editor.formatOnSave": true
5
+ },
6
+ "rubyLsp.formatter": "rubyfmt",
7
+ }
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # 0.1.0
2
+
3
+ - Initial project setup
4
+ - Add a custom lint for enforcing multiline rubocop disables
5
+ - Add a few initial rubocop configurations
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 Reese Williams
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,85 @@
1
+ # `rubocop-rubyfmt`
2
+
3
+ [`rubyfmt`](https://github.com/fables-tales/rubyfmt)-compliant Rubocop configuration.
4
+
5
+ ## Installation
6
+
7
+ Install the gem and add to the application's Gemfile by executing:
8
+
9
+ ```bash
10
+ bundle add rubocop-rubyfmt
11
+ ```
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ ```bash
16
+ gem install rubocop-rubyfmt
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Add the plugin to your `.rubocop.yml`:
22
+
23
+ ```yaml
24
+ plugins:
25
+ - rubocop-rubyfmt
26
+ ```
27
+
28
+ ## Custom Lints
29
+
30
+ ### `Rubyfmt/NoEndOfLineRubocopDisables`
31
+
32
+ Rubocop allows two ways to disable lints for a certain expression. You can disable them at the end of a line, like so:
33
+
34
+ ```ruby
35
+ 'foo' # rubocop:disable Some/Lint
36
+ ```
37
+
38
+ Alternatively, you can wrap the whole expression with comments on multiple lines.
39
+
40
+ ```ruby
41
+ # rubocop:disable Some/Lint
42
+ 'foo'
43
+ # rubocop:enable Some/Lint
44
+ ```
45
+
46
+ `rubyfmt` only supports comments on their own line, and thus the latter is the only one that will work.
47
+
48
+ `Rubyfmt/NoEndOfLineRubocopDisables` is a custom lint that prevents usages of the end-of-line format, and it includes an autocorrect.
49
+
50
+ > [!TIP]
51
+ > If you're adopting `rubyfmt` for the first time and want to codemod your rubocop disables, you can run _only_ this cop with `bundle exec rubocop --autocorrect --only Rubyfmt/NoEndOfLineRubocopDisables`
52
+ >
53
+ > Note that the autocorrect for this lint is best-effort and may require manual fixups. That said, it'll only move comments and should be safe, and RuboCop will notify you of any new failures.
54
+
55
+ ## Disabled Lints
56
+
57
+ All the disabled lints along with the rationale for disabling them is listed in [`config/default.yml`](https://github.com/reese/rubocop-rubyfmt/blob/main/config/default.yml).
58
+
59
+ The goal of these lints is not to prevent _any_ violations, but to prevent lint violations _specific to `rubyfmt`_. Other lint violations may still occur after formatting: for example, a method body may get longer and cause a `Metrics/MethodLength` violation. Such violations are not covered by `rubocop-rubyfmt`.
60
+
61
+ ### Optional Lint Disables
62
+
63
+ There are also some matters of taste (especially in RuboCop's `Style` department) that `rubyfmt` doesn't automatically enforce but which may be modified by `rubyfmt` and cause failures. For example, `rubyfmt` does not currently enforce that multiline blocks must use `do`/`end`. For those who wish to leave formatting decisions entirely up to `rubyfmt` and not worry about them, there's a "full" config that more aggressively disables such lints, which you can include in your RuboCop config:
64
+
65
+ ```yaml
66
+ inherit_gem:
67
+ rubocop-rubyfmt:
68
+ - config/full.yml
69
+ ```
70
+
71
+ You may also wish to instead [read the file](https://github.com/reese/rubocop-rubyfmt/blob/main/config/full.yml) and pick-and-choose which ones to disable.
72
+
73
+ ## Development
74
+
75
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
76
+
77
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
78
+
79
+ ## Contributing
80
+
81
+ Bug reports and pull requests are welcome on GitHub at https://github.com/reese/rubocop-rubyfmt.
82
+
83
+ ## License
84
+
85
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop/rake_task"
4
+
5
+ require "rspec/core/rake_task"
6
+
7
+ RuboCop::RakeTask.new
8
+ RSpec::Core::RakeTask.new(:spec)
9
+
10
+ task(default: %i[spec rubocop])
@@ -0,0 +1,111 @@
1
+ # Custom cops provided by rubocop-rubyfmt
2
+ Rubyfmt/NoEndOfLineRubocopDisables:
3
+ Description: 'Prevents end-of-line disable comments. `rubyfmt` only supports comments on their own line.'
4
+ Enabled: true
5
+ VersionAdded: '0.1.0'
6
+
7
+ # Cops disabled because rubyfmt handles formatting.
8
+ # rubyfmt is intended to supersede any and all other formatting styles,
9
+ # so we disable the entire Layout department.
10
+ Layout:
11
+ Enabled: false
12
+
13
+ # Strings are always double-quoted
14
+ Style/StringLiterals:
15
+ Enabled: false
16
+ Style/StringLiteralsInInterpolation:
17
+ Enabled: false
18
+
19
+ # %Q/%q/% literals are transformed into quoted strings
20
+ Style/PercentQLiterals:
21
+ Enabled: false
22
+ Style/RedundantPercentQ:
23
+ Enabled: false
24
+ Style/BarePercentLiterals:
25
+ Enabled: false
26
+
27
+ # Methods always render their `end` keyword on a new line
28
+ Style/EmptyMethod:
29
+ Enabled: false
30
+
31
+ # `rubyfmt` doesn't make a preference, neither should you
32
+ Style/BlockDelimiters:
33
+ Enabled: false
34
+
35
+ # Trailing commas are always removed
36
+ Style/TrailingCommaInArguments:
37
+ Enabled: false
38
+ Style/TrailingCommaInHashLiteral:
39
+ Enabled: false
40
+ Style/TrailingCommaInArrayLiteral:
41
+ Enabled: false
42
+
43
+ # These arguments will be wrapped in parens, so they won't be ambiguous
44
+ Lint/AmbiguousRegexpLiteral:
45
+ Enabled: false
46
+ Lint/AmbiguousBlockAssociation:
47
+ Enabled: false
48
+
49
+ # Ternaries are always rendered on a single line
50
+ Style/MultilineTernaryOperator:
51
+ Enabled: false
52
+
53
+ # Comments are always put on their own line, so this is redundant
54
+ Style/CommentedKeyword:
55
+ Enabled: false
56
+
57
+ # Character literals are converted to quoted strings
58
+ Style/CharacterLiteral:
59
+ Enabled: false
60
+
61
+ # Statements on the same line are always split to multiple lines
62
+ Style/Semicolon:
63
+ Enabled: false
64
+
65
+ # Methods with end keywords (even empty ones) are always on multiple lines
66
+ Style/SingleLineMethods:
67
+ Enabled: false
68
+
69
+ # `then` is never used
70
+ Style/WhenThen:
71
+ Enabled: false
72
+ Style/MultilineWhenThen:
73
+ Enabled: false
74
+ Style/InPatternThen:
75
+ Enabled: false
76
+ Style/MultilineIfThen:
77
+ Enabled: false
78
+
79
+ # `do` in loops is never used
80
+ Style/WhileUntilDo:
81
+ Enabled: false
82
+
83
+ # Lambda parameters always use parentheses when present
84
+ # and are removed when not
85
+ Style/StabbyLambdaParentheses:
86
+ Enabled: false
87
+ Style/EmptyLambdaParameter:
88
+ Enabled: false
89
+
90
+ # Parens are always used for method defs with parameters
91
+ # and always stripped without parameters
92
+ Style/MethodDefParentheses:
93
+ Enabled: false
94
+ Style/DefWithParentheses:
95
+ Enabled: false
96
+
97
+ # Percent arrays always use `[]` delimiters
98
+ Style/PercentLiteralDelimiters:
99
+ Enabled: false
100
+
101
+ # Calls with args inside other callers will always use parens
102
+ Style/NestedParenthesizedCalls:
103
+ Enabled: false
104
+
105
+ # Statements on the same line as `else` will always be moved
106
+ Lint/ElseLayout:
107
+ Enabled: false
108
+
109
+ # `if` modifiers with multiline bodies will convert to a multiline if block
110
+ Style/MultilineIfModifier:
111
+ Enabled: false
data/config/full.yml ADDED
@@ -0,0 +1,7 @@
1
+ # `rubyfmt` has no preference on block delimiters
2
+ Style/BlockDelimiters:
3
+ Enabled: false
4
+
5
+ # `rubyfmt` has no preference for `lambda { }` or `-> () { }`
6
+ Style/Lambda:
7
+ Enabled: false
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ module RuboCop
6
+ module Cop
7
+ module Rubyfmt
8
+
9
+ # `rubyfmt` always places comments at the beginning of lines, so end-of-line
10
+ # disables cannot be used. The autocorrect for this lint
11
+ #
12
+ # @safety
13
+ # This cop should only be moving comments and thus should be safe.
14
+ # However, this is largely intended as a migration mechanism when first
15
+ # adopting rubyfmt, so this is best-effort and may require some manual fixups.
16
+ # That said, since it's moving comments only, those fixups should be pretty much entirely
17
+ # caught by Rubocop, and most of the fixups should just be moving comments such that they
18
+ # appropriately wrap the offending code.
19
+ #
20
+ #
21
+ # rubocop:disable Lint/RedundantCopDisableDirective
22
+ # ```ruby
23
+ # # good
24
+ # # rubocop:disable Style/ExampleCop
25
+ # bad_call!
26
+ # # rubocop:enable Style/ExampleCop
27
+ #
28
+ # # bad
29
+ # bad_call! # rubocop:disable Style/ExampleCop
30
+ # ````
31
+ # rubocop:enable Lint/RedundantCopDisableDirective
32
+ #
33
+ class NoEndOfLineRubocopDisables < Base
34
+ extend AutoCorrector
35
+
36
+ MSG = "Use multiline rubocop directives instead of end-of-line comments."
37
+ DISABLE_PATTERN = /# rubocop:(disable|todo) .*/.freeze
38
+
39
+ def on_new_investigation
40
+ processed_source.comments.each do |comment|
41
+ next unless same_line_directive?(comment)
42
+
43
+ register_offense(comment)
44
+ end
45
+ end
46
+
47
+ private def same_line_directive?(comment)
48
+ comment.text.match?(DISABLE_PATTERN) && !comment_only_line?(comment)
49
+ end
50
+
51
+ private def comment_only_line?(comment)
52
+ processed_source.lines[comment.loc.line - 1].strip == comment.text
53
+ end
54
+
55
+ private def register_offense(comment)
56
+ add_offense(comment) do |corrector|
57
+ autocorrect(corrector, comment)
58
+ end
59
+ end
60
+
61
+ private def autocorrect(corrector, comment)
62
+ line_number = comment.source_range.line
63
+ line = processed_source.lines[line_number - 1]
64
+
65
+ # Extract the disable directive
66
+ disable_text = comment.text.match(DISABLE_PATTERN)[0]
67
+
68
+ # Find the position where the whitespace before the comment starts
69
+ line_without_comment = line.sub(/#.*$/, "")
70
+ whitespace_before_comment_length = line_without_comment.length - line_without_comment.rstrip.length
71
+
72
+ # Create a range that includes the whitespace before the comment
73
+ comment_with_leading_space_range = if whitespace_before_comment_length.positive?
74
+ range_with_leading_space = Parser::Source::Range.new(
75
+ comment.source_range.source_buffer,
76
+ comment.source_range.begin_pos - whitespace_before_comment_length,
77
+ comment.source_range.end_pos
78
+ )
79
+ range_with_leading_space
80
+ else
81
+ comment.source_range
82
+ end
83
+
84
+ # Remove the end-of-line comment with its leading whitespace
85
+ corrector.remove(comment_with_leading_space_range)
86
+
87
+ # Add the disable directive on the line before
88
+ indent = line[/\A\s*/]
89
+ beginning_of_line = processed_source.buffer.line_range(line_number)
90
+ corrector.insert_before(
91
+ beginning_of_line,
92
+ "#{indent}#{disable_text}\n"
93
+ )
94
+
95
+ # Add the enable directive on the line after
96
+ enable_text = disable_text.gsub("disable", "enable")
97
+ end_of_line = processed_source.buffer.line_range(line_number)
98
+ corrector.insert_after(
99
+ end_of_line,
100
+ "\n#{indent}#{enable_text}"
101
+ )
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rubyfmt/no_end_of_line_rubocop_disables"
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "lint_roller"
4
+
5
+ module RuboCop
6
+ module Rubyfmt
7
+ # A plugin that integrates rubocop-rubyfmt with RuboCop's plugin system.
8
+ class Plugin < LintRoller::Plugin
9
+ def about
10
+ LintRoller::About.new(
11
+ name: "rubocop-rubyfmt",
12
+ version: VERSION,
13
+ homepage: "https://github.com/reese/rubocop-rubyfmt",
14
+ description: "Rubocop rules compatible with `rubyfmt`"
15
+ )
16
+ end
17
+
18
+ def supported?(context)
19
+ context.engine == :rubocop
20
+ end
21
+
22
+ def rules(_context)
23
+ LintRoller::Rules.new(
24
+ type: :path,
25
+ config_format: :rubocop,
26
+ value: Pathname.new(__dir__).join("../../../config/default.yml")
27
+ )
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Rubyfmt
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "rubyfmt/version"
4
+
5
+ module RuboCop
6
+ module Rubyfmt
7
+ class Error < StandardError
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ require_relative "rubocop/rubyfmt"
6
+ require_relative "rubocop/rubyfmt/version"
7
+ require_relative "rubocop/rubyfmt/plugin"
8
+
9
+ require_relative "rubocop/cop/rubyfmt_cops"
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubocop-rubyfmt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Reese Williams
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: lint_roller
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.1'
26
+ - !ruby/object:Gem::Dependency
27
+ name: rubocop
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.72.2
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 1.72.2
40
+ description: Rubocop rules for adopting `rubyfmt`
41
+ email:
42
+ - reese@reesew.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - ".rubocop.yml"
48
+ - ".vscode/settings.json"
49
+ - CHANGELOG.md
50
+ - LICENSE.txt
51
+ - README.md
52
+ - Rakefile
53
+ - config/default.yml
54
+ - config/full.yml
55
+ - lib/rubocop-rubyfmt.rb
56
+ - lib/rubocop/cop/rubyfmt/no_end_of_line_rubocop_disables.rb
57
+ - lib/rubocop/cop/rubyfmt_cops.rb
58
+ - lib/rubocop/rubyfmt.rb
59
+ - lib/rubocop/rubyfmt/plugin.rb
60
+ - lib/rubocop/rubyfmt/version.rb
61
+ homepage: https://github.com/reese/rubocop-rubyfmt
62
+ licenses:
63
+ - MIT
64
+ metadata:
65
+ homepage_uri: https://github.com/reese/rubocop-rubyfmt
66
+ source_code_uri: https://github.com/reese/rubocop-rubyfmt
67
+ changelog_uri: https://github.com/reese/rubocop-rubyfmt/tree/main/CHANGELOG.md
68
+ rubygems_mfa_required: 'true'
69
+ default_lint_roller_plugin: RuboCop::Rubyfmt::Plugin
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: 2.7.0
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubygems_version: 4.0.3
85
+ specification_version: 4
86
+ summary: Rubocop rules for adopting `rubyfmt`
87
+ test_files: []