Odd kak-lsp/pylsp issue

I’ve reasonably recently switched from the old pyls to the new fork pylsp (I spent a while not doing much python, so I my setup got stale), and I’ve noticed an odd issue - I’m just wondering if anyone else has seen it before I put in a bug report.

What’s happening is that when I run lsp-formatting (either directly or via a BufWritePre hook), if any changes are made to the file my position in the file is lost and I end up at the bottom of the file, with a new empty line at the end. This doesn’t happen with Rust (using rust-analyzer/rustfmt); I don’t have any other language servers set up to test with beyond these two.

Has anyone else seen this, or have any idea what might be happening?

Does the same thing happen if you pipe the buffer through cat or similar? Perhaps something like:

eval -draft %{ exec <percent>|cat<ret> }

…so the <percent> doesn’t move the cursor to the end of the buffer anyway and ruin the test.

When the content of a buffer is replaced, Kakoune tries to diff the old and new versions to determine what the change (if any) was, but if the buffer is too large (10KB? 100KB? 1MB?) it won’t bother trying to diff and just assumes the whole thing changed, and so puts the cursor at the end.

Size of the file definitely doesn’t seem like the issue - it’s happening with a two line file. Your command doesn’t replicate the behaviour either. Unless something in the pylsp output is confusing Kakoune about the size of the buffer or something like that, which seems odd given that it’s behaving as expected with rust-analyzer, and they’re both on the other side of kak-lsp.

This is a regression from Avoid shell calls when applying text edits · kak-lsp/kak-lsp@88f52f0 · GitHub
visible with pylsp formatting because that server sends a single text edit that replaces the entire buffer.
We used to apply that with |cat ... but changed it to c..., which doesn’t do the diffing. A shame.

BTW I think lsp-formatting in a BufWritePre hook can lead to unexpected results because it’s async; prefer lsp-formatting-sync

1 Like

Yeah, I use the recommended lsp-formatting-sync in the hook, sadly it shows the same issue.

So I should report this as a bug against kak-lsp? I did check there and saw nothing, so I wasn’t sure whether it was a pylsp or kak-lsp issue.

should be fixed as of Preserve cursor positions again when applying text edits · kak-lsp/kak-lsp@40d3f78 · GitHub

Thank you for such a prompt response - I can confirm the current head kak-lsp doesn’t drop me to the bottom of the file, which is much more usable than before.

If the cursor is on a line that’s changed in the reformatting it’ll move to the next line, or if it’s a contiguous block of lines that have changed it’ll move the cursor to the line after the last changed line in the block - that means it’ll effectively move the cursor from wherever it is to the next unchanged line. That’s the same behaviour I see with rust-analyzer, so I’m guessing it’s imposed by kakoune’s diffing logic . . . It’s definitely a lot more usable than the bottom of the file, though.

Again, thanks for the prompt response.

right, there is still some added cursor movement that we should get rid of

one option is to just compute new buffer & selections on the server side instead of letting Kakoune infer the selections

my solution also broke some other scenarios. I have reverted it in favor of an alternative workaround: if the server sends a text edit that replaces the entire buffer, then kak-lsp runs a minimal edit sequence instead, so it only touches the affected lines.

I can confirm that’s working sensibly for me.

It’s a shame this is such a big pain - it might be nice if Kakoune had support for something to save and restore selections across any kind of operation so that you could do an automated buffer rewrite without needing to worry about the detailed impact of /how/ you did the rewrite. Given the increasing prevalence of automated formatting tools and the like it’d be very useful.

Unless I’m just misunderstanding the core issues here, of course.

Anyway, thanks again for the work on this.

Well Kakoune does a good job at restoring selections after |fmt<ret> aka :format<ret> by computing a diff. We could make the diff more accurate, however, in this case it’s really the language server’s fault. It should just provide a sequence of small text edits, then neither Kakoune nor kak-lsp would need to compute a diff. Of course this is not mandated in LSP (too bad) so we have to handle it.