solargraph 0.26.1 → 0.27.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/lib/solargraph.rb +5 -2
  3. data/lib/solargraph/api_map.rb +236 -234
  4. data/lib/solargraph/api_map/store.rb +18 -53
  5. data/lib/solargraph/bundle.rb +22 -0
  6. data/lib/solargraph/complex_type.rb +9 -5
  7. data/lib/solargraph/complex_type/type_methods.rb +113 -0
  8. data/lib/solargraph/complex_type/unique_type.rb +35 -0
  9. data/lib/solargraph/core_fills.rb +1 -0
  10. data/lib/solargraph/diagnostics.rb +6 -4
  11. data/lib/solargraph/diagnostics/base.rb +3 -0
  12. data/lib/solargraph/diagnostics/require_not_found.rb +2 -1
  13. data/lib/solargraph/diagnostics/rubocop.rb +21 -6
  14. data/lib/solargraph/diagnostics/type_not_defined.rb +4 -3
  15. data/lib/solargraph/diagnostics/update_errors.rb +18 -0
  16. data/lib/solargraph/language_server/host.rb +90 -222
  17. data/lib/solargraph/language_server/host/cataloger.rb +68 -0
  18. data/lib/solargraph/language_server/host/diagnoser.rb +85 -0
  19. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +35 -24
  20. data/lib/solargraph/language_server/message/text_document/completion.rb +6 -8
  21. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +1 -1
  22. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +0 -1
  23. data/lib/solargraph/language_server/transport/socket.rb +4 -6
  24. data/lib/solargraph/language_server/transport/stdio.rb +4 -6
  25. data/lib/solargraph/library.rb +152 -99
  26. data/lib/solargraph/live_map.rb +1 -1
  27. data/lib/solargraph/location.rb +28 -0
  28. data/lib/solargraph/pin.rb +2 -0
  29. data/lib/solargraph/pin/attribute.rb +26 -12
  30. data/lib/solargraph/pin/base.rb +15 -35
  31. data/lib/solargraph/pin/base_variable.rb +7 -15
  32. data/lib/solargraph/pin/block.rb +5 -9
  33. data/lib/solargraph/pin/block_parameter.rb +9 -7
  34. data/lib/solargraph/pin/conversions.rb +5 -5
  35. data/lib/solargraph/pin/duck_method.rb +1 -1
  36. data/lib/solargraph/pin/instance_variable.rb +0 -4
  37. data/lib/solargraph/pin/keyword.rb +4 -0
  38. data/lib/solargraph/pin/localized.rb +5 -3
  39. data/lib/solargraph/pin/method.rb +11 -0
  40. data/lib/solargraph/pin/namespace.rb +7 -3
  41. data/lib/solargraph/pin/proxy_type.rb +3 -7
  42. data/lib/solargraph/pin/reference.rb +2 -2
  43. data/lib/solargraph/pin/symbol.rb +1 -1
  44. data/lib/solargraph/pin/yard_pin/method.rb +2 -2
  45. data/lib/solargraph/pin/yard_pin/namespace.rb +16 -7
  46. data/lib/solargraph/position.rb +103 -0
  47. data/lib/solargraph/range.rb +70 -0
  48. data/lib/solargraph/source.rb +159 -328
  49. data/lib/solargraph/source/chain.rb +38 -55
  50. data/lib/solargraph/source/chain/call.rb +47 -29
  51. data/lib/solargraph/source/chain/class_variable.rb +2 -2
  52. data/lib/solargraph/source/chain/constant.rb +3 -3
  53. data/lib/solargraph/source/chain/definition.rb +7 -3
  54. data/lib/solargraph/source/chain/global_variable.rb +1 -1
  55. data/lib/solargraph/source/chain/head.rb +22 -9
  56. data/lib/solargraph/source/chain/instance_variable.rb +2 -2
  57. data/lib/solargraph/source/chain/link.rb +4 -4
  58. data/lib/solargraph/source/chain/literal.rb +1 -1
  59. data/lib/solargraph/source/chain/variable.rb +2 -2
  60. data/lib/solargraph/source/change.rb +0 -6
  61. data/lib/solargraph/source/cursor.rb +161 -0
  62. data/lib/solargraph/source/encoding_fixes.rb +1 -1
  63. data/lib/solargraph/source/node_chainer.rb +28 -21
  64. data/lib/solargraph/source/node_methods.rb +1 -1
  65. data/lib/solargraph/source/source_chainer.rb +217 -0
  66. data/lib/solargraph/source_map.rb +138 -0
  67. data/lib/solargraph/source_map/clip.rb +123 -0
  68. data/lib/solargraph/{source → source_map}/completion.rb +3 -3
  69. data/lib/solargraph/{source → source_map}/mapper.rb +143 -41
  70. data/lib/solargraph/version.rb +1 -1
  71. data/lib/solargraph/workspace.rb +13 -20
  72. data/lib/solargraph/yard_map.rb +77 -48
  73. metadata +17 -11
  74. data/lib/solargraph/basic_type.rb +0 -33
  75. data/lib/solargraph/basic_type_methods.rb +0 -111
  76. data/lib/solargraph/source/call_chainer.rb +0 -273
  77. data/lib/solargraph/source/fragment.rb +0 -342
  78. data/lib/solargraph/source/location.rb +0 -23
  79. data/lib/solargraph/source/position.rb +0 -95
  80. data/lib/solargraph/source/range.rb +0 -64
@@ -0,0 +1,123 @@
1
+ module Solargraph
2
+ class SourceMap
3
+ class Clip
4
+ # @param api_map [ApiMap]
5
+ # @param cursor [Source::Cursor]
6
+ def initialize api_map, cursor
7
+ # @todo Just some temporary stuff while I make sure this works
8
+ raise "Not a cursor: #{cursor.class}" unless cursor.is_a?(Source::Cursor)
9
+ @api_map = api_map
10
+ @cursor = cursor
11
+ end
12
+
13
+ # @return [Array<Pin::Base>]
14
+ def define
15
+ return [] if cursor.chain.literal?
16
+ result = cursor.chain.define(api_map, context_pin, locals)
17
+ result.concat(source_map.pins.select{ |p| p.location.range.start.line == cursor.position.line }) if result.empty?
18
+ result
19
+ end
20
+
21
+ # @return [Completion]
22
+ def complete
23
+ return package_completions(api_map.get_symbols) if cursor.chain.literal? && cursor.chain.links.last.word == '<Symbol>'
24
+ return Completion.new([], cursor.range) if cursor.chain.literal? || cursor.comment?
25
+ result = []
26
+ type = cursor.chain.base.infer(api_map, context_pin, locals)
27
+ if cursor.chain.constant? || cursor.start_of_constant?
28
+ result.concat api_map.get_constants(type.undefined? ? '' : type.namespace, cursor.start_of_constant? ? '' : context_pin.context.namespace)
29
+ else
30
+ result.concat api_map.get_complex_type_methods(type, context_pin.context.namespace, cursor.chain.links.length == 1)
31
+ if cursor.chain.links.length == 1
32
+ if cursor.word.start_with?('@@')
33
+ return package_completions(api_map.get_class_variable_pins(context_pin.context.namespace))
34
+ elsif cursor.word.start_with?('@')
35
+ return package_completions(api_map.get_instance_variable_pins(context_pin.context.namespace, context_pin.context.scope))
36
+ elsif cursor.word.start_with?('$')
37
+ return package_completions(api_map.get_global_variable_pins)
38
+ end
39
+ result.concat prefer_non_nil_variables(locals)
40
+ result.concat api_map.get_constants('', context_pin.context.namespace)
41
+ result.concat api_map.get_methods(context_pin.context.namespace, scope: context_pin.context.scope, visibility: [:public, :private, :protected])
42
+ result.concat api_map.get_methods('Kernel')
43
+ result.concat ApiMap.keywords
44
+ end
45
+ end
46
+ package_completions(result)
47
+ end
48
+
49
+ # @return [Array<Pin::Base>]
50
+ def signify
51
+ return [] unless cursor.argument?
52
+ clip = Clip.new(api_map, cursor.recipient)
53
+ clip.define.select{|pin| pin.kind == Pin::METHOD}
54
+ end
55
+
56
+ def infer
57
+ cursor.chain.infer(api_map, context_pin, locals)
58
+ end
59
+
60
+ # The context at the current position.
61
+ #
62
+ # @return [Pin::Base]
63
+ def context_pin
64
+ @context ||= source_map.locate_named_path_pin(cursor.node_position.line, cursor.node_position.character)
65
+ end
66
+
67
+ # Get an array of all the locals that are visible from the cursors's
68
+ # position. Locals can be local variables, method parameters, or block
69
+ # parameters. The array starts with the nearest local pin.
70
+ #
71
+ # @return [Array<Solargraph::Pin::Base>]
72
+ def locals
73
+ @locals ||= source_map.locals.select { |pin|
74
+ pin.visible_from?(block, Position.new(cursor.position.line, (cursor.position.column.zero? ? 0 : cursor.position.column - 1)))
75
+ }.reverse
76
+ end
77
+
78
+ private
79
+
80
+ # @return [ApiMap]
81
+ attr_reader :api_map
82
+
83
+ # @return [Source::Cursor]
84
+ attr_reader :cursor
85
+
86
+ # @return [SourceMap]
87
+ def source_map
88
+ @source_map ||= api_map.source_map(cursor.filename)
89
+ end
90
+
91
+ # @return [Solargraph::Pin::Base]
92
+ def block
93
+ @block ||= source_map.locate_block_pin(cursor.node_position.line, cursor.node_position.character)
94
+ end
95
+
96
+ # @param cursor [cursor]
97
+ # @param result [Array<Pin::Base>]
98
+ # @return [Completion]
99
+ def package_completions result
100
+ frag_start = cursor.start_of_word.to_s.downcase
101
+ filtered = result.uniq(&:name).select{|s| s.name.downcase.start_with?(frag_start) and (s.kind != Pin::METHOD or s.name.match(/^[a-z0-9_]+(\!|\?|=)?$/i))}
102
+ Completion.new(filtered, cursor.range)
103
+ end
104
+
105
+ # Sort an array of pins to put nil or undefined variables last.
106
+ #
107
+ # @param pins [Array<Pin::Base>]
108
+ # @return [Array<Pin::Base>]
109
+ def prefer_non_nil_variables pins
110
+ result = []
111
+ nil_pins = []
112
+ pins.each do |pin|
113
+ if pin.variable? and pin.nil_assignment?
114
+ nil_pins.push pin
115
+ else
116
+ result.push pin
117
+ end
118
+ end
119
+ result + nil_pins
120
+ end
121
+ end
122
+ end
123
+ end
@@ -1,5 +1,5 @@
1
1
  module Solargraph
2
- class Source
2
+ class SourceMap
3
3
  # The result of a completion request containing the pins that describe
4
4
  # completion options and the range to be replaced.
5
5
  #
@@ -7,11 +7,11 @@ module Solargraph
7
7
  # @return [Array<Solargraph::Pin::Base>]
8
8
  attr_reader :pins
9
9
 
10
- # @return [Solargraph::Source::Range]
10
+ # @return [Solargraph::Range]
11
11
  attr_reader :range
12
12
 
13
13
  # @param pins [Array<Solargraph::Pin::Base>]
14
- # @param range [Solargraph::Source::Range]
14
+ # @param range [Solargraph::Range]
15
15
  def initialize pins, range
16
16
  @pins = pins
17
17
  @range = range
@@ -1,13 +1,12 @@
1
1
  module Solargraph
2
- class Source
3
- # The Mapper generates pins and other data for Sources.
2
+ class SourceMap
3
+ # The Mapper generates pins and other data for SourceMaps.
4
4
  #
5
- # This class is used internally by the Source class on initialization,
6
- # e.g., via Source.new or Source.load. Users should not normally need to
7
- # call it directly.
5
+ # This class is used internally by the SourceMap class. Users should not
6
+ # normally need to call it directly.
8
7
  #
9
8
  class Mapper
10
- include NodeMethods
9
+ include Source::NodeMethods
11
10
 
12
11
  private_class_method :new
13
12
 
@@ -18,10 +17,11 @@ module Solargraph
18
17
  @filename = filename
19
18
  @code = code
20
19
  @node = node
20
+ @comments = comments
21
21
  @node_stack = []
22
22
  @directives = {}
23
23
  @comment_ranges = comments.map do |c|
24
- Source::Range.from_to(c.loc.expression.line, c.loc.expression.column, c.loc.expression.last_line, c.loc.expression.last_column)
24
+ Range.from_to(c.loc.expression.line, c.loc.expression.column, c.loc.expression.last_line, c.loc.expression.last_column)
25
25
  end
26
26
  @node_comments = associate_comments(node, comments)
27
27
  @pins = []
@@ -30,25 +30,29 @@ module Solargraph
30
30
  @locals = []
31
31
  @strings = []
32
32
 
33
- @used_comment_locs = []
34
-
35
33
  # HACK make sure the first node gets processed
36
34
  root = AST::Node.new(:source, [filename])
37
35
  root = root.append node
38
36
  # @todo Is the root namespace a class or a module? Assuming class for now.
39
37
  @pins.push Pin::Namespace.new(get_node_location(nil), '', '', nil, :class, :public, nil)
40
38
  process root
41
- @node_comments.reject{|k, v| @used_comment_locs.include?(k)}.each do |k, v|
42
- @pins.first.comments.concat v
43
- end
39
+ process_comment_directives
40
+ [@pins, @locals, @requires, @symbols]
41
+ end
44
42
 
45
- [@pins, @locals, @requires, @symbols, @strings, @comment_ranges]
43
+ def unmap filename, code
44
+ s = Position.new(0, 0)
45
+ e = Position.from_offset(code, code.length)
46
+ location = Location.new(filename, Range.new(s, e))
47
+ [[Pin::Namespace.new(location, '', '', '', :class, :public, nil)], [], [], []]
46
48
  end
47
49
 
48
50
  class << self
51
+ # @param source [Source]
49
52
  # @return [Array]
50
- def map filename, code, node, comments
51
- new.map filename, code, node, comments
53
+ def map source
54
+ return new.unmap(source.filename, source.code) unless source.parsed?
55
+ new.map source.filename, source.code, source.node, source.comments
52
56
  end
53
57
  end
54
58
 
@@ -59,8 +63,8 @@ module Solargraph
59
63
  # slightly more efficient than calculating offsets.
60
64
  b = node.location.expression.begin.begin_pos
61
65
  e = node.location.expression.end.end_pos
62
- # b = Source::Position.line_char_to_offset(@code, node.location.line - 1, node.location.column)
63
- # e = Source::Position.line_char_to_offset(@code, node.location.last_line - 1, node.location.last_column)
66
+ # b = Position.line_char_to_offset(@code, node.location.line - 1, node.location.column)
67
+ # e = Position.line_char_to_offset(@code, node.location.last_line - 1, node.location.last_column)
64
68
  frag = source_from_parser[b..e-1].to_s
65
69
  frag.strip.gsub(/,$/, '')
66
70
  end
@@ -78,10 +82,7 @@ module Solargraph
78
82
  # @param node [Parser::AST::Node]
79
83
  def process node, tree = [], visibility = :public, scope = :instance, fqn = '', stack = []
80
84
  return unless node.is_a?(AST::Node)
81
- if node.type == :str
82
- @strings.push Source::Range.from_to(node.loc.expression.line, node.loc.expression.column, node.loc.expression.last_line, node.loc.expression.last_column)
83
- return
84
- end
85
+ return if node.type == :str
85
86
  stack.push node
86
87
  if node.kind_of?(AST::Node)
87
88
  @node_stack.unshift node
@@ -89,6 +90,7 @@ module Solargraph
89
90
  visibility = :public
90
91
  if node.children[0].kind_of?(AST::Node) and node.children[0].children[0].kind_of?(AST::Node) and node.children[0].children[0].type == :cbase
91
92
  tree = pack_name(node.children[0])
93
+ tree.shift if tree.first.empty?
92
94
  else
93
95
  tree = tree + pack_name(node.children[0])
94
96
  end
@@ -104,22 +106,22 @@ module Solargraph
104
106
  if c.kind_of?(AST::Node)
105
107
  if c.type == :ivasgn
106
108
  here = get_node_start_position(c)
107
- context = get_named_path_pin(here)
109
+ named_path = get_named_path_pin(here)
108
110
  if c.children[1].nil?
109
111
  ora = find_parent(stack, :or_asgn)
110
112
  unless ora.nil?
111
113
  u = c.updated(:ivasgn, c.children + ora.children[1..-1], nil)
112
- pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), u.children[1], infer_literal_node_type(u.children[1]), context)
113
- if visibility == :module_function and context.kind == Pin::METHOD
114
- other = pins.select{|pin| pin.path == "#{context.namespace}.#{context.name}"}.first
115
- pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), u.children[1], infer_literal_node_type(u.children[1]), other) unless other.nil?
114
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), u.children[1], infer_literal_node_type(u.children[1]), named_path.context)
115
+ if visibility == :module_function and named_path.kind == Pin::METHOD
116
+ other = ComplexType.parse("Module<#{named_path.context.namespace}>")
117
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(u), fqn || '', c.children[0].to_s, comments_for(u), u.children[1], infer_literal_node_type(u.children[1]), other) #unless other.nil?
116
118
  end
117
119
  end
118
120
  else
119
- pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), context)
120
- if visibility == :module_function and context.kind == Pin::METHOD
121
- other = pins.select{|pin| pin.path == "#{context.namespace}.#{context.name}"}.first
122
- pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), other)
121
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), named_path.context)
122
+ if visibility == :module_function and named_path.kind == Pin::METHOD
123
+ other = ComplexType.parse("Module<#{named_path.context.namespace}>")
124
+ pins.push Solargraph::Pin::InstanceVariable.new(get_node_location(c), fqn || '',c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), other)
123
125
  end
124
126
  end
125
127
  elsif c.type == :cvasgn
@@ -138,18 +140,26 @@ module Solargraph
138
140
  here = get_node_start_position(c)
139
141
  context = get_named_path_pin(here)
140
142
  block = get_block_pin(here)
141
- presence = Source::Range.new(here, block.location.range.ending)
143
+ presence = Range.new(here, block.location.range.ending)
142
144
  if c.children[1].nil?
143
145
  ora = find_parent(stack, :or_asgn)
144
146
  unless ora.nil?
145
147
  u = c.updated(:lvasgn, c.children + ora.children[1..-1], nil)
146
- @locals.push Solargraph::Pin::LocalVariable.new(get_node_location(u), fqn, u.children[0].to_s, comments_for(ora), c.children[1], infer_literal_node_type(c.children[1]), context, block, presence)
148
+ @locals.push Solargraph::Pin::LocalVariable.new(get_node_location(u), fqn, u.children[0].to_s, comments_for(ora), u.children[1], infer_literal_node_type(c.children[1]), context, block, presence)
147
149
  end
148
150
  else
149
151
  @locals.push Solargraph::Pin::LocalVariable.new(get_node_location(c), fqn, c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), context, block, presence)
150
152
  end
151
153
  elsif c.type == :gvasgn
152
- pins.push Solargraph::Pin::GlobalVariable.new(get_node_location(c), fqn, c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), @pins.first)
154
+ if c.children[1].nil?
155
+ ora = find_parent(stack, :or_asgn)
156
+ unless ora.nil?
157
+ u = c.updated(:gvasgn, c.children + ora.children[1..-1], nil)
158
+ pins.push Solargraph::Pin::GlobalVariable.new(get_node_location(c), fqn, u.children[0].to_s, comments_for(c), u.children[1], infer_literal_node_type(c.children[1]), @pins.first)
159
+ end
160
+ else
161
+ pins.push Solargraph::Pin::GlobalVariable.new(get_node_location(c), fqn, c.children[0].to_s, comments_for(c), c.children[1], infer_literal_node_type(c.children[1]), @pins.first)
162
+ end
153
163
  elsif c.type == :sym
154
164
  @symbols.push Solargraph::Pin::Symbol.new(get_node_location(c), ":#{c.children[0]}")
155
165
  elsif c.type == :casgn
@@ -224,7 +234,7 @@ module Solargraph
224
234
  mm = Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.comments, :class, :public, ref.parameters)
225
235
  cm = Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.comments, :instance, :private, ref.parameters)
226
236
  pins.push mm, cm
227
- pins.select{|pin| pin.kind == Pin::INSTANCE_VARIABLE and pin.context == ref}.each do |ivar|
237
+ pins.select{|pin| pin.kind == Pin::INSTANCE_VARIABLE and pin.context == ref.context}.each do |ivar|
228
238
  pins.delete ivar
229
239
  pins.push Solargraph::Pin::InstanceVariable.new(ivar.location, ivar.namespace, ivar.name, ivar.comments, ivar.signature, ivar.instance_variable_get(:@literal), mm)
230
240
  pins.push Solargraph::Pin::InstanceVariable.new(ivar.location, ivar.namespace, ivar.name, ivar.comments, ivar.signature, ivar.instance_variable_get(:@literal), cm)
@@ -268,10 +278,10 @@ module Solargraph
268
278
  elsif c.type == :send and [:attr_reader, :attr_writer, :attr_accessor].include?(c.children[1])
269
279
  c.children[2..-1].each do |a|
270
280
  if c.children[1] == :attr_reader or c.children[1] == :attr_accessor
271
- pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}", comments_for(c), :reader, scope)
281
+ pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}", comments_for(c), :reader, scope, visibility)
272
282
  end
273
283
  if c.children[1] == :attr_writer or c.children[1] == :attr_accessor
274
- pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}=", comments_for(c), :writer, scope)
284
+ pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}=", comments_for(c), :writer, scope, visibility)
275
285
  end
276
286
  end
277
287
  elsif c.type == :sclass and c.children[0].type == :self
@@ -296,12 +306,14 @@ module Solargraph
296
306
  here = get_node_start_position(u)
297
307
  context = get_named_path_pin(here)
298
308
  block = get_block_pin(here)
299
- presence = Source::Range.new(here, block.location.range.ending)
309
+ presence = Range.new(here, block.location.range.ending)
300
310
  @locals.push Solargraph::Pin::MethodParameter.new(get_node_location(u), fqn, u.children[0].to_s, comments_for(c), resolve_node_signature(u.children[1]), infer_literal_node_type(u.children[1]), context, block, presence)
301
311
  end
302
312
  end
303
313
  elsif c.type == :block
304
- @pins.push Solargraph::Pin::Block.new(get_node_location(c), fqn || '', '', comments_for(c), c.children[0], scope)
314
+ here = get_node_start_position(c)
315
+ named_path = get_named_path_pin(here)
316
+ @pins.push Solargraph::Pin::Block.new(get_node_location(c), fqn || '', '', comments_for(c), c.children[0], named_path.context)
305
317
  end
306
318
  process c, tree, visibility, scope, fqn, stack
307
319
  end
@@ -329,16 +341,19 @@ module Solargraph
329
341
  @pins.select{|pin| [Pin::NAMESPACE, Pin::METHOD].include?(pin.kind) and pin.location.range.contain?(position)}.last
330
342
  end
331
343
 
344
+ def get_namespace_pin position
345
+ @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.location.range.contain?(position)}.last
346
+ end
347
+
332
348
  # @return [String]
333
349
  def comments_for node
334
350
  result = @node_comments[node.loc]
335
351
  return nil if result.nil?
336
- @used_comment_locs.push node.loc
337
352
  result
338
353
  end
339
354
 
340
355
  # @param node [Parser::AST::Node]
341
- # @return [Solargraph::Source::Location]
356
+ # @return [Solargraph::Location]
342
357
  def get_node_location(node)
343
358
  if node.nil?
344
359
  st = Position.new(0, 0)
@@ -382,7 +397,11 @@ module Solargraph
382
397
  # @param node [Parser::AST::Node]
383
398
  # @return [Solargraph::Pin::Namespace]
384
399
  def namespace_for(node)
385
- position = Source::Position.new(node.loc.line, node.loc.column)
400
+ position = Position.new(node.loc.line, node.loc.column)
401
+ namespace_at(position)
402
+ end
403
+
404
+ def namespace_at(position)
386
405
  @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.location.range.contain?(position)}.last
387
406
  end
388
407
 
@@ -425,6 +444,89 @@ module Solargraph
425
444
  def source_from_parser
426
445
  @source_from_parser ||= @code.gsub(/\r\n/, "\n")
427
446
  end
447
+
448
+ def process_comment position, comment
449
+ cmnt = remove_inline_comment_hashes(comment)
450
+ return unless cmnt =~ /(@\!method|@\!attribute|@\!domain|@\!macro)/
451
+ parse = YARD::Docstring.parser.parse(cmnt)
452
+ parse.directives.each { |d| process_directive(position, d) }
453
+ end
454
+
455
+ # @param position [Position]
456
+ # @param directive [YARD::Tags::Directive]
457
+ def process_directive position, directive
458
+ docstring = YARD::Docstring.parser.parse(directive.tag.text).to_docstring
459
+ location = Location.new(@filename, Range.new(position, position))
460
+ case directive.tag.tag_name
461
+ when 'method'
462
+ namespace = namespace_at(position)
463
+ gen_src = Solargraph::SourceMap.load_string("def #{directive.tag.name};end")
464
+ gen_pin = gen_src.pins.last # Method is last pin after root namespace
465
+ @pins.push Solargraph::Pin::Method.new(location, namespace.path, gen_pin.name, docstring.all, :instance, :public, gen_pin.parameters)
466
+ when 'attribute'
467
+ namespace = namespace_at(position)
468
+ t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('')
469
+ if t.nil? or t.include?('r')
470
+ # location, namespace, name, docstring, access
471
+ pins.push Solargraph::Pin::Attribute.new(location, namespace.path, directive.tag.name, docstring.all, :reader, :instance, :public)
472
+ end
473
+ if t.nil? or t.include?('w')
474
+ pins.push Solargraph::Pin::Attribute.new(location, namespace.path, "#{directive.tag.name}=", docstring.all, :writer, :instance, :public)
475
+ end
476
+ when 'domain'
477
+ namespace = namespace_at(position)
478
+ namespace.domains.push directive.tag.text
479
+ when 'macro'
480
+ nxt_pos = Position.new(position.line + 1, @code.lines[position.line + 1].length)
481
+ path_pin = get_named_path_pin(nxt_pos)
482
+ path_pin.macros.push directive
483
+ end
484
+ end
485
+
486
+ def remove_inline_comment_hashes comment
487
+ ctxt = ''
488
+ num = nil
489
+ started = false
490
+ comment.lines.each { |l|
491
+ # Trim the comment and minimum leading whitespace
492
+ p = l.gsub(/^#/, '')
493
+ if num.nil? and !p.strip.empty?
494
+ num = p.index(/[^ ]/)
495
+ started = true
496
+ elsif started and !p.strip.empty?
497
+ cur = p.index(/[^ ]/)
498
+ num = cur if cur < num
499
+ end
500
+ ctxt += "#{p[num..-1]}\n" if started
501
+ }
502
+ ctxt
503
+ end
504
+
505
+ def process_comment_directives
506
+ current = []
507
+ last_line = nil
508
+ @comments.each do |cmnt|
509
+ if cmnt.inline?
510
+ if last_line.nil? || cmnt.loc.expression.line == last_line + 1
511
+ if cmnt.loc.expression.column.zero? || @code.lines[cmnt.loc.expression.line][0..cmnt.loc.expression.column-1].strip.empty?
512
+ current.push cmnt
513
+ else
514
+ # @todo Connected to a line of code. Handle separately
515
+ end
516
+ elsif !current.empty?
517
+ process_comment Position.new(current.last.loc.expression.line, current.last.loc.expression.column), current.map(&:text).join("\n")
518
+ current.clear
519
+ current.push cmnt
520
+ end
521
+ else
522
+ # @todo Handle block comments
523
+ end
524
+ last_line = cmnt.loc.expression.line
525
+ end
526
+ unless current.empty?
527
+ process_comment Position.new(current.last.loc.expression.line, current.last.loc.expression.column), current.map(&:text).join("\n")
528
+ end
529
+ end
428
530
  end
429
531
  end
430
532
  end