solargraph 0.39.0 → 0.40.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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -7
  3. data/CHANGELOG.md +984 -0
  4. data/Rakefile +12 -1
  5. data/SPONSORS.md +6 -0
  6. data/lib/.rubocop.yml +21 -0
  7. data/lib/solargraph/api_map/cache.rb +4 -0
  8. data/lib/solargraph/api_map/store.rb +35 -51
  9. data/lib/solargraph/api_map.rb +94 -87
  10. data/lib/solargraph/{bundle.rb → bench.rb} +6 -2
  11. data/lib/solargraph/compat.rb +23 -0
  12. data/lib/solargraph/complex_type.rb +2 -2
  13. data/lib/solargraph/convention/base.rb +16 -8
  14. data/lib/solargraph/convention/gemfile.rb +2 -5
  15. data/lib/solargraph/convention/gemspec.rb +3 -6
  16. data/lib/solargraph/convention/rspec.rb +3 -6
  17. data/lib/solargraph/convention.rb +14 -5
  18. data/lib/solargraph/diagnostics/rubocop.rb +2 -1
  19. data/lib/solargraph/diagnostics/rubocop_helpers.rb +4 -1
  20. data/lib/solargraph/documentor.rb +4 -0
  21. data/lib/solargraph/environ.rb +11 -6
  22. data/lib/solargraph/language_server/host.rb +1 -1
  23. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +6 -1
  24. data/lib/solargraph/language_server/message/text_document/definition.rb +1 -1
  25. data/lib/solargraph/language_server/message/text_document/formatting.rb +7 -3
  26. data/lib/solargraph/language_server/uri_helpers.rb +23 -3
  27. data/lib/solargraph/library.rb +22 -8
  28. data/lib/solargraph/parser/legacy/class_methods.rb +4 -0
  29. data/lib/solargraph/parser/legacy/node_chainer.rb +10 -7
  30. data/lib/solargraph/parser/legacy/node_methods.rb +16 -3
  31. data/lib/solargraph/parser/legacy/node_processors/ivasgn_node.rb +1 -1
  32. data/lib/solargraph/parser/legacy/node_processors/send_node.rb +48 -23
  33. data/lib/solargraph/parser/node_processor/base.rb +3 -0
  34. data/lib/solargraph/parser/rubyvm/class_methods.rb +6 -2
  35. data/lib/solargraph/parser/rubyvm/node_chainer.rb +11 -9
  36. data/lib/solargraph/parser/rubyvm/node_methods.rb +6 -1
  37. data/lib/solargraph/parser/rubyvm/node_processors/args_node.rb +35 -11
  38. data/lib/solargraph/parser/rubyvm/node_processors/ivasgn_node.rb +1 -1
  39. data/lib/solargraph/parser/rubyvm/node_processors/kw_arg_node.rb +6 -1
  40. data/lib/solargraph/parser/rubyvm/node_processors/opt_arg_node.rb +6 -1
  41. data/lib/solargraph/parser/rubyvm/node_processors/send_node.rb +52 -67
  42. data/lib/solargraph/parser/rubyvm/node_processors.rb +2 -0
  43. data/lib/solargraph/pin/base.rb +2 -2
  44. data/lib/solargraph/pin/base_variable.rb +1 -1
  45. data/lib/solargraph/pin/block.rb +28 -14
  46. data/lib/solargraph/pin/closure.rb +1 -1
  47. data/lib/solargraph/pin/common.rb +1 -1
  48. data/lib/solargraph/pin/constant.rb +1 -1
  49. data/lib/solargraph/pin/conversions.rb +3 -4
  50. data/lib/solargraph/pin/documenting.rb +6 -11
  51. data/lib/solargraph/pin/local_variable.rb +1 -1
  52. data/lib/solargraph/pin/method.rb +143 -9
  53. data/lib/solargraph/pin/method_alias.rb +2 -2
  54. data/lib/solargraph/pin/namespace.rb +1 -1
  55. data/lib/solargraph/pin/parameter.rb +2 -2
  56. data/lib/solargraph/pin/proxy_type.rb +1 -1
  57. data/lib/solargraph/pin.rb +0 -3
  58. data/lib/solargraph/position.rb +2 -14
  59. data/lib/solargraph/shell.rb +2 -1
  60. data/lib/solargraph/source/chain/call.rb +9 -0
  61. data/lib/solargraph/source/chain/head.rb +14 -8
  62. data/lib/solargraph/source/chain/z_super.rb +30 -0
  63. data/lib/solargraph/source/chain.rb +19 -36
  64. data/lib/solargraph/source/source_chainer.rb +2 -1
  65. data/lib/solargraph/source.rb +24 -9
  66. data/lib/solargraph/source_map/clip.rb +78 -44
  67. data/lib/solargraph/source_map/mapper.rb +41 -21
  68. data/lib/solargraph/source_map.rb +20 -2
  69. data/lib/solargraph/type_checker/param_def.rb +1 -1
  70. data/lib/solargraph/type_checker.rb +61 -42
  71. data/lib/solargraph/version.rb +1 -1
  72. data/lib/solargraph/views/_name_type_tag.erb +1 -1
  73. data/lib/solargraph/workspace.rb +1 -1
  74. data/lib/solargraph/yard_map/core_fills.rb +185 -0
  75. data/lib/solargraph/yard_map/helpers.rb +16 -0
  76. data/lib/solargraph/{pin/yard_pin/constant.rb → yard_map/mapper/to_constant.rb} +6 -6
  77. data/lib/solargraph/yard_map/mapper/to_method.rb +78 -0
  78. data/lib/solargraph/{pin/yard_pin/namespace.rb → yard_map/mapper/to_namespace.rb} +6 -6
  79. data/lib/solargraph/yard_map/mapper.rb +17 -8
  80. data/lib/solargraph/yard_map/rdoc_to_yard.rb +26 -15
  81. data/lib/solargraph/yard_map/stdlib_fills.rb +43 -0
  82. data/lib/solargraph/yard_map/to_method.rb +79 -0
  83. data/lib/solargraph/yard_map.rb +140 -92
  84. data/lib/solargraph.rb +5 -4
  85. data/solargraph.gemspec +5 -5
  86. data/yardoc/2.2.2.tar.gz +0 -0
  87. metadata +29 -41
  88. data/.rubocop.yml +0 -6
  89. data/lib/solargraph/core_fills.rb +0 -159
  90. data/lib/solargraph/pin/attribute.rb +0 -49
  91. data/lib/solargraph/pin/base_method.rb +0 -141
  92. data/lib/solargraph/pin/yard_pin/method.rb +0 -65
  93. data/lib/solargraph/pin/yard_pin/yard_mixin.rb +0 -26
  94. data/lib/solargraph/pin/yard_pin.rb +0 -12
  95. data/lib/solargraph/stdlib_fills.rb +0 -32
  96. data/travis-bundler.rb +0 -11
@@ -20,6 +20,7 @@ module Solargraph
20
20
  autoload :Head, 'solargraph/source/chain/head'
21
21
  autoload :Or, 'solargraph/source/chain/or'
22
22
  autoload :BlockVariable, 'solargraph/source/chain/block_variable'
23
+ autoload :ZSuper, 'solargraph/source/chain/z_super'
23
24
 
24
25
  @@inference_stack = []
25
26
  @@inference_depth = 0
@@ -56,7 +57,6 @@ module Solargraph
56
57
  # @param locals [Array<Pin::Base>]
57
58
  # @return [Array<Pin::Base>]
58
59
  def define api_map, name_pin, locals
59
- rebind_block name_pin, api_map, locals
60
60
  return [] if undefined?
61
61
  working_pin = name_pin
62
62
  links[0..-2].each do |link|
@@ -78,7 +78,6 @@ module Solargraph
78
78
  # @param locals [Array<Pin::Base>]
79
79
  # @return [ComplexType]
80
80
  def infer api_map, name_pin, locals
81
- rebind_block name_pin, api_map, locals
82
81
  pins = define(api_map, name_pin, locals)
83
82
  infer_first_defined(pins, links.last.last_context, api_map)
84
83
  end
@@ -111,18 +110,21 @@ module Solargraph
111
110
  # @param api_map [ApiMap]
112
111
  # @return [ComplexType]
113
112
  def infer_first_defined pins, context, api_map
114
- type = ComplexType::UNDEFINED
113
+ possibles = []
115
114
  pins.each do |pin|
116
115
  # Avoid infinite recursion
117
116
  next if @@inference_stack.include?(pin.identity)
118
117
  @@inference_stack.push pin.identity
119
118
  type = pin.typify(api_map)
120
119
  @@inference_stack.pop
121
- break if type.defined?
120
+ if type.defined?
121
+ possibles.push type
122
+ break if pin.is_a?(Pin::Method)
123
+ end
122
124
  end
123
- if type.undefined?
125
+ if possibles.empty?
124
126
  # Limit method inference recursion
125
- return type if @@inference_depth >= 10 && pins.first.is_a?(Pin::BaseMethod)
127
+ return ComplexType::UNDEFINED if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
126
128
  @@inference_depth += 1
127
129
  pins.each do |pin|
128
130
  # Avoid infinite recursion
@@ -130,41 +132,22 @@ module Solargraph
130
132
  @@inference_stack.push pin.identity
131
133
  type = pin.probe(api_map)
132
134
  @@inference_stack.pop
133
- break if type.defined?
135
+ if type.defined?
136
+ possibles.push type
137
+ break if pin.is_a?(Pin::Method)
138
+ end
134
139
  end
135
140
  @@inference_depth -= 1
136
141
  end
137
- return type if context.nil? || context.return_type.undefined?
138
- type.self_to(context.return_type.namespace)
139
- end
140
-
141
- def skippable_block_receivers api_map
142
- @@skippable_block_receivers ||= (
143
- api_map.get_methods('Array', deep: false).map(&:name) +
144
- api_map.get_methods('Enumerable', deep: false).map(&:name) +
145
- api_map.get_methods('Hash', deep: false).map(&:name) +
146
- ['new']
147
- ).to_set
148
- end
149
-
150
- def rebind_block pin, api_map, locals
151
- return unless pin.is_a?(Pin::Block) && pin.receiver && !pin.rebound?
152
- # This first rebind just sets the block pin's rebound state
153
- pin.rebind ComplexType::UNDEFINED
154
- chain = Parser.chain(pin.receiver, pin.location.filename)
155
- return if skippable_block_receivers(api_map).include?(chain.links.last.word)
156
- if ['instance_eval', 'instance_exec', 'class_eval', 'class_exec', 'module_eval', 'module_exec'].include?(chain.links.last.word)
157
- type = chain.base.infer(api_map, pin, locals)
158
- pin.rebind type
142
+ return ComplexType::UNDEFINED if possibles.empty?
143
+ type = if possibles.length > 1
144
+ sorted = possibles.map { |t| t.rooted? ? "::#{t}" : t.to_s }.sort { |a, _| a == 'nil' ? 1 : 0 }
145
+ ComplexType.parse(*sorted)
159
146
  else
160
- receiver_pin = chain.define(api_map, pin, locals).first
161
- return if receiver_pin.nil? || receiver_pin.docstring.nil?
162
- ys = receiver_pin.docstring.tag(:yieldself)
163
- unless ys.nil? || ys.types.nil? || ys.types.empty?
164
- ysct = ComplexType.try_parse(*ys.types).qualify(api_map, receiver_pin.context.namespace)
165
- pin.rebind ysct
166
- end
147
+ possibles.first
167
148
  end
149
+ return type if context.nil? || context.return_type.undefined?
150
+ type.self_to(context.return_type.namespace)
168
151
  end
169
152
  end
170
153
  end
@@ -17,7 +17,6 @@ module Solargraph
17
17
  # @param position [Position]
18
18
  # @return [Source::Chain]
19
19
  def chain source, position
20
- # raise "Not a source" unless source.is_a?(Source)
21
20
  new(source, position).chain
22
21
  end
23
22
  end
@@ -32,6 +31,8 @@ module Solargraph
32
31
 
33
32
  # @return [Source::Chain]
34
33
  def chain
34
+ # Special handling for files that end with an integer and a period
35
+ return Chain.new([Chain::Literal.new('Integer'), Chain::UNDEFINED_CALL]) if phrase =~ /^[0-9]+\.$/
35
36
  return Chain.new([Chain::Literal.new('Symbol')]) if phrase.start_with?(':') && !phrase.start_with?('::')
36
37
  begin
37
38
  return Chain.new([]) if phrase.end_with?('..')
@@ -284,7 +284,7 @@ module Solargraph
284
284
 
285
285
  FOLDING_NODE_TYPES = if Parser.rubyvm?
286
286
  %i[
287
- CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR
287
+ CLASS SCLASS MODULE DEFN DEFS IF WHILE UNLESS ITER STR HASH ARRAY LIST
288
288
  ].freeze
289
289
  else
290
290
  %i[
@@ -338,25 +338,29 @@ module Solargraph
338
338
 
339
339
  def first_not_empty_from line
340
340
  cursor = line
341
- cursor += 1 while cursor < code.lines.length && code.lines[cursor].strip.empty?
342
- cursor = line if cursor > code.lines.length
341
+ cursor += 1 while cursor < code_lines.length && code_lines[cursor].strip.empty?
342
+ cursor = line if cursor > code_lines.length - 1
343
343
  cursor
344
344
  end
345
345
 
346
346
  # @param top [Parser::AST::Node]
347
347
  # @param result [Array<Range>]
348
+ # @param parent [Symbol]
348
349
  # @return [void]
349
- def inner_folding_ranges top, result = []
350
+ def inner_folding_ranges top, result = [], parent = nil
350
351
  # return unless top.is_a?(::Parser::AST::Node)
351
352
  return unless Parser.is_ast_node?(top)
352
353
  if FOLDING_NODE_TYPES.include?(top.type)
353
- range = Range.from_node(top)
354
- if result.empty? || range.start.line > result.last.start.line
355
- result.push range unless range.ending.line - range.start.line < 2
354
+ # @todo Smelly exception for hash's first-level array in RubyVM
355
+ unless [:ARRAY, :LIST].include?(top.type) && parent == :HASH
356
+ range = Range.from_node(top)
357
+ if result.empty? || range.start.line > result.last.start.line
358
+ result.push range unless range.ending.line - range.start.line < 2
359
+ end
356
360
  end
357
361
  end
358
362
  top.children.each do |child|
359
- inner_folding_ranges(child, result)
363
+ inner_folding_ranges(child, result, top.type)
360
364
  end
361
365
  end
362
366
 
@@ -468,8 +472,12 @@ module Solargraph
468
472
  # @return [Integer]
469
473
  attr_writer :version
470
474
 
475
+ # @param val [String]
471
476
  # @return [String]
472
- attr_writer :code
477
+ def code=(val)
478
+ @code_lines= nil
479
+ @code = val
480
+ end
473
481
 
474
482
  # @return [Parser::AST::Node]
475
483
  attr_writer :node
@@ -492,6 +500,13 @@ module Solargraph
492
500
  # @return [Source::Updater]
493
501
  attr_accessor :last_updater
494
502
 
503
+ private
504
+
505
+ # @return [Array<String>]
506
+ def code_lines
507
+ @code_lines ||= code.lines
508
+ end
509
+
495
510
  class << self
496
511
  # @param filename [String]
497
512
  # @return [Solargraph::Source]
@@ -25,51 +25,12 @@ module Solargraph
25
25
  def complete
26
26
  return package_completions([]) if !source_map.source.parsed? || cursor.string?
27
27
  return package_completions(api_map.get_symbols) if cursor.chain.literal? && cursor.chain.links.last.word == '<Symbol>'
28
- return Completion.new([], cursor.range) if cursor.chain.literal? || cursor.comment?
29
- result = []
30
- result.concat complete_keyword_parameters
31
- if cursor.chain.constant? || cursor.start_of_constant?
32
- full = cursor.chain.links.first.word
33
- type = if cursor.chain.undefined?
34
- cursor.chain.base.infer(api_map, context_pin, locals)
35
- else
36
- if full.include?('::') && cursor.chain.links.length == 1
37
- ComplexType.try_parse(full.split('::')[0..-2].join('::'))
38
- elsif cursor.chain.links.length > 1
39
- ComplexType.try_parse(full)
40
- else
41
- ComplexType::UNDEFINED
42
- end
43
- end
44
- if type.undefined?
45
- if full.include?('::')
46
- result.concat api_map.get_constants(full, *gates)
47
- else
48
- result.concat api_map.get_constants('', cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) #.select { |pin| pin.name.start_with?(full) }
49
- end
50
- else
51
- result.concat api_map.get_constants(type.namespace, cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates)
52
- end
28
+ return Completion.new([], cursor.range) if cursor.chain.literal?
29
+ if cursor.comment?
30
+ tag_complete
53
31
  else
54
- type = cursor.chain.base.infer(api_map, block, locals)
55
- result.concat api_map.get_complex_type_methods(type, block.binder.namespace, cursor.chain.links.length == 1)
56
- if cursor.chain.links.length == 1
57
- if cursor.word.start_with?('@@')
58
- return package_completions(api_map.get_class_variable_pins(context_pin.full_context.namespace))
59
- elsif cursor.word.start_with?('@')
60
- return package_completions(api_map.get_instance_variable_pins(block.binder.namespace, block.binder.scope))
61
- elsif cursor.word.start_with?('$')
62
- return package_completions(api_map.get_global_variable_pins)
63
- end
64
- result.concat locals
65
- result.concat api_map.get_constants(context_pin.context.namespace, *gates)
66
- result.concat api_map.get_methods(block.binder.namespace, scope: block.binder.scope, visibility: [:public, :private, :protected])
67
- result.concat api_map.get_methods('Kernel')
68
- result.concat ApiMap.keywords
69
- result.concat yielded_self_pins
70
- end
32
+ code_complete
71
33
  end
72
- package_completions(result)
73
34
  end
74
35
 
75
36
  # @return [Array<Pin::Base>]
@@ -111,6 +72,13 @@ module Solargraph
111
72
  end
112
73
  end
113
74
 
75
+ # @param phrase [String]
76
+ # @return [Array<Solargraph::Pin::Base>]
77
+ def translate phrase
78
+ chain = Parser.chain(Parser.parse(phrase))
79
+ chain.define(api_map, block, locals)
80
+ end
81
+
114
82
  private
115
83
 
116
84
  # @return [ApiMap]
@@ -181,10 +149,76 @@ module Solargraph
181
149
  frag_start = cursor.start_of_word.to_s.downcase
182
150
  filtered = result.uniq(&:name).select { |s|
183
151
  s.name.downcase.start_with?(frag_start) &&
184
- (!s.is_a?(Pin::BaseMethod) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
152
+ (!s.is_a?(Pin::Method) || s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))
185
153
  }
186
154
  Completion.new(filtered, cursor.range)
187
155
  end
156
+
157
+ def tag_complete
158
+ result = []
159
+ match = source_map.code[0..cursor.offset-1].match(/[\[<, ]([a-z0-9_:]*)\z/i)
160
+ if match
161
+ full = match[1]
162
+ if full.include?('::')
163
+ if full.end_with?('::')
164
+ result.concat api_map.get_constants(full[0..-3], *gates)
165
+ else
166
+ result.concat api_map.get_constants(full.split('::')[0..-2].join('::'), *gates)
167
+ end
168
+ else
169
+ result.concat api_map.get_constants('', full.end_with?('::') ? '' : context_pin.full_context.namespace, *gates) #.select { |pin| pin.name.start_with?(full) }
170
+ end
171
+ end
172
+ package_completions(result)
173
+ end
174
+
175
+ def code_complete
176
+ result = []
177
+ result.concat complete_keyword_parameters
178
+ if cursor.chain.constant? || cursor.start_of_constant?
179
+ full = cursor.chain.links.first.word
180
+ type = if cursor.chain.undefined?
181
+ cursor.chain.base.infer(api_map, context_pin, locals)
182
+ else
183
+ if full.include?('::') && cursor.chain.links.length == 1
184
+ ComplexType.try_parse(full.split('::')[0..-2].join('::'))
185
+ elsif cursor.chain.links.length > 1
186
+ ComplexType.try_parse(full)
187
+ else
188
+ ComplexType::UNDEFINED
189
+ end
190
+ end
191
+ if type.undefined?
192
+ if full.include?('::')
193
+ result.concat api_map.get_constants(full, *gates)
194
+ else
195
+ result.concat api_map.get_constants('', cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates) #.select { |pin| pin.name.start_with?(full) }
196
+ end
197
+ else
198
+ result.concat api_map.get_constants(type.namespace, cursor.start_of_constant? ? '' : context_pin.full_context.namespace, *gates)
199
+ end
200
+ else
201
+ type = cursor.chain.base.infer(api_map, block, locals)
202
+ result.concat api_map.get_complex_type_methods(type, block.binder.namespace, cursor.chain.links.length == 1)
203
+ if cursor.chain.links.length == 1
204
+ if cursor.word.start_with?('@@')
205
+ return package_completions(api_map.get_class_variable_pins(context_pin.full_context.namespace))
206
+ elsif cursor.word.start_with?('@')
207
+ return package_completions(api_map.get_instance_variable_pins(block.binder.namespace, block.binder.scope))
208
+ elsif cursor.word.start_with?('$')
209
+ return package_completions(api_map.get_global_variable_pins)
210
+ end
211
+ result.concat locals
212
+ result.concat api_map.get_constants(context_pin.context.namespace, *gates)
213
+ result.concat api_map.get_methods(block.binder.namespace, scope: block.binder.scope, visibility: [:public, :private, :protected])
214
+ result.concat api_map.get_methods('Kernel')
215
+ # result.concat ApiMap.keywords
216
+ result.concat api_map.keyword_pins
217
+ result.concat yielded_self_pins
218
+ end
219
+ end
220
+ package_completions(result)
221
+ end
188
222
  end
189
223
  end
190
224
  end
@@ -68,7 +68,7 @@ module Solargraph
68
68
  # @param d [YARD::Tags::Directive]
69
69
  parse.directives.each do |d|
70
70
  line_num = find_directive_line_number(cmnt, d.tag.tag_name, last_line)
71
- pos = Solargraph::Position.new(comment_position.line + line_num, comment_position.column)
71
+ pos = Solargraph::Position.new(comment_position.line + line_num - 1, comment_position.column)
72
72
  process_directive(source_position, pos, d)
73
73
  last_line = line_num + 1
74
74
  # @todo The below call assumes the topmost comment line. The above
@@ -79,7 +79,11 @@ module Solargraph
79
79
  end
80
80
  end
81
81
 
82
+ # @param comment [String]
83
+ # @return [Integer]
82
84
  def find_directive_line_number comment, tag, start
85
+ # Avoid overruning the index
86
+ return start unless start < comment.lines.length
83
87
  num = comment.lines[start..-1].find_index do |line|
84
88
  # Legacy method directives might be `@method` instead of `@!method`
85
89
  # @todo Legacy syntax should probably emit a warning
@@ -91,23 +95,24 @@ module Solargraph
91
95
  # @param source_position [Position]
92
96
  # @param comment_position [Position]
93
97
  # @param directive [YARD::Tags::Directive]
98
+ # @return [void]
94
99
  def process_directive source_position, comment_position, directive
95
100
  docstring = Solargraph::Source.parse_docstring(directive.tag.text).to_docstring
96
101
  location = Location.new(@filename, Range.new(comment_position, comment_position))
97
102
  case directive.tag.tag_name
98
103
  when 'method'
99
- namespace = closure_at(source_position)
104
+ namespace = closure_at(source_position) || @pins.first
100
105
  if namespace.location.range.start.line < comment_position.line
101
106
  namespace = closure_at(comment_position)
102
107
  end
103
- region = Parser::Region.new(source: @source, closure: namespace)
104
108
  begin
105
- src_node = Parser.parse("def #{directive.tag.name};end", @filename, location.range.start.line)
106
- gen_pin = Parser.process_node(src_node, region).first.last
109
+ src = Solargraph::Source.load_string("def #{directive.tag.name};end", @source.filename)
110
+ region = Parser::Region.new(source: src, closure: namespace)
111
+ gen_pin = Parser.process_node(src.node, region).first.last
107
112
  return if gen_pin.nil?
108
- # Move the location to the end of the line so the it gets
109
- # recognized as originating from a comment
110
- shifted = Solargraph::Position.new(comment_position.line, @code.lines[comment_position.line].chomp.length)
113
+ # Move the location to the end of the line so it gets recognized
114
+ # as originating from a comment
115
+ shifted = Solargraph::Position.new(comment_position.line, @code.lines[comment_position.line].to_s.chomp.length)
111
116
  # @todo: Smelly instance variable access
112
117
  gen_pin.instance_variable_set(:@comments, docstring.all.to_s)
113
118
  gen_pin.instance_variable_set(:@location, Solargraph::Location.new(@filename, Range.new(shifted, shifted)))
@@ -121,35 +126,50 @@ module Solargraph
121
126
  namespace = closure_at(source_position)
122
127
  t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('')
123
128
  if t.nil? || t.include?('r')
124
- pins.push Solargraph::Pin::Attribute.new(
129
+ pins.push Solargraph::Pin::Method.new(
125
130
  location: location,
126
131
  closure: namespace,
127
132
  name: directive.tag.name,
128
133
  comments: docstring.all.to_s,
129
- access: :reader,
130
134
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
131
135
  visibility: :public,
132
- explicit: false
136
+ explicit: false,
137
+ attribute: true
133
138
  )
134
139
  end
135
140
  if t.nil? || t.include?('w')
136
- pins.push Solargraph::Pin::Attribute.new(
141
+ pins.push Solargraph::Pin::Method.new(
137
142
  location: location,
138
143
  closure: namespace,
139
144
  name: "#{directive.tag.name}=",
140
145
  comments: docstring.all.to_s,
141
- access: :writer,
142
146
  scope: namespace.is_a?(Pin::Singleton) ? :class : :instance,
143
- visibility: :public
147
+ visibility: :public,
148
+ attribute: true
144
149
  )
150
+ pins.last.parameters.push Pin::Parameter.new(name: 'value', decl: :arg, closure: pins.last)
151
+ if pins.last.return_type.defined?
152
+ pins.last.docstring.add_tag YARD::Tags::Tag.new(:param, '', pins.last.return_type.to_s.split(', '), 'value')
153
+ end
145
154
  end
146
155
  when 'parse'
147
- ns = closure_at(source_position)
148
- region = Parser::Region.new(source: @source, closure: ns)
149
156
  begin
150
- node = Parser.parse(directive.tag.text, @filename, comment_position.line)
157
+ ns = closure_at(source_position)
158
+ src = Solargraph::Source.load_string(directive.tag.text, @source.filename)
159
+ region = Parser::Region.new(source: src, closure: ns)
151
160
  # @todo These pins may need to be marked not explicit
152
- Parser.process_node(node, region, @pins)
161
+ index = @pins.length
162
+ loff = if @code.lines[comment_position.line].strip.end_with?('@!parse')
163
+ comment_position.line + 1
164
+ else
165
+ comment_position.line
166
+ end
167
+ Parser.process_node(src.node, region, @pins)
168
+ @pins[index..-1].each do |p|
169
+ # @todo Smelly instance variable access
170
+ p.location.range.start.instance_variable_set(:@line, p.location.range.start.line + loff)
171
+ p.location.range.ending.instance_variable_set(:@line, p.location.range.ending.line + loff)
172
+ end
153
173
  rescue Parser::SyntaxError => e
154
174
  # @todo Handle parser errors in !parse directives
155
175
  end
@@ -183,10 +203,10 @@ module Solargraph
183
203
  # @return [void]
184
204
  def process_comment_directives
185
205
  return unless @code =~ MACRO_REGEXP
186
- used = []
206
+ code_lines = @code.lines
187
207
  @source.associated_comments.each do |line, comments|
188
- src_pos = line ? Position.new(line, @code.lines[line].to_s.chomp.index(/[^\s]/) || 0) : Position.new(@code.lines.length, 0)
189
- com_pos = Position.new(line - 1, 0)
208
+ src_pos = line ? Position.new(line, code_lines[line].to_s.chomp.index(/[^\s]/) || 0) : Position.new(code_lines.length, 0)
209
+ com_pos = Position.new(line + 1 - comments.lines.length, 0)
190
210
  process_comment(src_pos, com_pos, comments)
191
211
  end
192
212
  end
@@ -3,6 +3,7 @@
3
3
  require 'jaro_winkler'
4
4
  require 'yard'
5
5
  require 'yard-solargraph'
6
+ require 'set'
6
7
 
7
8
  module Solargraph
8
9
  # An index of pins and other ApiMap-related data for a Source.
@@ -29,7 +30,20 @@ module Solargraph
29
30
  @source = source.dup
30
31
  @pins = pins
31
32
  @locals = locals
32
- environ.merge Convention.for(source)
33
+ environ.merge Convention.for_local(self) unless filename.nil?
34
+ @pin_class_hash = pins.to_set.classify(&:class).transform_values(&:to_a)
35
+ @pin_select_cache = {}
36
+ end
37
+
38
+ def pins_by_class klass
39
+ @pin_select_cache[klass] ||= @pin_class_hash.select { |key, _| key <= klass }.values.flatten
40
+ end
41
+
42
+ def rebindable_method_names
43
+ @rebindable_method_names ||= pins_by_class(Pin::Method)
44
+ .select { |pin| pin.comments && pin.comments.include?('@yieldself') }
45
+ .map(&:name)
46
+ .to_set
33
47
  end
34
48
 
35
49
  # @return [String]
@@ -44,7 +58,7 @@ module Solargraph
44
58
 
45
59
  # @return [Array<Pin::Reference::Require>]
46
60
  def requires
47
- @requires ||= pins.select{ |p| p.is_a?(Pin::Reference::Require) }
61
+ pins_by_class(Pin::Reference::Require)
48
62
  end
49
63
 
50
64
  # @return [Environ]
@@ -62,6 +76,7 @@ module Solargraph
62
76
  # @param query [String]
63
77
  # @return [Array<Pin::Base>]
64
78
  def query_symbols query
79
+ return document_symbols if query && query.empty?
65
80
  document_symbols.select{ |pin| fuzzy_string_match(pin.path, query) || fuzzy_string_match(pin.name, query) }
66
81
  end
67
82
 
@@ -153,6 +168,9 @@ module Solargraph
153
168
  position = Position.new(line, character)
154
169
  found = nil
155
170
  pins.each do |pin|
171
+ # @todo Attribute pins should not be treated like closures, but
172
+ # there's probably a better way to handle it
173
+ next if pin.is_a?(Pin::Method) && pin.attribute?
156
174
  found = pin if (klasses.empty? || klasses.any? { |kls| pin.is_a?(kls) } ) && pin.location.range.contain?(position)
157
175
  break if pin.location.range.start.line > line
158
176
  end
@@ -20,7 +20,7 @@ module Solargraph
20
20
  class << self
21
21
  # Get an array of ParamDefs from a method pin.
22
22
  #
23
- # @param pin [Solargraph::Pin::BaseMethod]
23
+ # @param pin [Solargraph::Pin::Method]
24
24
  # @return [Array<ParamDef>]
25
25
  def from pin
26
26
  result = []