sublime-fmt icon indicating copy to clipboard operation
sublime-fmt copied to clipboard

The viewport jumping problem

Open kaste opened this issue 3 years ago • 1 comments

Hi!

I read https://discord.com/channels/280102180189634562/280157067396775936/840207551065096284 and the linked issues, for example https://github.com/sublimehq/sublime_text/issues/4144

From my experience working on GitSavvy and SublimeLinter you can split the problem to make it more simple.

First, you can stabilize the viewport, just using

vx, vy = view.viewport_position()  # read

view.replace(...)

view.set_viewport_position((0, 0), animate=False)  # intentional force!
view.set_viewport_position((vx, vy), animate=False)  # write

This has to be done sync as a TextCommand. (see: https://github.com/timbrel/GitSavvy/blob/d6e24d714c08e5b975ae08bafbebb2f75eabe303/core/view.py#L183-L194)

This is a good start. However, you might want to improve further by first reading a row_offset and reapplying it after replace and setting the new cursor. With the row_offset you basically measure that the cursor is at the nth row/line, or x pixels, from the top of the viewport. (Refer: https://github.com/timbrel/GitSavvy/blob/d6e24d714c08e5b975ae08bafbebb2f75eabe303/core/view.py#L111-L115)

Reapplying:

    vy = (new_row - row_offset) * view.line_height()
    vx, _ = view.viewport_position()
    view.set_viewport_position((vx, vy), animate=False)

So, in other words, you first see the cursor is on the row 50, after applying the patch/formatting, you observe the cursor is now on row 40. You modify vy so that the distance from the top of the viewport to the cursor (the row_offset) does not change.

The second problem is where the cursor actually lands or ideally should land after applying the patch. That's a problem you cannot solve under all circumstances. (That's why Prettier can - given a cursor position - return the new cursor position; computed as part of the formatting job.)

Doing a diff is probably a good approach. Iirc, you only have to "fix" the cursor if you replace (including expanding or shrinking) a region where the cursor is in ("contained") or at ("at the edges"). This is of course always true if you replace the whole buffer.

From my understanding, it would be enough to diff the text up to the line the last cursor is on. Then compute the new cursors using the diff, replace the whole view, and set the new cursors.

You could also apply the diff hunk by hunk, t.i. calling replace et.al. multiple times. As Sublime controls the cursor just perfect as long as the replaced region does not own a cursor, you only have to "fine" diff a hunk iff it contains one of the cursors which should be a good speed up computational-wise.

That's an unrequested wall of text. Take what you want out of it.

Regards

kaste avatar May 12 '21 10:05 kaste