solargraph 0.60.4 → 0.62.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.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +24 -2
  3. data/.github/workflows/plugins.yml +19 -1
  4. data/.github/workflows/rspec.yml +17 -2
  5. data/.github/workflows/typecheck.yml +6 -0
  6. data/CHANGELOG.md +54 -0
  7. data/Gemfile +0 -3
  8. data/lib/solargraph/api_map/index.rb +28 -4
  9. data/lib/solargraph/api_map/store.rb +19 -0
  10. data/lib/solargraph/api_map.rb +91 -80
  11. data/lib/solargraph/cache_dir.rb +58 -0
  12. data/lib/solargraph/collection/base.rb +50 -0
  13. data/lib/solargraph/collection/core.rb +17 -0
  14. data/lib/solargraph/collection/gem.rb +59 -0
  15. data/lib/solargraph/collection/stdlib.rb +25 -0
  16. data/lib/solargraph/collection.rb +30 -0
  17. data/lib/solargraph/complex_type/unique_type.rb +13 -0
  18. data/lib/solargraph/complex_type.rb +23 -3
  19. data/lib/solargraph/convention.rb +5 -7
  20. data/lib/solargraph/diagnostics/require_not_found.rb +0 -4
  21. data/lib/solargraph/diagnostics/type_check.rb +0 -1
  22. data/lib/solargraph/external.rb +187 -0
  23. data/lib/solargraph/language_server/host/diagnoser.rb +1 -1
  24. data/lib/solargraph/language_server/host/dispatch.rb +4 -2
  25. data/lib/solargraph/language_server/host.rb +10 -2
  26. data/lib/solargraph/language_server/message/completion_item/resolve.rb +10 -3
  27. data/lib/solargraph/language_server/message/text_document/definition.rb +5 -2
  28. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -2
  29. data/lib/solargraph/library.rb +26 -53
  30. data/lib/solargraph/metagem.rb +80 -0
  31. data/lib/solargraph/parser/node_processor/base.rb +6 -0
  32. data/lib/solargraph/parser/parser_gem/node_chainer.rb +22 -3
  33. data/lib/solargraph/parser/parser_gem/node_methods.rb +29 -6
  34. data/lib/solargraph/parser/parser_gem/node_processors/class_definition/class_assignment_node.rb +98 -0
  35. data/lib/solargraph/parser/parser_gem/node_processors/class_definition.rb +54 -0
  36. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +1 -0
  37. data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_assignment_node.rb +65 -0
  38. data/lib/solargraph/parser/parser_gem/node_processors/data_definition/data_definition_node.rb +97 -0
  39. data/lib/solargraph/parser/parser_gem/node_processors/data_definition.rb +110 -0
  40. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +1 -0
  41. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +2 -0
  42. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +1 -0
  43. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +185 -0
  44. data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_assignment_node.rb +66 -0
  45. data/lib/solargraph/parser/parser_gem/node_processors/struct_definition/struct_definition_node.rb +108 -0
  46. data/lib/solargraph/parser/parser_gem/node_processors/struct_definition.rb +170 -0
  47. data/lib/solargraph/parser/parser_gem/node_processors.rb +33 -29
  48. data/lib/solargraph/pin/base.rb +25 -1
  49. data/lib/solargraph/pin/base_variable.rb +31 -1
  50. data/lib/solargraph/pin/callable.rb +11 -2
  51. data/lib/solargraph/pin/closure.rb +17 -0
  52. data/lib/solargraph/pin/delegated_method.rb +5 -0
  53. data/lib/solargraph/pin/duck_method.rb +36 -16
  54. data/lib/solargraph/pin/factory_parameter.rb +84 -0
  55. data/lib/solargraph/pin/instance_variable.rb +45 -0
  56. data/lib/solargraph/pin/method.rb +10 -0
  57. data/lib/solargraph/pin/method_alias.rb +4 -0
  58. data/lib/solargraph/pin/parameter.rb +1 -1
  59. data/lib/solargraph/pin/reference/included_mixin.rb +22 -0
  60. data/lib/solargraph/pin/reference.rb +1 -0
  61. data/lib/solargraph/pin.rb +1 -0
  62. data/lib/solargraph/plugin/active_support.rb +100 -0
  63. data/lib/solargraph/plugin/addon.rb +16 -0
  64. data/lib/solargraph/plugin/base.rb +25 -0
  65. data/lib/solargraph/plugin/panel.rb +37 -0
  66. data/lib/solargraph/plugin.rb +26 -0
  67. data/lib/solargraph/rbs_collection.rb +119 -0
  68. data/lib/solargraph/rbs_map/base.rb +31 -0
  69. data/lib/solargraph/rbs_map/conversions.rb +109 -48
  70. data/lib/solargraph/rbs_map/{core_map.rb → core.rb} +18 -39
  71. data/lib/solargraph/rbs_map/core_fills.rb +1 -1
  72. data/lib/solargraph/rbs_map/gem.rb +16 -0
  73. data/lib/solargraph/rbs_map/helpers.rb +61 -0
  74. data/lib/solargraph/rbs_map/path.rb +19 -0
  75. data/lib/solargraph/rbs_map/stdlib.rb +24 -0
  76. data/lib/solargraph/rbs_map.rb +7 -198
  77. data/lib/solargraph/rbs_translator.rb +105 -70
  78. data/lib/solargraph/repo.rb +177 -0
  79. data/lib/solargraph/shell.rb +49 -99
  80. data/lib/solargraph/source/chain/call.rb +58 -14
  81. data/lib/solargraph/source/chain/literal.rb +17 -17
  82. data/lib/solargraph/source/chain/parameter.rb +79 -0
  83. data/lib/solargraph/source/chain.rb +8 -0
  84. data/lib/solargraph/source/node_map.rb +27 -0
  85. data/lib/solargraph/source/source_chainer.rb +40 -44
  86. data/lib/solargraph/source.rb +17 -0
  87. data/lib/solargraph/source_map/call.rb +18 -0
  88. data/lib/solargraph/source_map/clip.rb +5 -3
  89. data/lib/solargraph/source_map/mapper.rb +6 -2
  90. data/lib/solargraph/source_map.rb +28 -9
  91. data/lib/solargraph/type_checker/rules.rb +3 -2
  92. data/lib/solargraph/type_checker.rb +4 -1
  93. data/lib/solargraph/version.rb +1 -1
  94. data/lib/solargraph/workspace.rb +0 -1
  95. data/lib/solargraph/yard_map/cache.rb +1 -1
  96. data/lib/solargraph/yard_map/directives/attribute_directive.rb +1 -1
  97. data/lib/solargraph/yard_map/directives/domain_directive.rb +1 -1
  98. data/lib/solargraph/yard_map/directives/method_directive.rb +1 -1
  99. data/lib/solargraph/yard_map/directives/override_directive.rb +1 -1
  100. data/lib/solargraph/yard_map/directives/parse_directive.rb +1 -1
  101. data/lib/solargraph/yard_map/directives/scope_directive.rb +178 -0
  102. data/lib/solargraph/yard_map/directives/visibility_directive.rb +1 -1
  103. data/lib/solargraph/yard_map/directives.rb +5 -2
  104. data/lib/solargraph/yard_map/helpers.rb +1 -1
  105. data/lib/solargraph/yard_map/macro.rb +85 -6
  106. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -1
  107. data/lib/solargraph/yard_map/mapper/to_method.rb +1 -1
  108. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -1
  109. data/lib/solargraph/yard_map/mapper.rb +2 -2
  110. data/lib/solargraph/yard_map.rb +1 -1
  111. data/lib/solargraph/yardoc.rb +43 -60
  112. data/lib/solargraph.rb +8 -3
  113. data/rbs/fills/boolean/0/boolean.rbs +12 -0
  114. data/solargraph.gemspec +2 -2
  115. metadata +41 -18
  116. data/lib/solargraph/convention/active_support_concern.rb +0 -111
  117. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +0 -61
  118. data/lib/solargraph/convention/data_definition/data_definition_node.rb +0 -93
  119. data/lib/solargraph/convention/data_definition.rb +0 -108
  120. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +0 -62
  121. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +0 -103
  122. data/lib/solargraph/convention/struct_definition.rb +0 -168
  123. data/lib/solargraph/doc_map.rb +0 -439
  124. data/lib/solargraph/gem_pins.rb +0 -87
  125. data/lib/solargraph/pin_cache.rb +0 -248
  126. data/lib/solargraph/rbs_map/stdlib_map.rb +0 -71
  127. data/lib/solargraph/workspace/gemspecs.rb +0 -373
@@ -12,7 +12,7 @@ module Solargraph
12
12
 
13
13
  private_class_method :new
14
14
 
15
- DIRECTIVE_REGEXP = /(@!method|@!attribute|@!visibility|@!domain|@!macro|@!parse|@!override)/
15
+ DIRECTIVE_REGEXP = /(@!method|@method|@!attribute|@!visibility|@!scope|@!domain|@!macro|@!parse|@!override)/
16
16
 
17
17
  # Generate the data.
18
18
  #
@@ -104,9 +104,13 @@ module Solargraph
104
104
  directive_processor = YardMap::Directives.for(directive)
105
105
  return unless directive_processor
106
106
 
107
- @pins += directive_processor.process_directive(
107
+ new_pins = directive_processor.process_directive(
108
108
  @source, @pins, source_position, comment_position, directive
109
109
  )
110
+ # A @!scope directive in the indented text of another directive applies
111
+ # only to the object that directive defines.
112
+ YardMap::Directives::ScopeDirective.apply_nested directive, new_pins
113
+ @pins += new_pins
110
114
  end
111
115
 
112
116
  # @param comment [String]
@@ -12,6 +12,7 @@ module Solargraph
12
12
  autoload :Clip, 'solargraph/source_map/clip'
13
13
  autoload :Completion, 'solargraph/source_map/completion'
14
14
  autoload :Data, 'solargraph/source_map/data'
15
+ autoload :Call, 'solargraph/source_map/call'
15
16
 
16
17
  # @return [Source]
17
18
  attr_reader :source
@@ -82,6 +83,16 @@ module Solargraph
82
83
  pins_by_class(Pin::Reference::Require)
83
84
  end
84
85
 
86
+ def calls *method_names
87
+ symbolized = method_names.map(&:to_sym)
88
+ node_map.find_by_type(:send).select { |node| node.children[0].nil? && symbolized.include?(node.children[1].to_sym) }
89
+ .map { |node| Call.new(self, Parser.chain(node), locate_namespace_pin(node.loc.line, node.loc.column)) }
90
+ end
91
+
92
+ def node_map
93
+ @node_map ||= Source::NodeMap.new(source.node)
94
+ end
95
+
85
96
  # @return [Environ]
86
97
  def conventions_environ
87
98
  @conventions_environ ||= Environ.new
@@ -128,6 +139,10 @@ module Solargraph
128
139
  _locate_pin line, character, Pin::Namespace, Pin::Method
129
140
  end
130
141
 
142
+ def locate_namespace_pin line, character
143
+ _locate_pin line, character, Pin::Namespace, Pin::Singleton
144
+ end
145
+
131
146
  # @param line [Integer]
132
147
  # @param character [Integer]
133
148
  # @return [Pin::Closure]
@@ -190,12 +205,14 @@ module Solargraph
190
205
  end
191
206
  end
192
207
 
193
- private
194
-
195
208
  # @return [Array<Pin::Base>]
196
- attr_writer :convention_pins
209
+ def convention_pins
210
+ @convention_pins || []
211
+ end
212
+
213
+ private
197
214
 
198
- # @return [Hash{Class<Pin::Base> => Array<Pin::Base>}]
215
+ # @return [Hash{Class, Pin::Base => Array<Pin::Base>}]
199
216
  def pin_class_hash
200
217
  # @todo Need to support generic resolution in classify and transform_values
201
218
  @pin_class_hash ||= pins.to_set.classify(&:class).transform_values(&:to_a)
@@ -206,9 +223,12 @@ module Solargraph
206
223
  @data ||= Data.new(source)
207
224
  end
208
225
 
226
+ # @param pins [Array<Pin::Base>]
209
227
  # @return [Array<Pin::Base>]
210
- def convention_pins
211
- @convention_pins || []
228
+ def convention_pins= pins
229
+ # unmemoizing the document_symbols in case it was called from any of conventions
230
+ @document_symbols = nil
231
+ @convention_pins = pins
212
232
  end
213
233
 
214
234
  # @generic T
@@ -221,9 +241,8 @@ module Solargraph
221
241
  position = Position.new(line, character)
222
242
  found = nil
223
243
  pins.each do |pin|
224
- # @todo Attribute pins should not be treated like closures, but
225
- # there's probably a better way to handle it
226
- next if pin.is_a?(Pin::Method) && pin.attribute?
244
+ # A bodyless method pin covers its declaring statement's range but isn't a closure.
245
+ next if pin.is_a?(Pin::Method) && !pin.body?
227
246
  found = pin if (klasses.empty? || klasses.any? do |kls|
228
247
  pin.is_a?(kls)
229
248
  # @sg-ignore Need to add nil check here
@@ -75,11 +75,11 @@ module Solargraph
75
75
  # @todo 22: Translate to something flow sensitive typing understands
76
76
  # @todo 3: Need a downcast here
77
77
  #
78
- # flow sensitive typing could handle (96):
78
+ # flow sensitive typing could handle (99):
79
79
  #
80
80
  # @todo 36: flow sensitive typing needs to handle attrs
81
81
  # @todo 29: flow sensitive typing should be able to handle redefinition
82
- # @todo 19: flow sensitive typing needs to narrow down type with an if is_a? check
82
+ # @todo 21: flow sensitive typing needs to narrow down type with an if is_a? check
83
83
  # @todo 13: Need to validate config
84
84
  # @todo 8: flow sensitive typing should support .class == .class
85
85
  # @todo 6: need boolish support for ? methods
@@ -113,6 +113,7 @@ module Solargraph
113
113
  # @todo 1: flow sensitive typing needs to create separate ranges for postfix if
114
114
  # @todo 1: flow sensitive typing needs to handle constants
115
115
  # @todo 1: flow sensitive typing needs to eliminate literal from union with return if foo == :bar
116
+ # @todo 1: Array#flatten returns a bare Array
116
117
  def require_all_unique_types_match_expected?
117
118
  report?(:require_all_unique_types_match_expected, :strong)
118
119
  end
@@ -141,6 +141,8 @@ module Solargraph
141
141
  # @return [Array<Problem>]
142
142
  def method_return_type_problems_for pin
143
143
  return [] if pin.is_a?(Pin::MethodAlias)
144
+ # An unresolvable DelegatedMethod's type comes from its @!method directive, not a @return tag.
145
+ return [] if pin.is_a?(Pin::DelegatedMethod) && !pin.resolvable?(api_map)
144
146
  result = []
145
147
  declared = pin.typify(api_map).self_to_type(pin.full_context).qualify(api_map, *pin.gates)
146
148
  if declared.undefined?
@@ -219,7 +221,7 @@ module Solargraph
219
221
  params = param_details_from_stack(sig, stack)
220
222
  if rules.require_type_tags?
221
223
  sig.parameters.each do |par|
222
- break if %i[restarg kwrestarg blockarg].include?(par.decl)
224
+ break if %i[restarg kwrestarg blockarg forward_args].include?(par.decl)
223
225
  unless params[par.name]
224
226
  if pin.attribute?
225
227
  inferred = pin.probe(api_map).self_to_type(pin.full_context)
@@ -388,6 +390,7 @@ module Solargraph
388
390
  return [] unless last_base_link.is_a?(Solargraph::Source::Chain::Call)
389
391
 
390
392
  arguments = last_base_link.arguments
393
+ return [] if any_forwarded_args?(arguments.map(&:node))
391
394
 
392
395
  pins = base.define(api_map, closure_pin, locals)
393
396
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.60.4')
4
+ VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.62.0')
5
5
  end
@@ -10,7 +10,6 @@ module Solargraph
10
10
  #
11
11
  class Workspace
12
12
  autoload :Config, 'solargraph/workspace/config'
13
- autoload :Gemspecs, 'solargraph/workspace/gemspecs'
14
13
  autoload :RequirePaths, 'solargraph/workspace/require_paths'
15
14
 
16
15
  # @return [String]
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  class Cache
6
6
  def initialize
7
7
  # @type [Hash{String => Array<Solargraph::Pin::Base>}]
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Directives
6
6
  module AttributeDirective
7
7
  module_function
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Directives
6
6
  module DomainDirective
7
7
  module_function
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Directives
6
6
  module MethodDirective
7
7
  module_function
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Directives
6
6
  module OverrideDirective
7
7
  module_function
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Directives
6
6
  module ParseDirective
7
7
  module_function
@@ -0,0 +1,178 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module YardMap
5
+ module Directives
6
+ # Implements YARD's @!scope directive.
7
+ #
8
+ # As described in YARD::Tags::ScopeDirective, a @!scope directive that
9
+ # appears in a docstring attached to a method definition applies only to
10
+ # that method. Anywhere else it applies to all of the methods that follow
11
+ # it within the namespace it was defined in.
12
+ #
13
+ # class Example
14
+ # # @!scope class
15
+ # def foo; end # => Example.foo
16
+ #
17
+ # def bar; end # => Example#bar
18
+ # end
19
+ #
20
+ # class Other
21
+ # # @!scope class
22
+ #
23
+ # def foo; end # => Other.foo
24
+ #
25
+ # def bar; end # => Other.bar
26
+ # end
27
+ #
28
+ module ScopeDirective
29
+ module_function
30
+
31
+ VALID_SCOPES = %i[class instance].freeze
32
+
33
+ # Directives that define an object whose scope is inherited from a
34
+ # @!scope directive. @!parse is not one of them because the code it
35
+ # contains already declares the scope of everything it defines.
36
+ SCOPED_DIRECTIVES = %w[method attribute].freeze
37
+
38
+ # @param source [Solargraph::Source]
39
+ # @param pins [Array<Solargraph::Pin::Base>]
40
+ # @param source_position [Position]
41
+ # @param comment_position [Position]
42
+ # @param directive [YARD::Tags::Directive]
43
+ # @return [Array<Solargraph::Pin::Base>]
44
+ def process_directive source, pins, source_position, comment_position, directive
45
+ kind = parse_scope directive
46
+ return [] if kind.nil?
47
+
48
+ attached = attached_method pins, source_position
49
+ if attached && no_empty_lines?(source.code, comment_position.line, source_position.line)
50
+ attached.scope = kind
51
+ else
52
+ apply_to_namespace pins, kind, source_position
53
+ end
54
+
55
+ []
56
+ end
57
+
58
+ # @param directive [YARD::Tags::Directive]
59
+ # @return [::Symbol, nil] :class, :instance, or nil if the directive is malformed
60
+ def parse_scope directive
61
+ kind = directive.tag.text.to_s.strip.downcase.to_sym
62
+ VALID_SCOPES.include?(kind) ? kind : nil
63
+ end
64
+
65
+ # True if the scope of the object defined by this directive can be set
66
+ # by a @!scope directive.
67
+ #
68
+ # @param directive [YARD::Tags::Directive]
69
+ # @return [Boolean]
70
+ def scopes_directive? directive
71
+ SCOPED_DIRECTIVES.include? directive.tag.tag_name
72
+ end
73
+
74
+ # The scope set by a @!scope directive that appears in the indented
75
+ # text of another directive:
76
+ #
77
+ # @!method foo
78
+ # @!scope class
79
+ #
80
+ # YARD parses the indented text of a directive with a state of its own,
81
+ # so a directive nested this way applies only to the object the
82
+ # enclosing directive defines. It never affects the objects that
83
+ # follow.
84
+ #
85
+ # @param directive [YARD::Tags::Directive]
86
+ # @return [::Symbol, nil] :class, :instance, or nil if there is none
87
+ def nested_scope directive
88
+ text = directive.tag.text.to_s
89
+ return nil unless text.include? '@!scope'
90
+
91
+ kind = nil
92
+ Solargraph::Source.parse_docstring(text).directives.each do |nested|
93
+ next unless nested.tag.tag_name == 'scope'
94
+
95
+ # A later directive overrides an earlier one, but an invalid value
96
+ # leaves the current one in place, as it does in YARD.
97
+ kind = parse_scope(nested) || kind
98
+ end
99
+ kind
100
+ end
101
+
102
+ # Set the scope of pins generated by a directive.
103
+ #
104
+ # @param pins [Array<Solargraph::Pin::Base>]
105
+ # @param kind [::Symbol, nil] :class or :instance; nothing happens if nil
106
+ # @return [void]
107
+ def apply_scope pins, kind
108
+ return if kind.nil?
109
+
110
+ # Only methods have a scope. Applying one to any other kind of pin,
111
+ # such as a namespace generated by @!parse, would corrupt its path.
112
+ pins.each { |pin| pin.scope = kind if pin.is_a?(Pin::Method) }
113
+ end
114
+
115
+ # Apply a @!scope directive from the indented text of another directive
116
+ # to the pins that directive generated.
117
+ #
118
+ # @param directive [YARD::Tags::Directive]
119
+ # @param pins [Array<Solargraph::Pin::Base>] the pins generated by the directive
120
+ # @return [void]
121
+ def apply_nested directive, pins
122
+ return unless scopes_directive? directive
123
+
124
+ apply_scope pins, nested_scope(directive)
125
+ end
126
+
127
+ # Applies the scope to each method that follows the directive within
128
+ # the namespace the directive appears in. Methods in nested namespaces
129
+ # are excluded, since the directive only affects its own namespace.
130
+ #
131
+ # @param pins [Array<Solargraph::Pin::Base>]
132
+ # @param kind [::Symbol] :class or :instance
133
+ # @param source_position [Position]
134
+ # @return [void]
135
+ def apply_to_namespace pins, kind, source_position
136
+ namespace = namespace_at pins, source_position
137
+ return if namespace.nil?
138
+
139
+ pins.each do |pin|
140
+ next unless pin.is_a?(Pin::Method)
141
+ next unless pin.namespace == namespace.path
142
+ # @sg-ignore flow sensitive typing should narrow location after the nil check
143
+ next if pin.location.nil? || pin.location.range.start.line < source_position.line
144
+
145
+ pin.scope = kind
146
+ end
147
+ end
148
+
149
+ # The method defined on the line the comment is attached to, if any.
150
+ #
151
+ # @param pins [Array<Solargraph::Pin::Base>]
152
+ # @param source_position [Position]
153
+ # @return [Pin::Method, nil]
154
+ def attached_method pins, source_position
155
+ pins.select { |pin| pin.is_a?(Pin::Method) && pin.location&.range&.contain?(source_position) }.last
156
+ end
157
+
158
+ # @param pins [Array<Solargraph::Pin::Base>]
159
+ # @param position [Position]
160
+ # @return [Pin::Namespace, nil]
161
+ def namespace_at pins, position
162
+ pins.select { |pin| pin.is_a?(Pin::Namespace) && pin.location&.range&.contain?(position) }.last
163
+ end
164
+
165
+ # @param code [String]
166
+ # @param line1 [Integer]
167
+ # @param line2 [Integer]
168
+ # @return [Boolean]
169
+ def no_empty_lines? code, line1, line2
170
+ lines = code.lines[line1..line2]
171
+ return false if lines.nil?
172
+
173
+ lines.none? { |line| line.strip.empty? }
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Directives
6
6
  module VisibilityDirective
7
7
  module_function
@@ -1,17 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Directives
6
6
  autoload :AttributeDirective, 'solargraph/yard_map/directives/attribute_directive'
7
7
  autoload :MethodDirective, 'solargraph/yard_map/directives/method_directive'
8
8
  autoload :DomainDirective, 'solargraph/yard_map/directives/domain_directive'
9
9
  autoload :OverrideDirective, 'solargraph/yard_map/directives/override_directive'
10
10
  autoload :ParseDirective, 'solargraph/yard_map/directives/parse_directive'
11
+ autoload :ScopeDirective, 'solargraph/yard_map/directives/scope_directive'
11
12
  autoload :VisibilityDirective, 'solargraph/yard_map/directives/visibility_directive'
12
13
 
13
14
  # @param directive [YARD::Tags::Directive]
14
- # @return [Class<AttributeDirective>, Class<MethodDirective>, Class<DomainDirective>, Class<OverrideDirective>, Class<ParseDirective>, Class<VisibilityDirective>, nil]
15
+ # @return [Class<AttributeDirective>, Class<MethodDirective>, Class<DomainDirective>, Class<OverrideDirective>, Class<ParseDirective>, Class<ScopeDirective>, Class<VisibilityDirective>, nil]
15
16
  def self.for directive
16
17
  case directive.tag.tag_name
17
18
  when 'attribute'
@@ -24,6 +25,8 @@ module Solargraph
24
25
  OverrideDirective
25
26
  when 'parse'
26
27
  ParseDirective
28
+ when 'scope'
29
+ ScopeDirective
27
30
  when 'visibility'
28
31
  VisibilityDirective
29
32
  else # rubocop:disable Style/EmptyElse
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  module Helpers
6
6
  module_function
7
7
 
@@ -1,9 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  class Macro
6
- PROCESSABLE_DIRECTIVES = %w[method attribute parse].freeze
6
+ PROCESSABLE_DIRECTIVES = %w[method attribute parse scope].freeze
7
+
8
+ # Directives whose text is a value rather than a definition. The comments
9
+ # attached to the macro's call site belong to the objects the macro
10
+ # generates, but adding them to one of these directives would invalidate
11
+ # its content.
12
+ VALUE_DIRECTIVES = %w[parse scope].freeze
13
+
14
+ # Directives that define an object capable of owning @overload tags.
15
+ OVERLOAD_OWNERS = %w[method attribute parse].freeze
16
+
17
+ # The directive used to generate a pin for the @overload tags a macro
18
+ # declares without an owning @!method directive. Its arguments are
19
+ # represented by (...) so that any of the overloads remains valid.
20
+ OVERLOAD_METHOD_DIRECTIVE = '@!method $1(...)'
21
+
22
+ # An @overload tag that starts a line of a macro's text.
23
+ OVERLOAD_TAG_LINE = /^\s*@overload(\s|$)/
7
24
 
8
25
  class << self
9
26
  # @param directive [YARD::Tags::Directive]
@@ -73,13 +90,34 @@ module Solargraph
73
90
  # @return [Array<Pin::Base>]
74
91
  def generate_pins_from chain, pin, source_map
75
92
  call_location = Solargraph::Location.from_node(chain.node)
93
+ return [] unless call_location
94
+
95
+ # A @!scope directive in a macro applies to the objects the macro
96
+ # generates after it, the same way it applies to the objects that
97
+ # follow it in a namespace. Its effect ends with the macro.
98
+ #
99
+ # @type [::Symbol, nil]
100
+ inherited_scope = nil
101
+
76
102
  # @param generated_pins [Array<Pin::Base>]
77
103
  generate_yardoc_from(chain, source_map).reduce([]) do |generated_pins, directive|
104
+ # @sg-ignore flow sensitive typing confuses this block variable with the #directive attr reader
105
+ if directive.tag.tag_name == 'scope'
106
+ inherited_scope = Directives::ScopeDirective.parse_scope(directive) || inherited_scope
107
+ next generated_pins
108
+ end
78
109
  directive_processor = YardMap::Directives.for(directive)
79
- next generated_pins unless directive_processor && call_location
80
- generated_pins + directive_processor.process_directive(
110
+ next generated_pins unless directive_processor
111
+ new_pins = directive_processor.process_directive(
81
112
  source_map.source, source_map.pins, call_location.range.start, call_location.range.start, directive
82
113
  )
114
+ if Directives::ScopeDirective.scopes_directive?(directive)
115
+ # A @!scope directive nested in another directive's text applies
116
+ # only to the object that directive defines.
117
+ scope = Directives::ScopeDirective.nested_scope(directive) || inherited_scope
118
+ Directives::ScopeDirective.apply_scope new_pins, scope
119
+ end
120
+ generated_pins + new_pins
83
121
  end
84
122
  end
85
123
 
@@ -96,18 +134,59 @@ module Solargraph
96
134
  code = source_map.source.code_for(chain.node)
97
135
  expanded_comment = macro_object.expand([name, *values], code)
98
136
  .gsub(/\n(?!@!|\s)/, "\n ")
99
- directives = Solargraph::Source.parse_docstring(expanded_comment).directives.select do |directive|
137
+ parsed_docstring = Solargraph::Source.parse_docstring(expanded_comment)
138
+ directives = parsed_docstring.directives.select do |directive|
100
139
  PROCESSABLE_DIRECTIVES.include?(directive.tag.tag_name)
101
140
  end
141
+ # A macro can document the methods it generates with bare @overload
142
+ # tags instead of wrapping them in a @!method directive. Those overloads
143
+ # have no pin to attach to and would otherwise be discarded, so
144
+ # generate the pin they describe.
145
+ if declares_overloads? && directives.none? { |directive| OVERLOAD_OWNERS.include?(directive.tag.tag_name) }
146
+ directives += generate_overload_directives([name, *values], code)
147
+ end
102
148
  directives.each do |directive|
149
+ next if VALUE_DIRECTIVES.include? directive.tag.tag_name
150
+
103
151
  # @sg-ignore chain.node is assumed to exist
104
152
  comments = source_map.source.comments_for(chain.node)
105
- if comments&.length&.positive? && directive.tag.tag_name != 'parse'
153
+ if comments&.length&.positive?
106
154
  directive.tag.text += "\n#{comments}"
107
155
  end
108
156
  end
109
157
  directives
110
158
  end
159
+
160
+ # True if the macro declares @overload tags. The tags are read from the
161
+ # macro's text and not from its parsed docstring because the indentation
162
+ # a macro's content ends up with can nest the tags inside other objects.
163
+ #
164
+ # @return [Boolean]
165
+ def declares_overloads?
166
+ text.match?(OVERLOAD_TAG_LINE)
167
+ end
168
+
169
+ # Generates the @!method directive for a macro's bare @overload tags. The
170
+ # pin takes the name the macro generates, that is, the pin a
171
+ # @!method $1(...) directive would produce, and keeps the overloads in
172
+ # its comments.
173
+ #
174
+ # The macro's unprocessed text is used here and not its macro data. The
175
+ # data is re-indented for @! directives, which flattens the nesting that
176
+ # keeps a @param attached to the @overload it describes.
177
+ #
178
+ # @param call_params [Array<String>]
179
+ # @param code [String]
180
+ # @return [Array<YARD::Tags::Directive>]
181
+ def generate_overload_directives call_params, code
182
+ body = text.each_line.map { |line| " #{line}" }.join
183
+ expanded = YARD::CodeObjects::MacroObject.expand(
184
+ "#{OVERLOAD_METHOD_DIRECTIVE}\n#{body}", call_params, code
185
+ )
186
+ Solargraph::Source.parse_docstring(expanded).directives.select do |directive|
187
+ directive.tag.tag_name == 'method'
188
+ end
189
+ end
111
190
  end
112
191
  end
113
192
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  class Mapper
6
6
  module ToConstant
7
7
  extend YardMap::Helpers
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  class Mapper
6
6
  module ToMethod
7
7
  extend YardMap::Helpers
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  class Mapper
6
6
  module ToNamespace
7
7
  extend YardMap::Helpers
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- class YardMap
4
+ module YardMap
5
5
  class Mapper
6
6
  autoload :ToMethod, 'solargraph/yard_map/mapper/to_method'
7
7
  autoload :ToNamespace, 'solargraph/yard_map/mapper/to_namespace'
@@ -33,7 +33,7 @@ module Solargraph
33
33
  private
34
34
 
35
35
  def core_store
36
- @core_store ||= ApiMap::Store.new(RbsMap::CoreMap.new.pins)
36
+ @core_store ||= ApiMap::Store.new(RbsMap::Core.new.pins)
37
37
  end
38
38
 
39
39
  # @param code_object [YARD::CodeObjects::Base]
@@ -7,7 +7,7 @@ module Solargraph
7
7
  # The YardMap provides access to YARD documentation for the Ruby core, the
8
8
  # stdlib, and gems.
9
9
  #
10
- class YardMap
10
+ module YardMap
11
11
  class NoYardocError < StandardError; end
12
12
 
13
13
  autoload :Cache, 'solargraph/yard_map/cache'