solargraph-rspec 0.5.2 → 0.5.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24c1205ed3940572fa4426a348213279990036bd48d6f097dfec5be2e81fcac5
4
- data.tar.gz: 26397bb59d638a2de7e64e30cc5687d5247feb23662c071a5eba85aadb71707b
3
+ metadata.gz: 8be3e0236b9ee7ad2bc78abe9764001fc4ec2432a381571e23dddc27923caacb
4
+ data.tar.gz: 217d891f9561406a322f037948f13a3197f39d980f8394e36fc86304b8bccde9
5
5
  SHA512:
6
- metadata.gz: 96641c86e4c32dcafa422efe61825c768ae1a7620c67d3004c4d7b0f5b794f25baaee0c11abd77c7a6b1df2cf0bb5c9beaaf0d6893ba2cf593f23b9022ce1b22
7
- data.tar.gz: e79b218d6df5102e1bb26d1b15deffc80cfb353f5b3f394492ea5c62001df507623828e3e7b50fafa74faf33c8d92587a31643fc696875d74264a585626a16c6
6
+ metadata.gz: 1c91744f1a8a2bb3a021f6cf008d627a292bf112c15b8215d7c9217d7bddf812b1f73b92a8d434d54755b10dd77b1f6b571c0b77ada0087b28e01e9f311e9c58
7
+ data.tar.gz: 3d9a7e4405f2ce232d31e7310a2af32c1f0e1d86cc8b48b461c899310b6bf9a8c9f43a1a0c661074fd670d1433827472161618a3ddb0cbfda551b8ab06953bac
data/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.3] - 2025-09-02
11
+
12
+ ### Fixed
13
+
14
+ - Fix local variable bindings (#16)
15
+ - Fix implicit subject generation for string descriptions (#17)
16
+
17
+ ### Changed
18
+
19
+ - Loosen solargraph dependency version >0.52.0
20
+ - Use solargraph's parser (#11)
21
+
10
22
  ## [0.5.2] - 2025-06-26
11
23
 
12
24
  ### Fixed
@@ -8,7 +8,6 @@ require_relative 'correctors/example_and_hook_blocks_binding_corrector'
8
8
  require_relative 'correctors/described_class_corrector'
9
9
  require_relative 'correctors/let_methods_corrector'
10
10
  require_relative 'correctors/subject_method_corrector'
11
- require_relative 'correctors/context_block_methods_corrector'
12
11
  require_relative 'correctors/dsl_methods_corrector'
13
12
  require_relative 'test_helpers'
14
13
  require_relative 'pin_factory'
@@ -52,7 +51,6 @@ module Solargraph
52
51
 
53
52
  # @type [Array<Class<Correctors::Base>>]
54
53
  CORRECTOR_CLASSES = [
55
- Correctors::ContextBlockMethodsCorrector,
56
54
  Correctors::ContextBlockNamespaceCorrector,
57
55
  Correctors::DescribedClassCorrector,
58
56
  Correctors::DslMethodsCorrector,
@@ -63,6 +63,16 @@ module Solargraph
63
63
  distance >= 0 ? distance : Float::INFINITY
64
64
  end
65
65
  end
66
+
67
+ # Overrides a pin's closure, and resets all the needed instance vars
68
+ # @param pin [Solargraph::Pin::Base]
69
+ # @param new_closure [Solargraph::Pin::Closure]
70
+ def override_closure(pin, new_closure)
71
+ pin.instance_variable_set('@closure', new_closure)
72
+ pin.reset_generated!
73
+
74
+ pin.remove_instance_variable(:@path) if pin.instance_variables.include? :@path
75
+ end
66
76
  end
67
77
  end
68
78
  end
@@ -12,7 +12,6 @@ module Solargraph
12
12
  # @param location_range [Solargraph::Range]
13
13
  rspec_walker.on_each_context_block do |namespace_name, location_range|
14
14
  original_block_pin = source_map.locate_block_pin(location_range.start.line, location_range.start.column)
15
- original_block_pin_index = source_map.pins.index(original_block_pin)
16
15
  location = PinFactory.build_location(location_range, source_map.filename)
17
16
 
18
17
  # Define a dynamic module for the example group block
@@ -26,17 +25,10 @@ module Solargraph
26
25
  location: location
27
26
  )
28
27
 
29
- fixed_namespace_block_pin = Solargraph::Pin::Block.new(
30
- closure: namespace_pin,
31
- location: original_block_pin.location,
32
- receiver: original_block_pin.receiver,
33
- scope: original_block_pin.scope
34
- )
35
-
36
- source_map.pins[original_block_pin_index] = fixed_namespace_block_pin
28
+ override_closure(original_block_pin, namespace_pin)
37
29
 
38
30
  # Include DSL methods in the example group block
39
- # TOOD: This does not work on solagraph! Class methods are not included from parent class.
31
+ # TODO: This does not work on solagraph! Class methods are not included from parent class.
40
32
  namespace_extend_pin = PinFactory.build_module_extend(
41
33
  namespace_pin,
42
34
  root_example_group_namespace_pin.name,
@@ -43,15 +43,8 @@ module Solargraph
43
43
 
44
44
  original_block_pin = source_map.locate_block_pin(location_range.start.line,
45
45
  location_range.start.column)
46
- original_block_pin_index = source_map.pins.index(original_block_pin)
47
- fixed_namespace_block_pin = Solargraph::Pin::Block.new(
48
- closure: example_run_method(namespace_pin),
49
- location: original_block_pin.location,
50
- receiver: original_block_pin.receiver,
51
- scope: original_block_pin.scope
52
- )
53
46
 
54
- source_map.pins[original_block_pin_index] = fixed_namespace_block_pin
47
+ override_closure(original_block_pin, example_run_method(namespace_pin))
55
48
  end
56
49
 
57
50
  # @param namespace_pin [Solargraph::Pin::Namespace]
@@ -23,7 +23,7 @@ module Solargraph
23
23
 
24
24
  # @param namespace [Pin::Namespace]
25
25
  # @param method_name [String]
26
- # @param node [RubyVM::AbstractSyntaxTree::Node]
26
+ # @param node [::Parser::AST::Node, nil]
27
27
  # @param types [Array<String>, nil]
28
28
  # @return [Pin::Method, nil]
29
29
  def rspec_let_method(namespace, method_name, location_range, node = nil, types: nil)
@@ -24,7 +24,7 @@ module Solargraph
24
24
 
25
25
  namespace_pin = closest_namespace_pin(namespace_pins, described_class_pin.location.range.start.line)
26
26
 
27
- if namespace_pin
27
+ if namespace_pin && described_class_pin.return_type.to_s != 'undefined'
28
28
  implicit_subject_pin = implicit_subject_method(described_class_pin, namespace_pin)
29
29
  add_pin(implicit_subject_pin)
30
30
  add_pins(one_liner_expectation_pins(implicit_subject_pin))
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Credits: This file is a copy of the file from the solargraph-rspec gem
3
+ # Credits: This file was originally copied and adapted from the log_bench gem.
4
4
 
5
5
  module Solargraph
6
6
  module Rspec
@@ -74,16 +74,10 @@ module Solargraph
74
74
  )
75
75
  end
76
76
 
77
- # @param ast [RubyVM::AbstractSyntaxTree::Node]
78
- # @see [RubyVM::AbstractSyntaxTree::NodeWrapper] - for why we need -1 for lineno
77
+ # @param ast [::Parser::AST::Node]
79
78
  # @return [Solargraph::Range]
80
79
  def self.build_location_range(ast)
81
- Solargraph::Range.from_to(
82
- ast.first_lineno - 1,
83
- ast.first_column,
84
- ast.last_lineno - 1,
85
- ast.last_column
86
- )
80
+ Solargraph::Parser.node_range(ast)
87
81
  end
88
82
 
89
83
  # @param location_range [Solargraph::Range]
@@ -9,30 +9,17 @@ module Solargraph
9
9
 
10
10
  class << self
11
11
  # Transforms let block to method ast node
12
- # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
13
- # @param code [String] code
12
+ # @param block_ast [::Parser::AST::Node]
14
13
  # @return [::Parser::AST::Node, nil]
15
- def transform_block(block_ast, code, method_name = nil)
14
+ def transform_block(block_ast, method_name = nil)
16
15
  method_name ||= NodeTypes.let_method_name(block_ast)
17
16
 
18
- code_lines = code.split("\n")
19
- # extract let definition block body code
20
- first_line = code_lines[block_ast.first_lineno - 1]
21
- last_line = code_lines[block_ast.last_lineno - 1]
22
- code_lines[block_ast.first_lineno - 1] = first_line[(block_ast.first_column)..]
23
- code_lines[block_ast.last_lineno - 1] = last_line[0..(block_ast.last_column)]
24
- let_definition_code = code_lines[
25
- (block_ast.first_lineno - 1)..(block_ast.last_lineno - 1)
26
- ].join("\n")
27
-
28
- let_definition_ast = Solargraph::Parser.parse(let_definition_code)
29
- method_body = let_definition_ast.children[2]
30
17
  ::Parser::AST::Node.new( # transform let block to a method ast node
31
18
  :def,
32
19
  [
33
20
  method_name.to_sym,
34
21
  ::Parser::AST::Node.new(:args, []),
35
- method_body
22
+ block_ast.children[2]
36
23
  ]
37
24
  )
38
25
  rescue SyntaxError => e
@@ -5,17 +5,24 @@ module Solargraph
5
5
  class SpecWalker
6
6
  class FullConstantName
7
7
  class << self
8
- # @param ast [RubyVM::AbstractSyntaxTree::Node]
8
+ # @param ast [::Parser::AST::Node]
9
9
  # @return [String]
10
10
  def from_ast(ast)
11
- raise 'Node is not a constant' unless NodeTypes.a_constant?(ast)
11
+ parts = []
12
12
 
13
- if ast.type == :CONST
14
- ast.children[0].to_s
15
- elsif ast.type == :COLON2
16
- name = ast.children[1].to_s
17
- "#{from_ast(ast.children[0])}::#{name}"
13
+ until ast.nil?
14
+ if ast.is_a? ::Parser::AST::Node
15
+ break unless ast.type == :const
16
+
17
+ parts << ast.children[1]
18
+ ast = ast.children[0]
19
+ else
20
+ parts << ast
21
+ break
22
+ end
18
23
  end
24
+
25
+ parts.reverse.join('::')
19
26
  end
20
27
 
21
28
  def from_context_block_ast(block_ast)
@@ -4,78 +4,71 @@ module Solargraph
4
4
  module Rspec
5
5
  class SpecWalker
6
6
  class NodeTypes
7
- # @param ast [RubyVM::AbstractSyntaxTree::Node]
7
+ # @param ast [::Parser::AST::Node]
8
8
  # @return [Boolean]
9
9
  def self.a_block?(ast)
10
- return false unless ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
11
-
12
- %i[ITER LAMBDA].include?(ast.type)
10
+ ast.is_a?(::Parser::AST::Node) && ast.type == :block
13
11
  end
14
12
 
15
- # @param ast [RubyVM::AbstractSyntaxTree::Node]
13
+ # @param ast [::Parser::AST::Node]
16
14
  # @return [Boolean]
17
15
  def self.a_context_block?(block_ast)
18
16
  Solargraph::Rspec::CONTEXT_METHODS.include?(method_with_block_name(block_ast))
19
17
  end
20
18
 
21
- # @param ast [RubyVM::AbstractSyntaxTree::Node]
19
+ # @param ast [::Parser::AST::Node]
22
20
  # @return [Boolean]
23
21
  def self.a_subject_block?(block_ast)
24
22
  Solargraph::Rspec::SUBJECT_METHODS.include?(method_with_block_name(block_ast))
25
23
  end
26
24
 
27
- # @param ast [RubyVM::AbstractSyntaxTree::Node]
25
+ # @param ast [::Parser::AST::Node]
28
26
  # @param config [Config]
29
27
  # @return [Boolean]
30
28
  def self.a_example_block?(block_ast, config)
31
29
  config.example_methods.map(&:to_s).include?(method_with_block_name(block_ast))
32
30
  end
33
31
 
34
- # @param ast [RubyVM::AbstractSyntaxTree::Node]
32
+ # @param ast [::Parser::AST::Node]
35
33
  # @param config [Config]
36
34
  # @return [Boolean]
37
35
  def self.a_let_block?(block_ast, config)
38
36
  config.let_methods.map(&:to_s).include?(method_with_block_name(block_ast))
39
37
  end
40
38
 
41
- # @param ast [RubyVM::AbstractSyntaxTree::Node]
39
+ # @param ast [::Parser::AST::Node]
42
40
  # @return [Boolean]
43
41
  def self.a_hook_block?(block_ast)
44
42
  Solargraph::Rspec::HOOK_METHODS.include?(method_with_block_name(block_ast))
45
43
  end
46
44
 
47
45
  def self.a_constant?(ast)
48
- %i[CONST COLON2].include?(ast.type)
46
+ ast.type == :const
49
47
  end
50
48
 
51
- # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
52
- # @return [String, nil]
49
+ # @param block_ast [::Parser::AST::Node]
50
+ # @return [String, nil] The name of the thing you are calling the block on
53
51
  def self.method_with_block_name(block_ast)
54
52
  return nil unless a_block?(block_ast)
53
+ return nil unless block_ast.children[0].type == :send
55
54
 
56
- method_call = %i[CALL FCALL].include?(block_ast.children[0].type)
57
- return nil unless method_call
58
-
59
- block_ast.children[0].children.select { |child| child.is_a?(Symbol) }.first&.to_s
55
+ block_ast.children[0].children[1].to_s
60
56
  end
61
57
 
62
- # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
63
- # @return [RubyVM::AbstractSyntaxTree::Node]
58
+ # @param block_ast [::Parser::AST::Node]
59
+ # @return [::Parser::AST::Node]
64
60
  def self.context_description_node(block_ast)
65
61
  return nil unless a_context_block?(block_ast)
66
62
 
67
- case block_ast.children[0].type
68
- when :CALL # RSpec.describe "something" do end
69
- block_ast.children[0].children[2].children[0]
70
- when :FCALL # describe "something" do end
71
- block_ast.children[0].children[1].children[0]
72
- end
63
+ block_ast.children[0].children[2]
73
64
  end
74
65
 
75
- # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
66
+ # @param block_ast [::Parser::AST::Node]
76
67
  # @return [String]
77
68
  def self.let_method_name(block_ast)
78
- block_ast.children[0].children[1]&.children&.[](0)&.children&.[](0)&.to_s # rubocop:disable Style/SafeNavigationChainLength
69
+ return nil unless a_block?(block_ast)
70
+
71
+ block_ast.children[0].children[2]&.children&.[](0)&.to_s # rubocop:disable Style/SafeNavigationChainLength
79
72
  end
80
73
  end
81
74
  end
@@ -5,13 +5,15 @@ module Solargraph
5
5
  class SpecWalker
6
6
  class RspecContextNamespace
7
7
  class << self
8
- # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
8
+ # @param block_ast [::Parser::AST::Node]
9
9
  # @return [String, nil]
10
10
  def from_block_ast(block_ast)
11
- return unless block_ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
11
+ return unless block_ast.is_a?(::Parser::AST::Node)
12
12
 
13
13
  ast = NodeTypes.context_description_node(block_ast)
14
- if ast.type == :STR
14
+ return unless ast
15
+
16
+ if ast.type == :str
15
17
  string_to_const_name(ast)
16
18
  elsif NodeTypes.a_constant?(ast)
17
19
  FullConstantName.from_ast(ast).gsub('::', '')
@@ -27,7 +29,7 @@ module Solargraph
27
29
  # @param ast [Parser::AST::Node]
28
30
  # @return [String]
29
31
  def string_to_const_name(string_ast)
30
- return unless string_ast.type == :STR
32
+ return unless string_ast.type == :str
31
33
 
32
34
  name = string_ast.children[0]
33
35
  return 'Anonymous'.dup if name.empty?
@@ -14,8 +14,7 @@ module Solargraph
14
14
  def initialize(source_map:, config:)
15
15
  @source_map = source_map
16
16
  @config = config
17
- # TODO: Implement SpecWalker with parser gem using default AST from `source_map.source.node`
18
- @walker = Rspec::Walker.new(ruby_vm_node(source_map))
17
+ @walker = Rspec::Walker.new(source_map.source.node)
19
18
  @handlers = {
20
19
  on_described_class: [],
21
20
  on_let_method: [],
@@ -45,7 +44,7 @@ module Solargraph
45
44
  # @param block [Proc]
46
45
  # @yieldparam method_name [String]
47
46
  # @yieldparam location_range [Solargraph::Range]
48
- # @yieldparam fake_method_ast [RubyVM::AbstractSyntaxTree::Node]
47
+ # @yieldparam fake_method_ast [::Parser::AST::Node]
49
48
  # @return [void]
50
49
  def on_let_method(&block)
51
50
  @handlers[:on_let_method] << block
@@ -54,7 +53,7 @@ module Solargraph
54
53
  # @param block [Proc]
55
54
  # @yieldparam method_name [String]
56
55
  # @yieldparam location_range [Solargraph::Range]
57
- # @yieldparam fake_method_ast [RubyVM::AbstractSyntaxTree::Node]
56
+ # @yieldparam fake_method_ast [::Parser::AST::Node]
58
57
  # @return [void]
59
58
  def on_subject(&block)
60
59
  @handlers[:on_subject] << block
@@ -114,54 +113,54 @@ module Solargraph
114
113
  end
115
114
  end
116
115
 
117
- walker.on :ITER do |block_ast|
116
+ walker.on :block do |block_ast|
118
117
  next unless NodeTypes.a_let_block?(block_ast, config)
119
118
 
120
119
  method_name = NodeTypes.let_method_name(block_ast)
121
120
  next unless method_name
122
121
 
123
- fake_method_ast = FakeLetMethod.transform_block(block_ast, @source_map.source.code, method_name)
122
+ fake_method_ast = FakeLetMethod.transform_block(block_ast, method_name)
124
123
 
125
124
  @handlers[:on_let_method].each do |handler|
126
125
  handler.call(method_name, PinFactory.build_location_range(block_ast.children[0]), fake_method_ast)
127
126
  end
128
127
  end
129
128
 
130
- walker.on :ITER do |block_ast|
129
+ walker.on :block do |block_ast|
131
130
  next unless NodeTypes.a_subject_block?(block_ast)
132
131
 
133
132
  method_name = NodeTypes.let_method_name(block_ast)
134
- fake_method_ast = FakeLetMethod.transform_block(block_ast, @source_map.source.code, method_name || 'subject')
133
+ fake_method_ast = FakeLetMethod.transform_block(block_ast, method_name || 'subject')
135
134
 
136
135
  @handlers[:on_subject].each do |handler|
137
136
  handler.call(method_name, PinFactory.build_location_range(block_ast.children[0]), fake_method_ast)
138
137
  end
139
138
  end
140
139
 
141
- walker.on :ITER do |block_ast|
140
+ walker.on :block do |block_ast|
142
141
  next unless NodeTypes.a_example_block?(block_ast, config)
143
142
 
144
143
  @handlers[:on_example_block].each do |handler|
145
144
  handler.call(PinFactory.build_location_range(block_ast))
146
145
  end
147
146
 
148
- # @param blocks_in_examples [RubyVM::AbstractSyntaxTree::Node]
149
- each_block(block_ast.children[1]) do |blocks_in_examples|
147
+ # @param blocks_in_examples [::Parser::AST::Node]
148
+ each_block(block_ast.children[2]) do |blocks_in_examples|
150
149
  @handlers[:on_blocks_in_examples].each do |handler|
151
150
  handler.call(PinFactory.build_location_range(blocks_in_examples))
152
151
  end
153
152
  end
154
153
  end
155
154
 
156
- walker.on :ITER do |block_ast|
155
+ walker.on :block do |block_ast|
157
156
  next unless NodeTypes.a_hook_block?(block_ast)
158
157
 
159
158
  @handlers[:on_hook_block].each do |handler|
160
159
  handler.call(PinFactory.build_location_range(block_ast))
161
160
  end
162
161
 
163
- # @param blocks_in_examples [RubyVM::AbstractSyntaxTree::Node]
164
- each_block(block_ast.children[1]) do |blocks_in_examples|
162
+ # @param blocks_in_examples [::Parser::AST::Node]
163
+ each_block(block_ast.children[2]) do |blocks_in_examples|
165
164
  @handlers[:on_blocks_in_examples].each do |handler|
166
165
  handler.call(PinFactory.build_location_range(blocks_in_examples))
167
166
  end
@@ -178,11 +177,9 @@ module Solargraph
178
177
  # @param ast [Parser::AST::Node]
179
178
  # @param parent_result [Object]
180
179
  def each_block(ast, parent_result = nil, &block)
181
- return unless ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
180
+ return nil unless ast.is_a?(::Parser::AST::Node)
182
181
 
183
- is_a_block = NodeTypes.a_block?(ast)
184
-
185
- if is_a_block
182
+ if NodeTypes.a_block?(ast)
186
183
  result = block&.call(ast, parent_result)
187
184
  parent_result = result if result
188
185
  end
@@ -211,12 +208,6 @@ module Solargraph
211
208
  next parent_namespace
212
209
  end
213
210
  end
214
-
215
- # @param source_map [SourceMap]
216
- # @return [RubyVM::AbstractSyntaxTree::Node]
217
- def ruby_vm_node(source_map)
218
- RubyVM::AbstractSyntaxTree.parse(source_map.source.code)
219
- end
220
211
  end
221
212
  end
222
213
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Solargraph
4
4
  module Rspec
5
- VERSION = '0.5.2'
5
+ VERSION = '0.5.4'
6
6
  end
7
7
  end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # Credits: This file was originally copied and adapted from the solargraph-rails gem
5
+
3
6
  module Solargraph
4
7
  module Rspec
5
8
  class Walker
@@ -17,7 +20,7 @@ module Solargraph
17
20
  @proc = Proc.new(&block)
18
21
  end
19
22
 
20
- # @param node [RubyVM::AbstractSyntaxTree::Node]
23
+ # @param node [::Parser::AST::Node]
21
24
  # @return [void]
22
25
  def visit(node)
23
26
  return unless matches?(node)
@@ -33,15 +36,15 @@ module Solargraph
33
36
 
34
37
  private
35
38
 
36
- # @param node [RubyVM::AbstractSyntaxTree::Node]
39
+ # @param node [::Parser::AST::Node]
37
40
  # @return [Boolean]
38
41
  def matches?(node)
39
42
  return false unless node.type == node_type
40
43
  return false unless node.children
41
44
  return true if @args.empty?
42
45
 
43
- a_child_matches = node.children.first.is_a?(RubyVM::AbstractSyntaxTree::Node) && node.children.any? do |child|
44
- child.is_a?(RubyVM::AbstractSyntaxTree::Node) &&
46
+ a_child_matches = node.children.first.is_a?(::Parser::AST::Node) && node.children.any? do |child|
47
+ child.is_a?(::Parser::AST::Node) &&
45
48
  match_children(child.children, @args[1..])
46
49
  end
47
50
 
@@ -50,12 +53,12 @@ module Solargraph
50
53
  match_children(node.children)
51
54
  end
52
55
 
53
- # @param children [Array<RubyVM::AbstractSyntaxTree::Node>]
56
+ # @param children [Array<::Parser::AST::Node>]
54
57
  def match_children(children, args = @args)
55
58
  args.each_with_index.all? do |arg, i|
56
59
  if arg == :any
57
60
  true
58
- elsif children[i].is_a?(RubyVM::AbstractSyntaxTree::Node) && arg.is_a?(Symbol)
61
+ elsif children[i].is_a?(::Parser::AST::Node) && arg.is_a?(Symbol)
59
62
  children[i].type == arg
60
63
  else
61
64
  children[i] == arg
@@ -66,7 +69,7 @@ module Solargraph
66
69
 
67
70
  attr_reader :ast, :comments
68
71
 
69
- # @param ast [RubyVM::AbstractSyntaxTree::Node]
72
+ # @param ast [::Parser::AST::Node]
70
73
  # @param comments [Hash]
71
74
  def initialize(ast, comments = {})
72
75
  @comments = comments
@@ -74,6 +77,8 @@ module Solargraph
74
77
  @hooks = Hash.new { |h, k| h[k] = [] }
75
78
  end
76
79
 
80
+ # @yieldparam [::Parser::AST::Node]
81
+ # @yieldparam [Walker]
77
82
  def on(node_type, args = [], &block)
78
83
  @hooks[node_type] << Hook.new(node_type, args, &block)
79
84
  end
@@ -85,7 +90,7 @@ module Solargraph
85
90
  private
86
91
 
87
92
  def traverse(node)
88
- return unless node.is_a?(RubyVM::AbstractSyntaxTree::Node)
93
+ return unless node.is_a?(::Parser::AST::Node)
89
94
 
90
95
  @hooks[node.type].each { |hook| hook.visit(node) }
91
96
 
data/release_gem.sh ADDED
@@ -0,0 +1,210 @@
1
+ #!/usr/bin/env bash
2
+ # Credits: This file was originally copied and adapted from the log_bench gem.
3
+ # https://raw.githubusercontent.com/silva96/log_bench/refs/heads/main/release_gem.sh
4
+ set -euo pipefail
5
+
6
+ ############################################
7
+ # Config — tweak if your gem/repo differs
8
+ ############################################
9
+ GEM_NAME="solargraph-rspec"
10
+ LIB_DIR="lib/solargraph/rspec"
11
+ DEFAULT_BRANCH="main"
12
+ VERSION_FILE="${LIB_DIR}/version.rb"
13
+ GEMSPEC_FILE="${GEM_NAME}.gemspec"
14
+ TAG_PREFIX="v"
15
+
16
+ # Bump mode: patch | minor | major (default patch)
17
+ BUMP="${BUMP:-patch}"
18
+
19
+ ############################################
20
+ # Arg parsing (optional)
21
+ ############################################
22
+ while [[ $# -gt 0 ]]; do
23
+ case "$1" in
24
+ --bump) BUMP="${2:-patch}"; shift 2 ;;
25
+ --bump=*) BUMP="${1#*=}"; shift 1 ;;
26
+ -h|--help)
27
+ cat <<EOF
28
+ Usage: $0 [--bump patch|minor|major]
29
+ Defaults to --bump patch (e.g., 0.2.6 -> 0.2.7).
30
+ EOF
31
+ exit 0 ;;
32
+ *) echo "Unknown arg: $1" >&2; exit 1 ;;
33
+ esac
34
+ done
35
+
36
+ case "$BUMP" in
37
+ patch|minor|major) : ;;
38
+ *) echo "Invalid --bump: $BUMP (use patch|minor|major)"; exit 1 ;;
39
+ esac
40
+
41
+ ############################################
42
+ # Pretty logging
43
+ ############################################
44
+ if command -v tput >/dev/null 2>&1 && [ -n "${TERM:-}" ]; then
45
+ BOLD=$(tput bold) || BOLD=""
46
+ RESET=$(tput sgr0) || RESET=""
47
+ GREEN=$(tput setaf 2) || GREEN=""
48
+ YELLOW=$(tput setaf 3) || YELLOW=""
49
+ RED=$(tput setaf 1) || RED=""
50
+ BLUE=$(tput setaf 4) || BLUE=""
51
+ else
52
+ BOLD=""; RESET=""; GREEN=""; YELLOW=""; RED=""; BLUE=""
53
+ fi
54
+
55
+ log() { printf "%b\n" "${BLUE}▸${RESET} $*"; }
56
+ success() { printf "%b\n" "${GREEN}✔${RESET} $*"; }
57
+ warn() { printf "%b\n" "${YELLOW}⚠${RESET} $*"; }
58
+ error() { printf "%b\n" "${RED}✖${RESET} $*"; }
59
+ die() { error "$*"; exit 1; }
60
+
61
+ ############################################
62
+ # Helpers
63
+ ############################################
64
+ require_cmd() { command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"; }
65
+
66
+ current_version() {
67
+ # Extract the first X.Y.Z from the VERSION file
68
+ grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' "$VERSION_FILE" | head -n1
69
+ }
70
+
71
+ validate_semver() { [[ "$1" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; }
72
+
73
+ bump_version() {
74
+ local mode="$1" ver="$2"
75
+ IFS='.' read -r MA MI PA <<<"$ver"
76
+ case "$mode" in
77
+ patch) echo "${MA}.${MI}.$((PA+1))" ;;
78
+ minor) echo "${MA}.$((MI+1)).0" ;;
79
+ major) echo "$((MA+1)).0.0" ;;
80
+ esac
81
+ }
82
+
83
+ update_version_file() {
84
+ local new="$1"
85
+ # Robust sed (preserves indentation, quote style, optional .freeze)
86
+ sed -E "s/^([[:space:]]*)VERSION[[:space:]]*=[[:space:]]*(['\"])[0-9]+\.[0-9]+\.[0-9]+(['\"])(\.freeze)?/\1VERSION = \2${new}\3\4/" \
87
+ "$VERSION_FILE" > "${VERSION_FILE}.tmp"
88
+ mv "${VERSION_FILE}.tmp" "$VERSION_FILE"
89
+ }
90
+
91
+ ensure_clean_worktree() {
92
+ if [ -n "$(git status --porcelain)" ]; then
93
+ warn "Your working tree has uncommitted changes:"
94
+ git status --short
95
+ read -rp "$(printf '%b' "${YELLOW}Proceed anyway? [y/N] ${RESET}")" ans
96
+ [[ "${ans:-}" =~ ^[Yy]$ ]] || die "Aborted due to dirty working tree."
97
+ fi
98
+ }
99
+
100
+ ############################################
101
+ # Checks
102
+ ############################################
103
+ require_cmd git
104
+ require_cmd bundle
105
+ require_cmd gem
106
+ require_cmd sed
107
+
108
+ if ! command -v gh >/dev/null 2>&1; then
109
+ warn "GitHub CLI (gh) not found. GitHub release creation will be skipped."
110
+ GH_AVAILABLE="false"
111
+ else
112
+ GH_AVAILABLE="true"
113
+ fi
114
+
115
+ [ -f "$VERSION_FILE" ] || die "Version file not found: $VERSION_FILE"
116
+ [ -f "$GEMSPEC_FILE" ] || die "Gemspec not found: $GEMSPEC_FILE"
117
+
118
+ ############################################
119
+ # Start
120
+ ############################################
121
+ log "${BOLD}Publishing ${GEM_NAME}${RESET}"
122
+ ensure_clean_worktree
123
+
124
+ log "Pulling latest from origin/${DEFAULT_BRANCH}…"
125
+ git checkout "$DEFAULT_BRANCH" >/dev/null 2>&1 || true
126
+ git pull origin "$DEFAULT_BRANCH"
127
+ success "Up to date."
128
+
129
+ CURR_VER="$(current_version)"
130
+ [ -n "$CURR_VER" ] || die "Could not determine current version from $VERSION_FILE"
131
+
132
+ log "Current version detected in ${VERSION_FILE}: ${BOLD}${CURR_VER}${RESET}"
133
+ PROPOSED="$(bump_version "$BUMP" "$CURR_VER")"
134
+ read -rp "$(printf '%b' "${BLUE}Proposed next ${BOLD}${BUMP}${RESET}${BLUE} version is ${BOLD}${PROPOSED}${RESET}${BLUE}. Press Enter to accept or type a different semver (X.Y.Z): ${RESET}")" NEW_VER
135
+ NEW_VER="${NEW_VER:-$PROPOSED}"
136
+ validate_semver "$NEW_VER" || die "Invalid semver: $NEW_VER"
137
+
138
+ TAG="${TAG_PREFIX}${NEW_VER}"
139
+
140
+ # Guard against existing tag
141
+ if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
142
+ die "Tag ${TAG} already exists."
143
+ fi
144
+
145
+ log "Updating version file to ${BOLD}${NEW_VER}${RESET}…"
146
+ before_contents="$(cat "$VERSION_FILE")"
147
+ update_version_file "$NEW_VER"
148
+ after_contents="$(cat "$VERSION_FILE")"
149
+ if [ "$before_contents" = "$after_contents" ]; then
150
+ AFTER_VER="$(current_version || true)"
151
+ if [ "$AFTER_VER" = "$NEW_VER" ]; then
152
+ warn "Version file already at ${NEW_VER}; nothing to change."
153
+ else
154
+ die "Failed to update ${VERSION_FILE}. Ensure it has a line like:
155
+ VERSION = \"${CURR_VER}\"
156
+ (or with single quotes / optional .freeze)."
157
+ fi
158
+ else
159
+ success "Updated ${VERSION_FILE}."
160
+ fi
161
+
162
+ log "Installing gems (bundle install)…"
163
+ bundle install
164
+ success "Dependencies installed."
165
+
166
+ log "Committing version bump (including Gemfile.lock if changed)…"
167
+ FILES_TO_COMMIT=()
168
+ if ! git diff --quiet -- "$VERSION_FILE"; then
169
+ FILES_TO_COMMIT+=("$VERSION_FILE")
170
+ fi
171
+ if [ -f "Gemfile.lock" ] && ! git diff --quiet -- "Gemfile.lock"; then
172
+ FILES_TO_COMMIT+=("Gemfile.lock")
173
+ fi
174
+
175
+ if [ "${#FILES_TO_COMMIT[@]}" -gt 0 ]; then
176
+ git add "${FILES_TO_COMMIT[@]}"
177
+ git commit -m "Bump version to ${NEW_VER}"
178
+ success "Committed: ${FILES_TO_COMMIT[*]}"
179
+ else
180
+ warn "No changes to commit (version file and Gemfile.lock unchanged)."
181
+ fi
182
+
183
+ log "Creating tag ${BOLD}${TAG}${RESET}…"
184
+ git tag "${TAG}" -m "Release version ${NEW_VER}"
185
+ success "Tag created."
186
+
187
+ log "Pushing branch & tag to origin…"
188
+ git push origin "$DEFAULT_BRANCH"
189
+ git push origin "${TAG}"
190
+ success "Pushed."
191
+
192
+ if [ "$GH_AVAILABLE" = "true" ]; then
193
+ log "Creating GitHub release ${BOLD}${TAG}${RESET} with auto-generated notes…"
194
+ gh release create "${TAG}" --title "Release version ${NEW_VER}" --generate-notes || warn "Failed to create GitHub release via gh."
195
+ success "GitHub release created."
196
+ else
197
+ warn "Skipping GitHub release creation (gh not installed)."
198
+ fi
199
+
200
+ log "Building gem…"
201
+ gem build "$GEMSPEC_FILE"
202
+ GEM_FILE="${GEM_NAME}-${NEW_VER}.gem"
203
+ [ -f "$GEM_FILE" ] || die "Gem file not found after build: $GEM_FILE"
204
+ success "Built ${GEM_FILE}."
205
+
206
+ log "Pushing gem to RubyGems…"
207
+ gem push "$GEM_FILE"
208
+ success "Gem pushed to RubyGems."
209
+
210
+ success "${BOLD}${GEM_NAME} ${NEW_VER}${RESET} has been published! 🎉"
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
28
28
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ['lib']
30
30
 
31
- spec.add_dependency 'solargraph', '~> 0.52', '>= 0.52.0'
31
+ spec.add_dependency 'solargraph', '>= 0.52.0'
32
32
 
33
33
  # For more information and examples about making a new gem, check out our
34
34
  # guide at: https://bundler.io/guides/creating_gem.html
metadata CHANGED
@@ -1,21 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.2
4
+ version: 0.5.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lekë Mula
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
10
+ date: 2025-09-06 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: solargraph
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
- - - "~>"
17
- - !ruby/object:Gem::Version
18
- version: '0.52'
19
16
  - - ">="
20
17
  - !ruby/object:Gem::Version
21
18
  version: 0.52.0
@@ -23,9 +20,6 @@ dependencies:
23
20
  prerelease: false
24
21
  version_requirements: !ruby/object:Gem::Requirement
25
22
  requirements:
26
- - - "~>"
27
- - !ruby/object:Gem::Version
28
- version: '0.52'
29
23
  - - ">="
30
24
  - !ruby/object:Gem::Version
31
25
  version: 0.52.0
@@ -54,7 +48,6 @@ files:
54
48
  - lib/solargraph/rspec/config.rb
55
49
  - lib/solargraph/rspec/convention.rb
56
50
  - lib/solargraph/rspec/correctors/base.rb
57
- - lib/solargraph/rspec/correctors/context_block_methods_corrector.rb
58
51
  - lib/solargraph/rspec/correctors/context_block_namespace_corrector.rb
59
52
  - lib/solargraph/rspec/correctors/described_class_corrector.rb
60
53
  - lib/solargraph/rspec/correctors/dsl_methods_corrector.rb
@@ -71,6 +64,7 @@ files:
71
64
  - lib/solargraph/rspec/version.rb
72
65
  - lib/solargraph/rspec/walker.rb
73
66
  - lib/solargraph_rspec.rb
67
+ - release_gem.sh
74
68
  - sig/solargraph/rspec.rbs
75
69
  - solargraph-rspec.gemspec
76
70
  licenses:
@@ -92,7 +86,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
92
86
  - !ruby/object:Gem::Version
93
87
  version: '0'
94
88
  requirements: []
95
- rubygems_version: 3.6.9
89
+ rubygems_version: 3.6.3
96
90
  specification_version: 4
97
91
  summary: Solargraph plugin supporting RSpec code completion
98
92
  test_files: []
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'base'
4
-
5
- module Solargraph
6
- module Rspec
7
- module Correctors
8
- # A corrector that corrects pure ruby method blocks namespace defined inside describe/context blocks.
9
- class ContextBlockMethodsCorrector < Base
10
- # @param source_map [Solargraph::SourceMap]
11
- def correct(source_map)
12
- rspec_walker.after_walk do
13
- source_map.pins.each_with_index do |pin, index|
14
- next unless pin.is_a?(Solargraph::Pin::Method)
15
-
16
- namespace_pin = closest_namespace_pin(namespace_pins, pin.location.range.start.line)
17
- next unless namespace_pin
18
-
19
- source_map.pins[index] = Solargraph::Pin::Method.new(
20
- visibility: pin.visibility,
21
- parameters: pin.parameters,
22
- closure: namespace_pin,
23
- node: pin.node,
24
- signatures: pin.signatures,
25
- location: pin.location,
26
- name: pin.name,
27
- scope: pin.scope,
28
- comments: pin.comments
29
- )
30
- end
31
- end
32
- end
33
- end
34
- end
35
- end
36
- end