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



412
413
414
415
416
417
# File 'lib/solargraph/api_map/source.rb', line 412

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



376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/solargraph/api_map/source.rb', line 376

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



360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/solargraph/api_map/source.rb', line 360

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



349
350
351
352
# File 'lib/solargraph/api_map/source.rb', line 349

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

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



355
356
357
358
# File 'lib/solargraph/api_map/source.rb', line 355

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