standard 1.27.0 → 1.28.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,170 @@
1
+ module Standard
2
+ module Plugin
3
+ class MergesPluginsIntoRubocopConfig
4
+ # Blank configuration object to merge plugins into, with only the following spared:
5
+ # - AllCops keys set to avoid warnings about unknown properties
6
+ # - Lint/Syntax must be set to avoid a nil error when verifying inherited configs
7
+ MANDATORY_RUBOCOP_CONFIG_KEYS = ["AllCops", "Lint/Syntax"].freeze
8
+
9
+ # AllCops keys that standard does not allow to be set by plugins
10
+ DISALLOWED_ALLCOPS_KEYS = [
11
+ "Include",
12
+ "Exclude",
13
+ "StyleGuideBaseURL",
14
+ "StyleGuideCopsOnly",
15
+ "TargetRubyVersion",
16
+ "EnabledByDefault",
17
+ "DisabledByDefault",
18
+
19
+ # The AllCops[Enabled] key is an unused artifact of #merge_with_default.
20
+ # See: https://github.com/rubocop/rubocop/blob/master/lib/rubocop/config_loader_resolver.rb#L81-L85
21
+ "Enabled"
22
+ ].freeze
23
+
24
+ def initialize
25
+ @creates_runner_context = Standard::Plugin::CreatesRunnerContext.new
26
+ end
27
+
28
+ def call(options_config, standard_config, plugins, permit_merging:)
29
+ runner_context = @creates_runner_context.call(standard_config)
30
+ plugin_config = combine_rubocop_configs(options_config, runner_context, plugins).to_h
31
+ merge_config_into_all_cops!(options_config, plugin_config)
32
+ merge_config_into_standard!(options_config, plugin_config, permit_merging: permit_merging)
33
+ end
34
+
35
+ private
36
+
37
+ def combine_rubocop_configs(options_config, runner_context, plugins)
38
+ fake_out_rubocop_default_configuration(options_config) do |fake_config|
39
+ all_cop_keys_configured_by_plugins = []
40
+
41
+ plugins.reduce(fake_config) do |combined_config, plugin|
42
+ RuboCop::ConfigLoader.instance_variable_set(:@default_configuration, combined_config)
43
+ next_config, path = config_for_plugin(plugin, runner_context)
44
+
45
+ next_config["AllCops"], all_cop_keys_configured_by_plugins = merge_all_cop_settings(
46
+ combined_config["AllCops"],
47
+ next_config["AllCops"],
48
+ all_cop_keys_configured_by_plugins
49
+ )
50
+ delete_already_configured_keys!(combined_config.keys, next_config, dont_delete_keys: ["AllCops"])
51
+
52
+ RuboCop::ConfigLoader.merge_with_default(next_config, path)
53
+ end
54
+ end
55
+ end
56
+
57
+ def config_for_plugin(plugin, runner_context)
58
+ rules = plugin.rules(runner_context)
59
+
60
+ if rules.type == :path
61
+ [RuboCop::ConfigLoader.load_file(rules.value), rules.value]
62
+ elsif rules.type == :object
63
+ path = plugin.method(:rules).source_location[0]
64
+ [RuboCop::Config.create(rules.value, path), path]
65
+ elsif rules.type == :error
66
+ raise "Plugin `#{plugin.about&.name || plugin.inspect}' failed to load with error: #{rules.value.respond_to?(:message) ? rules.value.message : rules.value}"
67
+ end
68
+ end
69
+
70
+ # This is how we ensure "first-in wins": plugins can override AllCops settings that are
71
+ # set by RuboCop's default configuration, but once a plugin sets an AllCop setting, they
72
+ # have exclusive first-in-wins rights to that setting.
73
+ #
74
+ # The one exception to this are array fields, because we don't want to
75
+ # overwrite the AllCops defaults but rather munge the arrays (`existing |
76
+ # new`) to allow plugins to add to the array, for example Include and
77
+ # Exclude paths and patterns.
78
+ def merge_all_cop_settings(existing_all_cops, new_all_cops, already_configured_keys)
79
+ return [existing_all_cops, already_configured_keys] unless new_all_cops.is_a?(Hash)
80
+
81
+ combined_all_cops = existing_all_cops.dup
82
+ combined_configured_keys = already_configured_keys.dup
83
+
84
+ new_all_cops.each do |key, value|
85
+ if combined_all_cops[key].is_a?(Array) && value.is_a?(Array)
86
+ combined_all_cops[key] |= value
87
+ combined_configured_keys |= [key]
88
+ elsif !combined_configured_keys.include?(key)
89
+ combined_all_cops[key] = value
90
+ combined_configured_keys << key
91
+ end
92
+ end
93
+
94
+ [combined_all_cops, combined_configured_keys]
95
+ end
96
+
97
+ def delete_already_configured_keys!(configured_keys, next_config, dont_delete_keys: [])
98
+ duplicate_keys = configured_keys & Array(next_config&.keys)
99
+
100
+ (duplicate_keys - dont_delete_keys).each do |key|
101
+ next_config.delete(key)
102
+ end
103
+ end
104
+
105
+ def merge_config_into_all_cops!(options_config, plugin_config)
106
+ options_config["AllCops"].merge!(
107
+ except(plugin_config["AllCops"], DISALLOWED_ALLCOPS_KEYS)
108
+ )
109
+ end
110
+
111
+ def merge_config_into_standard!(options_config, plugin_config, permit_merging:)
112
+ if permit_merging
113
+ plugin_config.each do |key, value|
114
+ options_config[key] = if options_config[key].is_a?(Hash)
115
+ merge(options_config[key], value)
116
+ else
117
+ value
118
+ end
119
+ end
120
+ else
121
+ except(plugin_config, options_config.keys).each do |key, value|
122
+ options_config[key] = value
123
+ end
124
+ end
125
+ end
126
+
127
+ def fake_out_rubocop_default_configuration(options_config)
128
+ og_default_config = RuboCop::ConfigLoader.instance_variable_get(:@default_configuration)
129
+ set_target_rails_version_on_all_cops_because_its_technically_not_allowed!(options_config)
130
+ result = yield blank_rubocop_config(options_config)
131
+ RuboCop::ConfigLoader.instance_variable_set(:@default_configuration, og_default_config)
132
+ result
133
+ end
134
+
135
+ # Avoid a warning that would otherwise be emitted by any plugin that set TargetRailsVersion
136
+ # because it's not a default AllCops key specified in RuboCop's embedded default config.
137
+ #
138
+ # See: https://github.com/rubocop/rubocop/pull/11833
139
+ def set_target_rails_version_on_all_cops_because_its_technically_not_allowed!(options_config)
140
+ return unless options_config.key?("AllCops")
141
+
142
+ options_config["AllCops"]["TargetRailsVersion"] = "~"
143
+ end
144
+
145
+ def blank_rubocop_config(example_config)
146
+ RuboCop::Config.new(example_config.to_h.slice(*MANDATORY_RUBOCOP_CONFIG_KEYS), "")
147
+ end
148
+
149
+ def except(hash_or_config, keys)
150
+ hash_or_config.to_h.reject { |key, _| keys.include?(key) }.to_h
151
+ end
152
+
153
+ # Always deletes nil entries, always overwrites arrays
154
+ # This is a simplified version of rubocop's ConfigLoader#merge:
155
+ # https://github.com/rubocop/rubocop/blob/v1.48.1/lib/rubocop/config_loader_resolver.rb#L98
156
+ def merge(old_hash, new_hash)
157
+ result = old_hash.merge(new_hash)
158
+ keys_appearing_in_both = old_hash.keys & new_hash.keys
159
+ keys_appearing_in_both.each do |key|
160
+ if new_hash[key].nil?
161
+ result.delete(key)
162
+ elsif old_hash[key].is_a?(Hash) && new_hash[key].is_a?(Hash)
163
+ result[key] = merge(old_hash[key], new_hash[key])
164
+ end
165
+ end
166
+ result
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,37 @@
1
+ module Standard
2
+ module Plugin
3
+ class StandardizesConfiguredPlugins
4
+ DEFAULT_PLUGIN_CONFIG = {
5
+ "enabled" => true,
6
+ "require_path" => nil, # If not set, will be set to the plugin name
7
+ "plugin_class_name" => nil # If not set, looks for gemspec `spec.metadata["default_lint_roller_plugin"]`
8
+ }.freeze
9
+
10
+ BUILT_INS = [
11
+ {"standard-base" => {
12
+ "require_path" => "standard/base",
13
+ "plugin_class_name" => "Standard::Base::Plugin"
14
+ }},
15
+ "standard-custom",
16
+ "standard-performance"
17
+ ].freeze
18
+
19
+ def call(plugins)
20
+ normalize_config_shape(BUILT_INS + plugins)
21
+ end
22
+
23
+ private
24
+
25
+ def normalize_config_shape(plugins)
26
+ plugins.map { |plugin|
27
+ if plugin.is_a?(Hash)
28
+ plugin_name = plugin.keys.first
29
+ [plugin_name, DEFAULT_PLUGIN_CONFIG.merge({"require_path" => plugin_name}, plugin.values.first)]
30
+ else
31
+ [plugin, DEFAULT_PLUGIN_CONFIG.merge("require_path" => plugin)]
32
+ end
33
+ }.to_h
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ module Standard
2
+ module Plugin
3
+ end
4
+ end
5
+
6
+ require_relative "plugin/creates_runner_context"
7
+ require_relative "plugin/combines_plugin_configs"
8
+ require_relative "plugin/merges_plugins_into_rubocop_config"
9
+ require_relative "plugin/standardizes_configured_plugins"
10
+ require_relative "plugin/determines_class_constant"
11
+ require_relative "plugin/initializes_plugins"
@@ -1,7 +1,7 @@
1
1
  require "pathname"
2
2
 
3
3
  module Standard
4
- class Railtie < Rails::Railtie
4
+ class Railtie < ::Rails::Railtie
5
5
  railtie_name :standard
6
6
 
7
7
  rake_tasks do
@@ -27,7 +27,7 @@ module Standard
27
27
  While Standard only offers a few configuration options, most can be set in
28
28
  a `.standard.yml` file. For full documentation, please visit:
29
29
 
30
- https://github.com/testdouble/standard
30
+ https://github.com/standardrb/standard
31
31
 
32
32
  Having trouble? Here's some diagnostic information:
33
33
 
@@ -39,7 +39,7 @@ module Standard
39
39
 
40
40
  Please report any problems (and include the above information) at the URL below:
41
41
 
42
- https://github.com/testdouble/standard/issues/new
42
+ https://github.com/standardrb/standard/issues/new
43
43
 
44
44
  MESSAGE
45
45
  end
@@ -19,7 +19,7 @@ module Standard
19
19
 
20
20
  # This is a workaround for an issue with how `parallel` and `stdin`
21
21
  # interact when invoked in this way. See:
22
- # https://github.com/testdouble/standard/issues/536
22
+ # https://github.com/standardrb/standard/issues/536
23
23
  def without_parallelizing_in_stdin_mode(options)
24
24
  if options[:stdin]
25
25
  options.delete(:parallel)
@@ -1,3 +1,3 @@
1
1
  module Standard
2
- VERSION = Gem::Version.new("1.27.0")
2
+ VERSION = Gem::Version.new("1.28.1")
3
3
  end
data/lib/standard.rb CHANGED
@@ -1,4 +1,8 @@
1
1
  require "rubocop"
2
+ require "lint_roller"
3
+
4
+ module Standard
5
+ end
2
6
 
3
7
  require "standard/rubocop/ext"
4
8
 
@@ -9,5 +13,4 @@ require "standard/railtie" if defined?(Rails) && defined?(Rails::Railtie)
9
13
  require "standard/formatter"
10
14
  require "standard/cop/block_single_line_braces"
11
15
 
12
- module Standard
13
- end
16
+ require "standard/plugin"
data/standard.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.required_ruby_version = ">= 2.6.0"
11
11
 
12
12
  spec.summary = "Ruby Style Guide, with linter & automatic code fixer"
13
- spec.homepage = "https://github.com/testdouble/standard"
13
+ spec.homepage = "https://github.com/standardrb/standard"
14
14
  spec.metadata["homepage_uri"] = spec.homepage
15
15
  spec.metadata["source_code_uri"] = spec.homepage
16
16
  spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
@@ -24,7 +24,10 @@ Gem::Specification.new do |spec|
24
24
  spec.metadata["rubygems_mfa_required"] = "true"
25
25
 
26
26
  spec.add_dependency "rubocop", "~> 1.50.2"
27
- spec.add_dependency "rubocop-performance", "~> 1.16.0"
27
+
28
+ spec.add_dependency "lint_roller", "~> 1.0"
29
+ spec.add_dependency "standard-custom", "~> 1.0.0"
30
+ spec.add_dependency "standard-performance", "~> 1.0.1"
28
31
 
29
32
  # not semver: first three are lsp protocol version, last is patch
30
33
  spec.add_dependency "language_server-protocol", "~> 3.17.0.2"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: standard
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.27.0
4
+ version: 1.28.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Searls
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-04-19 00:00:00.000000000 Z
11
+ date: 2023-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -25,19 +25,47 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.50.2
27
27
  - !ruby/object:Gem::Dependency
28
- name: rubocop-performance
28
+ name: lint_roller
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.16.0
33
+ version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.16.0
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: standard-custom
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: standard-performance
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.1
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.0.1
41
69
  - !ruby/object:Gem::Dependency
42
70
  name: language_server-protocol
43
71
  requirement: !ruby/object:Gem::Requirement
@@ -52,7 +80,7 @@ dependencies:
52
80
  - - "~>"
53
81
  - !ruby/object:Gem::Version
54
82
  version: 3.17.0.2
55
- description:
83
+ description:
56
84
  email:
57
85
  - searls@gmail.com
58
86
  executables:
@@ -72,8 +100,10 @@ files:
72
100
  - README.md
73
101
  - Rakefile
74
102
  - bin/console
103
+ - bin/run
75
104
  - bin/setup
76
105
  - config/base.yml
106
+ - config/default.yml
77
107
  - config/ruby-1.8.yml
78
108
  - config/ruby-1.9.yml
79
109
  - config/ruby-2.0.yml
@@ -86,13 +116,15 @@ files:
86
116
  - config/ruby-2.7.yml
87
117
  - config/ruby-3.0.yml
88
118
  - config/ruby-3.1.yml
119
+ - docs/ARCHITECTURE.md
89
120
  - docs/NEW_RUBIES.md
90
121
  - docs/RELEASE.md
91
122
  - exe/standardrb
92
123
  - lib/standard.rb
124
+ - lib/standard/base.rb
125
+ - lib/standard/base/plugin.rb
93
126
  - lib/standard/builds_config.rb
94
127
  - lib/standard/cli.rb
95
- - lib/standard/cop/block_single_line_braces.rb
96
128
  - lib/standard/creates_config_store.rb
97
129
  - lib/standard/creates_config_store/assigns_rubocop_yaml.rb
98
130
  - lib/standard/creates_config_store/configures_ignored_paths.rb
@@ -108,6 +140,13 @@ files:
108
140
  - lib/standard/lsp/server.rb
109
141
  - lib/standard/lsp/standardizer.rb
110
142
  - lib/standard/merges_settings.rb
143
+ - lib/standard/plugin.rb
144
+ - lib/standard/plugin/combines_plugin_configs.rb
145
+ - lib/standard/plugin/creates_runner_context.rb
146
+ - lib/standard/plugin/determines_class_constant.rb
147
+ - lib/standard/plugin/initializes_plugins.rb
148
+ - lib/standard/plugin/merges_plugins_into_rubocop_config.rb
149
+ - lib/standard/plugin/standardizes_configured_plugins.rb
111
150
  - lib/standard/railtie.rb
112
151
  - lib/standard/rake.rb
113
152
  - lib/standard/resolves_yaml_option.rb
@@ -120,14 +159,14 @@ files:
120
159
  - lib/standard/runners/version.rb
121
160
  - lib/standard/version.rb
122
161
  - standard.gemspec
123
- homepage: https://github.com/testdouble/standard
162
+ homepage: https://github.com/standardrb/standard
124
163
  licenses: []
125
164
  metadata:
126
- homepage_uri: https://github.com/testdouble/standard
127
- source_code_uri: https://github.com/testdouble/standard
128
- changelog_uri: https://github.com/testdouble/standard/blob/main/CHANGELOG.md
165
+ homepage_uri: https://github.com/standardrb/standard
166
+ source_code_uri: https://github.com/standardrb/standard
167
+ changelog_uri: https://github.com/standardrb/standard/blob/main/CHANGELOG.md
129
168
  rubygems_mfa_required: 'true'
130
- post_install_message:
169
+ post_install_message:
131
170
  rdoc_options: []
132
171
  require_paths:
133
172
  - lib
@@ -142,8 +181,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
181
  - !ruby/object:Gem::Version
143
182
  version: '0'
144
183
  requirements: []
145
- rubygems_version: 3.1.6
146
- signing_key:
184
+ rubygems_version: 3.4.10
185
+ signing_key:
147
186
  specification_version: 4
148
187
  summary: Ruby Style Guide, with linter & automatic code fixer
149
188
  test_files: []
@@ -1,96 +0,0 @@
1
- module RuboCop::Cop
2
- module Standard
3
- # Check for uses of braces around single line blocks, but allows either
4
- # braces or do/end for multi-line blocks.
5
- #
6
- # @example
7
- # # bad - single line block
8
- # items.each do |item| item / 5 end
9
- #
10
- # # good - single line block
11
- # items.each { |item| item / 5 }
12
- #
13
- class BlockSingleLineBraces < RuboCop::Cop::Base
14
- extend RuboCop::Cop::AutoCorrector
15
-
16
- def on_send(node)
17
- return unless node.arguments?
18
- return if node.parenthesized?
19
- return if node.operator_method? || node.assignment_method?
20
-
21
- node.arguments.each do |arg|
22
- get_blocks(arg) do |block|
23
- # If there are no parentheses around the arguments, then braces
24
- # and do-end have different meaning due to how they bind, so we
25
- # allow either.
26
- ignore_node(block)
27
- end
28
- end
29
- end
30
-
31
- def on_block(node)
32
- return if ignored_node?(node)
33
- return if proper_block_style?(node)
34
-
35
- message = message(node)
36
- add_offense(node.loc.begin, message: message) do |corrector|
37
- autocorrect(corrector, node)
38
- end
39
- end
40
-
41
- private
42
-
43
- def get_blocks(node, &block)
44
- case node.type
45
- when :block
46
- yield node
47
- when :send
48
- get_blocks(node.receiver, &block) if node.receiver
49
- when :hash
50
- # A hash which is passed as method argument may have no braces
51
- # In that case, one of the K/V pairs could contain a block node
52
- # which could change in meaning if do...end replaced {...}
53
- return if node.braces?
54
-
55
- node.each_child_node { |child| get_blocks(child, &block) }
56
- when :pair
57
- node.each_child_node { |child| get_blocks(child, &block) }
58
- end
59
- end
60
-
61
- def proper_block_style?(node)
62
- node.multiline? || node.braces?
63
- end
64
-
65
- def message(node)
66
- "Prefer `{...}` over `do...end` for single-line blocks."
67
- end
68
-
69
- def autocorrect(corrector, node)
70
- return if correction_would_break_code?(node)
71
-
72
- replace_do_end_with_braces(corrector, node.loc)
73
- end
74
-
75
- def correction_would_break_code?(node)
76
- return unless node.keywords?
77
-
78
- node.send_node.arguments? && !node.send_node.parenthesized?
79
- end
80
-
81
- def replace_do_end_with_braces(corrector, loc)
82
- b = loc.begin
83
- e = loc.end
84
-
85
- corrector.insert_after(b, " ") unless whitespace_after?(b, 2)
86
-
87
- corrector.replace(b, "{")
88
- corrector.replace(e, "}")
89
- end
90
-
91
- def whitespace_after?(range, length = 1)
92
- /\s/.match?(range.source_buffer.source[range.begin_pos + length, 1])
93
- end
94
- end
95
- end
96
- end