Class: RubyLsp::Document

Inherits:
Object
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/ruby_lsp/document.rb

Defined Under Namespace

Classes: Scanner

Constant Summary collapse

PositionShape =
T.type_alias { { line: Integer, character: Integer } }
RangeShape =
T.type_alias { { start: PositionShape, end: PositionShape } }
EditShape =
T.type_alias { { range: RangeShape, text: String } }

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source:, version:, uri:, encoding: Constant::PositionEncodingKind::UTF8) ⇒ Document

Returns a new instance of Document.



25
26
27
28
29
30
31
32
33
# File 'lib/ruby_lsp/document.rb', line 25

def initialize(source:, version:, uri:, encoding: Constant::PositionEncodingKind::UTF8)
  @cache = T.let({}, T::Hash[String, T.untyped])
  @encoding = T.let(encoding, String)
  @source = T.let(source, String)
  @version = T.let(version, Integer)
  @uri = T.let(uri, URI::Generic)
  @needs_parsing = T.let(false, T::Boolean)
  @parse_result = T.let(YARP.parse(@source), YARP::ParseResult)
end

Instance Attribute Details

#parse_resultObject (readonly)

Returns the value of attribute parse_result.



13
14
15
# File 'lib/ruby_lsp/document.rb', line 13

def parse_result
  @parse_result
end

#sourceObject (readonly)

Returns the value of attribute source.



16
17
18
# File 'lib/ruby_lsp/document.rb', line 16

def source
  @source
end

#uriObject (readonly)

Returns the value of attribute uri.



22
23
24
# File 'lib/ruby_lsp/document.rb', line 22

def uri
  @uri
end

#versionObject (readonly)

Returns the value of attribute version.



19
20
21
# File 'lib/ruby_lsp/document.rb', line 19

def version
  @version
end

Instance Method Details

#==(other) ⇒ Object



46
47
48
# File 'lib/ruby_lsp/document.rb', line 46

def ==(other)
  @source == other.source
end

#cache_fetch(request_name, &block) ⇒ Object



58
59
60
61
62
63
64
65
# File 'lib/ruby_lsp/document.rb', line 58

def cache_fetch(request_name, &block)
  cached = @cache[request_name]
  return cached if cached

  result = block.call(self)
  @cache[request_name] = result
  result
end

#cache_get(request_name) ⇒ Object



73
74
75
# File 'lib/ruby_lsp/document.rb', line 73

def cache_get(request_name)
  @cache[request_name]
end

#cache_set(request_name, value) ⇒ Object



68
69
70
# File 'lib/ruby_lsp/document.rb', line 68

def cache_set(request_name, value)
  @cache[request_name] = value
end

#commentsObject



41
42
43
# File 'lib/ruby_lsp/document.rb', line 41

def comments
  @parse_result.comments
end

#create_scannerObject



108
109
110
# File 'lib/ruby_lsp/document.rb', line 108

def create_scanner
  Scanner.new(@source, @encoding)
end

#locate(node, char_position, node_types: []) ⇒ Object



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
# File 'lib/ruby_lsp/document.rb', line 129

def locate(node, char_position, node_types: [])
  queue = T.let(node.child_nodes.compact, T::Array[T.nilable(YARP::Node)])
  closest = node
  parent = T.let(nil, T.nilable(YARP::Node))
  nesting = T.let([], T::Array[T.any(YARP::ClassNode, YARP::ModuleNode)])

  until queue.empty?
    candidate = queue.shift

    # Skip nil child nodes
    next if candidate.nil?

    # Add the next child_nodes to the queue to be processed. The order here is important! We want to move in the
    # same order as the visiting mechanism, which means searching the child nodes before moving on to the next
    # sibling
    T.unsafe(queue).unshift(*candidate.child_nodes)

    # Skip if the current node doesn't cover the desired position
    loc = candidate.location
    next unless (loc.start_offset...loc.end_offset).cover?(char_position)

    # If the node's start character is already past the position, then we should've found the closest node
    # already
    break if char_position < loc.start_offset

    # If the candidate starts after the end of the previous nesting level, then we've exited that nesting level and
    # need to pop the stack
    previous_level = nesting.last
    nesting.pop if previous_level && loc.start_offset > previous_level.location.end_offset

    # Keep track of the nesting where we found the target. This is used to determine the fully qualified name of the
    # target when it is a constant
    if candidate.is_a?(YARP::ClassNode) || candidate.is_a?(YARP::ModuleNode)
      nesting << candidate
    end

    # If there are node types to filter by, and the current node is not one of those types, then skip it
    next if node_types.any? && node_types.none? { |type| candidate.class == type }

    # If the current node is narrower than or equal to the previous closest node, then it is more precise
    closest_loc = closest.location
    if loc.end_offset - loc.start_offset <= closest_loc.end_offset - closest_loc.start_offset
      parent = closest
      closest = candidate
    end
  end

  [closest, parent, nesting.map { |n| n.constant_path.location.slice }]
end

#locate_node(position, node_types: []) ⇒ Object



118
119
120
# File 'lib/ruby_lsp/document.rb', line 118

def locate_node(position, node_types: [])
  locate(@parse_result.value, create_scanner.find_char_position(position), node_types: node_types)
end

#parseObject



95
96
97
98
99
100
# File 'lib/ruby_lsp/document.rb', line 95

def parse
  return unless @needs_parsing

  @needs_parsing = false
  @parse_result = YARP.parse(@source)
end

#push_edits(edits, version:) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ruby_lsp/document.rb', line 78

def push_edits(edits, version:)
  edits.each do |edit|
    range = edit[:range]
    scanner = create_scanner

    start_position = scanner.find_char_position(range[:start])
    end_position = scanner.find_char_position(range[:end])

    @source[start_position...end_position] = edit[:text]
  end

  @version = version
  @needs_parsing = true
  @cache.clear
end

#syntax_error?Boolean

Returns:

  • (Boolean)


103
104
105
# File 'lib/ruby_lsp/document.rb', line 103

def syntax_error?
  @parse_result.failure?
end

#treeObject



36
37
38
# File 'lib/ruby_lsp/document.rb', line 36

def tree
  @parse_result.value
end