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.
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
#build_line_starts ⇒ Array[Integer]
49 50 51 52 53 54 55 56 57 |
# File 'lib/ibex/lsp/position_codec.rb', line 49 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
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
# File 'lib/ibex/lsp/position_codec.rb', line 74 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
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 |
#content_end(line) ⇒ Integer
60 61 62 63 64 65 66 |
# File 'lib/ibex/lsp/position_codec.rb', line 60 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
94 95 96 97 98 99 |
# File 'lib/ibex/lsp/position_codec.rb', line 94 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]
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) ⇒ Hash[String, Hash[String, Integer]]
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 |
#utf16_length(text) ⇒ Integer
69 70 71 |
# File 'lib/ibex/lsp/position_codec.rb', line 69 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.
102 103 104 105 106 |
# File 'lib/ibex/lsp/position_codec.rb', line 102 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 |