solargraph 0.59.1 → 0.60.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +6 -0
  3. data/.github/workflows/plugins.yml +8 -0
  4. data/.github/workflows/rspec.yml +4 -1
  5. data/.github/workflows/typecheck.yml +2 -0
  6. data/.rubocop.yml +1 -0
  7. data/.rubocop_todo.yml +1 -1
  8. data/CHANGELOG.md +18 -0
  9. data/Gemfile +3 -0
  10. data/lib/solargraph/api_map/index.rb +13 -2
  11. data/lib/solargraph/api_map/store.rb +22 -8
  12. data/lib/solargraph/api_map.rb +38 -8
  13. data/lib/solargraph/complex_type/type_methods.rb +1 -0
  14. data/lib/solargraph/complex_type/unique_type.rb +16 -13
  15. data/lib/solargraph/complex_type.rb +5 -0
  16. data/lib/solargraph/convention/active_support_concern.rb +111 -111
  17. data/lib/solargraph/convention/base.rb +50 -50
  18. data/lib/solargraph/diagnostics.rb +55 -55
  19. data/lib/solargraph/doc_map.rb +1 -0
  20. data/lib/solargraph/environ.rb +52 -52
  21. data/lib/solargraph/gem_pins.rb +0 -11
  22. data/lib/solargraph/language_server/message/extended/environment.rb +25 -25
  23. data/lib/solargraph/language_server/message/initialized.rb +28 -28
  24. data/lib/solargraph/language_server/message/text_document.rb +28 -28
  25. data/lib/solargraph/language_server/progress.rb +143 -143
  26. data/lib/solargraph/language_server/transport/adapter.rb +68 -68
  27. data/lib/solargraph/language_server.rb +20 -20
  28. data/lib/solargraph/parser/parser_gem/node_chainer.rb +1 -0
  29. data/lib/solargraph/parser/parser_gem/node_methods.rb +42 -0
  30. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +24 -24
  31. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +36 -36
  32. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +24 -24
  33. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +24 -24
  34. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +29 -5
  35. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +20 -20
  36. data/lib/solargraph/pin/base.rb +31 -3
  37. data/lib/solargraph/pin/callable.rb +2 -2
  38. data/lib/solargraph/pin/common.rb +12 -0
  39. data/lib/solargraph/pin/method.rb +56 -16
  40. data/lib/solargraph/pin/reference/require.rb +14 -14
  41. data/lib/solargraph/pin/singleton.rb +11 -11
  42. data/lib/solargraph/rbs_map/conversions.rb +103 -145
  43. data/lib/solargraph/rbs_translator.rb +206 -0
  44. data/lib/solargraph/shell.rb +131 -64
  45. data/lib/solargraph/source/chain/array.rb +1 -12
  46. data/lib/solargraph/source/chain/block_symbol.rb +13 -13
  47. data/lib/solargraph/source/chain/block_variable.rb +13 -13
  48. data/lib/solargraph/source/chain/call.rb +8 -76
  49. data/lib/solargraph/source/chain/head.rb +19 -19
  50. data/lib/solargraph/source/chain/literal.rb +18 -14
  51. data/lib/solargraph/source/source_chainer.rb +4 -4
  52. data/lib/solargraph/source_map/mapper.rb +5 -135
  53. data/lib/solargraph/source_map.rb +14 -0
  54. data/lib/solargraph/version.rb +19 -1
  55. data/lib/solargraph/yard_map/cache.rb +25 -25
  56. data/lib/solargraph/yard_map/directives/attribute_directive.rb +65 -0
  57. data/lib/solargraph/yard_map/directives/domain_directive.rb +30 -0
  58. data/lib/solargraph/yard_map/directives/method_directive.rb +51 -0
  59. data/lib/solargraph/yard_map/directives/override_directive.rb +30 -0
  60. data/lib/solargraph/yard_map/directives/parse_directive.rb +53 -0
  61. data/lib/solargraph/yard_map/directives/visibility_directive.rb +70 -0
  62. data/lib/solargraph/yard_map/directives.rb +35 -0
  63. data/lib/solargraph/yard_map/macro.rb +113 -0
  64. data/lib/solargraph/yard_map/mapper/to_constant.rb +28 -28
  65. data/lib/solargraph/yard_map/mapper.rb +19 -1
  66. data/lib/solargraph/yard_map.rb +2 -0
  67. data/lib/solargraph.rb +1 -0
  68. data/solargraph.gemspec +1 -0
  69. metadata +24 -2
  70. data/rbs/fills/tuple/tuple.rbs +0 -177
@@ -61,13 +61,6 @@ module Solargraph
61
61
  @pins ||= []
62
62
  end
63
63
 
64
- # @param position [Solargraph::Position]
65
- # @return [Solargraph::Pin::Closure]
66
- def closure_at position
67
- # @sg-ignore Need to add nil check here
68
- pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location.range.contain?(position) }.last
69
- end
70
-
71
64
  # @param source_position [Position]
72
65
  # @param comment_position [Position]
73
66
  # @param comment [String]
@@ -108,135 +101,12 @@ module Solargraph
108
101
  # @param directive [YARD::Tags::Directive]
109
102
  # @return [void]
110
103
  def process_directive source_position, comment_position, directive
111
- # @sg-ignore Need to add nil check here
112
- docstring = Solargraph::Source.parse_docstring(directive.tag.text).to_docstring
113
- location = Location.new(@filename, Range.new(comment_position, comment_position))
114
- case directive.tag.tag_name
115
- when 'method'
116
- namespace = closure_at(source_position) || @pins.first
117
- # @sg-ignore Need to add nil check here
118
- namespace = closure_at(comment_position) if namespace.location.range.start.line < comment_position.line
119
- begin
120
- src = Solargraph::Source.load_string("def #{directive.tag.name};end", @source.filename)
121
- region = Parser::Region.new(source: src, closure: namespace)
122
- # @type [Array<Pin::Method>]
123
- method_gen_pins = Parser.process_node(src.node, region).first.select { |pin| pin.is_a?(Pin::Method) }
124
- gen_pin = method_gen_pins.last
125
- return if gen_pin.nil?
126
- # Move the location to the end of the line so it gets recognized
127
- # as originating from a comment
128
- shifted = Solargraph::Position.new(comment_position.line,
129
- @code.lines[comment_position.line].to_s.chomp.length)
130
- # @todo: Smelly instance variable access
131
- gen_pin.instance_variable_set(:@comments, docstring.all.to_s)
132
- gen_pin.instance_variable_set(:@location, Solargraph::Location.new(@filename, Range.new(shifted, shifted)))
133
- gen_pin.instance_variable_set(:@explicit, false)
134
- @pins.push gen_pin
135
- rescue Parser::SyntaxError
136
- # @todo Handle error in directive
137
- end
138
- when 'attribute'
139
- return if directive.tag.name.nil?
140
- namespace = closure_at(source_position)
141
- t = directive.tag.types.nil? || directive.tag.types.empty? ? nil : directive.tag.types.join
142
- if t.nil? || t.include?('r')
143
- pins.push Solargraph::Pin::Method.new(
144
- location: location,
145
- closure: namespace,
146
- name: directive.tag.name,
147
- comments: docstring.all.to_s,
148
- scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
149
- visibility: :public,
150
- explicit: false,
151
- attribute: true,
152
- source: :source_map
153
- )
154
- end
155
- if t.nil? || t.include?('w')
156
- method_pin = Solargraph::Pin::Method.new(
157
- location: location,
158
- closure: namespace,
159
- name: "#{directive.tag.name}=",
160
- comments: docstring.all.to_s,
161
- scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
162
- visibility: :public,
163
- attribute: true,
164
- source: :source_map
165
- )
166
- pins.push method_pin
167
- method_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last,
168
- source: :source_map)
169
- if pins.last.return_type.defined?
170
- pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '),
171
- 'value')
172
- end
173
- end
174
- when 'visibility'
104
+ directive_processor = YardMap::Directives.for(directive)
105
+ return unless directive_processor
175
106
 
176
- kind = directive.tag.text&.to_sym
177
- return unless %i[private protected public].include?(kind)
178
-
179
- name = directive.tag.name
180
- closure = closure_at(source_position) || @pins.first
181
- # @sg-ignore Need to add nil check here
182
- closure = closure_at(comment_position) if closure.location.range.start.line < comment_position.line
183
- if closure.is_a?(Pin::Method) && no_empty_lines?(comment_position.line, source_position.line)
184
- # @todo Smelly instance variable access
185
- closure.instance_variable_set(:@visibility, kind)
186
- else
187
- matches = pins.select do |pin|
188
- pin.is_a?(Pin::Method) && pin.name == name && pin.namespace == namespace && pin.context.scope == namespace.is_a?(Pin::Singleton) ? :class : :instance
189
- end
190
- matches.each do |pin|
191
- # @todo Smelly instance variable access
192
- pin.instance_variable_set(:@visibility, kind)
193
- end
194
- end
195
-
196
- when 'parse'
197
- begin
198
- ns = closure_at(source_position)
199
- # @sg-ignore Need to add nil check here
200
- src = Solargraph::Source.load_string(directive.tag.text, @source.filename)
201
- region = Parser::Region.new(source: src, closure: ns)
202
- # @todo These pins may need to be marked not explicit
203
- index = @pins.length
204
- loff = if @code.lines[comment_position.line].strip.end_with?('@!parse')
205
- comment_position.line + 1
206
- else
207
- comment_position.line
208
- end
209
- locals = []
210
- ivars = []
211
- Parser.process_node(src.node, region, @pins, locals, ivars)
212
- @pins.concat ivars
213
- # @sg-ignore Need to add nil check here
214
- @pins[index..].each do |p|
215
- # @todo Smelly instance variable access
216
- p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff)
217
- p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff)
218
- end
219
- rescue Parser::SyntaxError
220
- # @todo Handle parser errors in !parse directives
221
- end
222
- when 'domain'
223
- namespace = closure_at(source_position) || Pin::ROOT_PIN
224
- # @sg-ignore flow sensitive typing should be able to handle redefinition
225
- namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
226
- when 'override'
227
- pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags,
228
- source: :source_map)
229
- when 'macro'
230
- # @todo Handle macros
231
- end
232
- end
233
-
234
- # @param line1 [Integer]
235
- # @param line2 [Integer]
236
- # @sg-ignore Need to add nil check here
237
- def no_empty_lines? line1, line2
238
- # @sg-ignore Need to add nil check here
239
- @code.lines[line1..line2].none? { |line| line.strip.empty? }
107
+ @pins += directive_processor.process_directive(
108
+ @source, @pins, source_position, comment_position, directive
109
+ )
240
110
  end
241
111
 
242
112
  # @param comment [String]
@@ -152,6 +152,20 @@ module Solargraph
152
152
  locals.select { |pin| pin.visible_at?(closure, location) }
153
153
  end
154
154
 
155
+ # @return [Array<Parser::AST::Node>]
156
+ def method_call_nodes
157
+ # @sg-ignore node expected Parser::AST::Node, received Parser::AST::Node, nil
158
+ @method_call_nodes ||= Solargraph::Parser::ParserGem::NodeMethods.call_nodes_from(source.node)
159
+ end
160
+
161
+ # @param macro_method_names [Array<String>]
162
+ # @return [Array<Parser::AST::Node>]
163
+ def macro_method_candidates macro_method_names
164
+ return @macro_method_candidates if @macro_method_names == macro_method_names
165
+ @macro_method_names = macro_method_names
166
+ @macro_method_candidates = method_call_nodes.select { |node| macro_method_names.include?(node.children[1].to_s) }
167
+ end
168
+
155
169
  class << self
156
170
  # @param filename [String]
157
171
  # @return [SourceMap]
@@ -1,5 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.59.1')
4
+ VERSION = ENV.fetch('SOLARGRAPH_FORCE_VERSION', '0.60.0')
5
+ end
6
+
7
+ # @generic T
8
+ class A
9
+ # @yieldparam param0 [generic<T>]
10
+ def foo
11
+ end
12
+ end
13
+
14
+ class B < A # [Integer]
15
+ def bar
16
+ foo { |param0| }
17
+ end
18
+ end
19
+
20
+ b = B.new
21
+ b.foo do |arg|
22
+ arg #=> Integer
5
23
  end
@@ -1,25 +1,25 @@
1
- # frozen_string_literal: true
2
-
3
- module Solargraph
4
- class YardMap
5
- class Cache
6
- def initialize
7
- # @type [Hash{String => Array<Solargraph::Pin::Base>}]
8
- @path_pins = {}
9
- end
10
-
11
- # @param path [String]
12
- # @param pins [Array<Solargraph::Pin::Base>]
13
- # @return [Array<Solargraph::Pin::Base>]
14
- def set_path_pins path, pins
15
- @path_pins[path] = pins
16
- end
17
-
18
- # @param path [String]
19
- # @return [Array<Solargraph::Pin::Base>]
20
- def get_path_pins path
21
- @path_pins[path]
22
- end
23
- end
24
- end
25
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ class Cache
6
+ def initialize
7
+ # @type [Hash{String => Array<Solargraph::Pin::Base>}]
8
+ @path_pins = {}
9
+ end
10
+
11
+ # @param path [String]
12
+ # @param pins [Array<Solargraph::Pin::Base>]
13
+ # @return [Array<Solargraph::Pin::Base>]
14
+ def set_path_pins path, pins
15
+ @path_pins[path] = pins
16
+ end
17
+
18
+ # @param path [String]
19
+ # @return [Array<Solargraph::Pin::Base>]
20
+ def get_path_pins path
21
+ @path_pins[path]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module AttributeDirective
7
+ module_function
8
+
9
+ # @param source [Solargraph::Source]
10
+ # @param pins [Array<Solargraph::Pin::Base>]
11
+ # @param source_position [Position]
12
+ # @param comment_position [Position]
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Array<Solargraph::Pin::Method>]
15
+ def process_directive source, pins, source_position, comment_position, directive
16
+ new_pins = []
17
+ location = Location.new(source.filename, Range.new(comment_position, comment_position))
18
+ docstring = Solargraph::Source.parse_docstring(directive.tag.text.to_s).to_docstring
19
+ return [] if directive.tag.name.nil?
20
+ namespace = closure_at(pins, source_position)
21
+ t = directive.tag.types.nil? || directive.tag.types.empty? ? nil : directive.tag.types.join
22
+ if t.nil? || t.include?('r')
23
+ new_pins.push Solargraph::Pin::Method.new(
24
+ location: location,
25
+ closure: namespace,
26
+ name: directive.tag.name,
27
+ comments: docstring.all.to_s,
28
+ scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
29
+ visibility: :public,
30
+ explicit: false,
31
+ attribute: true,
32
+ source: :yard_map
33
+ )
34
+ end
35
+ if t.nil? || t.include?('w')
36
+ write_pin = Solargraph::Pin::Method.new(
37
+ location: location,
38
+ closure: namespace,
39
+ name: "#{directive.tag.name}=",
40
+ comments: docstring.all.to_s,
41
+ scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
42
+ visibility: :public,
43
+ attribute: true,
44
+ source: :yard_map
45
+ )
46
+ new_pins.push(write_pin)
47
+ write_pin.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: write_pin, source: :yard_map)
48
+ if write_pin.return_type&.defined?
49
+ write_pin.docstring.add_tag YARD::Tags::Tag.new(:param, '', write_pin.return_type.to_s.split(', '), 'value')
50
+ end
51
+ end
52
+
53
+ new_pins.compact
54
+ end
55
+
56
+ # @param [Array<Pin::Base>] pins
57
+ # @param [Position] position
58
+ # @return [Pin::Closure]
59
+ def closure_at pins, position
60
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module DomainDirective
7
+ module_function
8
+
9
+ # @param source [Solargraph::Source]
10
+ # @param pins [Array<Solargraph::Pin::Base>]
11
+ # @param source_position [Position]
12
+ # @param _comment_position [Position]
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Array<Solargraph::Pin::Method>]
15
+ def process_directive source, pins, source_position, _comment_position, directive
16
+ namespace = closure_at(pins, source_position) || Pin::ROOT_PIN
17
+ namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
18
+ []
19
+ end
20
+
21
+ # @param [Array<Pin::Base>] pins
22
+ # @param [Position] position
23
+ # @return [Pin::Namespace]
24
+ def closure_at pins, position
25
+ pins.select { |pin| pin.is_a?(Pin::Namespace) and pin.location&.range&.contain?(position) }.last
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module MethodDirective
7
+ module_function
8
+
9
+ # @param source [Solargraph::Source]
10
+ # @param pins [Array<Solargraph::Pin::Base>]
11
+ # @param source_position [Position]
12
+ # @param comment_position [Position]
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Array<Solargraph::Pin::Method>]
15
+ def process_directive source, pins, source_position, comment_position, directive
16
+ namespace = closure_at(pins, source_position) || pins.first
17
+
18
+ namespace = closure_at(pins, comment_position) if namespace.location&.range&.start&.line&.< comment_position.line # rubocop:disable Style/SafeNavigationChainLength
19
+ begin
20
+ src = Solargraph::Source.load_string("def #{directive.tag.name};end", source.filename)
21
+ region = Parser::Region.new(source: src, closure: namespace)
22
+ method_gen_pins = Parser.process_node(src.node, region).first.select { |pin| pin.is_a?(Pin::Method) }
23
+ gen_pin = method_gen_pins.last
24
+ return [] if gen_pin.nil?
25
+ # Move the location to the end of the line so it gets recognized
26
+ # as originating from a comment
27
+ shifted = Solargraph::Position.new(comment_position.line,
28
+ source.code.lines[comment_position.line].to_s.chomp.length)
29
+ comments = Solargraph::Source.parse_docstring(directive.tag.text.to_s).to_docstring.all.to_s
30
+ # @todo: Smelly instance variable access
31
+ gen_pin.instance_variable_set(:@comments, comments)
32
+ gen_pin.instance_variable_set(:@location,
33
+ Solargraph::Location.new(source.filename, Range.new(shifted, shifted)))
34
+ gen_pin.instance_variable_set(:@explicit, false)
35
+ [gen_pin]
36
+ rescue Parser::SyntaxError
37
+ # @todo Handle error in directive
38
+ []
39
+ end
40
+ end
41
+
42
+ # @param [Array<Pin::Base>] pins
43
+ # @param [Position] position
44
+ # @return [Pin::Closure]
45
+ def closure_at pins, position
46
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module OverrideDirective
7
+ module_function
8
+
9
+ # @param source [Solargraph::Source]
10
+ # @param _pins [Array<Solargraph::Pin::Base>]
11
+ # @param _source_position [Position]
12
+ # @param comment_position [Position]
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Array<Pin::Base>]
15
+ def process_directive source, _pins, _source_position, comment_position, directive
16
+ docstring = Solargraph::Source.parse_docstring(directive.tag.text.to_s).to_docstring
17
+ location = Location.new(source.filename, Range.new(comment_position, comment_position))
18
+ [Pin::Reference::Override.new(location, directive.tag.name.to_s, docstring.tags, source: :yard_map)]
19
+ end
20
+
21
+ # @param [Array<Pin::Base>] pins
22
+ # @param [Position] position
23
+ # @return [Pin::Closure]
24
+ def closure_at pins, position
25
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module ParseDirective
7
+ module_function
8
+
9
+ # @param source [Solargraph::Source]
10
+ # @param pins [Array<Solargraph::Pin::Base>]
11
+ # @param source_position [Position]
12
+ # @param comment_position [Position]
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Array<Solargraph::Pin::Base>]
15
+ def process_directive source, pins, source_position, comment_position, directive
16
+ ns = closure_at(pins, source_position)
17
+ pins_copy = pins.dup
18
+ src = Solargraph::Source.load_string(directive.tag.text.to_s, source.filename)
19
+ region = Parser::Region.new(source: src, closure: ns)
20
+ # @todo These pins may need to be marked not explicit
21
+ old_pins_index = pins.length
22
+ loff = if source.code.lines[comment_position.line].strip.end_with?('@!parse')
23
+ comment_position.line + 1
24
+ else
25
+ comment_position.line
26
+ end
27
+ Parser.process_node(src.node, region, pins_copy)
28
+ new_pins = pins_copy[old_pins_index..] || []
29
+ new_pins.each do |p|
30
+ # @todo Smelly instance variable access
31
+ next if p.location.nil?
32
+ # @sg-ignore Unresolved call to range on Solargraph::Location, nil - does not account for next clause above.
33
+ p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff)
34
+ # @sg-ignore Unresolved call to range on Solargraph::Location, nil
35
+ p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff)
36
+ end
37
+
38
+ new_pins
39
+ rescue Parser::SyntaxError
40
+ # @todo Handle parser errors in !parse directives
41
+ []
42
+ end
43
+
44
+ # @param [Array<Pin::Base>] pins
45
+ # @param [Position] position
46
+ # @return [Pin::Closure]
47
+ def closure_at pins, position
48
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ module VisibilityDirective
7
+ module_function
8
+
9
+ VALID_VISIBILITIES = %i[public protected private].freeze
10
+
11
+ # @param source [Solargraph::Source]
12
+ # @param pins [Array<Solargraph::Pin::Base>]
13
+ # @param source_position [Position]
14
+ # @param comment_position [Position]
15
+ # @param directive [YARD::Tags::Directive]
16
+ # @return [Array<Solargraph::Pin::Base>]
17
+ def process_directive source, pins, source_position, comment_position, directive
18
+ kind = directive.tag.text&.to_sym
19
+
20
+ # @sg-ignore include? only expects Symbol, but receives Symbol or nil
21
+ return [] unless VALID_VISIBILITIES.include?(kind.to_sym)
22
+
23
+ name = directive.tag.name
24
+ closure = closure_at(pins, source_position) || pins.first
25
+ closure = closure_at(pins, comment_position) if closure.location&.range&.start&.line&.< comment_position.line # rubocop:disable Style/SafeNavigationChainLength
26
+ if closure.is_a?(Pin::Method) && no_empty_lines?(source.code, comment_position.line, source_position.line)
27
+ # @todo Smelly instance variable access
28
+ closure.instance_variable_set(:@visibility, kind)
29
+ else
30
+ namespace = closure_at(pins, source_position)
31
+ matches = pins.select do |pin|
32
+ if pin.is_a?(Pin::Method) &&
33
+ pin.name == name &&
34
+ pin.namespace == namespace &&
35
+ pin.context.scope == namespace.is_a?(Pin::Singleton)
36
+ :class
37
+ else
38
+ :instance
39
+ end
40
+ end
41
+
42
+ matches.each do |pin|
43
+ # @todo Smelly instance variable access
44
+ pin.instance_variable_set(:@visibility, kind)
45
+ end
46
+ end
47
+
48
+ []
49
+ end
50
+
51
+ # @param [String] code
52
+ # @param [Integer] line1
53
+ # @param [Integer] line2
54
+ # @return [Boolean]
55
+ # @sg-ignore return type could not be inferred
56
+ def no_empty_lines? code, line1, line2
57
+ # @sg-ignore unresolved call none? on the array.
58
+ code.lines[line1..line2].none? { |line| line.strip.empty? }
59
+ end
60
+
61
+ # @param [Array<Pin::Base>] pins
62
+ # @param [Position] position
63
+ # @return [Pin::Closure]
64
+ def closure_at pins, position
65
+ pins.select { |pin| pin.is_a?(Pin::Closure) and pin.location&.range&.contain?(position) }.last
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ class YardMap
5
+ module Directives
6
+ autoload :AttributeDirective, 'solargraph/yard_map/directives/attribute_directive'
7
+ autoload :MethodDirective, 'solargraph/yard_map/directives/method_directive'
8
+ autoload :DomainDirective, 'solargraph/yard_map/directives/domain_directive'
9
+ autoload :OverrideDirective, 'solargraph/yard_map/directives/override_directive'
10
+ autoload :ParseDirective, 'solargraph/yard_map/directives/parse_directive'
11
+ autoload :VisibilityDirective, 'solargraph/yard_map/directives/visibility_directive'
12
+
13
+ # @param directive [YARD::Tags::Directive]
14
+ # @return [Class<AttributeDirective>, Class<MethodDirective>, Class<DomainDirective>, Class<OverrideDirective>, Class<ParseDirective>, Class<VisibilityDirective>, nil]
15
+ def self.for directive
16
+ case directive.tag.tag_name
17
+ when 'attribute'
18
+ AttributeDirective
19
+ when 'method'
20
+ MethodDirective
21
+ when 'domain'
22
+ DomainDirective
23
+ when 'override'
24
+ OverrideDirective
25
+ when 'parse'
26
+ ParseDirective
27
+ when 'visibility'
28
+ VisibilityDirective
29
+ else # rubocop:disable Style/EmptyElse
30
+ nil
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end