New Implementation of Undo/Redo

The old implementation is very slow (and since wish8.7 more or less useless because of polynomial computation time), and the undo mechanism is not properly working. See this example:

before deletion after deletion and undo

You may try the script by yourself:

Expand/Collapse Script

Press Control-d (delete all) + Control-u (undo last deletion), after undoing the deletion the result is not the deleted content. But after applying the revised version the script works.

I've re-implemented the undo/redo stuff completely. The new implementation is directly working on the segments. Compared to the old implementation the new implementation is super-fast, it is not wasting memory anymore, and more importantly, the undo (and also the redo of course) works correct. See Additional Command 'edit inspect' for a good demonstration of the new undo/redo mechanism.

For the realization I've implemented an alternative undo stack (tkTextUndo), the reason is documented in the header file of the alternative implementation, see tkTextUndo.h.

Here are the new descriptions of undo/redo:

pathName edit undo

Undoes the last edit action when the -undo option is true. An edit action is defined as all the commands modifying any text, or tag associations (but not the changes of tag options) that are recorded on the undo stack in between two separators, furthermore this includes the (dis-)embedding of images and windows, but not changes of the content or options of images and windows. Generates an error when the undo stack is empty. Does nothing when the -undo option is false, or when -maxredo is set to zero.

The marks are not influencing the display, thus operations on marks will not be recorded, except if option -steadymarks is enabled: this is the appropriate mode for programmed editor control, and here the recovery of the marks (position and gravity) is essential. But operations on the special marks insert and current, as well as operations on generated marks, will never be recorded, these marks should in general not be used if undoing the operation on a mark is essential.

pathName edit redo

When the -undo option is true, reapplies the last undone edits provided no other edits were done since then. Generates an error when the redo stack is empty. Does nothing when the -undo option is false.

Some Problems Eliminated

The new implementation is eliminating some problems, see Issues With Undo.

New Tag Option '-undo'

Because the revised implementation also regards changes in tag associations, it's a must to have the following additional tag option. The purpose of this option is to prevent that temporary changes of tagged text will be pushed onto the undo stack.

-undo boolean

Specifies whether adding/removing this tag to/from text will be undone with undo operation. If this flag is false, then changes of tag associations will not be undone for this tag. The default is true, except for the special selection flag sel, the default for this tag is false.

It's important to note that the implementation of the new undo behavior is optimizing changes of tagging and marks. For example, if any tag remove operation is reverting a previously performed tag add operation, then the last tag remove operation will be removed from undo stack – instead of pushing the tag add operation. This optimization is catching temporary changes.

The revised version is fulfilling these bug reports: 1561991, 3192483.

Tip #449

Recently TIP #449 (undo/redo to Return Range of Characters) has been implemented, with polynomial runtime behavior (this is a catastrophe). I have not adapted this into the revised implementation, because request 1217222 – the basis for TIP #449 – now will be featured by:

  1. The new undo implementation, because also the tag associations will be restored.

  2. The powerful watch command, which also provides the affected ranges (with constant runtime behavior).

But I won't ignore this TIP, so I have added the following convenience function:


tk_mergeRange - insert/merge a given range into a sorted list of ranges.


tk_mergeRange varName range


This procedure is inserting range into a sorted list of ranges, given with varName. If given range is adjacent to, or intersecting a range in given list, then it will be amalgamated. This means that the result is a sorted list of disjoint ranges. This procedure return the new content of varName.

With the use of the watch command a sorted and disjoint list of impacted ranges can be generated, see this code snippet:

This gives the result described in TIP #449. But take into account that this solution has quadratic complexity.