Class: Ibex::LSP::PositionCodec

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/lsp/position_codec.rb

Overview

Converts between frontend byte offsets and zero-based LSP UTF-16 positions.

Instance Method Summary collapse

Constructor Details

#initialize(source) ⇒ PositionCodec

Returns a new instance of PositionCodec.



8
9
10
11
# File 'lib/ibex/lsp/position_codec.rb', line 8

def initialize(source)
  @source = Frontend::SourceEncoding.validated_utf8(source, "(lsp)")
  @line_starts = build_line_starts.freeze #: Array[Integer]
end

Instance Method Details

#byte_offset(position) ⇒ Object

Raises:

  • (ArgumentError)


29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ibex/lsp/position_codec.rb', line 29

def byte_offset(position)
  line = integer_member(position, "line")
  character = integer_member(position, "character")
  raise ArgumentError, "line and character must be non-negative" if line.negative? || character.negative?

  line_start = @line_starts[line]
  raise ArgumentError, "line is outside the document" unless line_start

  text = @source.byteslice(line_start, content_end(line) - line_start) || ""
  line_start + byte_length_at_utf16(text, character)
end

#position(byte_offset) ⇒ Object

Raises:

  • (ArgumentError)


14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/ibex/lsp/position_codec.rb', line 14

def position(byte_offset)
  validate_byte_offset(byte_offset)
  prefix = @source.byteslice(0, byte_offset) || ""
  raise ArgumentError, "byte offset is not on a UTF-8 character boundary" unless prefix.valid_encoding?

  line = (@line_starts.bsearch_index { |start| start > byte_offset } || @line_starts.length) - 1
  line_start = @line_starts.fetch(line)
  line_end = content_end(line)
  raise ArgumentError, "byte offset points inside a line ending" if byte_offset > line_end

  text = @source.byteslice(line_start, byte_offset - line_start) || ""
  { "line" => line, "character" => utf16_length(text) }
end

#range(span) ⇒ Object



42
43
44
# File 'lib/ibex/lsp/position_codec.rb', line 42

def range(span)
  { "start" => position(span.start_byte), "end" => position(span.end_byte) }
end