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 => Solargraph::Parser::Snippet}

Returns:



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

def comments
  finalize
  @comments
end

#error_rangesArray<Range>

Returns:



187
188
189
# File 'lib/solargraph/source.rb', line 187

def error_ranges
  @error_ranges ||= []
end

#filenameString?

Returns:

  • (String, nil)


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:



494
495
496
497
498
499
# File 'lib/solargraph/source.rb', line 494

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:



505
506
507
# File 'lib/solargraph/source.rb', line 505

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)


511
512
513
514
515
516
517
518
519
# File 'lib/solargraph/source.rb', line 511

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, nil}

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

Returns:

  • (Hash{Integer => String, nil})


253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/solargraph/source.rb', line 253

def associated_comments
  @associated_comments ||= begin
    # @type [Hash{Integer => String}]
    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)


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

def code_for node
  rng = Range.from_node(node)
  # @sg-ignore Need to add nil check here
  b = Position.line_char_to_offset(code, rng.start.line, rng.start.column)
  # @sg-ignore Need to add nil check here
  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)


171
172
173
174
175
176
177
178
# File 'lib/solargraph/source.rb', line 171

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 (AST::Node)

Returns:

  • (String, nil)


206
207
208
209
210
211
212
213
214
# File 'lib/solargraph/source.rb', line 206

def comments_for node
  rng = Range.from_node(node)
  # @sg-ignore Need to add nil check here
  stringified_comments[rng.start.line] ||= begin
    # @sg-ignore Need to add nil check here
    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:



115
116
117
118
# File 'lib/solargraph/source.rb', line 115

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:



236
237
238
239
240
241
242
243
244
# File 'lib/solargraph/source.rb', line 236

def folding_ranges
  @folding_ranges ||= begin
    # @type [Array<Range>]
    result = []
    inner_folding_ranges node, result
    result.concat foldable_comment_block_ranges
    result
  end
end

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

@sg-ignore Need to add nil check here

Parameters:

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

Returns:

  • (String)


66
67
68
69
70
# File 'lib/solargraph/source.rb', line 66

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:



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

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:

  • (AST::Node)


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

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

#parsed?Boolean

Returns:

  • (Boolean)


121
122
123
124
# File 'lib/solargraph/source.rb', line 121

def parsed?
  finalize
  @parsed
end

#references(name) ⇒ Array<Location>

Parameters:

  • name (String)

Returns:



182
183
184
# File 'lib/solargraph/source.rb', line 182

def references name
  Parser.references self, name
end

#repaired?Boolean

Returns:

  • (Boolean)


126
127
128
# File 'lib/solargraph/source.rb', line 126

def repaired?
  code != @repaired
end

#string_at?(position) ⇒ Boolean

Parameters:

Returns:

  • (Boolean)


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
# File 'lib/solargraph/source.rb', line 132

def string_at? position
  return false if Position.to_offset(code, position) >= code.length
  string_nodes.each do |node|
    range = Range.from_node(node)
    # @sg-ignore Need to add nil check here
    next if range.ending.line < position.line
    # @sg-ignore Need to add nil check here
    break if range.ending.line > position.line
    # @sg-ignore Need to add nil check here
    return true if node.type == :str && range.include?(position) && range.start != position
    # @sg-ignore Need to add nil check here
    return true if %i[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)
      # @sg-ignore Need to add nil check here
      next unless range.include?(inner_range.ending)
      return true if inner.type == :str
      # @sg-ignore Need to add nil check here
      inner_code = at(Solargraph::Range.new(inner_range.start, position))
      # @sg-ignore Need to add nil check here
      return true if (inner.type == :dstr && inner_range.ending.character <= position.character && !inner_code.end_with?('}')) ||
                     # @sg-ignore Need to add nil check here
                     (inner.type != :dstr && inner_range.ending.line == position.line && position.character <= inner_range.ending.character && inner_code.end_with?('}'))
    end
    # @sg-ignore Need to add nil check here
    break if range.ending.line > position.line
  end
  false
end

#string_ranges::Array<Range>

Returns:



165
166
167
# File 'lib/solargraph/source.rb', line 165

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:



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

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)


246
247
248
# File 'lib/solargraph/source.rb', line 246

def synchronized?
  true
end

#tree_at(line, column) ⇒ Array<Parser::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:

  • (Array<Parser::AST::Node>)


87
88
89
90
91
92
# File 'lib/solargraph/source.rb', line 87

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