I wrote two text editors from scratch, and they both use a very primitive form of undo / revert functionality. By "primitive" I mean that the functionality was very easy to implement, but it is uneconomical in very large files (say >> 10 MB). However, the system is very flexible; for example, it supports unlimited undo levels.
In essence, I define the structure as
type TUndoDataItem = record text: /array of/ string; selBegin: integer; selEnd: integer; scrollPos: TPoint; end;
and then define an array
var UndoData: array of TUndoDataItem;
Then each member of this array indicates the stored state of the text. Now, each time you edit the text (pressing the down key, returning to the down key, deleting the down key, cutting / pasting, selection moved by the mouse, etc.), I (re) start the timer, say, for one second. When triggered, the timer saves the current state as a new element in the UndoData array.
When canceling (Ctrl + Z), I return the editor to the state of UndoData[UndoLevel - 1] and reduce UndoLevel by one. By default, UndoLevel is the index of the last member of the UndoData array. When returning (Ctrl + Y or Shift + Ctrl + Z) I return the editor to the state UndoData[UndoLevel + 1] and increase UndoLevel by one. Of course, if the editing timer fires when UndoLevel not equal to the length (minus one) of the UndoData array, I clear all the elements of this array after UndoLevel , as is usually the case on the Microsoft Windows platform. (but Emacs is better, if I remember correctly - the disadvantage of the Microsoft Windows approach is that if you cancel a lot of changes and then accidentally edit the buffer, the previous content (which was canceled) will be lost forever). You might want to skip this array reduction.
In another type of program, for example, in an image editor, the same technique can be applied, but, of course, with a completely different structure of UndoDataItem . A more advanced approach that does not require a lot of memory is to save only changes between undo levels (that is, instead of saving "alpha \ nbeta \ gamma" and "alpha \ nbeta \ ngamma \ ndelta", you could save " alpha \ nbeta \ ngamma "and" ADD \ ndelta ", if you know what I mean). In very large files, where each change is small compared to the file size, this will significantly reduce memory usage for canceled data, but it is more difficult to implement and possibly more error prone.