sublime-treesitter / TreeSitter

Sublime Text Tree-sitter configuration and abstraction layer
MIT License
19 stars 1 forks source link

Undo selection #15

Closed Mister7F closed 6 months ago

Mister7F commented 6 months ago

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)

kylebebak commented 6 months ago

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?

Mister7F commented 6 months ago

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)

Mister7F commented 6 months ago

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])
kylebebak commented 6 months ago

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 =)

kaste commented 4 months ago

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.