solargraph-rspec 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,13 +10,53 @@ module Solargraph
10
10
  # @param source_map [Solargraph::SourceMap]
11
11
  # @return [void]
12
12
  def correct(_source_map)
13
- rspec_walker.on_subject do |ast|
14
- namespace_pin = closest_namespace_pin(namespace_pins, ast.loc.line)
13
+ rspec_walker.on_subject do |subject_name, location_range, fake_method_ast|
14
+ namespace_pin = closest_namespace_pin(namespace_pins, location_range.start.line)
15
15
  next unless namespace_pin
16
16
 
17
- subject_pin = rspec_let_method(namespace_pin, ast)
18
- yield [subject_pin].compact if block_given?
17
+ subject_pin = rspec_subject_method(namespace_pin, subject_name, location_range, fake_method_ast)
18
+ add_pin(subject_pin)
19
19
  end
20
+
21
+ rspec_walker.after_walk do
22
+ next unless described_class_pin
23
+
24
+ namespace_pin = closest_namespace_pin(namespace_pins, described_class_pin.location.range.start.line)
25
+
26
+ add_pin(implicit_subject_pin(described_class_pin, namespace_pin)) if namespace_pin
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ # @return [Pin::Method, nil]
33
+ def described_class_pin
34
+ @described_class_pin ||= added_pins.find { |pin| pin.is_a?(Pin::Method) && pin.name == 'described_class' }
35
+ end
36
+
37
+ # @param namespace_pin [Pin::Namespace]
38
+ # @param subject_name [String, nil]
39
+ # @param location_range [Solargraph::Range]
40
+ # @param fake_method_ast [Parser::AST::Node]
41
+ # @return [Pin::Method]
42
+ def rspec_subject_method(namespace_pin, subject_name, location_range, fake_method_ast)
43
+ method_name = subject_name || 'subject'
44
+ rspec_let_method(namespace_pin, method_name, location_range, fake_method_ast)
45
+ end
46
+
47
+ # @param described_class_pin [Pin::Method]
48
+ # @param namespace_pin [Pin::Namespace]
49
+ # @return [Pin::Method]
50
+ def implicit_subject_pin(described_class_pin, namespace_pin)
51
+ described_class = described_class_pin.return_type.first.subtypes.first.name
52
+
53
+ PinFactory.build_public_method(
54
+ namespace_pin,
55
+ 'subject',
56
+ types: ["::#{described_class}"],
57
+ location: described_class_pin.location,
58
+ scope: :instance
59
+ )
20
60
  end
21
61
  end
22
62
  end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Credits: This file is a copy of the file from the solargraph-rspec gem
4
+
5
+ module Solargraph
6
+ module Rspec
7
+ # Factory class for building pins and references.
8
+ module PinFactory
9
+ # @param namespace [Solargraph::Pin::Namespace]
10
+ # @param name [String]
11
+ # @param types [Array<String>]
12
+ # @param location [Solargraph::Location]
13
+ # @param comments [Array<String>]
14
+ # @param attribute [Boolean]
15
+ # @param scope [:instance, :class]
16
+ # @return [Solargraph::Pin::Method]
17
+ def self.build_public_method(
18
+ namespace,
19
+ name,
20
+ types: nil,
21
+ location: nil,
22
+ comments: [],
23
+ attribute: false,
24
+ scope: :instance,
25
+ node: nil
26
+ )
27
+ opts = {
28
+ name: name,
29
+ location: location,
30
+ closure: namespace,
31
+ scope: scope,
32
+ attribute: attribute,
33
+ comments: [],
34
+ node: node
35
+ }
36
+
37
+ comments << "@return [#{types.join(",")}]" if types
38
+
39
+ opts[:comments] = comments.join("\n")
40
+
41
+ Solargraph::Pin::Method.new(**opts)
42
+ end
43
+
44
+ # @param namespace [Solargraph::Pin::Namespace]
45
+ # @param name [String]
46
+ # @param location [Solargraph::Location]
47
+ # @return [Solargraph::Pin::Reference::Include]
48
+ def self.build_module_include(namespace, module_name, location)
49
+ Solargraph::Pin::Reference::Include.new(
50
+ closure: namespace,
51
+ name: module_name,
52
+ location: location
53
+ )
54
+ end
55
+
56
+ # @param namespace [Solargraph::Pin::Namespace]
57
+ # @param module_name [String]
58
+ # @param location [Solargraph::Location]
59
+ # @return [Solargraph::Pin::Reference::Extend]
60
+ def self.build_module_extend(namespace, module_name, location)
61
+ Solargraph::Pin::Reference::Extend.new(
62
+ closure: namespace,
63
+ name: module_name,
64
+ location: location
65
+ )
66
+ end
67
+
68
+ # @param path [String]
69
+ # @return [Solargraph::Location]
70
+ def self.dummy_location(path)
71
+ Solargraph::Location.new(
72
+ File.expand_path(path),
73
+ Solargraph::Range.from_to(0, 0, 0, 0)
74
+ )
75
+ end
76
+
77
+ # @param ast [RubyVM::AbstractSyntaxTree::Node]
78
+ # @see [RubyVM::AbstractSyntaxTree::NodeWrapper] - for why we need -1 for lineno
79
+ # @return [Solargraph::Range]
80
+ 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
+ )
87
+ end
88
+
89
+ # @param location_range [Solargraph::Range]
90
+ # @param path [String]
91
+ # @return [Solargraph::Location]
92
+ def self.build_location(location_range, path)
93
+ Solargraph::Location.new(
94
+ File.expand_path(path),
95
+ location_range
96
+ )
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Rspec
5
+ class SpecWalker
6
+ class FakeLetMethod
7
+ MATCH_BODY = Regexp.union(
8
+ /do(.*)end/m,
9
+ /{(.*)}/m
10
+ )
11
+
12
+ # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
13
+ # @return [RubyVM::AbstractSyntaxTree::Node]
14
+ def self.transform_block(block_ast, code, method_name = nil)
15
+ method_name ||= NodeTypes.let_method_name(block_ast)
16
+ block_body = block_ast.children[1]
17
+ matches = code.lines[block_body.first_lineno - 1..block_body.last_lineno - 1].join.match(MATCH_BODY)
18
+ method_body = (matches[1] || matches[2]).strip
19
+
20
+ ast = RubyVM::AbstractSyntaxTree.parse <<~RUBY
21
+ def #{method_name}
22
+ #{method_body}
23
+ end
24
+ RUBY
25
+
26
+ ast.children[2]
27
+ rescue SyntaxError
28
+ raise "Failed to build fake let method: #{block_ast.inspect}, message: #{e.message}"
29
+ ensure
30
+ nil
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Rspec
5
+ class SpecWalker
6
+ class FullConstantName
7
+ class << self
8
+ # @param ast [RubyVM::AbstractSyntaxTree::Node]
9
+ # @return [String]
10
+ def from_ast(ast)
11
+ raise 'Node is not a constant' unless NodeTypes.a_constant?(ast)
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}"
18
+ end
19
+ end
20
+
21
+ def from_context_block_ast(block_ast)
22
+ ast = NodeTypes.context_description_node(block_ast)
23
+ from_ast(ast)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Rspec
5
+ class SpecWalker
6
+ class NodeTypes
7
+ # @param ast [RubyVM::AbstractSyntaxTree::Node]
8
+ # @return [Boolean]
9
+ def self.a_block?(ast)
10
+ return false unless ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
11
+
12
+ %i[ITER LAMBDA].include?(ast.type)
13
+ end
14
+
15
+ # @param ast [RubyVM::AbstractSyntaxTree::Node]
16
+ # @return [Boolean]
17
+ def self.a_context_block?(block_ast)
18
+ Solargraph::Rspec::CONTEXT_METHODS.include?(method_with_block_name(block_ast))
19
+ end
20
+
21
+ # @param ast [RubyVM::AbstractSyntaxTree::Node]
22
+ # @return [Boolean]
23
+ def self.a_subject_block?(block_ast)
24
+ Solargraph::Rspec::SUBJECT_METHODS.include?(method_with_block_name(block_ast))
25
+ end
26
+
27
+ # @param ast [RubyVM::AbstractSyntaxTree::Node]
28
+ # @return [Boolean]
29
+ def self.a_example_block?(block_ast)
30
+ Solargraph::Rspec::EXAMPLE_METHODS.include?(method_with_block_name(block_ast))
31
+ end
32
+
33
+ # @param ast [RubyVM::AbstractSyntaxTree::Node]
34
+ # @param config [Config]
35
+ # @return [Boolean]
36
+ def self.a_let_block?(block_ast, config)
37
+ config.let_methods.map(&:to_s).include?(method_with_block_name(block_ast))
38
+ end
39
+
40
+ # @param ast [RubyVM::AbstractSyntaxTree::Node]
41
+ # @return [Boolean]
42
+ def self.a_hook_block?(block_ast)
43
+ Solargraph::Rspec::HOOK_METHODS.include?(method_with_block_name(block_ast))
44
+ end
45
+
46
+ def self.a_constant?(ast)
47
+ %i[CONST COLON2].include?(ast.type)
48
+ end
49
+
50
+ # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
51
+ # @return [String, nil]
52
+ def self.method_with_block_name(block_ast)
53
+ return nil unless a_block?(block_ast)
54
+
55
+ method_call = %i[CALL FCALL].include?(block_ast.children[0].type)
56
+ return nil unless method_call
57
+
58
+ block_ast.children[0].children.select { |child| child.is_a?(Symbol) }.first&.to_s
59
+ end
60
+
61
+ # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
62
+ # @return [RubyVM::AbstractSyntaxTree::Node]
63
+ def self.context_description_node(block_ast)
64
+ return nil unless a_context_block?(block_ast)
65
+
66
+ case block_ast.children[0].type
67
+ when :CALL # RSpec.describe "something" do end
68
+ block_ast.children[0].children[2].children[0]
69
+ when :FCALL # describe "something" do end
70
+ block_ast.children[0].children[1].children[0]
71
+ end
72
+ end
73
+
74
+ # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
75
+ # @return [String]
76
+ def self.let_method_name(block_ast)
77
+ block_ast.children[0].children[1]&.children&.[](0)&.children&.[](0)&.to_s
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Rspec
5
+ class SpecWalker
6
+ class RspecContextNamespace
7
+ class << self
8
+ # @param block_ast [RubyVM::AbstractSyntaxTree::Node]
9
+ # @return [String, nil]
10
+ def from_block_ast(block_ast)
11
+ return unless block_ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
12
+
13
+ ast = NodeTypes.context_description_node(block_ast)
14
+ if ast.type == :STR
15
+ string_to_const_name(ast)
16
+ elsif NodeTypes.a_constant?(ast)
17
+ FullConstantName.from_ast(ast).gsub('::', '')
18
+ else
19
+ Solargraph.logger.warn "[RSpec] Unexpected AST type #{ast.type}"
20
+ nil
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ # @see https://github.com/rspec/rspec-core/blob/1eeadce5aa7137ead054783c31ff35cbfe9d07cc/lib/rspec/core/example_group.rb#L862
27
+ # @param ast [Parser::AST::Node]
28
+ # @return [String]
29
+ def string_to_const_name(string_ast)
30
+ return unless string_ast.type == :STR
31
+
32
+ name = string_ast.children[0]
33
+ return 'Anonymous'.dup if name.empty?
34
+
35
+ # Convert to CamelCase.
36
+ name = +" #{name}"
37
+ name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
38
+ match = ::Regexp.last_match[1]
39
+ match.upcase!
40
+ match
41
+ end
42
+
43
+ name.lstrip! # Remove leading whitespace
44
+ name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
45
+
46
+ # Ruby requires first const letter to be A-Z. Use `Nested`
47
+ # as necessary to enforce that.
48
+ name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
49
+
50
+ name
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'walker'
4
+ require_relative 'spec_walker/node_types'
5
+ require_relative 'spec_walker/full_constant_name'
6
+ require_relative 'spec_walker/rspec_context_namespace'
7
+ require_relative 'spec_walker/fake_let_method'
4
8
 
5
9
  module Solargraph
6
10
  module Rspec
@@ -10,7 +14,7 @@ module Solargraph
10
14
  def initialize(source_map:, config:)
11
15
  @source_map = source_map
12
16
  @config = config
13
- @walker = Rspec::Walker.from_source(source_map.source)
17
+ @walker = Rspec::Walker.new(source_map.source.node)
14
18
  @handlers = {
15
19
  on_described_class: [],
16
20
  on_let_method: [],
@@ -30,24 +34,34 @@ module Solargraph
30
34
  attr_reader :config
31
35
 
32
36
  # @param block [Proc]
37
+ # @yieldparam class_name [String]
38
+ # @yieldparam location_range [Solargraph::Range]
33
39
  # @return [void]
34
40
  def on_described_class(&block)
35
41
  @handlers[:on_described_class] << block
36
42
  end
37
43
 
38
44
  # @param block [Proc]
45
+ # @yieldparam method_name [String]
46
+ # @yieldparam location_range [Solargraph::Range]
47
+ # @yieldparam fake_method_ast [RubyVM::AbstractSyntaxTree::Node]
39
48
  # @return [void]
40
49
  def on_let_method(&block)
41
50
  @handlers[:on_let_method] << block
42
51
  end
43
52
 
44
53
  # @param block [Proc]
54
+ # @yieldparam method_name [String]
55
+ # @yieldparam location_range [Solargraph::Range]
56
+ # @yieldparam fake_method_ast [RubyVM::AbstractSyntaxTree::Node]
45
57
  # @return [void]
46
58
  def on_subject(&block)
47
59
  @handlers[:on_subject] << block
48
60
  end
49
61
 
50
62
  # @param block [Proc]
63
+ # @yieldparam namespace_name [String]
64
+ # @yieldparam location_range [Solargraph::Range]
51
65
  # @return [void]
52
66
  def on_each_context_block(&block)
53
67
  @handlers[:on_each_context_block] << block
@@ -55,18 +69,21 @@ module Solargraph
55
69
 
56
70
  #
57
71
  # @param block [Proc]
72
+ # @yieldparam location_range [Solargraph::Range]
58
73
  # @return [void]
59
74
  def on_example_block(&block)
60
75
  @handlers[:on_example_block] << block
61
76
  end
62
77
 
63
78
  # @param block [Proc]
79
+ # @yieldparam location_range [Solargraph::Range]
64
80
  # @return [void]
65
81
  def on_hook_block(&block)
66
82
  @handlers[:on_hook_block] << block
67
83
  end
68
84
 
69
85
  # @param block [Proc]
86
+ # @yieldparam location_range [Solargraph::Range]
70
87
  # @return [void]
71
88
  def on_blocks_in_examples(&block)
72
89
  @handlers[:on_blocks_in_examples] << block
@@ -80,77 +97,72 @@ module Solargraph
80
97
 
81
98
  # @return [void]
82
99
  def walk!
83
- each_context_block(@walker.ast, Rspec::ROOT_NAMESPACE) do |namespace_name, ast|
100
+ each_context_block(@walker.ast, Rspec::ROOT_NAMESPACE) do |namespace_name, block_ast|
101
+ desc_node = NodeTypes.context_description_node(block_ast)
102
+
84
103
  @handlers[:on_each_context_block].each do |handler|
85
- handler.call(namespace_name, ast)
104
+ handler.call(namespace_name, PinFactory.build_location_range(block_ast))
86
105
  end
87
- end
88
106
 
89
- rspec_const = ::Parser::AST::Node.new(:const, [nil, :RSpec])
90
- walker.on :send, [rspec_const, :describe, :any] do |ast|
91
- @handlers[:on_described_class].each do |handler|
92
- class_ast = ast.children[2]
93
- next unless class_ast
94
-
95
- class_name = full_constant_name(class_ast)
96
- handler.call(class_ast, class_name)
107
+ if NodeTypes.a_constant?(desc_node) # rubocop:disable Style/Next
108
+ @handlers[:on_described_class].each do |handler|
109
+ class_name_ast = NodeTypes.context_description_node(block_ast)
110
+ class_name = FullConstantName.from_ast(class_name_ast)
111
+ handler.call(class_name, PinFactory.build_location_range(class_name_ast))
112
+ end
97
113
  end
98
114
  end
99
115
 
100
- config.let_methods.each do |let_method|
101
- walker.on :send, [nil, let_method] do |ast|
102
- @handlers[:on_let_method].each do |handler|
103
- handler.call(ast)
104
- end
116
+ walker.on :ITER do |block_ast|
117
+ next unless NodeTypes.a_let_block?(block_ast, config)
118
+
119
+ method_name = NodeTypes.let_method_name(block_ast)
120
+ next unless method_name
121
+
122
+ fake_method_ast = FakeLetMethod.transform_block(block_ast, @source_map.source.code)
123
+
124
+ @handlers[:on_let_method].each do |handler|
125
+ handler.call(method_name, PinFactory.build_location_range(block_ast.children[0]), fake_method_ast)
105
126
  end
106
127
  end
107
128
 
108
- walker.on :block do |block_ast|
109
- next if block_ast.children.first.type != :send
129
+ walker.on :ITER do |block_ast|
130
+ next unless NodeTypes.a_subject_block?(block_ast)
110
131
 
111
- method_ast = block_ast.children.first
112
- method_name = method_ast.children[1]
113
- next unless Rspec::SUBJECT_METHODS.include?(method_name.to_s)
132
+ method_name = NodeTypes.let_method_name(block_ast)
133
+ fake_method_ast = FakeLetMethod.transform_block(block_ast, @source_map.source.code, method_name || 'subject')
114
134
 
115
135
  @handlers[:on_subject].each do |handler|
116
- handler.call(method_ast)
136
+ handler.call(method_name, PinFactory.build_location_range(block_ast.children[0]), fake_method_ast)
117
137
  end
118
138
  end
119
139
 
120
- walker.on :block do |block_ast|
121
- next if block_ast.children.first.type != :send
122
-
123
- method_ast = block_ast.children.first
124
- method_name = method_ast.children[1]
125
- next unless Rspec::EXAMPLE_METHODS.include?(method_name.to_s)
140
+ walker.on :ITER do |block_ast|
141
+ next unless NodeTypes.a_example_block?(block_ast)
126
142
 
127
143
  @handlers[:on_example_block].each do |handler|
128
- handler.call(block_ast)
144
+ handler.call(PinFactory.build_location_range(block_ast))
129
145
  end
130
146
 
131
- # @param blocks_in_examples [Parser::AST::Node]
132
- each_block(block_ast.children[2]) do |blocks_in_examples|
147
+ # @param blocks_in_examples [RubyVM::AbstractSyntaxTree::Node]
148
+ each_block(block_ast.children[1]) do |blocks_in_examples|
133
149
  @handlers[:on_blocks_in_examples].each do |handler|
134
- handler.call(blocks_in_examples)
150
+ handler.call(PinFactory.build_location_range(blocks_in_examples))
135
151
  end
136
152
  end
137
153
  end
138
154
 
139
- walker.on :block do |block_ast|
140
- next if block_ast.children.first.type != :send
141
-
142
- method_ast = block_ast.children.first
143
- method_name = method_ast.children[1]
144
- next unless Rspec::HOOK_METHODS.include?(method_name.to_s)
155
+ walker.on :ITER do |block_ast|
156
+ next unless NodeTypes.a_hook_block?(block_ast)
145
157
 
146
158
  @handlers[:on_hook_block].each do |handler|
147
- handler.call(block_ast)
159
+ handler.call(PinFactory.build_location_range(block_ast))
148
160
  end
149
161
 
150
- # @param blocks_in_examples [Parser::AST::Node]
151
- each_block(block_ast.children[2]) do |blocks_in_examples|
162
+ # @param blocks_in_examples [RubyVM::AbstractSyntaxTree::Node]
163
+ each_block(block_ast.children[1]) do |blocks_in_examples|
152
164
  @handlers[:on_blocks_in_examples].each do |handler|
153
- handler.call(blocks_in_examples)
165
+ handler.call(PinFactory.build_location_range(blocks_in_examples))
154
166
  end
155
167
  end
156
168
  end
@@ -165,9 +177,9 @@ module Solargraph
165
177
  # @param ast [Parser::AST::Node]
166
178
  # @param parent_result [Object]
167
179
  def each_block(ast, parent_result = nil, &block)
168
- return unless ast.is_a?(::Parser::AST::Node)
180
+ return unless ast.is_a?(RubyVM::AbstractSyntaxTree::Node)
169
181
 
170
- is_a_block = ast.type == :block && ast.children[0].type == :send
182
+ is_a_block = NodeTypes.a_block?(ast)
171
183
 
172
184
  if is_a_block
173
185
  result = block&.call(ast, parent_result)
@@ -182,72 +194,22 @@ module Solargraph
182
194
  # @yield [String, Parser::AST::Node]
183
195
  def each_context_block(ast, root_namespace = Rspec::ROOT_NAMESPACE, &block)
184
196
  each_block(ast, root_namespace) do |block_ast, parent_namespace|
185
- is_a_context = %i[describe context].include?(block_ast.children[0].children[1])
197
+ is_a_context = NodeTypes.a_context_block?(block_ast)
186
198
 
187
199
  next unless is_a_context
188
200
 
189
- description_node = block_ast.children[0].children[2]
190
- block_name = rspec_describe_class_name(description_node)
201
+ block_name = RspecContextNamespace.from_block_ast(block_ast)
191
202
  next unless block_name
192
203
 
204
+ # @HACK: When we describe `SomeClass` without a namespace, Solargraph confuses described_class with the
205
+ # `RSpec::ExampleGroups::SomeClass` constant. To avoid this, we append the root namespace with "Test"
206
+ block_name = "Test#{block_name}" if parent_namespace == Rspec::ROOT_NAMESPACE
207
+
193
208
  parent_namespace = namespace_name = "#{parent_namespace}::#{block_name}"
194
209
  block&.call(namespace_name, block_ast)
195
210
  next parent_namespace
196
211
  end
197
212
  end
198
-
199
- # @param ast [Parser::AST::Node]
200
- # @return [String, nil]
201
- def rspec_describe_class_name(ast)
202
- if ast.type == :str
203
- string_to_const_name(ast)
204
- elsif ast.type == :const
205
- full_constant_name(ast).gsub('::', '')
206
- else
207
- Solargraph.logger.warn "[RSpec] Unexpected AST type #{ast.type}"
208
- nil
209
- end
210
- end
211
-
212
- # @param ast [Parser::AST::Node]
213
- # @return [String]
214
- def full_constant_name(ast)
215
- raise 'Node is not a constant' unless ast.type == :const
216
-
217
- name = ast.children[1].to_s
218
- if ast.children[0].nil?
219
- name
220
- else
221
- "#{full_constant_name(ast.children[0])}::#{name}"
222
- end
223
- end
224
-
225
- # @see https://github.com/rspec/rspec-core/blob/1eeadce5aa7137ead054783c31ff35cbfe9d07cc/lib/rspec/core/example_group.rb#L862
226
- # @param ast [Parser::AST::Node]
227
- # @return [String]
228
- def string_to_const_name(string_ast)
229
- return unless string_ast.type == :str
230
-
231
- name = string_ast.children[0]
232
- return 'Anonymous'.dup if name.empty?
233
-
234
- # Convert to CamelCase.
235
- name = +" #{name}"
236
- name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do
237
- match = ::Regexp.last_match[1]
238
- match.upcase!
239
- match
240
- end
241
-
242
- name.lstrip! # Remove leading whitespace
243
- name.gsub!(/\W/, '') # JRuby, RBX and others don't like non-ascii in const names
244
-
245
- # Ruby requires first const letter to be A-Z. Use `Nested`
246
- # as necessary to enforce that.
247
- name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1')
248
-
249
- name
250
- end
251
213
  end
252
214
  end
253
215
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Solargraph
4
4
  module Rspec
5
- VERSION = '0.1.1'
5
+ VERSION = '0.2.1'
6
6
  end
7
7
  end