Class: Ibex::LSP::PositionCodec
- Inherits:
-
Object
- Object
- Ibex::LSP::PositionCodec
- Defined in:
- lib/ibex/lsp/position_codec.rb,
sig/ibex/lsp/position_codec.rbs
Overview
Converts between frontend byte offsets and zero-based LSP UTF-16 positions.
Instance Method Summary collapse
- #build_line_starts ⇒ Array[Integer]
- #byte_length_at_utf16(text, units) ⇒ Integer
- #byte_offset(position) ⇒ Integer
- #content_end(line) ⇒ Integer
-
#initialize(source) ⇒ PositionCodec
constructor
A new instance of PositionCodec.
- #integer_member(position, name) ⇒ Integer
- #position(byte_offset) ⇒ Hash[String, Integer]
- #range(span) ⇒ Hash[String, Hash[String, Integer]]
- #utf16_length(text) ⇒ Integer
- #validate_byte_offset(byte_offset) ⇒ void
Constructor Details
#initialize(source) ⇒ PositionCodec
Returns a new instance of PositionCodec.
12 13 14 15 |
# File 'lib/ibex/lsp/position_codec.rb', line 12 def initialize(source) @source = Frontend::SourceEncoding.validated_utf8(source, "(lsp)") @line_starts = build_line_starts.freeze #: Array[Integer] end |
Instance Method Details
#build_line_starts ⇒ Array[Integer]
53 54 55 56 57 58 59 60 61 |
# File 'lib/ibex/lsp/position_codec.rb', line 53 def build_line_starts starts = [0] offset = 0 @source.each_char do |character| offset += character.bytesize starts << offset if character == "\n" end starts end |
#byte_length_at_utf16(text, units) ⇒ Integer
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/ibex/lsp/position_codec.rb', line 78 def byte_length_at_utf16(text, units) consumed_units = 0 consumed_bytes = 0 text.each_char do |character| return consumed_bytes if consumed_units == units width = character.ord > 0xFFFF ? 2 : 1 if consumed_units + width > units raise ArgumentError, "character points into the middle of a UTF-16 surrogate pair" end consumed_units += width consumed_bytes += character.bytesize end return consumed_bytes if consumed_units == units raise ArgumentError, "character is outside the document line" end |
#byte_offset(position) ⇒ Integer
33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/ibex/lsp/position_codec.rb', line 33 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 |
#content_end(line) ⇒ Integer
64 65 66 67 68 69 70 |
# File 'lib/ibex/lsp/position_codec.rb', line 64 def content_end(line) boundary = @line_starts[line + 1] || @source.bytesize return boundary unless @line_starts[line + 1] before_newline = boundary - 1 @source.getbyte(before_newline - 1) == 13 ? before_newline - 1 : before_newline end |
#integer_member(position, name) ⇒ Integer
98 99 100 101 102 103 |
# File 'lib/ibex/lsp/position_codec.rb', line 98 def integer_member(position, name) value = position[name] raise ArgumentError, "#{name} must be an integer" unless value.is_a?(Integer) value end |
#position(byte_offset) ⇒ Hash[String, Integer]
18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/ibex/lsp/position_codec.rb', line 18 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) ⇒ Hash[String, Hash[String, Integer]]
46 47 48 |
# File 'lib/ibex/lsp/position_codec.rb', line 46 def range(span) { "start" => position(span.start_byte), "end" => position(span.end_byte) } end |
#utf16_length(text) ⇒ Integer
73 74 75 |
# File 'lib/ibex/lsp/position_codec.rb', line 73 def utf16_length(text) text.each_codepoint.sum { |codepoint| codepoint > 0xFFFF ? 2 : 1 } end |
#validate_byte_offset(byte_offset) ⇒ void
This method returns an undefined value.
106 107 108 109 110 |
# File 'lib/ibex/lsp/position_codec.rb', line 106 def validate_byte_offset(byte_offset) return if byte_offset.is_a?(Integer) && byte_offset.between?(0, @source.bytesize) raise ArgumentError, "byte offset is outside the document" end |