Class: Pikuri::Lsp::Range

Inherits:
Data
  • Object
show all
Defined in:
lib/pikuri/lsp/range.rb

Overview

A span between two Positions, in pikuri's 1-based coordinates. Servers answer with ranges everywhere — a definition's whole body, an identifier's own extent, the slice of a class file worth showing — so this is mostly a parse target:

range = Range.from_wire({ 'start' => { 'line' => 12, 'character' => 6 },
                        'end'   => { 'line' => 12, 'character' => 22 } },
                      text: File.read(path), encoding: PositionEncoding::UTF8)
range.to_s         # => "13:7-13:23"
range.start.line   # => 13

There is deliberately no #to_wire: pikuri sends positions (an anchor for a query), never ranges. The one client-to-server range in LSP is a didChange edit, and pikuri re-sends didOpen instead.

Note the shadowing: inside Pikuri::Lsp, Range is this class, so Ruby's own needs ::Range.

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#endPosition (readonly)

Returns one position past the span, LSP's half-open convention, so a zero-width range has start == end.

Returns:

  • (Position)

    one position past the span, LSP's half-open convention, so a zero-width range has start == end.



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
60
61
62
63
# File 'lib/pikuri/lsp/range.rb', line 28

Range = Data.define(:start, :end) do
  # Parse LSP's +Range+ object.
  #
  # @param hash [Hash{String => Hash}] +{"start" =>, "end" =>}+.
  # @param text [String, nil] the *whole* document the range points into,
  #   which is what makes both columns exact; +nil+ when the text is not to
  #   hand (see {PositionEncoding.column_for}).
  # @param encoding [String] the server's negotiated +positionEncoding+.
  # @return [Range]
  # @raise [KeyError] if +start+ or +end+ is missing.
  def self.from_wire(hash, text: nil, encoding: PositionEncoding::DEFAULT)
    lines = text&.lines
    wire_start = hash.fetch('start')
    wire_end = hash.fetch('end')
    new(start: Position.from_wire(wire_start, line_text: line_at(lines, wire_start), encoding: encoding),
        end: Position.from_wire(wire_end, line_text: line_at(lines, wire_end), encoding: encoding))
  end

  # The text of the line a wire position sits on, or +nil+ when the
  # document text was not supplied.
  def self.line_at(lines, wire_position)
    lines && lines[Integer(wire_position.fetch('line'))]
  end
  private_class_method :line_at

  # @return [Boolean] whether the span begins and ends on one line, i.e.
  #   whether a one-line snippet can show all of it.
  def single_line?
    start.line == self.end.line
  end

  # @return [String] +"13:7-13:23"+.
  def to_s
    "#{start}-#{self.end}"
  end
end

#startPosition (readonly)

Returns first position inside the span.

Returns:

  • (Position)

    first position inside the span.



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
60
61
62
63
# File 'lib/pikuri/lsp/range.rb', line 28

Range = Data.define(:start, :end) do
  # Parse LSP's +Range+ object.
  #
  # @param hash [Hash{String => Hash}] +{"start" =>, "end" =>}+.
  # @param text [String, nil] the *whole* document the range points into,
  #   which is what makes both columns exact; +nil+ when the text is not to
  #   hand (see {PositionEncoding.column_for}).
  # @param encoding [String] the server's negotiated +positionEncoding+.
  # @return [Range]
  # @raise [KeyError] if +start+ or +end+ is missing.
  def self.from_wire(hash, text: nil, encoding: PositionEncoding::DEFAULT)
    lines = text&.lines
    wire_start = hash.fetch('start')
    wire_end = hash.fetch('end')
    new(start: Position.from_wire(wire_start, line_text: line_at(lines, wire_start), encoding: encoding),
        end: Position.from_wire(wire_end, line_text: line_at(lines, wire_end), encoding: encoding))
  end

  # The text of the line a wire position sits on, or +nil+ when the
  # document text was not supplied.
  def self.line_at(lines, wire_position)
    lines && lines[Integer(wire_position.fetch('line'))]
  end
  private_class_method :line_at

  # @return [Boolean] whether the span begins and ends on one line, i.e.
  #   whether a one-line snippet can show all of it.
  def single_line?
    start.line == self.end.line
  end

  # @return [String] +"13:7-13:23"+.
  def to_s
    "#{start}-#{self.end}"
  end
end

Class Method Details

.from_wire(hash, text: nil, encoding: PositionEncoding::DEFAULT) ⇒ Range

Parse LSP's Range object.

Parameters:

  • hash (Hash{String => Hash})

    {"start" =>, "end" =>}.

  • text (String, nil) (defaults to: nil)

    the whole document the range points into, which is what makes both columns exact; nil when the text is not to hand (see PositionEncoding.column_for).

  • encoding (String) (defaults to: PositionEncoding::DEFAULT)

    the server's negotiated positionEncoding.

Returns:

Raises:

  • (KeyError)

    if start or end is missing.



38
39
40
41
42
43
44
# File 'lib/pikuri/lsp/range.rb', line 38

def self.from_wire(hash, text: nil, encoding: PositionEncoding::DEFAULT)
  lines = text&.lines
  wire_start = hash.fetch('start')
  wire_end = hash.fetch('end')
  new(start: Position.from_wire(wire_start, line_text: line_at(lines, wire_start), encoding: encoding),
      end: Position.from_wire(wire_end, line_text: line_at(lines, wire_end), encoding: encoding))
end

Instance Method Details

#single_line?Boolean

Returns whether the span begins and ends on one line, i.e. whether a one-line snippet can show all of it.

Returns:

  • (Boolean)

    whether the span begins and ends on one line, i.e. whether a one-line snippet can show all of it.



55
56
57
# File 'lib/pikuri/lsp/range.rb', line 55

def single_line?
  start.line == self.end.line
end

#to_sString

Returns "13:7-13:23".

Returns:

  • (String)

    "13:7-13:23".



60
61
62
# File 'lib/pikuri/lsp/range.rb', line 60

def to_s
  "#{start}-#{self.end}"
end