Positions
LSP addresses a position as a line plus a character counted in UTF-16 code
units. Go indexes strings by byte.
For ASCII the two coincide. That is precisely why the difference survives review: everything works until a document contains an accent, a CJK character or an emoji.
offset := lsp.OffsetFromPosition(src, lsp.Position{Line: 2, Character: 14})
pos := lsp.PositionFromOffset(src, offset)Why counting runes is also wrong01
A rune above the Basic Multilingual Plane, which is most emoji, is one rune, up to four bytes, and two code units.
| Measure | An emoji above the BMP |
| Runes | 1 |
| Bytes | up to 4 |
| UTF-16 code units | 2 |
Counting runes is as wrong as counting bytes. It just fails later, and on a narrower set of documents, which makes it harder to find.
Out-of-range positions clamp02
A position past the end of a line or a document is clamped instead of returning an error.
This is deliberate. Editors legitimately send positions against a document version the server has not applied yet, and treating that as a protocol violation produces a server that disconnects during ordinary fast typing.