sublime-treesitter/TreeSitter

Undo selection

Closed this issue · 5 comments

Thanks for this amazing addin :)

It would be nice to be able to keep an history of the selection, and to restore the previous one (for example if you select the ancestor, but you did the shortcut one too many)

I agree, being able to "jump back" to the previous selection in the stack, or "jump forward" to the next selection, is very useful...

Fortunately, this functionality is built in to Sublime Text!

Screenshot 2024-03-05 at 5 21 10 PM

I use this all the time to do exactly what you're describing. Here are my key bindings for these commands. Does it work for you?

it doesn't work well no ? it won't have the same steps as you did in the reverse order
See: https://drive.google.com/file/d/1jaN0CNtM2V9b1KMuFTySUWahM5Dtcff7/view?usp=sharing
(I think this shortcut is more about cursor position than selection)

I did this small piece of code and it's exactly the behavior I want (not sure if it can be improve to not have global variables, not clearing the entire selection to restore the previous one, etc)

import sublime, sublime_plugin
from copy import deepcopy

_history = []
skip_next = False


def _selection_to_tuple(sel):
    return [(s.a, s.b) for s in sel]


class SelectionListener(sublime_plugin.EventListener):
    def run(self, edit):
        if _history:
            self.view.sel(_history.pop())

    def on_selection_modified(self, view):
        global _history, skip_next
        if skip_next:
            skip_next = False
            return
        selection = view.sel()
        if len(selection) == 1 and selection[0].a == selection[0].b:
            _history = [[(selection[0].a, selection[0].b)]]
        else:
            to_add = _selection_to_tuple(selection)
            if not _history or _history[-1] != to_add:
                _history.append(to_add)


class PreviousSelectionCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        global _history, skip_next
        if _history:
            skip_next = True
            sel = self.view.sel()
            while True:
                new_selection = _history.pop() if len(_history) > 1 else _history[0]
                if len(_history) <= 1 or _selection_to_tuple(sel) != new_selection:
                    break
            sel.clear()
            regions = [sublime.Region(a=a, b=b) for a, b in new_selection]
            sel.add_all(regions)
            self.view.show(regions[0])

You're right, the jump_back and jump_forward commands seem to group multiple selection changes into a single "jump". This is also how the undo and redo_or_repeat commands work as well -- they group multiple text changes into a single "undo" or "redo". I usually don't mind this behavior, but it's cool to see the more granular alternative you built =)

If I'm not mistaken and read this issue correctly. 1. Selection changes can (generally) be done by soft_undo (ctrl+u) but here we're usually too fast and Sublime would combine entries. 2. You can manual add entries to the jump back/forth history:

    view.run_command("add_jump_record", {
        "selection": [(r.a, r.b) for r in view.sel()]
    })

Maybe adding this does the trick for soft_undo as well.