Class: Solargraph::ApiMap::Source

Inherits:
Object
  • Object
show all
Includes:
NodeMethods
Defined in:
lib/solargraph/api_map/source.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from NodeMethods

#const_from, #infer_literal_node_type, #pack_name, #resolve_node_signature, #unpack_name

Constructor Details

#initialize(code, node, comments, filename, stubbed_lines = []) ⇒ Source

Returns a new instance of Source.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/solargraph/api_map/source.rb', line 23

def initialize code, node, comments, filename, stubbed_lines = []
  @code = code
  root = AST::Node.new(:source, [filename])
  root = root.append node
  @node = root
  @comments = comments
  @directives = {}
  @docstring_hash = associate_comments(node, comments)
  @filename = filename
  @namespace_nodes = {}
  @all_nodes = []
  @node_stack = []
  @node_tree = {}
  @stubbed_lines = stubbed_lines
  inner_map_node @node
  @directives.each_pair do |k, v|
    v.each do |d|
      ns = namespace_for(k.node)
      docstring = YARD::Docstring.parser.parse(d.tag.text).to_docstring
      if d.tag.tag_name == 'attribute'
        t = (d.tag.types.nil? || d.tag.types.empty?) ? nil : d.tag.types.flatten.join('')
        if t.nil? or t.include?('r')
          attribute_pins.push Solargraph::Pin::Directed::Attribute.new(self, k.node, ns, :reader, docstring, d.tag.name)
        end
        if t.nil? or t.include?('w')
          attribute_pins.push Solargraph::Pin::Directed::Attribute.new(self, k.node, ns, :writer, docstring, "#{d.tag.name}=")
        end
      elsif d.tag.tag_name == 'method'
        gen_src = Source.virtual("def #{d.tag.name};end", filename)
        gen_pin = gen_src.method_pins.first
        method_pins.push Solargraph::Pin::Directed::Method.new(gen_src, gen_pin.node, ns, :instance, :public, docstring, gen_pin.name)
      else
        STDERR.puts "Nothing to do for directive: #{d}"
      end
    end
  end
end

Instance Attribute Details

#code ⇒ String (readonly)

Returns:

  • (String)


7
8
9
# File 'lib/solargraph/api_map/source.rb', line 7

def code
  @code
end

#comments ⇒ Array (readonly)

Returns:

  • (Array)


13
14
15
# File 'lib/solargraph/api_map/source.rb', line 13

def comments
  @comments
end

#filename ⇒ String (readonly)

Returns:

  • (String)


16
17
18
# File 'lib/solargraph/api_map/source.rb', line 16

def filename
  @filename
end

#node ⇒ Parser::AST::Node (readonly)

Returns:

  • (Parser::AST::Node)


10
11
12
# File 'lib/solargraph/api_map/source.rb', line 10

def node
  @node
end

#stubbed_lines ⇒ Array<Integer> (readonly)

Returns:

  • (Array<Integer>)


19
20
21
# File 'lib/solargraph/api_map/source.rb', line 19

def stubbed_lines
  @stubbed_lines
end

Class Method Details

.beginning_of_line_from(str, i) ⇒ Object



383
384
385
386
387
388
# File 'lib/solargraph/api_map/source.rb', line 383

def beginning_of_line_from str, i
  while i > 0 and str[i-1] != "\n"
    i -= 1
  end
  i
end

.fix(code, filename = nil, offset = nil) ⇒ Object



347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
# File 'lib/solargraph/api_map/source.rb', line 347

def fix code, filename = nil, offset = nil
  tries = 0
  code.gsub!(/\r/, '')
  offset = CodeMap.get_offset(code, offset[0], offset[1]) if offset.kind_of?(Array)
  pos = nil
  pos = get_position_at(code, offset) unless offset.nil?
  stubs = []
  fixed_position = false
  tmp = code
  begin
    node, comments = Parser::CurrentRuby.parse_with_comments(tmp)
    Source.new(code, node, comments, filename, stubs)
  rescue Parser::SyntaxError => e
    if tries < 10
      tries += 1
      # Stub periods before the offset to retain the expected node tree
      if !offset.nil? and tmp[offset-1] == '.'
        tmp = tmp[0, offset-1] + '_' + tmp[offset..-1]
      elsif !fixed_position and !offset.nil?
        fixed_position = true
        beg = beginning_of_line_from(tmp, offset)
        tmp = tmp[0, beg] + '#' + tmp[beg+1..-1]
        stubs.push(pos[0])
      elsif e.message.include?('token $end')
        tmp += "\nend"
      elsif e.message.include?("unexpected `@'")
        tmp = tmp[0, e.diagnostic.location.begin_pos] + '_' + tmp[e.diagnostic.location.begin_pos+1..-1]
      end
      retry
    end
    STDERR.puts "Unable to parse code: #{e.message}"
    virt = Source.virtual('', filename)
    Source.new(code, virt.node, virt.comments, filename)
  end
end

.get_position_at(code, offset) ⇒ Object



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/solargraph/api_map/source.rb', line 331

def get_position_at(code, offset)
  cursor = 0
  line = 0
  col = nil
  code.each_line do |l|
    if cursor + l.length >= offset
      col = offset - cursor
      break
    end
    cursor += l.length
    line += 1
  end
  raise "Invalid offset" if col.nil?
  [line, col]
end

.load(filename) ⇒ Solargraph::ApiMap::Source



320
321
322
323
# File 'lib/solargraph/api_map/source.rb', line 320

def load filename
  code = File.read(filename).gsub(/\r/, '')
  Source.virtual(code, filename)
end

.virtual(code, filename = nil) ⇒ Solargraph::ApiMap::Source



326
327
328
329
# File 'lib/solargraph/api_map/source.rb', line 326

def virtual code, filename = nil
  node, comments = Parser::CurrentRuby.parse_with_comments(code)
  Source.new(code, node, comments, filename)
end

Instance Method Details

#attribute_pins ⇒ Array<Solargraph::Pin::Attribute>

Returns:



83
84
85
# File 'lib/solargraph/api_map/source.rb', line 83

def attribute_pins
  @attribute_pins ||= []
end

#class_variable_pins ⇒ Array<Solargraph::Pin::ClassVariable>

Returns:



93
94
95
# File 'lib/solargraph/api_map/source.rb', line 93

def class_variable_pins
  @class_variable_pins ||= []
end

#code_for(node) ⇒ String

Returns:

  • (String)


134
135
136
137
138
139
# File 'lib/solargraph/api_map/source.rb', line 134

def code_for node
  b = node.location.expression.begin.begin_pos
  e = node.location.expression.end.end_pos
  frag = code[b..e-1].to_s
  frag.strip.gsub(/,$/, '')
end

#constant_pins ⇒ Array<Solargraph::Pin::Constant>

Returns:



108
109
110
# File 'lib/solargraph/api_map/source.rb', line 108

def constant_pins
  @constant_pins ||= []
end

#docstring_for(node) ⇒ YARD::Docstring

Returns:

  • (YARD::Docstring)


128
129
130
131
# File 'lib/solargraph/api_map/source.rb', line 128

def docstring_for node
  return @docstring_hash[node.loc] if node.respond_to?(:loc)
  nil
end

#global_variable_pins ⇒ Array<Solargraph::Pin::GlobalVariable>



103
104
105
# File 'lib/solargraph/api_map/source.rb', line 103

def global_variable_pins
  @global_variable_pins ||= []
end

#include?(node) ⇒ Boolean

Returns:

  • (Boolean)


156
157
158
# File 'lib/solargraph/api_map/source.rb', line 156

def include? node
  @all_nodes.include? node
end

#instance_variable_pins ⇒ Array<Solargraph::Pin::InstanceVariable>



88
89
90
# File 'lib/solargraph/api_map/source.rb', line 88

def instance_variable_pins
  @instance_variable_pins ||= []
end

#local_variable_pins ⇒ Array<Solargraph::Pin::LocalVariable>

Returns:



98
99
100
# File 'lib/solargraph/api_map/source.rb', line 98

def local_variable_pins
  @local_variable_pins ||= []
end

#method_pins ⇒ Array<Solargraph::Pin::Method>

Returns:



78
79
80
# File 'lib/solargraph/api_map/source.rb', line 78

def method_pins
  @method_pins ||= []
end

#namespace_for(node) ⇒ String

Returns:

  • (String)


146
147
148
149
150
151
152
153
154
# File 'lib/solargraph/api_map/source.rb', line 146

def namespace_for node
  parts = []
  ([node] + (@node_tree[node] || [])).each do |n|
    if n.type == :class or n.type == :module
      parts.unshift unpack_name(n.children[0])
    end
  end
  parts.join('::')
end

#namespace_includes ⇒ Object



69
70
71
# File 'lib/solargraph/api_map/source.rb', line 69

def namespace_includes
  @namespace_includes ||= {}
end

#namespace_nodes ⇒ Object



65
66
67
# File 'lib/solargraph/api_map/source.rb', line 65

def namespace_nodes
  @namespace_nodes
end

#namespace_pins ⇒ Array<Solargraph::Pin::Namespace>

Returns:



118
119
120
# File 'lib/solargraph/api_map/source.rb', line 118

def namespace_pins
  @namespace_pins ||= []
end

#namespaces ⇒ Object



61
62
63
# File 'lib/solargraph/api_map/source.rb', line 61

def namespaces
  @namespace_nodes.keys
end

#required ⇒ Array<String>

Returns:

  • (Array<String>)


123
124
125
# File 'lib/solargraph/api_map/source.rb', line 123

def required
  @required ||= []
end

#superclasses ⇒ Object



73
74
75
# File 'lib/solargraph/api_map/source.rb', line 73

def superclasses
  @superclasses ||= {}
end

#symbol_pins ⇒ Array<Solargraph::Pin::Symbol>

Returns:



113
114
115
# File 'lib/solargraph/api_map/source.rb', line 113

def symbol_pins
  @symbol_pins ||= []
end

#tree_for(node) ⇒ Object



141
142
143
# File 'lib/solargraph/api_map/source.rb', line 141

def tree_for node
  @node_tree[node] || []
end