Class: Solargraph::Source

Inherits:
Object
  • Object
show all
Includes:
EncodingFixes
Defined in:
lib/solargraph/source.rb,
lib/solargraph/source/chain.rb,
lib/solargraph/source/change.rb,
lib/solargraph/source/cursor.rb,
lib/solargraph/source/updater.rb,
lib/solargraph/source/chain/if.rb,
lib/solargraph/source/chain/or.rb,
lib/solargraph/source/chain/call.rb,
lib/solargraph/source/chain/hash.rb,
lib/solargraph/source/chain/head.rb,
lib/solargraph/source/chain/link.rb,
lib/solargraph/source/chain/array.rb,
lib/solargraph/source/chain/q_call.rb,
lib/solargraph/source/chain/literal.rb,
lib/solargraph/source/chain/z_super.rb,
lib/solargraph/source/chain/constant.rb,
lib/solargraph/source/chain/variable.rb,
lib/solargraph/source/encoding_fixes.rb,
lib/solargraph/source/source_chainer.rb,
lib/solargraph/source/chain/block_symbol.rb,
lib/solargraph/source/chain/block_variable.rb,
lib/solargraph/source/chain/class_variable.rb,
lib/solargraph/source/chain/global_variable.rb,
lib/solargraph/source/chain/instance_variable.rb

Overview

A Ruby file that has been parsed into an AST.

Defined Under Namespace

Modules: EncodingFixes Classes: Chain, Change, Cursor, SourceChainer, Updater

Constant Summary collapse

FOLDING_NODE_TYPES =
%i[
  class sclass module def defs if str dstr array while unless kwbegin hash block
].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from EncodingFixes

normalize

Constructor Details

#initialize(code, filename = nil, version = 0) ⇒ Source

Returns a new instance of Source.

Parameters:

  • code (String)
  • filename (String, nil) (defaults to: nil)
  • version (Integer) (defaults to: 0)


46
47
48
49
50
51
# File 'lib/solargraph/source.rb', line 46

def initialize code, filename = nil, version = 0
  @code = normalize(code)
  @repaired = code
  @filename = filename
  @version = version
end

Instance Attribute Details

#commentsHash{Integer => Array<String>}

Returns:

  • (Hash{Integer => Array<String>})


34
35
36
37
# File 'lib/solargraph/source.rb', line 34

def comments
  finalize
  @comments
end

#error_rangesArray<Range>

Returns:



176
177
178
# File 'lib/solargraph/source.rb', line 176

def error_ranges
  @error_ranges ||= []
end

#filenameString

Returns:

  • (String)


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

def filename
  @filename
end

#nodeParser::AST::Node?

Returns:

  • (Parser::AST::Node, nil)


28
29
30
31
# File 'lib/solargraph/source.rb', line 28

def node
  finalize
  @node
end

#versionInteger

TODO:

Deprecate?

Returns:

  • (Integer)


41
42
43
# File 'lib/solargraph/source.rb', line 41

def version
  @version
end

Class Method Details

.load(filename) ⇒ Solargraph::Source

Parameters:

  • filename (String)

Returns:



467
468
469
470
471
472
# File 'lib/solargraph/source.rb', line 467

def load filename
  file = File.open(filename)
  code = file.read
  file.close
  Source.load_string(code, filename)
end

.load_string(code, filename = nil, version = 0) ⇒ Solargraph::Source

Parameters:

  • code (String)
  • filename (String, nil) (defaults to: nil)
  • version (Integer) (defaults to: 0)

Returns:



478
479
480
# File 'lib/solargraph/source.rb', line 478

def load_string code, filename = nil, version = 0
  Source.new code, filename, version
end

.parse_docstring(comments) ⇒ YARD::DocstringParser

Parameters:

  • comments (String)

Returns:

  • (YARD::DocstringParser)


484
485
486
487
488
489
490
491
492
# File 'lib/solargraph/source.rb', line 484

def parse_docstring comments
  # HACK: Pass a dummy code object to the parser for plugins that
  # expect it not to be nil
  YARD::Docstring.parser.parse(comments, YARD::CodeObjects::Base.new(:root, 'stub'))
rescue StandardError => e
  Solargraph.logger.info "YARD failed to parse docstring: [#{e.class}] #{e.message}"
  Solargraph.logger.debug "Unparsed comment: #{comments}"
  YARD::Docstring.parser
end

Instance Method Details

#associated_commentsHash{Integer => String}

Get a hash of comments grouped by the line numbers of the associated code.

Returns:

  • (Hash{Integer => String})


236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'lib/solargraph/source.rb', line 236

def associated_comments
  @associated_comments ||= begin
    result = {}
    buffer = String.new('')
    # @type [Integer, nil]
    last = nil
    comments.each_pair do |num, snip|
      if !last || num == last + 1
        buffer.concat "#{snip.text}\n"
      else
        result[first_not_empty_from(last + 1)] = buffer.clone
        buffer.replace "#{snip.text}\n"
      end
      last = num
    end
    result[first_not_empty_from(last + 1)] = buffer unless buffer.empty? || last.nil?
    result
  end
end

#at(range) ⇒ String

Parameters:

Returns:

  • (String)


55
56
57
# File 'lib/solargraph/source.rb', line 55

def at range
  from_to range.start.line, range.start.character, range.ending.line, range.ending.character
end

#codeString

Returns:

  • (String)


22
23
24
25
# File 'lib/solargraph/source.rb', line 22

def code
  finalize
  @code
end

#code_for(node) ⇒ String

Parameters:

  • node (Parser::AST::Node)

Returns:

  • (String)


182
183
184
185
186
187
188
# File 'lib/solargraph/source.rb', line 182

def code_for(node)
  rng = Range.from_node(node)
  b = Position.line_char_to_offset(code, rng.start.line, rng.start.column)
  e = Position.line_char_to_offset(code, rng.ending.line, rng.ending.column)
  frag = code[b..e-1].to_s
  frag.strip.gsub(/,$/, '')
end

#comment_at?(position) ⇒ Boolean

Parameters:

Returns:

  • (Boolean)


160
161
162
163
164
165
166
167
# File 'lib/solargraph/source.rb', line 160

def comment_at? position
  comment_ranges.each do |range|
    return true if range.include?(position) ||
      (range.ending.line == position.line && range.ending.column < position.column)
    break if range.ending.line > position.line
  end
  false
end

#comments_for(node) ⇒ String?

Parameters:

  • node (Parser::AST::Node)

Returns:

  • (String, nil)


192
193
194
195
196
197
198
# File 'lib/solargraph/source.rb', line 192

def comments_for node
  rng = Range.from_node(node)
  stringified_comments[rng.start.line] ||= begin
    buff = associated_comments[rng.start.line]
    buff ? stringify_comment_array(buff) : nil
  end
end

#cursor_at(position) ⇒ Source::Cursor

Parameters:

  • position (Position, Array(Integer, Integer))

Returns:



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

def cursor_at position
  finalize
  Cursor.new(self, position)
end

#folding_rangesArray<Range>

Get an array of ranges that can be folded, e.g., the range of a class definition or an if condition.

See FOLDING_NODE_TYPES for the list of node types that can be folded.

Returns:



220
221
222
223
224
225
226
227
# File 'lib/solargraph/source.rb', line 220

def folding_ranges
  @folding_ranges ||= begin
    result = []
    inner_folding_ranges node, result
    result.concat foldable_comment_block_ranges
    result
  end
end

#from_to(l1, c1, l2, c2) ⇒ String

Parameters:

  • l1 (Integer)
  • c1 (Integer)
  • l2 (Integer)
  • c2 (Integer)

Returns:

  • (String)


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

def from_to l1, c1, l2, c2
  b = Solargraph::Position.line_char_to_offset(code, l1, c1)
  e = Solargraph::Position.line_char_to_offset(code, l2, c2)
  code[b..e-1]
end

#locationLocation

A location representing the file in its entirety.

Returns:



203
204
205
206
207
208
# File 'lib/solargraph/source.rb', line 203

def location
  st = Position.new(0, 0)
  en = Position.from_offset(code, code.length)
  range = Range.new(st, en)
  Location.new(filename, range)
end

#node_at(line, column) ⇒ AST::Node

Get the nearest node that contains the specified index.

Parameters:

  • line (Integer)
  • column (Integer)

Returns:



75
76
77
# File 'lib/solargraph/source.rb', line 75

def node_at(line, column)
  tree_at(line, column).first
end

#parsed?Boolean

Returns:

  • (Boolean)


119
120
121
122
# File 'lib/solargraph/source.rb', line 119

def parsed?
  finalize
  @parsed
end

#references(name) ⇒ Array<Location>

Parameters:

  • name (String)

Returns:



171
172
173
# File 'lib/solargraph/source.rb', line 171

def references name
  Parser.references self, name
end

#repaired?Boolean

Returns:

  • (Boolean)


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

def repaired?
  code != @repaired
end

#string_at?(position) ⇒ Boolean

Parameters:

Returns:

  • (Boolean)


130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/solargraph/source.rb', line 130

def string_at? position
  return false if Position.to_offset(code, position) >= code.length
  string_nodes.each do |node|
    range = Range.from_node(node)
    next if range.ending.line < position.line
    break if range.ending.line > position.line
    return true if node.type == :str && range.include?(position) && range.start != position
    return true if [:STR, :str].include?(node.type) && range.include?(position) && range.start != position
    if node.type == :dstr
      inner = node_at(position.line, position.column)
      next if inner.nil?
      inner_range = Range.from_node(inner)
      next unless range.include?(inner_range.ending)
      return true if inner.type == :str
      inner_code = at(Solargraph::Range.new(inner_range.start, position))
      return true if (inner.type == :dstr && inner_range.ending.character <= position.character) && !inner_code.end_with?('}') ||
                     (inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}'))
    end
    break if range.ending.line > position.line
  end
  false
end

#string_ranges::Array<Range>

Returns:



154
155
156
# File 'lib/solargraph/source.rb', line 154

def string_ranges
  @string_ranges ||= Parser.string_ranges(node)
end

#synchronize(updater) ⇒ Source

Synchronize the Source with an update. This method applies changes to the code, parses the new code’s AST, and returns the resulting Source object.

Parameters:

Returns:



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/solargraph/source.rb', line 97

def synchronize updater
  raise 'Invalid synchronization' unless updater.filename == filename
  real_code = updater.write(@code)
  if real_code == @code
    @version = updater.version
    return self
  end
  Source.new(@code, filename, updater.version).tap do |src|
    src.repaired = @repaired
    src.error_ranges.concat error_ranges
    src.changes.concat(changes + updater.changes)
  end
end

#synchronized?Boolean

Returns:

  • (Boolean)


229
230
231
# File 'lib/solargraph/source.rb', line 229

def synchronized?
  true
end

#tree_at(line, column) ⇒ Array<AST::Node>

Get an array of nodes containing the specified index, starting with the nearest node and ending with the root.

Parameters:

  • line (Integer)
  • column (Integer)

Returns:



85
86
87
88
89
90
# File 'lib/solargraph/source.rb', line 85

def tree_at(line, column)
  position = Position.new(line, column)
  stack = []
  inner_tree_at node, position, stack
  stack
end