Class: Solargraph::SourceMap::Mapper

Inherits:
Object
  • Object
show all
Includes:
Solargraph::Source::NodeMethods
Defined in:
lib/solargraph/source_map/mapper.rb

Overview

The Mapper generates pins and other data for SourceMaps.

This class is used internally by the SourceMap class. Users should not normally need to call it directly.

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Solargraph::Source::NodeMethods

const_from, drill_signature, get_node_end_position, get_node_start_position, infer_literal_node_type, pack_name, resolve_node_signature, unpack_name

Class Method Details

.map(source) ⇒ Array

Parameters:

Returns:

  • (Array)


51
52
53
54
# File 'lib/solargraph/source_map/mapper.rb', line 51

def map source
  return new.unmap(source.filename, source.code) unless source.parsed?
  new.map source.filename, source.code, source.node, source.comments
end

Instance Method Details

#associate_comments(node, comments) ⇒ Hash

Parameters:

  • node (Parser::AST::Node)
  • comments (Array)

Returns:

  • (Hash)


395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/solargraph/source_map/mapper.rb', line 395

def associate_comments node, comments
  return nil if comments.nil?
  comment_hash = Parser::Source::Comment.associate_locations(node, comments)
  result_hash = {}
  comment_hash.each_pair { |k, v|
    ctxt = ''
    num = nil
    started = false
    v.each { |l|
      # Trim the comment and minimum leading whitespace
      p = l.text.gsub(/^#/, '')
      if num.nil? and !p.strip.empty?
        num = p.index(/[^ ]/)
        started = true
      elsif started and !p.strip.empty?
        cur = p.index(/[^ ]/)
        num = cur if cur < num
      end
      ctxt += "#{p[num..-1]}\n" if started
    }
    result_hash[k] = ctxt
  }
  result_hash
end

#code_for(node) ⇒ String

Parameters:

  • node (Parser::AST::Node)

Returns:

  • (String)


59
60
61
62
63
64
65
66
67
68
# File 'lib/solargraph/source_map/mapper.rb', line 59

def code_for node
  # @todo Using node locations on code with converted EOLs seems
  #   slightly more efficient than calculating offsets.
  b = node.location.expression.begin.begin_pos
  e = node.location.expression.end.end_pos
  # b = Position.line_char_to_offset(@code, node.location.line - 1, node.location.column)
  # e = Position.line_char_to_offset(@code, node.location.last_line - 1, node.location.last_column)
  frag = source_from_parser[b..e-1].to_s
  frag.strip.gsub(/,$/, '')
end

#comments_for(node) ⇒ String

Returns:

  • (String)


372
373
374
375
376
# File 'lib/solargraph/source_map/mapper.rb', line 372

def comments_for node
  result = @node_comments[node.loc]
  return nil if result.nil?
  result
end

#filenameString

Returns:

  • (String)


71
72
73
# File 'lib/solargraph/source_map/mapper.rb', line 71

def filename
  @filename
end

#find_parent(stack, *types) ⇒ Object



431
432
433
434
435
436
# File 'lib/solargraph/source_map/mapper.rb', line 431

def find_parent(stack, *types)
  stack.reverse.each { |p|
    return p if types.include?(p.type)
  }
  nil
end

#get_block_pin(position) ⇒ Object



359
360
361
# File 'lib/solargraph/source_map/mapper.rb', line 359

def get_block_pin position
  @pins.select{|pin| [Pin::BLOCK, Pin::NAMESPACE, Pin::METHOD].include?(pin.kind) and pin.location.range.contain?(position)}.last
end

#get_last_in_stack_not_begin(stack) ⇒ Object



349
350
351
352
353
354
355
356
357
# File 'lib/solargraph/source_map/mapper.rb', line 349

def get_last_in_stack_not_begin stack
  index = stack.length - 1
  last = stack[index]
  while !last.nil? and last.type == :begin
    index -= 1
    last = stack[index]
  end
  last
end

#get_method_args(node) ⇒ Object



438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
# File 'lib/solargraph/source_map/mapper.rb', line 438

def get_method_args node
  return [] if node.nil?
  list = nil
  args = []
  node.children.each { |c|
    if c.kind_of?(AST::Node) and c.type == :args
      list = c
      break
    end
  }
  return args if list.nil?
  list.children.each { |c|
    if c.type == :arg
      args.push c.children[0].to_s
    elsif c.type == :restarg
      args.push "*#{c.children[0]}"
    elsif c.type == :optarg
      args.push "#{c.children[0]} = #{code_for(c.children[1])}"
    elsif c.type == :kwarg
      args.push "#{c.children[0]}:"
    elsif c.type == :kwoptarg
      args.push "#{c.children[0]}: #{code_for(c.children[1])}"
    elsif c.type == :blockarg
      args.push "&#{c.children[0]}"
    end
  }
  args
end

#get_named_path_pin(position) ⇒ Object



363
364
365
# File 'lib/solargraph/source_map/mapper.rb', line 363

def get_named_path_pin position
  @pins.select{|pin| [Pin::NAMESPACE, Pin::METHOD].include?(pin.kind) and pin.location.range.contain?(position)}.last
end

#get_namespace_pin(position) ⇒ Object



367
368
369
# File 'lib/solargraph/source_map/mapper.rb', line 367

def get_namespace_pin position
  @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.location.range.contain?(position)}.last
end

#get_node_location(node) ⇒ Solargraph::Location

Parameters:

  • node (Parser::AST::Node)

Returns:



380
381
382
383
384
385
386
387
388
389
390
# File 'lib/solargraph/source_map/mapper.rb', line 380

def get_node_location(node)
  if node.nil?
    st = Position.new(0, 0)
    en = Position.from_offset(@code, @code.length)
  else
    st = Position.new(node.loc.line, node.loc.column)
    en = Position.new(node.loc.last_line, node.loc.last_column)
  end
  range = Range.new(st, en)
  Location.new(filename, range)
end

#map(filename, code, node, comments) ⇒ Array

Generate the data.

Returns:

  • (Array)


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/solargraph/source_map/mapper.rb', line 16

def map filename, code, node, comments
  @filename = filename
  @code = code
  @node = node
  @comments = comments
  @node_stack = []
  @directives = {}
  @comment_ranges = comments.map do |c|
    Range.from_to(c.loc.expression.line, c.loc.expression.column, c.loc.expression.last_line, c.loc.expression.last_column)
  end
  @node_comments = associate_comments(node, comments)
  @pins = []
  @locals = []
  @strings = []

  # HACK make sure the first node gets processed
  root = AST::Node.new(:source, [filename])
  root = root.append node
  # @todo Is the root namespace a class or a module? Assuming class for now.
  @pins.push Pin::Namespace.new(get_node_location(nil), '', '', nil, :class, :public)
  process root
  process_comment_directives
  [@pins, @locals]
end

#namespace_at(position) ⇒ Object



427
428
429
# File 'lib/solargraph/source_map/mapper.rb', line 427

def namespace_at(position)
  @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.location.range.contain?(position)}.last
end

#namespace_for(node) ⇒ Solargraph::Pin::Namespace

Parameters:

  • node (Parser::AST::Node)

Returns:



422
423
424
425
# File 'lib/solargraph/source_map/mapper.rb', line 422

def namespace_for(node)
  position = Position.new(node.loc.line, node.loc.column)
  namespace_at(position)
end

#pinsArray<Solargraph::Pin::Base>

Returns:



76
77
78
# File 'lib/solargraph/source_map/mapper.rb', line 76

def pins
  @pins ||= []
end

#process(node, tree = [], visibility = :public, scope = :instance, fqn = '', stack = []) ⇒ Object

Parameters:

  • node (Parser::AST::Node)


81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
# File 'lib/solargraph/source_map/mapper.rb', line 81

def process node, tree = [], visibility = :public, scope = :instance, fqn = '', stack = []
  return unless node.is_a?(AST::Node)
  return if node.type == :str
  stack.push node
  if node.kind_of?(AST::Node)
    @node_stack.unshift node
    if node.type == :class or node.type == :module
      visibility = :public
      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
        tree = pack_name(node.children[0])
        tree.shift if tree.first.empty?
      else
        tree = tree + pack_name(node.children[0])
      end
      fqn = tree.join('::')
      sc = nil
      if node.type == :class and !node.children[1].nil?
        sc = unpack_name(node.children[1])
      end
      pins.push Solargraph::Pin::Namespace.new(get_node_location(node), tree[0..-2].join('::') || '', pack_name(node.children[0]).last.to_s, comments_for(node), node.type, visibility)
      pins.push Pin::Reference::Superclass.new(pins.last.location, pins.last.path, sc) unless sc.nil?
    end
    file = filename
    node.children.each do |c|
      if c.kind_of?(AST::Node)
        if c.type == :ivasgn
          here = get_node_start_position(c)
          named_path = get_named_path_pin(here)
          if c.children[1].nil?
            ora = find_parent(stack, :or_asgn)
            unless ora.nil?
              u = c.updated(:ivasgn, c.children + ora.children[1..-1], nil)
              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)
              if visibility == :module_function and named_path.kind == Pin::METHOD
                other = ComplexType.parse("Module<#{named_path.context.namespace}>")
                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?
              end
            end
          else
            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)
            if visibility == :module_function and named_path.kind == Pin::METHOD
              other = ComplexType.parse("Module<#{named_path.context.namespace}>")
               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)
            end
          end
        elsif c.type == :cvasgn
          here = get_node_start_position(c)
          context = get_named_path_pin(here)
          if c.children[1].nil?
            ora = find_parent(stack, :or_asgn)
            unless ora.nil?
              u = c.updated(:cvasgn, c.children + ora.children[1..-1], nil)
              pins.push Solargraph::Pin::ClassVariable.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.context)
            end
          else
            pins.push Solargraph::Pin::ClassVariable.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.context)
          end
        elsif c.type == :lvasgn
          here = get_node_start_position(c)
          context = get_named_path_pin(here)
          block = get_block_pin(here)
          presence = Range.new(here, block.location.range.ending)
          if c.children[1].nil?
            ora = find_parent(stack, :or_asgn)
            unless ora.nil?
              u = c.updated(:lvasgn, c.children + ora.children[1..-1], nil)
              @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.context, block, presence)
            end
          else
            @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.context, block, presence)
          end
        elsif c.type == :gvasgn
          if c.children[1].nil?
            ora = find_parent(stack, :or_asgn)
            unless ora.nil?
              u = c.updated(:gvasgn, c.children + ora.children[1..-1], nil)
              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.context)
            end
          else
            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.context)
          end
        elsif c.type == :sym
          pins.push Solargraph::Pin::Symbol.new(get_node_location(c), ":#{c.children[0]}")
        elsif c.type == :casgn
          here = get_node_start_position(c)
          block = get_block_pin(here)
          pins.push Solargraph::Pin::Constant.new(get_node_location(c), fqn, c.children[1].to_s, comments_for(c), c.children[2], infer_literal_node_type(c.children[2]), block.context, :public)
        elsif c.type == :def
          methpin = Solargraph::Pin::Method.new(get_node_location(c), fqn || '', c.children[(c.type == :def ? 0 : 1)].to_s, comments_for(c), scope, visibility, get_method_args(c))
          if methpin.name == 'initialize' and methpin.scope == :instance
            pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, 'new', methpin.comments, :class, :public, methpin.parameters)
            # @todo Smelly instance variable access.
            pins.last.instance_variable_set(:@return_complex_type, ComplexType.parse(methpin.namespace))
            pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, methpin.name, methpin.comments, methpin.scope, :private, methpin.parameters)
          elsif visibility == :module_function
            pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, methpin.name, methpin.comments, :class, :public, methpin.parameters)
            pins.push Solargraph::Pin::Method.new(methpin.location, methpin.namespace, methpin.name, methpin.comments, :instance, :private, methpin.parameters)
          else
            pins.push methpin
          end
        elsif c.type == :defs
          s_visi = visibility
          s_visi = :public if s_visi == :module_function or scope != :class
          if c.children[0].is_a?(AST::Node) and c.children[0].type == :self
            dfqn = fqn || ''
          else
            dfqn = unpack_name(c.children[0])
          end
          unless dfqn.nil?
            pins.push Solargraph::Pin::Method.new(get_node_location(c), dfqn, "#{c.children[(node.type == :def ? 0 : 1)]}", comments_for(c), :class, s_visi, get_method_args(c))
            process c, tree, scope, :class, dfqn, stack
          end
          next
        elsif c.type == :send and [:public, :protected, :private].include?(c.children[1])
          visibility = c.children[1]
        elsif c.type == :send and [:private_class_method].include?(c.children[1]) and c.children[2].kind_of?(AST::Node)
          if c.children[2].type == :sym or c.children[2].type == :str
            ref = pins.select{|p| p.namespace == (fqn || '') and p.name == c.children[2].children[0].to_s}.first
            unless ref.nil?
              pins.delete ref
              pins.push Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.comments, ref.scope, :private, ref.parameters)
            end
          else
            process c, tree, :private, :class, fqn, stack
            next
          end
        elsif c.type == :send and [:private_constant].include?(c.children[1]) and c.children[2].kind_of?(AST::Node)
          if c.children[2].type == :sym or c.children[2].type == :str
            # @todo What to do about references?
            cn = c.children[2].children[0].to_s
            ref = pins.select{|p| p.namespace == (fqn || '') and p.name == cn}.first
            unless ref.nil?
              pins.delete ref
              # Might be either a namespace or constant
              if ref.kind == Pin::CONSTANT
                pins.push ref.class.new(ref.location, ref.namespace, ref.name, ref.comments, ref.signature, ref.return_type, ref.context, :private)
              else
                # pins.push ref.class.new(ref.location, ref.namespace, ref.name, ref.comments, ref.type, :private, (ref.superclass_reference.nil? ? nil : ref.superclass_reference.name))
                pins.push ref.class.new(ref.location, ref.namespace, ref.name, ref.comments, ref.type, :private)
              end
            end
          end
          next
        elsif c.type == :send and c.children[1] == :module_function
          if c.children[2].nil?
            visibility = :module_function
          elsif c.children[2].type == :sym or c.children[2].type == :str
            # @todo What to do about references?
            c.children[2..-1].each do |x|
              cn = x.children[0].to_s
              ref = pins.select{|p| p.namespace == (fqn || '') and p.name == cn}.first
              unless ref.nil?
                pins.delete ref
                mm = Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.comments, :class, :public, ref.parameters)
                cm = Solargraph::Pin::Method.new(ref.location, ref.namespace, ref.name, ref.comments, :instance, :private, ref.parameters)
                pins.push mm, cm
                pins.select{|pin| pin.kind == Pin::INSTANCE_VARIABLE and pin.context == ref.context}.each do |ivar|
                  pins.delete ivar
                  pins.push Solargraph::Pin::InstanceVariable.new(ivar.location, ivar.namespace, ivar.name, ivar.comments, ivar.signature, ivar.instance_variable_get(:@literal), mm)
                  pins.push Solargraph::Pin::InstanceVariable.new(ivar.location, ivar.namespace, ivar.name, ivar.comments, ivar.signature, ivar.instance_variable_get(:@literal), cm)
                end
              end
            end
          elsif c.children[2].type == :def
            # @todo A single function
            process c, tree, :module_function, :class, fqn, stack
            next
          end
        elsif c.type == :send and c.children[1] == :include and c.children[0].nil?
          last_node = get_last_in_stack_not_begin(stack)
          if last_node.nil? or last_node.type == :class or last_node.type == :module or last_node.type == :source
            if c.children[2].kind_of?(AST::Node) and c.children[2].type == :const
              c.children[2..-1].each do |i|
                nspin = @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.path == fqn}.last
                unless nspin.nil?
                  # iref = Pin::Reference.new(get_node_location(c), nspin.path, unpack_name(i))
                  # nspin.include_references.push(iref)
                  pins.push Pin::Reference::Include.new(get_node_location(c), nspin.path, unpack_name(i))
                end
              end
            end
          end
        elsif c.type == :send and c.children[1] == :extend and c.children[0].nil?
          last_node = get_last_in_stack_not_begin(stack)
          if last_node.nil? or last_node.type == :class or last_node.type == :module or last_node.type == :source
            c.children[2..-1].each do |i|
              nspin = @pins.select{|pin| pin.kind == Pin::NAMESPACE and pin.path == fqn}.last
              unless nspin.nil?
                ref = nil
                if i.type == :self
                  # ref = Pin::Reference.new(get_node_location(c), nspin.path, nspin.path)
                  ref = Pin::Reference::Extend.new(get_node_location(c), nspin.path, nspin.path)
                elsif i.type == :const
                  # ref = Pin::Reference.new(get_node_location(c), nspin.path, unpack_name(i))
                  ref = Pin::Reference::Extend.new(get_node_location(c), nspin.path, unpack_name(i))
                end
                # nspin.extend_references.push(ref) unless ref.nil?
                pins.push ref unless ref.nil?
              end
            end
          end
        elsif c.type == :send && [:attr_reader, :attr_writer, :attr_accessor].include?(c.children[1])
          c.children[2..-1].each do |a|
            if c.children[1] == :attr_reader or c.children[1] == :attr_accessor
              pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}", comments_for(c), :reader, scope, visibility)
            end
            if c.children[1] == :attr_writer or c.children[1] == :attr_accessor
              pins.push Solargraph::Pin::Attribute.new(get_node_location(c), fqn || '', "#{a.children[0]}=", comments_for(c), :writer, scope, visibility)
            end
          end
        elsif c.type == :alias
          pin = pins.select{|p| p.name == c.children[1].children[0].to_s && p.namespace == fqn && p.scope == scope}.first
          unless pin.nil?
            if pin.is_a?(Solargraph::Pin::Method)
              pins.push Solargraph::Pin::Method.new(get_node_location(c), pin.namespace, c.children[0].children[0].to_s, comments_for(c) || pin.comments, pin.scope, pin.visibility, pin.parameters)
            elsif pin.is_a?(Solargraph::Pin::Attribute)
              pins.push Solargraph::Pin::Attribute.new(get_node_location(c), pin.namespace, c.children[0].children[0].to_s, comments_for(c) || pin.comments, pin.access, pin.scope, pin.visibility)
            end
          end
        elsif c.type == :send && c.children[1] == :alias_method && c.children[2] && c.children[2] && c.children[2].type == :sym && c.children[3] && c.children[3].type == :sym
          pin = pins.select{|p| p.name == c.children[3].children[0].to_s && p.namespace == fqn && p.scope == scope}.first
          unless pin.nil?
            if pin.is_a?(Solargraph::Pin::Method)
              pins.push Solargraph::Pin::Method.new(get_node_location(c), pin.namespace, c.children[2].children[0].to_s, comments_for(c) || pin.comments, pin.scope, pin.visibility, pin.parameters)
            elsif pin.is_a?(Solargraph::Pin::Attribute)
              pins.push Solargraph::Pin::Attribute.new(get_node_location(c), pin.namespace, c.children[2].children[0].to_s, comments_for(c) || pin.comments, pin.access, pin.scope, pin.visibility)
            end
          end
        elsif c.type == :sclass && c.children[0].type == :self
          process c, tree, :public, :class, fqn || '', stack
          next
        elsif c.type == :send && c.children[1] == :require
          if c.children[2].kind_of?(AST::Node) and c.children[2].type == :str
            # @requires.push Solargraph::Pin::Reference.new(get_node_location(c), fqn, c.children[2].children[0].to_s)
            pins.push Pin::Reference::Require.new(get_node_location(c), c.children[2].children[0].to_s)
          end
        elsif c.type == :args
          if @node_stack.first.type == :block
            pi = 0
            c.children.each do |u|
              here = get_node_start_position(c)
              blk = get_block_pin(here)
              @locals.push Solargraph::Pin::BlockParameter.new(get_node_location(u), fqn || '', "#{u.children[0]}", comments_for(c), blk)
              blk.parameters.push @locals.push.last
              pi += 1
            end
          else
            c.children.each do |u|
              here = get_node_start_position(u)
              context = get_named_path_pin(here)
              block = get_block_pin(here)
              presence = Range.new(here, block.location.range.ending)
              @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)
            end
          end
        elsif c.type == :block
          here = get_node_start_position(c)
          named_path = get_named_path_pin(here)
          @pins.push Solargraph::Pin::Block.new(get_node_location(c), fqn || '', '', comments_for(c), c.children[0], named_path.context)
        end
        process c, tree, visibility, scope, fqn, stack
      end
    end
    @node_stack.shift
  end
  stack.pop
end

#process_comment(position, comment) ⇒ Object



471
472
473
474
475
476
# File 'lib/solargraph/source_map/mapper.rb', line 471

def process_comment position, comment
  cmnt = remove_inline_comment_hashes(comment)
  return unless cmnt =~ /(@\!method|@\!attribute|@\!domain|@\!macro)/
  parse = YARD::Docstring.parser.parse(cmnt)
  parse.directives.each { |d| process_directive(position, d) }
end

#process_comment_directivesObject



528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
# File 'lib/solargraph/source_map/mapper.rb', line 528

def process_comment_directives
  current = []
  last_line = nil
  @comments.each do |cmnt|
    if cmnt.inline?
      if last_line.nil? || cmnt.loc.expression.line == last_line + 1
        if cmnt.loc.expression.column.zero? || @code.lines[cmnt.loc.expression.line][0..cmnt.loc.expression.column-1].strip.empty?
          current.push cmnt
        else
          # @todo Connected to a line of code. Handle separately
        end
      elsif !current.empty?
        process_comment Position.new(current.last.loc.expression.line, current.last.loc.expression.column), current.map(&:text).join("\n")
        current.clear
        current.push cmnt
      end
    else
      # @todo Handle block comments
    end
    last_line = cmnt.loc.expression.line
  end
  unless current.empty?
    process_comment Position.new(current.last.loc.expression.line, current.last.loc.expression.column), current.map(&:text).join("\n")
  end
end

#process_directive(position, directive) ⇒ Object

Parameters:

  • position (Position)
  • directive (YARD::Tags::Directive)


480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
# File 'lib/solargraph/source_map/mapper.rb', line 480

def process_directive position, directive
  docstring = YARD::Docstring.parser.parse(directive.tag.text).to_docstring
  location = Location.new(@filename, Range.new(position, position))
  case directive.tag.tag_name
  when 'method'
    namespace = namespace_at(position)
    gen_src = Solargraph::SourceMap.load_string("def #{directive.tag.name};end")
    gen_pin = gen_src.pins.last # Method is last pin after root namespace
    @pins.push Solargraph::Pin::Method.new(location, namespace.path, gen_pin.name, docstring.all, :instance, :public, gen_pin.parameters)
  when 'attribute'
    namespace = namespace_at(position)
    t = (directive.tag.types.nil? || directive.tag.types.empty?) ? nil : directive.tag.types.flatten.join('')
    if t.nil? or t.include?('r')
      # location, namespace, name, docstring, access
      pins.push Solargraph::Pin::Attribute.new(location, namespace.path, directive.tag.name, docstring.all, :reader, :instance, :public)
    end
    if t.nil? or t.include?('w')
      pins.push Solargraph::Pin::Attribute.new(location, namespace.path, "#{directive.tag.name}=", docstring.all, :writer, :instance, :public)
    end
  when 'domain'
    namespace = namespace_at(position)
    namespace.domains.push directive.tag.text
  when 'macro'
    nxt_pos = Position.new(position.line + 1, @code.lines[position.line + 1].length)
    path_pin = get_named_path_pin(nxt_pos)
    path_pin.macros.push directive
  end
end

#remove_inline_comment_hashes(comment) ⇒ Object



509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
# File 'lib/solargraph/source_map/mapper.rb', line 509

def remove_inline_comment_hashes comment
  ctxt = ''
  num = nil
  started = false
  comment.lines.each { |l|
    # Trim the comment and minimum leading whitespace
    p = l.gsub(/^#/, '')
    if num.nil? and !p.strip.empty?
      num = p.index(/[^ ]/)
      started = true
    elsif started and !p.strip.empty?
      cur = p.index(/[^ ]/)
      num = cur if cur < num
    end
    ctxt += "#{p[num..-1]}\n" if started
  }
  ctxt
end

#source_from_parserObject



467
468
469
# File 'lib/solargraph/source_map/mapper.rb', line 467

def source_from_parser
  @source_from_parser ||= @code.gsub(/\r\n/, "\n")
end

#unmap(filename, code) ⇒ Object



41
42
43
44
45
46
# File 'lib/solargraph/source_map/mapper.rb', line 41

def unmap filename, code
  s = Position.new(0, 0)
  e = Position.from_offset(code, code.length)
  location = Location.new(filename, Range.new(s, e))
  [[Pin::Namespace.new(location, '', '', '', :class, :public)], []]
end