RSyntaxTextArea icon indicating copy to clipboard operation
RSyntaxTextArea copied to clipboard

Undo removes all entered text

Open raydac opened this issue 3 years ago • 7 comments

I have written a small test where just a form contains RSyntaxTextArea component. As I see undo/redo should work just out of the box but if I press ctrl+z then all entered text (which contains multiple lines) is removed like the component doesn't have any saved intermediate undo steps. May be I should turn on something in RSyntaxTextArea to save intermediate undo steps?

raydac avatar Mar 07 '21 23:03 raydac

RSTA's undo will delete all contiguous text. If you moved the caret to another location and typed, it would trigger a separate undo action.

Can you confirm this is the behavior you see?

I see other code editors (at least IntelliJ and VS Code) start a new undo when pressing Enter as well. I'm guessing this is what you expected but aren't seeing?

bobbylight avatar Mar 08 '21 14:03 bobbylight

I made some small screen-cast, may I be have skipped something but looks like there is no new undo for enter pressing, the test program is visible in left part https://user-images.githubusercontent.com/3008810/110342733-6a006e80-8034-11eb-9a34-a7794493812e.mp4

raydac avatar Mar 08 '21 15:03 raydac

Yes, that's the current behavior - all contiguous text, including line endings, are treated as a single undo. I'll leave this open as a feature request for a new undo to restart on Enter presses.

bobbylight avatar Mar 10 '21 04:03 bobbylight

and is there any way for programmers to replace undo manager in RSyntaxTextArea by custom one? looks like that use of RUndoManager is hard-coded

raydac avatar Mar 11 '21 22:03 raydac

You'd have to create a custom subclass of RUndoManager that reimplements the logic in undoableEditHappened() so that newlines start a new compound edit. This should be straightforward since RUndoManager just wraps Swing's standard javax.swing.undo package's functionality.

Then inject an instance of your custom RUndoManager subclass via a subclass of RSyntaxTextArea with createUndoManager() overridden.

bobbylight avatar Mar 12 '21 03:03 bobbylight

Reopening since I meant to actually look at implementing this in the library.

bobbylight avatar Mar 12 '21 03:03 bobbylight

You'd have to create a custom subclass of RUndoManager that reimplements the logic in undoableEditHappened() so that newlines start a new compound edit. This should be straightforward since RUndoManager just wraps Swing's standard javax.swing.undo package's functionality.

Then inject an instance of your custom RUndoManager subclass via a subclass of RSyntaxTextArea with createUndoManager() overridden.

Unfortunately this isn't so simple due to sub-classes not being able to access compoundEdit. I figured out a hack using beginInternalAtomicEdit and endInternalAtomicEdit though that allows newlines to be counted as their own actions:

    private final RTextArea textArea;

    public MyUndoManager(RTextArea textArea) {
        super(textArea);
        this.textArea = textArea;
    }

    @Override public void undoableEditHappened(UndoableEditEvent e) {
        UndoableEdit edit = e.getEdit();

        if ("addition".equals(edit.getPresentationName())) {
            try {
                String inserted = textArea.getText(textArea.getCaretPosition() - 1, 1);
                if ("\n".equals(inserted)) {
                    // Create an edit action just for the newline.
                    beginInternalAtomicEdit();
                    super.undoableEditHappened(e);
                    endInternalAtomicEdit();
                    return;
                }
            } catch (BadLocationException ignored) {
                // This shouldn't happen...
            }
        }
        super.undoableEditHappened(e);
    }

While you are implementing this functionality into RUndoManager, it might also be good to expose some extra methods so that sub-classes can handle the compoundEdit themselves more easily. Also, it would be nice if there was a way to get the undo manager of an RSyntaxTextArea. Currently I have to override createUndoManager() and then store the undo manager in a field to access it.

Cheers

Sothatsit avatar Jul 13 '21 06:07 Sothatsit