solargraph-rspec 0.5.4 → 0.6.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.
@@ -2,16 +2,15 @@
2
2
 
3
3
  module Solargraph
4
4
  module Rspec
5
- class TestHelpers
6
- class GemHelpers < Struct.new(:required_gems, :helper_modules, keyword_init: true)
7
- # @!attribute [r] required_gems
8
- # @return [Array<String>]
9
- # @!attribute [r] helper_modules
10
- # @return [Array<String>]
11
- end
5
+ class Gems
6
+ # rubocop:disable YARD/MeaninglessTag
7
+ # @param required_gems [Array<String>]
8
+ # @param helper_modules [Array<String>]
9
+ # rubocop:enable YARD/MeaninglessTag
10
+ GemHelpers = Struct.new(:required_gems, :helper_modules, keyword_init: true)
12
11
 
13
12
  class << self
14
- # @return [String]
13
+ # @return [Array<String>]
15
14
  def gem_names
16
15
  GEM_HELPERS.flat_map(&:required_gems)
17
16
  end
@@ -37,7 +36,7 @@ module Solargraph
37
36
 
38
37
  GEM_HELPERS = [
39
38
  GemHelpers.new(
40
- required_gems: %w[rspec],
39
+ required_gems: %w[rspec rspec-expectations rspec-core],
41
40
  helper_modules: %w[RSpec::Matchers]
42
41
  ),
43
42
  # https://github.com/rspec/rspec-mocks
@@ -48,7 +47,7 @@ module Solargraph
48
47
  # @see https://github.com/rspec/rspec-rails#what-tests-should-i-write
49
48
  # @see https://github.com/rspec/rspec-rails#helpful-rails-matchers
50
49
  GemHelpers.new(
51
- required_gems: %w[rspec-rails actionmailer activesupport activerecord],
50
+ required_gems: %w[rspec-rails actionmailer activesupport actionpack],
52
51
  helper_modules: [
53
52
  'RSpec::Rails::Matchers',
54
53
  'ActionController::TestCase::Behavior',
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Credits: This file was originally copied and adapted from the log_bench gem.
3
+ # Credits: This file was originally copied and adapted from the solargraph-rails gem.
4
4
 
5
5
  module Solargraph
6
6
  module Rspec
@@ -8,11 +8,12 @@ module Solargraph
8
8
  module PinFactory
9
9
  # @param namespace [Solargraph::Pin::Namespace]
10
10
  # @param name [String]
11
- # @param types [Array<String>]
12
- # @param location [Solargraph::Location]
11
+ # @param types [Array<String>, nil]
12
+ # @param [Parser::AST::Node, nil] node
13
+ # @param location [Solargraph::Location, nil]
13
14
  # @param comments [Array<String>]
14
15
  # @param attribute [Boolean]
15
- # @param scope [:instance, :class]
16
+ # @param scope [:instance, :class] # rubocop:disable YARD/TagTypeSyntax
16
17
  # @return [Solargraph::Pin::Method]
17
18
  def self.build_public_method(
18
19
  namespace,
@@ -42,7 +43,7 @@ module Solargraph
42
43
  end
43
44
 
44
45
  # @param namespace [Solargraph::Pin::Namespace]
45
- # @param name [String]
46
+ # @param module_name [String]
46
47
  # @param location [Solargraph::Location]
47
48
  # @return [Solargraph::Pin::Reference::Include]
48
49
  def self.build_module_include(namespace, module_name, location)
@@ -74,10 +75,36 @@ module Solargraph
74
75
  )
75
76
  end
76
77
 
78
+ # Given the following code, Solargraph::Parser.node_range returns the following range for block ast:
79
+ #
80
+ # some_method_with_block do
81
+ # ^ - block start
82
+ # end
83
+ # ^ - block end
84
+ #
85
+ # Instead we want the range to be:
86
+ #
87
+ # some_method_with_block do
88
+ # ^ - block start
89
+ # end
90
+ # ^ - block end
91
+ #
77
92
  # @param ast [::Parser::AST::Node]
78
93
  # @return [Solargraph::Range]
79
94
  def self.build_location_range(ast)
80
- Solargraph::Parser.node_range(ast)
95
+ if ast.type == :block
96
+ method_range = Solargraph::Parser.node_range(ast.children[0])
97
+ full_block_range = Solargraph::Parser.node_range(ast)
98
+
99
+ Solargraph::Range.from_to(
100
+ method_range.ending.line,
101
+ method_range.ending.character,
102
+ full_block_range.ending.line,
103
+ full_block_range.ending.character
104
+ )
105
+ else
106
+ Solargraph::Parser.node_range(ast)
107
+ end
81
108
  end
82
109
 
83
110
  # @param location_range [Solargraph::Range]
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'spec_configuration_walker'
4
+ require_relative 'pin_factory'
5
+
6
+ module Solargraph
7
+ module Rspec
8
+ # Handles RSpec.configure ... config.include parsing
9
+ # Provides pins for modules included globally via RSpec.configure
10
+ class RSpecConfigure
11
+ # @param config [Config]
12
+ # @param root_namespace_pin [Solargraph::Pin::Namespace]
13
+ def initialize(config:, root_namespace_pin:)
14
+ @config = config
15
+ @root_namespace_pin = root_namespace_pin
16
+ @included_modules = nil
17
+ end
18
+
19
+ # @return [Array<Solargraph::Pin::Reference::Include>]
20
+ def pins
21
+ included_modules.map do |mod|
22
+ Solargraph::Pin::Reference::Include.new(
23
+ closure: @root_namespace_pin,
24
+ name: mod.module_name,
25
+ location: Solargraph::Location.new(mod.file, Solargraph::Parser.node_range(mod.node))
26
+ )
27
+ end
28
+ end
29
+
30
+ # @return [Array<String>]
31
+ def extra_requires
32
+ included_modules.map(&:file).uniq + support_files
33
+ end
34
+
35
+ # @return [Array<SpecConfigurationWalker::IncludedModule>]
36
+ def included_modules
37
+ @included_modules ||= parse_included_modules
38
+ end
39
+
40
+ private
41
+
42
+ # @return [Array<String>]
43
+ def support_files
44
+ Dir['spec/support/**/*.rb']
45
+ end
46
+
47
+ # @return [Array<SpecConfigurationWalker::IncludedModule>]
48
+ def parse_included_modules
49
+ modules = []
50
+
51
+ @config.config_helper_files.each do |file_path|
52
+ ast = Solargraph::Parser.parse(File.read(file_path), file_path)
53
+ walker = SpecConfigurationWalker.new(ast, file_path)
54
+ modules += walker.extract_included_modules
55
+ rescue Errno::ENOENT
56
+ # Ignore missing files - they may not exist in all projects
57
+ rescue StandardError => e
58
+ Solargraph.logger.error(
59
+ "[RSpec] [RSpecConfigure] Error reading helper file '#{file_path}': #{e.message}"
60
+ )
61
+ end
62
+
63
+ modules
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'walker'
4
+ require_relative 'spec_walker/full_constant_name'
5
+
6
+ module Solargraph
7
+ module Rspec
8
+ # Walks AST to find RSpec.configure blocks and extract included modules
9
+ class SpecConfigurationWalker
10
+ # @param ast [::Parser::AST::Node]
11
+ # @param filename [String] The file being parsed
12
+ def initialize(ast, filename)
13
+ @ast = ast
14
+ @filename = filename
15
+ @walker = Walker.new(ast)
16
+ @included_modules = []
17
+ end
18
+
19
+ # Represents a module included via config.include in RSpec.configure
20
+ # @param node [::Parser::AST::Node] The AST node for the include call
21
+ # @param file [String] The file where this module is included
22
+ # @param module_name [String] The fully qualified name of the included module
23
+ class IncludedModule < Struct.new(:node, :file, :module_name)
24
+ end
25
+
26
+ # Walk the AST and extract included modules
27
+ # @return [Array<IncludedModule>]
28
+ def extract_included_modules
29
+ @walker.on :block, [:send] do |node|
30
+ send_node = node.children[0]
31
+ send_receiver = send_node.children[0]
32
+
33
+ # Check if this is RSpec.configure
34
+ next unless send_receiver&.type == :const
35
+ next unless send_receiver.children[1] == :RSpec
36
+ next unless send_node.children[1] == :configure
37
+
38
+ # Ensure block has arguments (the config parameter)
39
+ next if node.children[1].children.empty?
40
+
41
+ process_configure_block(node)
42
+ end
43
+
44
+ @walker.walk
45
+ @included_modules
46
+ end
47
+
48
+ private
49
+
50
+ # Process the RSpec.configure block to find config.include calls
51
+ # @param configure_block [::Parser::AST::Node]
52
+ # @return [void]
53
+ def process_configure_block(configure_block)
54
+ # Get the config variable name (e.g., |config| or |c|)
55
+ config_name = configure_block.children[1].children[0].children[0]
56
+
57
+ config_walker = Walker.new(configure_block)
58
+ config_walker.on :send, [:lvar, config_name] do |include_node|
59
+ # Look for config.include calls
60
+ next unless include_node.children[1] == :include
61
+
62
+ mod_node = include_node.children[2]
63
+ next unless mod_node.is_a?(::Parser::AST::Node)
64
+ next unless mod_node.type == :const
65
+
66
+ module_name = SpecWalker::FullConstantName.from_ast(mod_node)
67
+ @included_modules << IncludedModule.new(include_node, @filename, module_name)
68
+ end
69
+
70
+ config_walker.walk
71
+ end
72
+ end
73
+ end
74
+ end
@@ -11,6 +11,7 @@ module Solargraph
11
11
  # Transforms let block to method ast node
12
12
  # @param block_ast [::Parser::AST::Node]
13
13
  # @return [::Parser::AST::Node, nil]
14
+ # @param [String, nil] method_name
14
15
  def transform_block(block_ast, method_name = nil)
15
16
  method_name ||= NodeTypes.let_method_name(block_ast)
16
17
 
@@ -22,11 +23,6 @@ module Solargraph
22
23
  block_ast.children[2]
23
24
  ]
24
25
  )
25
- rescue SyntaxError => e
26
- Solargraph.logger.warn "[RSpec] Failed to build fake let method: #{e.message}, \
27
- \n\nlet_definition_code: \n```\n#{let_definition_code}\n```, \
28
- \n\nmethod_body: \n```\n#{method_body}\n```, \
29
- \nast: #{block_ast.inspect}"
30
26
  end
31
27
  end
32
28
  end
@@ -10,38 +10,40 @@ module Solargraph
10
10
  ast.is_a?(::Parser::AST::Node) && ast.type == :block
11
11
  end
12
12
 
13
- # @param ast [::Parser::AST::Node]
13
+ # @param block_ast [::Parser::AST::Node]
14
14
  # @return [Boolean]
15
15
  def self.a_context_block?(block_ast)
16
16
  Solargraph::Rspec::CONTEXT_METHODS.include?(method_with_block_name(block_ast))
17
17
  end
18
18
 
19
- # @param ast [::Parser::AST::Node]
19
+ # @param block_ast [::Parser::AST::Node]
20
20
  # @return [Boolean]
21
21
  def self.a_subject_block?(block_ast)
22
22
  Solargraph::Rspec::SUBJECT_METHODS.include?(method_with_block_name(block_ast))
23
23
  end
24
24
 
25
- # @param ast [::Parser::AST::Node]
25
+ # @param block_ast [::Parser::AST::Node]
26
26
  # @param config [Config]
27
27
  # @return [Boolean]
28
28
  def self.a_example_block?(block_ast, config)
29
29
  config.example_methods.map(&:to_s).include?(method_with_block_name(block_ast))
30
30
  end
31
31
 
32
- # @param ast [::Parser::AST::Node]
32
+ # @param block_ast [::Parser::AST::Node]
33
33
  # @param config [Config]
34
34
  # @return [Boolean]
35
35
  def self.a_let_block?(block_ast, config)
36
36
  config.let_methods.map(&:to_s).include?(method_with_block_name(block_ast))
37
37
  end
38
38
 
39
- # @param ast [::Parser::AST::Node]
39
+ # @param block_ast [::Parser::AST::Node]
40
40
  # @return [Boolean]
41
41
  def self.a_hook_block?(block_ast)
42
42
  Solargraph::Rspec::HOOK_METHODS.include?(method_with_block_name(block_ast))
43
43
  end
44
44
 
45
+ # @param [::Parser::AST::Node] ast
46
+ # @return [Boolean]
45
47
  def self.a_constant?(ast)
46
48
  ast.type == :const
47
49
  end
@@ -56,7 +58,7 @@ module Solargraph
56
58
  end
57
59
 
58
60
  # @param block_ast [::Parser::AST::Node]
59
- # @return [::Parser::AST::Node]
61
+ # @return [::Parser::AST::Node, nil]
60
62
  def self.context_description_node(block_ast)
61
63
  return nil unless a_context_block?(block_ast)
62
64
 
@@ -64,7 +66,7 @@ module Solargraph
64
66
  end
65
67
 
66
68
  # @param block_ast [::Parser::AST::Node]
67
- # @return [String]
69
+ # @return [String, nil]
68
70
  def self.let_method_name(block_ast)
69
71
  return nil unless a_block?(block_ast)
70
72
 
@@ -26,7 +26,7 @@ module Solargraph
26
26
  private
27
27
 
28
28
  # @see https://github.com/rspec/rspec-core/blob/1eeadce5aa7137ead054783c31ff35cbfe9d07cc/lib/rspec/core/example_group.rb#L862
29
- # @param ast [Parser::AST::Node]
29
+ # @param string_ast [Parser::AST::Node]
30
30
  # @return [String]
31
31
  def string_to_const_name(string_ast)
32
32
  return unless string_ast.type == :str
@@ -188,8 +188,9 @@ module Solargraph
188
188
  end
189
189
 
190
190
  # Find all describe/context blocks in the AST.
191
- # @param ast [Parser::AST::Node]
192
191
  # @yield [String, Parser::AST::Node]
192
+ # @param [Parser::AST::Node] ast
193
+ # @param [String] root_namespace
193
194
  def each_context_block(ast, root_namespace = Rspec::ROOT_NAMESPACE, &block)
194
195
  each_block(ast, root_namespace) do |block_ast, parent_namespace|
195
196
  is_a_context = NodeTypes.a_context_block?(block_ast)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Solargraph
4
4
  module Rspec
5
- VERSION = '0.5.4'
5
+ VERSION = '0.6.0'
6
6
  end
7
7
  end
@@ -54,6 +54,7 @@ module Solargraph
54
54
  end
55
55
 
56
56
  # @param children [Array<::Parser::AST::Node>]
57
+ # @param args [Array]
57
58
  def match_children(children, args = @args)
58
59
  args.each_with_index.all? do |arg, i|
59
60
  if arg == :any
@@ -0,0 +1,20 @@
1
+
2
+ # Download sources
3
+ sources:
4
+ - type: git
5
+ name: ruby/gem_rbs_collection
6
+ remote: https://github.com/ruby/gem_rbs_collection.git
7
+ revision: main
8
+ repo_dir: gems
9
+
10
+ # You can specify local directories as sources also.
11
+ # - type: local
12
+ # path: path/to/your/local/repository
13
+
14
+ # A directory to install the downloaded RBSs
15
+ path: .gem_rbs_collection
16
+
17
+ # gems:
18
+ # # If you want to avoid installing rbs files for gems, you can specify them here.
19
+ # - name: GEM_NAME
20
+ # ignore: true
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.4
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lekë Mula
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 2025-09-06 00:00:00.000000000 Z
11
+ date: 2026-08-12 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: solargraph
@@ -33,6 +34,7 @@ extra_rdoc_files: []
33
34
  files:
34
35
  - ".rspec"
35
36
  - ".rubocop.yml"
37
+ - ".solargraph.yml.example"
36
38
  - Appraisals
37
39
  - CHANGELOG.md
38
40
  - CODE_OF_CONDUCT.md
@@ -54,24 +56,29 @@ files:
54
56
  - lib/solargraph/rspec/correctors/example_and_hook_blocks_binding_corrector.rb
55
57
  - lib/solargraph/rspec/correctors/let_methods_corrector.rb
56
58
  - lib/solargraph/rspec/correctors/subject_method_corrector.rb
59
+ - lib/solargraph/rspec/gems.rb
57
60
  - lib/solargraph/rspec/pin_factory.rb
61
+ - lib/solargraph/rspec/rspec_configure.rb
62
+ - lib/solargraph/rspec/spec_configuration_walker.rb
58
63
  - lib/solargraph/rspec/spec_walker.rb
59
64
  - lib/solargraph/rspec/spec_walker/fake_let_method.rb
60
65
  - lib/solargraph/rspec/spec_walker/full_constant_name.rb
61
66
  - lib/solargraph/rspec/spec_walker/node_types.rb
62
67
  - lib/solargraph/rspec/spec_walker/rspec_context_namespace.rb
63
- - lib/solargraph/rspec/test_helpers.rb
64
68
  - lib/solargraph/rspec/version.rb
65
69
  - lib/solargraph/rspec/walker.rb
66
70
  - lib/solargraph_rspec.rb
71
+ - rbs_collection.yaml
67
72
  - release_gem.sh
68
73
  - sig/solargraph/rspec.rbs
69
74
  - solargraph-rspec.gemspec
75
+ homepage:
70
76
  licenses:
71
77
  - MIT
72
78
  metadata:
73
79
  source_code_uri: https://github.com/lekemula/solargraph-rspec
74
80
  rubygems_mfa_required: 'true'
81
+ post_install_message:
75
82
  rdoc_options: []
76
83
  require_paths:
77
84
  - lib
@@ -86,7 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
93
  - !ruby/object:Gem::Version
87
94
  version: '0'
88
95
  requirements: []
89
- rubygems_version: 3.6.3
96
+ rubygems_version: 3.5.22
97
+ signing_key:
90
98
  specification_version: 4
91
99
  summary: Solargraph plugin supporting RSpec code completion
92
100
  test_files: []