Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to call `add_regions` without affecting the soft-undo history #2975

Open
evandrocoan opened this issue Sep 8, 2019 · 1 comment
Open

Allow to call `add_regions` without affecting the soft-undo history #2975

evandrocoan opened this issue Sep 8, 2019 · 1 comment

Comments

@evandrocoan
Copy link

@evandrocoan evandrocoan commented Sep 8, 2019

Problem description

I am implementing a plugin which live reloads the user input adding and removing regions while the user is typing. But, this is adding countless undo items into the soft_undo stack.

Preferred solution

Add a new flag to add_regions to not add these items into the soft_undo stack.

sublime.NO_SOFT_UNDO_HISTORY: Disable adding these regions on the soft undo stack.

Additional Information (optional)

class IncrementorFindRegexInputHandler(sublime_plugin.TextInputHandler):
    ...
    def preview(self, regex):
        regions = view.find_all( regex )
        ...
        view.add_regions( 'IncrementorMarks', regions, 'comment', '', sublime.DRAW_OUTLINED )

Related threads

  1. add_regions() underlines don't draw under spaces #137
  2. Add add_regions() DRAW_BLOCK flag to make region non-rounded #2134
  3. Can't set region.colorish foreground #2122
  4. add_regions causes Sublime Text to hang for ever after eating 1.81GB of RAM #1846
  5. Commands "soft_undo" and "soft_redo" do not update visualization of dirty flag #2087
  6. Soft undo does not work for the same text command called multiple times #2924
@evandrocoan

This comment has been minimized.

Copy link
Author

@evandrocoan evandrocoan commented Sep 10, 2019

I created this small utility called LivePreviewInputHandler which undo each change before doing a new one:

# I am saving the state in this class because 
# it is a royal pain in the ass to keep typing `global` 
# every time/everywhere I would like to use a global! 
class State(object):
    ## While this is not 0, the input panel is open
    view_id = 0


def status(msg, *args):
    msg = "[Package Name] %s" % ( msg % args )
    print( msg )
    sublime.status_message( msg )


class LivePreviewInputHandler(object):
    """ https://github.com/jbrooksuk/InsertNums/blob/master/InsertNums.py#L157 """
    def __init__(self, command, action):
        self.command = command
        self.action = action

    def start_preview_mode(self):
        view = self.view

        if State.view_id:
            self.revert_changes()

        else:
            State.view_id = view.id()

    def reset_preview_mode(self):
        if State.view_id:
            self.revert_changes()
            State.view_id = 0

    def revert_changes(self):
        """ Revert changes for clean undo history """
        last_command = self.view.command_history( 0 )

        if last_command[0].endswith( self.command ):
            self.view.run_command( self.action )

I first used it here with both TextInputHandler and panel input:

  1. https://github.com/evandroforks/Incrementor/blob/master/incrementor.py#L331

I resumed the code on the above with with this general usage for a input panel:

class IncrementorPromptPanelCommand(LivePreviewInputHandler, sublime_plugin.WindowCommand):
    def __init__(self, window):
        sublime_plugin.WindowCommand.__init__( self, window )

    def preview_find(self, text):

        if self.validate_find( text ):
            self.start_preview_mode()
            self.view.run_command( 'incrementor_highlight' )

    def on_cancel(self):
        self.reset_preview_mode()

    def show_find_panel(self):
        LivePreviewInputHandler.__init__( self, 'incrementor_highlight', 'soft_undo' )

        self.window.show_input_panel(
                'Find (w/ RegEx) :',
                '',
                on_done=self.find_callback_on_done,
                on_change=self.preview_find,
                on_cancel=self.on_cancel
            )

    def find_callback_on_done(self, find):
        self.reset_preview_mode()
        # ...
        self.show_replace_panel()

    def validate_find(self, text):

        if len( text ):
            try:
                return bool( re.compile( text ) )

            except Exception as error:
                status( "%s, %s", text, error )

    def show_replace_panel(self):
        LivePreviewInputHandler.__init__( self, 'incrementor_replace_helper', 'undo' )

        self.window.show_input_panel(
                'Replace (w/o RegEx) :',
                '',
                on_done=self.replace_callback_on_done,
                on_cancel=self.on_cancel,
                on_change=self.preview_replace
            )

    def replace_callback_on_done(self, text):
        self.reset_preview_mode()
        self.view.run_command( 'incrementor_replace_helper' )

    def preview_replace(self, text):

        if self.validate_replace( text ):
            self.start_preview_mode()
            self.view.run_command( 'incrementor_replace_helper' )

    def validate_replace(self, text):
        return len( text ) > 1

    def run(self):
        self.view = self.window.active_view()
        self.show_find_panel()

And I resumed the code on the above with with this general usage for a TextInputHandler:

class IncrementorPromptInputHandlerCommand(sublime_plugin.WindowCommand):

    def input(self, args):
        if "find_regex" not in args:
            view = self.window.active_view()
            return IncrementorFindRegexInputHandler( view )

        elif "replace_regex" not in args:
            return IncrementorReplaceInputHandler( self.window.active_view() )

        else:
            return None

    def run(self, find_regex, replace_regex):
        view = self.window.active_view()
        view.run_command( 'incrementor_replace_helper' )


class IncrementorFindRegexInputHandler(LivePreviewInputHandler, sublime_plugin.TextInputHandler):
    def __init__(self, view):
        LivePreviewInputHandler.__init__( self, 'incrementor_highlight', 'soft_undo' )
        self.view = view

    def name(self):
        return "find_regex"

    def placeholder(self):
        return "Find Regex"

    def cancel(self):
        self.reset_preview_mode()

    def initial_text(self):
        return ""

    def preview(self, text):

        if self.validate( text ):
            self.start_preview_mode()
            self.view.run_command( 'incrementor_highlight', { 'regex': text } )

    def confirm(self, text):
        self.reset_preview_mode()
        # ...
        self.view.run_command( 'incrementor_selection_mark_restore' )

    def validate(self, text):

        if len( text ):
            try:
                return bool( re.compile( text ) )

            except Exception as error:
                status( "%s, %s", text, error )

    def next_input(self, args):

        if "replace_regex" not in args:
            return IncrementorReplaceInputHandler( self.view )


class IncrementorReplaceInputHandler(LivePreviewInputHandler, sublime_plugin.TextInputHandler):
    def __init__(self, view):
        LivePreviewInputHandler.__init__( self, 'incrementor_replace_helper', 'undo' )
        self.view = view

    def name(self):
        return "replace_regex"

    def placeholder(self):
        return "Replace Regex"

    def initial_text(self):
        return ""

    def validate(self, text):
        return len( text ) > 1

    def preview(self, text):

        if self.validate( text ):
            self.start_preview_mode()
            self.view.run_command( 'incrementor_replace_helper' )

    def cancel(self):
        self.reset_preview_mode()

    def confirm(self, text):
        self.reset_preview_mode()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
1 participant
You can’t perform that action at this time.