Module: Pikuri::Lsp::PositionEncoding
- Defined in:
- lib/pikuri/lsp/position_encoding.rb
Overview
LSP's positionEncoding values and the offset arithmetic each one
implies. A column is a character index inside pikuri and a code-unit
offset on the wire; column 12 of the same line is three different numbers
depending on who is asking:
line = 'x = "héllo🎉"' # 'é' is 2 bytes, '🎉' is 4
PositionEncoding.offset_for(line, 12, UTF32) # => 11 (just 0-based)
PositionEncoding.offset_for(line, 12, UTF16) # => 12 (the emoji counts twice)
PositionEncoding.offset_for(line, 12, UTF8) # => 15
PositionEncoding.column_for(line, 12, UTF16) # => 12 (round-trips)
Converting in the wrong direction, or not at all, yields a plausible column — so the server answers confidently about the wrong token instead of failing. DEFAULT is what a server that negotiates nothing means.
Constant Summary collapse
- UTF8 =
Offsets count UTF-8 bytes. ruby-lsp accepts pikuri's offer of this.
'utf-8'- UTF16 =
Offsets count UTF-16 code units, so an astral character (emoji, rare CJK) counts twice. jdtls ignores the offer and stays here.
'utf-16'- UTF32 =
Offsets count codepoints, i.e. exactly pikuri's own columns.
'utf-32'- DEFAULT =
What a server that answers no
positionEncodingmeans: UTF-16 is the 3.17 default, and the conversion is therefore mandatory code rather than a fallback for a hypothetical server. UTF16- OFFERED =
Encodings pikuri offers in
general.positionEncodings, best first. UTF-32 is out: no measured server offers it, and it would be one more branch nothing exercises. [UTF8, UTF16].freeze
- SUPPORTED =
Every value a caller may pass.
[UTF8, UTF16, UTF32].freeze
Class Method Summary collapse
-
.column_for(line_text, offset, encoding) ⇒ Integer
1-based character column for a wire offset into
line_text. -
.offset_for(line_text, column, encoding) ⇒ Integer
Wire offset for a 1-based character column on
line_text. -
.utf16_units(str) ⇒ Integer
UTF-16 code units
stroccupies.
Class Method Details
.column_for(line_text, offset, encoding) ⇒ Integer
1-based character column for a wire offset into line_text.
An offset past the end of the line clamps to one-past-the-last
character rather than raising: a range's end legitimately sits there,
and a server that overshoots is relayed, not fought.
91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/pikuri/lsp/position_encoding.rb', line 91 def column_for(line_text, offset, encoding) raise ArgumentError, "offset must be >= 0, got #{offset}" if offset.negative? raise ArgumentError, "unknown positionEncoding #{encoding.inspect}" unless SUPPORTED.include?(encoding) return offset + 1 if line_text.nil? || encoding == UTF32 chars = if encoding == UTF8 (line_text.byteslice(0, offset) || line_text).scrub.length else chars_before_utf16(line_text, offset) end chars + 1 end |
.offset_for(line_text, column, encoding) ⇒ Integer
Wire offset for a 1-based character column on line_text.
offset_for('def résolve', 8, UTF8) # => 8 ("def rés" is 8 bytes)
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/pikuri/lsp/position_encoding.rb', line 57 def offset_for(line_text, column, encoding) raise ArgumentError, "column must be >= 1, got #{column}" if column < 1 chars = column - 1 if chars > line_text.length raise ArgumentError, "column #{column} is past the end of a #{line_text.length}-character " \ "line: #{line_text.inspect}" end prefix = line_text[0, chars] case encoding when UTF32 then chars when UTF8 then prefix.bytesize when UTF16 then utf16_units(prefix) else raise ArgumentError, "unknown positionEncoding #{encoding.inspect}" end end |
.utf16_units(str) ⇒ Integer
UTF-16 code units str occupies.
109 110 111 |
# File 'lib/pikuri/lsp/position_encoding.rb', line 109 def utf16_units(str) str.each_char.sum { |ch| ch.valid_encoding? && ch.ord > 0xFFFF ? 2 : 1 } end |