Skip to content

Apply bottom-most SnippetTextEdit in TextDocumentEdit as snippet - #2944

Merged
jwortmann merged 3 commits into
sublimelsp:mainfrom
jwortmann:improve-snippet-text-edit
May 31, 2026
Merged

jwortmann merged 3 commits into
sublimelsp:mainfrom
jwortmann:improve-snippet-text-edit

Conversation

@jwortmann

Copy link
Copy Markdown
Member

This relaxes the condition that the SnippetTextEdit from a TextDocumentEdit needs to be the topmost edit in the file, (if there are multiple edits), in order to be applied as a snippet (otherwise it was downgraded to a regular TextEdit).

Now we apply the first, i.e the bottom-most SnippetTextEdit as a snippet, and in case there are more SnippetTextEdit's later, they are downgraded to regular TextEdit's.

It would be good to test this with the example from #2941 (comment) to confirm that it works as expected (i.e. test that edits to the file which are applied after the insert_snippet command don't break the snippet behavior).

@jwortmann

Copy link
Copy Markdown
Member Author

It looks like Ruff rule C413 is a false-positive, because sorted(..., reverse=True) is not equivalent to reversed(sorted(...)).

@rchl

rchl commented May 31, 2026

Copy link
Copy Markdown
Member

I haven't tested yet but switching from top to bottom most doesn't sound good because rust analyzer has a feature to move function up or down and each triggers opposite snippet case.

@rchl

rchl commented May 31, 2026

Copy link
Copy Markdown
Member

Maybe I misunderstood. It does actually work both ways (with the change in sublimelsp/LSP-rust-analyzer#208)

Screen.Recording.2026-05-31.at.10.13.28.mov

@jwortmann

jwortmann commented May 31, 2026 •

Copy link
Copy Markdown
Member Author

If there is only one SnippetTextEdit in the edits, it is automatically the bottom-most, even if it is the topmost edit. The "bottom-most" is only relevant if server puts multiple SnippetTextEdit's in the edits. In that case we apply the first one that we see, and that is the bottom-most, because edits are applied from bottom to top.

@jwortmann

Copy link
Copy Markdown
Member Author

It would be nice to have a use case where the SnippetTextEdit contains placeholder texts and multiple tab stops, to confirm that the snippet still works correctly even when there are other edits applied to the buffer after the insert_snippet command. But iirc the jdtls server which I initially used for testing only had the SnippetTextEdit as a single edit, and no other edits to the file.

@rchl

rchl commented May 31, 2026 •

Copy link
Copy Markdown
Member

I've manually overridden response for RA and tested both with:

{'range': {'end': {'character': 1, 'line': 6}, 'start': {'character': 0, 'line': 4}}, 'snippet': {'kind': 'snippet', 'value': 'fn tw$1o() {\n    println!("Hello, world1!");\n}$0'}}
{'insertTextFormat': 2, 'newText': '"Hello, world!"', 'range': {'end': {'character': 29, 'line': 1}, 'start': {'character': 13, 'line': 1}}}
{'insertTextFormat': 2, 'newText': 'main', 'range': {'end': {'character': 6, 'line': 0}, 'start': {'character': 3, 'line': 0}}}

and

{'insertTextFormat': 2, 'newText': '"Hello, world!"', 'range': {'end': {'character': 29, 'line': 5}, 'start': {'character': 13, 'line': 5}}}
{'insertTextFormat': 2, 'newText': 'main', 'range': {'end': {'character': 6, 'line': 4}, 'start': {'character': 3, 'line': 4}}}
{'range': {'end': {'character': 1, 'line': 2}, 'start': {'character': 0, 'line': 0}}, 'snippet': {'kind': 'snippet', 'value': 'fn tw$1o() {\n    println!("Hello, world1!");\n}$0'}}

And in both cases I was able to jump to the last ($0) tabstop.

(those are listed in the order they were applied)

@rchl

rchl commented May 31, 2026

Copy link
Copy Markdown
Member

It looks like Ruff rule C413 is a false-positive, because sorted(..., reverse=True) is not equivalent to reversed(sorted(...)).

Documentation says that it's unsafe for this specific case: https://docs.astral.sh/ruff/rules/unnecessary-call-around-sorted/#fix-safety so ignoring is the right way to go.

Comment thread plugin/edit.py
Comment on lines +130 to +132
# The insert_snippet command automatically adds additional indentation to each row, based on the
# indentation level of the row where it gets inserted. Therefore we first have to remove that same
# indentation whitespace after each line break in the snippet text from the language server.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps worth creating a feature request for ST so that we don't have to do this potentially fragile logic.

@jwortmann
jwortmann merged commit 0d301ed into sublimelsp:main May 31, 2026
8 checks passed
@jwortmann
jwortmann deleted the improve-snippet-text-edit branch May 31, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants