solargraph 0.26.1 → 0.27.0

Sign up to get free protection for your applications and to get access to all the features.
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