sublimehq / sublime_text

Issue tracker for Sublime Text
https://www.sublimetext.com
801 stars 35 forks source link

Soft undo does not work for the same text command called multiple times #2924

Open evandrocoan opened 4 years ago

evandrocoan commented 4 years ago

Description

  1. Create this keybinding on your user settings

    [
        { "keys": ["alt+d"], "command": "select_highlighted_next_word" },
    ]
  2. Create the plugin bug.py

    import sublime
    import sublime_plugin
    
    class SelectHighlightedNextWordCommand(sublime_plugin.TextCommand):
        def run(self, edit):
            view = self.view
            selections = view.sel()
    
            if selections:
                word_regions = view.find_all( 'selections' )
    
                if word_regions:
    
                    for next_word in word_regions:
    
                        if next_word in selections:
                            continue
    
                        selections.add(next_word)
                        view.show(next_word)
                        break

Expected behavior

  1. Open a Sublime Text Vanilla Install
  2. Create the file bug.py and its keybinding
  3. Press Alt+D 3 times
  4. Press Ctrl+U (soft_undo)
  5. Only one selection is undone (unselected)

Actual behavior

  1. Open a Sublime Text Vanilla Install
  2. Create the file bug.py and its keybinding
  3. Press Alt+D 3 times
  4. Press Ctrl+U (soft_undo)
  5. Now, all 3 selections were undone (it is expected for only one selection to be "undone")

Workaround

A fix or workaround for this problem was figured out accidentally by @OdatNurd and @mattst on https://forum.sublimetext.com/t/plugin-soft-undo/40064

If you create the bug.py by using two text commands chained by a sublime.set_timeout(), everything works as expected:

import sublime
import sublime_plugin

class SelectHighlightedNextWordCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        view = self.view
        sublime.set_timeout( lambda: view.run_command( 'select_highlighted_next_word_bug_fixer' ), 0 )

class SelectHighlightedNextWordBugFixerCommand(sublime_plugin.TextCommand):
    def run(self, edit):
        view = self.view
        selections = view.sel()

        if selections:
            word_regions = view.find_all( 'selections' )

            if word_regions:

                for next_word in word_regions:

                    if next_word in selections:
                        continue

                    selections.add(next_word)
                    view.show(next_word)
                    break

Environment

Related threads

  1. https://forum.sublimetext.com/t/plugin-soft-undo/40064
  2. https://github.com/SublimeTextIssues/Core/issues/2087 Commands "soft_undo" and "soft_redo" do not update visualization of dirty flag
BenjaminSchaaf commented 5 months ago

This is because Sublime Text joins together non-modifying commands that happen shortly after another. If you wait between each alt+d press then those will have separate entries in the undo stack.

There currently isn't a way for plugins to control this, so I'll take this issue to track adding such a feature.