fix(solidlsp): convert LSP UTF-16 positions to code points before indexing by AmirF194 · Pull Request #2115 · oraios/serena · GitHub
Skip to content

fix(solidlsp): convert LSP UTF-16 positions to code points before indexing - #2115

Draft
AmirF194 wants to merge 2 commits into
oraios:mainfrom
AmirF194:fix/2064-utf16-position-encoding
Draft

AmirF194 wants to merge 2 commits into
oraios:mainfrom
AmirF194:fix/2064-utf16-position-encoding

Conversation

@AmirF194

@AmirF194 AmirF194 commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Position.character in the LSP spec is a UTF-16 code unit offset, not a Python code-point index. TextUtils already documents that it indexes by code point (see the docstring on insert_text_at_position), but nothing converts at the boundary: SolidLanguageServer.insert_text_at_position/delete_text_between_positions take the raw LSP character and pass it straight through. Any character outside the Basic Multilingual Plane before the edit position, most emoji among them, puts every edit after it one column too far.

This fixes it at the boundary: convert the incoming UTF-16 offset to a code-point offset before indexing, and convert back before returning a position that claims to be UTF-16 again.

test/solidlsp/test_position_encoding.py drives insert_text_at_position/delete_text_between_positions directly with a line containing an astral character. Both new tests fail on main with the exact predicted corruption (inserting one character late, deleting the wrong one) and pass on this branch. test/solidlsp/util/test_ls_utils.py covers the new conversion helpers on their own.

Not touched: JetBrainsCodeEditor gets its positions from the JetBrains plugin, not the LSP, so it's outside this fix's scope. I haven't checked whether it has the same problem.

Fixes #2064

…exing

LSP `Position.character` is a UTF-16 code unit offset (the spec's own
docstring gives the classic example, an astral character between two ASCII
ones shifts every later offset). `SolidLanguageServer.insert_text_at_position`
and `delete_text_between_positions` receive these straight from a language
server (or echoed from one via LanguageServerSymbol's body positions) and
indexed the in-memory buffer with them directly, which TextUtils treats as a
plain Python string index (code points). Any character outside the Basic
Multilingual Plane before the edit position, most emoji among them, made
every following edit one code point too far.

Convert at that boundary: TextUtils.utf16_offset_to_codepoint_offset before
indexing, and its inverse before returning a cursor position that claims to
be a Position again.

Fixes oraios#2064
@AmirF194 AmirF194 changed the title test draft fix(solidlsp): convert LSP UTF-16 positions to code points before indexing Sep 28, 2026
…ng fixture

_make_server hardcoded repository_root_path as the POSIX literal "/repo" and
passed it straight into _resolve_file_uri, which builds a pathlib.Path and
calls as_uri(). On Windows, pathlib.Path treats a path with no drive letter
as not absolute, so as_uri() raised "relative path can't be expressed as a
file URI" for all three tests in this file on windows-latest CI.

Build the fixture root from tempfile.gettempdir() instead, which returns a
real absolute path in whichever platform's own convention the tests run
under.

Signed-off-by: Amir Fathi <amirfathi.me@gmail.com>
@opcode81

opcode81 commented Oct 5, 2026

Copy link
Copy Markdown
Member

@opcode81 opcode81 added the waiting for OP The maintainers are waiting for a response/further action from the original poster/creator. label Oct 5, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting for OP The maintainers are waiting for a response/further action from the original poster/creator.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SolidLSP sends Python code-point columns as LSP Position.character (UTF-16 mismatch)

2 participants