Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDani Megert2006-01-27 05:07:32 -0500
committerDani Megert2006-01-27 05:07:32 -0500
commit8a89058d31f42190510688d5a6a900d36ed1481c (patch)
treef7d68b4aad0791ee20ddd0249edf8e3f6831f905 /org.eclipse.text/src/org/eclipse/text/undo
parent6aa0563125b59db0947dbf191c424d42fa52145c (diff)
downloadeclipse.platform.text-8a89058d31f42190510688d5a6a900d36ed1481c.tar.gz
eclipse.platform.text-8a89058d31f42190510688d5a6a900d36ed1481c.tar.xz
eclipse.platform.text-8a89058d31f42190510688d5a6a900d36ed1481c.zip
Fixed bug 89599: [api][typing] Text Editor Undo stack (context) should be keyed of common document
Diffstat (limited to 'org.eclipse.text/src/org/eclipse/text/undo')
-rw-r--r--org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManager.java583
-rw-r--r--org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManagerRegistry.java5
-rw-r--r--org.eclipse.text/src/org/eclipse/text/undo/IDocumentUndoManager.java56
-rw-r--r--org.eclipse.text/src/org/eclipse/text/undo/UndoableCompoundTextChange.java173
-rw-r--r--org.eclipse.text/src/org/eclipse/text/undo/UndoableTextChange.java381
5 files changed, 615 insertions, 583 deletions
diff --git a/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManager.java b/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManager.java
index 009e01b9d..97d470075 100644
--- a/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManager.java
+++ b/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManager.java
@@ -13,6 +13,8 @@ package org.eclipse.text.undo;
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.core.commands.ExecutionException;
+import org.eclipse.core.commands.operations.AbstractOperation;
import org.eclipse.core.commands.operations.IContextReplacingOperation;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IOperationHistoryListener;
@@ -22,7 +24,11 @@ import org.eclipse.core.commands.operations.ObjectUndoContext;
import org.eclipse.core.commands.operations.OperationHistoryEvent;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Status;
import org.eclipse.jface.text.Assert;
import org.eclipse.jface.text.BadLocationException;
@@ -58,6 +64,515 @@ import org.eclipse.jface.text.TextUtilities;
* @since 3.2
*/
public class DocumentUndoManager implements IDocumentUndoManager {
+
+
+ /**
+ * Represents an undo-able text change, described as the
+ * replacement of some preserved text with new text.
+ * <p>
+ * Based on the DefaultUndoManager.TextCommand from R3.1.
+ * </p>
+ */
+ private static class UndoableTextChange extends AbstractOperation {
+
+ /** The start index of the replaced text. */
+ protected int fStart= -1;
+
+ /** The end index of the replaced text. */
+ protected int fEnd= -1;
+
+ /** The newly inserted text. */
+ protected String fText;
+
+ /** The replaced text. */
+ protected String fPreservedText;
+
+ /** The undo modification stamp. */
+ protected long fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+
+ /** The redo modification stamp. */
+ protected long fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+
+ /** The undo manager that generated the change. */
+ protected DocumentUndoManager fDocumentUndoManager;
+
+ /**
+ * Creates a new text change.
+ *
+ * @param manager the undo manager for this change
+ */
+ UndoableTextChange(DocumentUndoManager manager) {
+ super(UndoMessages.getString("DocumentUndoManager.operationLabel")); //$NON-NLS-1$
+ this.fDocumentUndoManager= manager;
+ addContext(manager.getUndoContext());
+ }
+
+ /**
+ * Re-initializes this text change.
+ */
+ protected void reinitialize() {
+ fStart= fEnd= -1;
+ fText= fPreservedText= null;
+ fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+ fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+ }
+
+ /**
+ * Sets the start and the end index of this change.
+ *
+ * @param start the start index
+ * @param end the end index
+ */
+ protected void set(int start, int end) {
+ fStart= start;
+ fEnd= end;
+ fText= null;
+ fPreservedText= null;
+ }
+
+ /*
+ * @see org.eclipse.core.commands.operations.IUndoableOperation#dispose()
+ */
+ public void dispose() {
+ reinitialize();
+ }
+
+ /**
+ * Undo the change described by this change.
+ */
+ protected void undoTextChange() {
+ try {
+ if (fDocumentUndoManager.fDocument instanceof IDocumentExtension4)
+ ((IDocumentExtension4) fDocumentUndoManager.fDocument).replace(fStart, fText
+ .length(), fPreservedText, fUndoModificationStamp);
+ else
+ fDocumentUndoManager.fDocument.replace(fStart, fText.length(),
+ fPreservedText);
+ } catch (BadLocationException x) {
+ }
+ }
+
+ /*
+ * @see org.eclipse.core.commands.operations.IUndoableOperation#canUndo()
+ */
+ public boolean canUndo() {
+ if (isValid()) {
+ if (fDocumentUndoManager.fDocument instanceof IDocumentExtension4) {
+ long docStamp= ((IDocumentExtension4) fDocumentUndoManager.fDocument)
+ .getModificationStamp();
+
+ // Normal case: an undo is valid if its redo will restore
+ // document to its current modification stamp
+ boolean canUndo= docStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP
+ || docStamp == getRedoModificationStamp();
+
+ /*
+ * Special case to check if the answer is false. If the last
+ * document change was empty, then the document's modification
+ * stamp was incremented but nothing was committed. The
+ * operation being queried has an older stamp. In this case
+ * only, the comparison is different. A sequence of document
+ * changes that include an empty change is handled correctly
+ * when a valid commit follows the empty change, but when
+ * #canUndo() is queried just after an empty change, we must
+ * special case the check. The check is very specific to prevent
+ * false positives. see
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=98245
+ */
+ if (!canUndo
+ && this == fDocumentUndoManager.fHistory
+ .getUndoOperation(fDocumentUndoManager.fUndoContext)
+ // this is the latest operation
+ && this != fDocumentUndoManager.fCurrent
+ // there is a more current operation not on the stack
+ && !fDocumentUndoManager.fCurrent.isValid()
+ // the current operation is not a valid document
+ // modification
+ && fDocumentUndoManager.fCurrent.fUndoModificationStamp !=
+ // the invalid current operation has a document stamp
+ IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) {
+ canUndo= fDocumentUndoManager.fCurrent.fRedoModificationStamp == docStamp;
+ }
+ /*
+ * When the composite is the current operation, it may hold the
+ * timestamp of a no-op change. We check this here rather than
+ * in an override of canUndo() in UndoableCompoundTextChange simply to
+ * keep all the special case checks in one place.
+ */
+ if (!canUndo
+ && this == fDocumentUndoManager.fHistory
+ .getUndoOperation(fDocumentUndoManager.fUndoContext)
+ && // this is the latest operation
+ this instanceof UndoableCompoundTextChange
+ && this == fDocumentUndoManager.fCurrent
+ && // this is the current operation
+ this.fStart == -1
+ && // the current operation text is not valid
+ fDocumentUndoManager.fCurrent.fRedoModificationStamp != IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) {
+ // but it has a redo stamp
+ canUndo= fDocumentUndoManager.fCurrent.fRedoModificationStamp == docStamp;
+ }
+
+ }
+ // if there is no timestamp to check, simply return true per the
+ // 3.0.1 behavior
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.core.commands.operations.IUndoableOperation#canRedo()
+ */
+ public boolean canRedo() {
+ if (isValid()) {
+ if (fDocumentUndoManager.fDocument instanceof IDocumentExtension4) {
+ long docStamp= ((IDocumentExtension4) fDocumentUndoManager.fDocument)
+ .getModificationStamp();
+ return docStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP
+ || docStamp == getUndoModificationStamp();
+ }
+ // if there is no timestamp to check, simply return true per the
+ // 3.0.1 behavior
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.core.commands.operations.IUndoableOperation#canExecute()
+ */
+ public boolean canExecute() {
+ return fDocumentUndoManager.isConnected();
+ }
+
+ /*
+ * @see org.eclipse.core.commands.operations.IUndoableOperation.IUndoableOperation#execute(IProgressMonitor, IAdaptable)
+ */
+ public IStatus execute(IProgressMonitor monitor, IAdaptable uiInfo) {
+ // Text changes execute as they are typed, so executing one has no
+ // effect.
+ return Status.OK_STATUS;
+ }
+
+ /**
+ * {@inheritDoc}
+ * Notifies clients about the undo.
+ */
+ public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) {
+ if (isValid()) {
+ fDocumentUndoManager.fireDocumentUndo(fStart, fPreservedText, fText, uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, false);
+ undoTextChange();
+ fDocumentUndoManager.fireDocumentUndo(fStart, fPreservedText, fText, uiInfo, DocumentUndoEvent.UNDONE, false);
+ return Status.OK_STATUS;
+ }
+ return IOperationHistory.OPERATION_INVALID_STATUS;
+ }
+
+ /**
+ * Re-applies the change described by this change.
+ */
+ protected void redoTextChange() {
+ try {
+ if (fDocumentUndoManager.fDocument instanceof IDocumentExtension4)
+ ((IDocumentExtension4) fDocumentUndoManager.fDocument).replace(fStart, fEnd - fStart, fText, fRedoModificationStamp);
+ else
+ fDocumentUndoManager.fDocument.replace(fStart, fEnd - fStart, fText);
+ } catch (BadLocationException x) {
+ }
+ }
+
+ /**
+ * Re-applies the change described by this change that was previously
+ * undone. Also notifies clients about the redo.
+ *
+ * @param monitor the progress monitor to use if necessary
+ * @param uiInfo an adaptable that can provide UI info if needed
+ * @return the status
+ */
+ public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) {
+ if (isValid()) {
+ fDocumentUndoManager.fireDocumentUndo(fStart, fText, fPreservedText, uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, false);
+ redoTextChange();
+ fDocumentUndoManager.fireDocumentUndo(fStart, fText, fPreservedText, uiInfo, DocumentUndoEvent.REDONE, false);
+ return Status.OK_STATUS;
+ }
+ return IOperationHistory.OPERATION_INVALID_STATUS;
+ }
+
+ /**
+ * Update the change in response to a commit.
+ */
+
+ protected void updateTextChange() {
+ fText= fDocumentUndoManager.fTextBuffer.toString();
+ fDocumentUndoManager.fTextBuffer.setLength(0);
+ fPreservedText= fDocumentUndoManager.fPreservedTextBuffer.toString();
+ fDocumentUndoManager.fPreservedTextBuffer.setLength(0);
+ }
+
+ /**
+ * Creates a new uncommitted text change depending on whether a compound
+ * change is currently being executed.
+ *
+ * @return a new, uncommitted text change or a compound text change
+ */
+ protected UndoableTextChange createCurrent() {
+ if (fDocumentUndoManager.fFoldingIntoCompoundChange)
+ return new UndoableCompoundTextChange(fDocumentUndoManager);
+ return new UndoableTextChange(fDocumentUndoManager);
+ }
+
+ /**
+ * Commits the current change into this one.
+ */
+ protected void commit() {
+ if (fStart < 0) {
+ if (fDocumentUndoManager.fFoldingIntoCompoundChange) {
+ fDocumentUndoManager.fCurrent= createCurrent();
+ } else {
+ reinitialize();
+ }
+ } else {
+ updateTextChange();
+ fDocumentUndoManager.fCurrent= createCurrent();
+ }
+ }
+
+ /**
+ * Updates the text from the buffers without resetting the buffers or adding
+ * anything to the stack.
+ */
+ protected void pretendCommit() {
+ if (fStart > -1) {
+ fText= fDocumentUndoManager.fTextBuffer.toString();
+ fPreservedText= fDocumentUndoManager.fPreservedTextBuffer.toString();
+ }
+ }
+
+ /**
+ * Attempt a commit of this change and answer true if a new fCurrent was
+ * created as a result of the commit.
+ *
+ * @return <code>true</code> if the change was committed and created
+ * a new <code>fCurrent</code>, <code>false</code> if not
+ */
+ protected boolean attemptCommit() {
+ pretendCommit();
+ if (isValid()) {
+ fDocumentUndoManager.commit();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether this text change is valid for undo or redo.
+ *
+ * @return <code>true</code> if the change is valid for undo or redo
+ */
+ protected boolean isValid() {
+ return fStart > -1 && fEnd > -1 && fText != null;
+ }
+
+ /*
+ * @see java.lang.Object#toString()
+ */
+ public String toString() {
+ String delimiter= ", "; //$NON-NLS-1$
+ StringBuffer text= new StringBuffer(super.toString());
+ text.append("\n"); //$NON-NLS-1$
+ text.append(this.getClass().getName());
+ text.append(" undo modification stamp: "); //$NON-NLS-1$
+ text.append(fUndoModificationStamp);
+ text.append(" redo modification stamp: "); //$NON-NLS-1$
+ text.append(fRedoModificationStamp);
+ text.append(" start: "); //$NON-NLS-1$
+ text.append(fStart);
+ text.append(delimiter);
+ text.append("end: "); //$NON-NLS-1$
+ text.append(fEnd);
+ text.append(delimiter);
+ text.append("text: '"); //$NON-NLS-1$
+ text.append(fText);
+ text.append('\'');
+ text.append(delimiter);
+ text.append("preservedText: '"); //$NON-NLS-1$
+ text.append(fPreservedText);
+ text.append('\'');
+ return text.toString();
+ }
+
+ /**
+ * Return the undo modification stamp
+ *
+ * @return the undo modification stamp for this change
+ */
+ protected long getUndoModificationStamp() {
+ return fUndoModificationStamp;
+ }
+
+ /**
+ * Return the redo modification stamp
+ *
+ * @return the redo modification stamp for this change
+ */
+ protected long getRedoModificationStamp() {
+ return fRedoModificationStamp;
+ }
+ }
+
+
+ /**
+ * Represents an undo-able text change consisting of several individual
+ * changes.
+ */
+ private static class UndoableCompoundTextChange extends UndoableTextChange {
+
+ /** The list of individual changes */
+ private List fChanges= new ArrayList();
+
+ /**
+ * Creates a new compound text change.
+ *
+ * @param manager
+ * the undo manager for this change
+ */
+ UndoableCompoundTextChange(DocumentUndoManager manager) {
+ super(manager);
+ }
+
+ /**
+ * Adds a new individual change to this compound change.
+ *
+ * @param change the change to be added
+ */
+ protected void add(UndoableTextChange change) {
+ fChanges.add(change);
+ }
+
+ /*
+ * @see org.eclipse.text.undo.UndoableTextChange#undo(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)
+ */
+ public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) {
+
+ int size= fChanges.size();
+ if (size > 0) {
+ UndoableTextChange c;
+
+ c= (UndoableTextChange) fChanges.get(0);
+ fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, true);
+
+ for (int i= size - 1; i >= 0; --i) {
+ c= (UndoableTextChange) fChanges.get(i);
+ c.undoTextChange();
+ }
+ fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, uiInfo,
+ DocumentUndoEvent.UNDONE, true);
+ }
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * @see org.eclipse.text.undo.UndoableTextChange#redo(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)
+ */
+ public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) {
+
+ int size= fChanges.size();
+ if (size > 0) {
+
+ UndoableTextChange c;
+ c= (UndoableTextChange) fChanges.get(size - 1);
+ fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, true);
+
+ for (int i= 0; i <= size - 1; ++i) {
+ c= (UndoableTextChange) fChanges.get(i);
+ c.redoTextChange();
+ }
+ fDocumentUndoManager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, uiInfo,
+ DocumentUndoEvent.REDONE, true);
+ }
+
+ return Status.OK_STATUS;
+ }
+
+ /*
+ * @see org.eclipse.text.undo.UndoableTextChange#updateTextChange()
+ */
+ protected void updateTextChange() {
+ // first gather the data from the buffers
+ super.updateTextChange();
+
+ // the result of the update is stored as a child change
+ UndoableTextChange c= new UndoableTextChange(fDocumentUndoManager);
+ c.fStart= fStart;
+ c.fEnd= fEnd;
+ c.fText= fText;
+ c.fPreservedText= fPreservedText;
+ c.fUndoModificationStamp= fUndoModificationStamp;
+ c.fRedoModificationStamp= fRedoModificationStamp;
+ add(c);
+
+ // clear out all indexes now that the child is added
+ reinitialize();
+ }
+
+ /*
+ * @see org.eclipse.text.undo.UndoableTextChange#createCurrent()
+ */
+ protected UndoableTextChange createCurrent() {
+
+ if (!fDocumentUndoManager.fFoldingIntoCompoundChange)
+ return new UndoableTextChange(fDocumentUndoManager);
+
+ reinitialize();
+ return this;
+ }
+
+ /*
+ * @see org.eclipse.text.undo.UndoableTextChange#commit()
+ */
+ protected void commit() {
+ // if there is pending data, update the text change
+ if (fStart > -1)
+ updateTextChange();
+ fDocumentUndoManager.fCurrent= createCurrent();
+ }
+
+ /*
+ * @see org.eclipse.text.undo.UndoableTextChange#isValid()
+ */
+ protected boolean isValid() {
+ return fStart > -1 || fChanges.size() > 0;
+ }
+
+ /*
+ * @see org.eclipse.text.undo.UndoableTextChange#getUndoModificationStamp()
+ */
+ protected long getUndoModificationStamp() {
+ if (fStart > -1)
+ return super.getUndoModificationStamp();
+ else if (fChanges.size() > 0)
+ return ((UndoableTextChange) fChanges.get(0))
+ .getUndoModificationStamp();
+
+ return fUndoModificationStamp;
+ }
+
+ /*
+ * @see org.eclipse.text.undo.UndoableTextChange#getRedoModificationStamp()
+ */
+ protected long getRedoModificationStamp() {
+ if (fStart > -1)
+ return super.getRedoModificationStamp();
+ else if (fChanges.size() > 0)
+ return ((UndoableTextChange) fChanges.get(fChanges.size() - 1))
+ .getRedoModificationStamp();
+
+ return fRedoModificationStamp;
+ }
+ }
+
/**
* Internal listener to document changes.
@@ -124,7 +639,8 @@ public class DocumentUndoManager implements IDocumentUndoManager {
/*
* @see IOperationHistoryListener
*/
- class HistoryListener implements IOperationHistoryListener {
+ private class HistoryListener implements IOperationHistoryListener {
+
private IUndoableOperation fOperation;
public void historyNotification(final OperationHistoryEvent event) {
@@ -172,17 +688,17 @@ public class DocumentUndoManager implements IDocumentUndoManager {
/**
* The undo context for this document undo manager.
*/
- ObjectUndoContext fUndoContext;
+ private ObjectUndoContext fUndoContext;
/**
* The document whose changes are being tracked.
*/
- IDocument fDocument;
+ private IDocument fDocument;
/**
* The currently constructed edit.
*/
- UndoableTextChange fCurrent;
+ private UndoableTextChange fCurrent;
/**
* The internal document listener.
@@ -192,12 +708,12 @@ public class DocumentUndoManager implements IDocumentUndoManager {
/**
* Indicates whether the current change belongs to a compound change.
*/
- boolean fFoldingIntoCompoundChange= false;
+ private boolean fFoldingIntoCompoundChange= false;
/**
* The operation history being used to store the undo history.
*/
- IOperationHistory fHistory;
+ private IOperationHistory fHistory;
/**
* The operation history listener used for managing undo and redo before and
@@ -215,17 +731,17 @@ public class DocumentUndoManager implements IDocumentUndoManager {
/**
* The document modification stamp for redo.
*/
- protected long fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+ private long fPreservedRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
/**
* Text buffer to collect viewer content which has been replaced
*/
- StringBuffer fPreservedTextBuffer;
+ private StringBuffer fPreservedTextBuffer;
/**
* The document modification stamp for undo.
*/
- protected long fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
+ private long fPreservedUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
/**
* The last delete text edit.
@@ -235,7 +751,7 @@ public class DocumentUndoManager implements IDocumentUndoManager {
/**
* Text buffer to collect text which is inserted into the viewer
*/
- StringBuffer fTextBuffer;
+ private StringBuffer fTextBuffer;
/** Indicates inserting state. */
private boolean fInserting= false;
@@ -305,6 +821,46 @@ public class DocumentUndoManager implements IDocumentUndoManager {
}
fCurrent.commit();
}
+
+ /*
+ * @see org.eclipse.text.undo.IDocumentUndoManager#reset()
+ */
+ public void reset() {
+ if (isConnected()) {
+ shutdown();
+ initialize();
+ }
+ }
+
+ /*
+ * @see org.eclipse.text.undo.IDocumentUndoManager#redoable()
+ */
+ public boolean redoable() {
+ return OperationHistoryFactory.getOperationHistory().canRedo(fUndoContext);
+ }
+
+ /*
+ * @see org.eclipse.text.undo.IDocumentUndoManager#undoable()
+ */
+ public boolean undoable() {
+ return OperationHistoryFactory.getOperationHistory().canUndo(fUndoContext);
+ }
+
+ /*
+ * @see org.eclipse.text.undo.IDocumentUndoManager#undo()
+ */
+ public void redo() throws ExecutionException {
+ if (isConnected() && redoable())
+ OperationHistoryFactory.getOperationHistory().redo(getUndoContext(), null, null);
+ }
+
+ /*
+ * @see org.eclipse.text.undo.IDocumentUndoManager#undo()
+ */
+ public void undo() throws ExecutionException {
+ if (undoable())
+ OperationHistoryFactory.getOperationHistory().undo(fUndoContext, null, null);
+ }
/*
* @see org.eclipse.jface.text.IDocumentUndoManager#connect(java.lang.Object)
@@ -350,7 +906,7 @@ public class DocumentUndoManager implements IDocumentUndoManager {
/*
* @see org.eclipse.jface.text.IDocumentUndoManager#setUndoLimit(int)
*/
- public void setUndoLimit(int undoLimit) {
+ public void setMaximalUndoLevel(int undoLimit) {
fHistory.setLimit(fUndoContext, undoLimit);
}
@@ -672,8 +1228,7 @@ public class DocumentUndoManager implements IDocumentUndoManager {
return false;
return !fConnected.isEmpty();
}
-
-
+
/*
* @see org.eclipse.jface.text.IDocumentUndoManager#transferUndoHistory(IDocumentUndoManager)
*/
@@ -692,7 +1247,7 @@ public class DocumentUndoManager implements IDocumentUndoManager {
}
// Now update the manager that owns the text edit.
if (op instanceof UndoableTextChange) {
- ((UndoableTextChange)op).manager= this;
+ ((UndoableTextChange)op).fDocumentUndoManager= this;
}
}
diff --git a/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManagerRegistry.java b/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManagerRegistry.java
index 404eb3c73..50b38e3d5 100644
--- a/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManagerRegistry.java
+++ b/org.eclipse.text/src/org/eclipse/text/undo/DocumentUndoManagerRegistry.java
@@ -30,15 +30,12 @@ import org.eclipse.jface.text.IDocument;
* <p>
* This class is not intended to be subclassed.
* </p>
- * <p>
- * XXX: This is work in progress and can change anytime until API for 3.2 is frozen.
- * </p>
*
* @since 3.2
*/
public final class DocumentUndoManagerRegistry {
- final private static class Record {
+ private static final class Record {
public Record(IDocument document) {
count= 0;
undoManager= new DocumentUndoManager(document);
diff --git a/org.eclipse.text/src/org/eclipse/text/undo/IDocumentUndoManager.java b/org.eclipse.text/src/org/eclipse/text/undo/IDocumentUndoManager.java
index 080d36197..3fc75a7f3 100644
--- a/org.eclipse.text/src/org/eclipse/text/undo/IDocumentUndoManager.java
+++ b/org.eclipse.text/src/org/eclipse/text/undo/IDocumentUndoManager.java
@@ -10,6 +10,7 @@
*******************************************************************************/
package org.eclipse.text.undo;
+import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IUndoContext;
/**
@@ -35,8 +36,6 @@ import org.eclipse.core.commands.operations.IUndoContext;
*
* @see DocumentUndoManagerRegistry
* @see IDocumentUndoListener
- * @see UndoableTextChange
- * @see UndoableCompoundTextChange
* @see org.eclipse.jface.text.IDocument
* @since 3.2
*/
@@ -59,7 +58,7 @@ public interface IDocumentUndoManager {
*
* @param listener the document undo listener to be added as a listener
*/
- public abstract void addDocumentUndoListener(IDocumentUndoListener listener);
+ void addDocumentUndoListener(IDocumentUndoListener listener);
/**
* Removes the specified listener from the list of document undo listeners.
@@ -69,19 +68,19 @@ public interface IDocumentUndoManager {
*
* @param listener the document undo listener to be removed
*/
- public abstract void removeDocumentUndoListener(IDocumentUndoListener listener);
+ void removeDocumentUndoListener(IDocumentUndoListener listener);
/**
* Returns the undo context registered for this document
*
* @return the undo context registered for this document
*/
- public abstract IUndoContext getUndoContext();
+ IUndoContext getUndoContext();
/**
* Closes the currently open text edit and open a new one.
*/
- public abstract void commit();
+ void commit();
/**
* Connects to the undo manager. Used to signify that a client is monitoring
@@ -90,7 +89,7 @@ public interface IDocumentUndoManager {
*
* @param client the object connecting to the undo manager
*/
- public abstract void connect(Object client);
+ void connect(Object client);
/**
* Disconnects from the undo manager. Used to signify that a client is no
@@ -100,20 +99,20 @@ public interface IDocumentUndoManager {
*
* @param client the object disconnecting from the undo manager
*/
- public abstract void disconnect(Object client);
+ void disconnect(Object client);
/**
* Signals the undo manager that all subsequent changes until
* <code>endCompoundChange</code> is called are to be undone in one piece.
*/
- public abstract void beginCompoundChange();
+ void beginCompoundChange();
/**
* Signals the undo manager that the sequence of changes which started with
* <code>beginCompoundChange</code> has been finished. All subsequent
* changes are considered to be individually undo-able.
*/
- public abstract void endCompoundChange();
+ void endCompoundChange();
/**
* Sets the limit of the undo history to the specified value. The provided
@@ -121,7 +120,42 @@ public interface IDocumentUndoManager {
*
* @param undoLimit the length of this undo manager's history
*/
- public abstract void setUndoLimit(int undoLimit);
+ void setMaximalUndoLevel(int undoLimit);
+
+ /**
+ * Resets the history of the undo manager. After that call,
+ * there aren't any undo-able or redo-able text changes.
+ */
+ void reset();
+
+ /**
+ * Returns whether at least one text change can be rolled back.
+ *
+ * @return <code>true</code> if at least one text change can be rolled back
+ */
+ boolean undoable();
+
+ /**
+ * Returns whether at least one text change can be repeated. A text change
+ * can be repeated only if it was executed and rolled back.
+ *
+ * @return <code>true</code> if at least on text change can be repeated
+ */
+ boolean redoable();
+
+ /**
+ * Rolls back the most recently executed text change.
+ *
+ * @throws ExecutionException if an exception occurred during undo
+ */
+ void undo() throws ExecutionException;
+
+ /**
+ * Repeats the most recently rolled back text change.
+ *
+ * @throws ExecutionException if an exception occurred during redo
+ */
+ void redo() throws ExecutionException;
/**
* Transfers the undo history from the specified document undo manager to
diff --git a/org.eclipse.text/src/org/eclipse/text/undo/UndoableCompoundTextChange.java b/org.eclipse.text/src/org/eclipse/text/undo/UndoableCompoundTextChange.java
deleted file mode 100644
index d7ed61d2a..000000000
--- a/org.eclipse.text/src/org/eclipse/text/undo/UndoableCompoundTextChange.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2006 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.text.undo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-
-/**
- * Represents an undo-able text change consisting of several individual
- * changes.
- *
- * @see org.eclipse.text.undo.UndoableTextChange
- * @see org.eclipse.text.undo.DocumentUndoManager
- * @since 3.2
- */
-class UndoableCompoundTextChange extends UndoableTextChange {
-
- /** The list of individual changes */
- private List fChanges= new ArrayList();
-
- /**
- * Creates a new compound text change.
- *
- * @param manager
- * the undo manager for this change
- */
- UndoableCompoundTextChange(DocumentUndoManager manager) {
- super(manager);
- }
-
- /**
- * Adds a new individual change to this compound change.
- *
- * @param change the change to be added
- */
- protected void add(UndoableTextChange change) {
- fChanges.add(change);
- }
-
- /*
- * @see org.eclipse.text.undo.UndoableTextChange#undo(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)
- */
- public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) {
-
- int size= fChanges.size();
- if (size > 0) {
- UndoableTextChange c;
-
- c= (UndoableTextChange) fChanges.get(0);
- manager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, true);
-
- for (int i= size - 1; i >= 0; --i) {
- c= (UndoableTextChange) fChanges.get(i);
- c.undoTextChange();
- }
- manager.fireDocumentUndo(c.fStart, c.fPreservedText, c.fText, uiInfo,
- DocumentUndoEvent.UNDONE, true);
- }
- return Status.OK_STATUS;
- }
-
- /*
- * @see org.eclipse.text.undo.UndoableTextChange#redo(org.eclipse.core.runtime.IProgressMonitor, org.eclipse.core.runtime.IAdaptable)
- */
- public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) {
-
- int size= fChanges.size();
- if (size > 0) {
-
- UndoableTextChange c;
- c= (UndoableTextChange) fChanges.get(size - 1);
- manager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, true);
-
- for (int i= 0; i <= size - 1; ++i) {
- c= (UndoableTextChange) fChanges.get(i);
- c.redoTextChange();
- }
- manager.fireDocumentUndo(c.fStart, c.fText, c.fPreservedText, uiInfo,
- DocumentUndoEvent.REDONE, true);
- }
-
- return Status.OK_STATUS;
- }
-
- /*
- * @see org.eclipse.text.undo.UndoableTextChange#updateTextChange()
- */
- protected void updateTextChange() {
- // first gather the data from the buffers
- super.updateTextChange();
-
- // the result of the update is stored as a child change
- UndoableTextChange c= new UndoableTextChange(manager);
- c.fStart= fStart;
- c.fEnd= fEnd;
- c.fText= fText;
- c.fPreservedText= fPreservedText;
- c.fUndoModificationStamp= fUndoModificationStamp;
- c.fRedoModificationStamp= fRedoModificationStamp;
- add(c);
-
- // clear out all indexes now that the child is added
- reinitialize();
- }
-
- /*
- * @see org.eclipse.text.undo.UndoableTextChange#createCurrent()
- */
- protected UndoableTextChange createCurrent() {
-
- if (!manager.fFoldingIntoCompoundChange)
- return new UndoableTextChange(manager);
-
- reinitialize();
- return this;
- }
-
- /*
- * @see org.eclipse.text.undo.UndoableTextChange#commit()
- */
- protected void commit() {
- // if there is pending data, update the text change
- if (fStart > -1)
- updateTextChange();
- manager.fCurrent= createCurrent();
- }
-
- /*
- * @see org.eclipse.text.undo.UndoableTextChange#isValid()
- */
- protected boolean isValid() {
- return fStart > -1 || fChanges.size() > 0;
- }
-
- /*
- * @see org.eclipse.text.undo.UndoableTextChange#getUndoModificationStamp()
- */
- protected long getUndoModificationStamp() {
- if (fStart > -1)
- return super.getUndoModificationStamp();
- else if (fChanges.size() > 0)
- return ((UndoableTextChange) fChanges.get(0))
- .getUndoModificationStamp();
-
- return fUndoModificationStamp;
- }
-
- /*
- * @see org.eclipse.text.undo.UndoableTextChange#getRedoModificationStamp()
- */
- protected long getRedoModificationStamp() {
- if (fStart > -1)
- return super.getRedoModificationStamp();
- else if (fChanges.size() > 0)
- return ((UndoableTextChange) fChanges.get(fChanges.size() - 1))
- .getRedoModificationStamp();
-
- return fRedoModificationStamp;
- }
-}
diff --git a/org.eclipse.text/src/org/eclipse/text/undo/UndoableTextChange.java b/org.eclipse.text/src/org/eclipse/text/undo/UndoableTextChange.java
deleted file mode 100644
index 21574aa1c..000000000
--- a/org.eclipse.text/src/org/eclipse/text/undo/UndoableTextChange.java
+++ /dev/null
@@ -1,381 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2006 IBM Corporation and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * IBM Corporation - initial API and implementation
- *******************************************************************************/
-package org.eclipse.text.undo;
-
-import org.eclipse.core.commands.operations.AbstractOperation;
-import org.eclipse.core.commands.operations.IOperationHistory;
-
-import org.eclipse.core.runtime.IAdaptable;
-import org.eclipse.core.runtime.IProgressMonitor;
-import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.Status;
-
-import org.eclipse.jface.text.BadLocationException;
-import org.eclipse.jface.text.IDocumentExtension4;
-
-/**
- * Represents an undo-able text change, described as the
- * replacement of some preserved text with new text.
- * <p>
- * Based on the DefaultUndoManager.TextCommand from R3.1.
- * </p>
- *
- * @see org.eclipse.text.undo.DocumentUndoManager
- * @since 3.2
- */
-class UndoableTextChange extends AbstractOperation {
-
- /** The start index of the replaced text. */
- protected int fStart= -1;
-
- /** The end index of the replaced text. */
- protected int fEnd= -1;
-
- /** The newly inserted text. */
- protected String fText;
-
- /** The replaced text. */
- protected String fPreservedText;
-
- /** The undo modification stamp. */
- protected long fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
-
- /** The redo modification stamp. */
- protected long fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
-
- /** The undo manager that generated the change. */
- protected DocumentUndoManager manager;
-
- /**
- * Creates a new text change.
- *
- * @param manager the undo manager for this change
- */
- UndoableTextChange(DocumentUndoManager manager) {
- super(UndoMessages.getString("DocumentUndoManager.operationLabel")); //$NON-NLS-1$
- this.manager= manager;
- addContext(manager.getUndoContext());
- }
-
- /**
- * Re-initializes this text change.
- */
- protected void reinitialize() {
- fStart= fEnd= -1;
- fText= fPreservedText= null;
- fUndoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
- fRedoModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP;
- }
-
- /**
- * Sets the start and the end index of this change.
- *
- * @param start the start index
- * @param end the end index
- */
- protected void set(int start, int end) {
- fStart= start;
- fEnd= end;
- fText= null;
- fPreservedText= null;
- }
-
- /*
- * @see org.eclipse.core.commands.operations.IUndoableOperation#dispose()
- */
- public void dispose() {
- reinitialize();
- }
-
- /**
- * Undo the change described by this change.
- */
- protected void undoTextChange() {
- try {
- if (manager.fDocument instanceof IDocumentExtension4)
- ((IDocumentExtension4) manager.fDocument).replace(fStart, fText
- .length(), fPreservedText, fUndoModificationStamp);
- else
- manager.fDocument.replace(fStart, fText.length(),
- fPreservedText);
- } catch (BadLocationException x) {
- }
- }
-
- /*
- * @see org.eclipse.core.commands.operations.IUndoableOperation#canUndo()
- */
- public boolean canUndo() {
- if (isValid()) {
- if (manager.fDocument instanceof IDocumentExtension4) {
- long docStamp= ((IDocumentExtension4) manager.fDocument)
- .getModificationStamp();
-
- // Normal case: an undo is valid if its redo will restore
- // document to its current modification stamp
- boolean canUndo= docStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP
- || docStamp == getRedoModificationStamp();
-
- /*
- * Special case to check if the answer is false. If the last
- * document change was empty, then the document's modification
- * stamp was incremented but nothing was committed. The
- * operation being queried has an older stamp. In this case
- * only, the comparison is different. A sequence of document
- * changes that include an empty change is handled correctly
- * when a valid commit follows the empty change, but when
- * #canUndo() is queried just after an empty change, we must
- * special case the check. The check is very specific to prevent
- * false positives. see
- * https://bugs.eclipse.org/bugs/show_bug.cgi?id=98245
- */
- if (!canUndo
- && this == manager.fHistory
- .getUndoOperation(manager.fUndoContext)
- // this is the latest operation
- && this != manager.fCurrent
- // there is a more current operation not on the stack
- && !manager.fCurrent.isValid()
- // the current operation is not a valid document
- // modification
- && manager.fCurrent.fUndoModificationStamp !=
- // the invalid current operation has a document stamp
- IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) {
- canUndo= manager.fCurrent.fRedoModificationStamp == docStamp;
- }
- /*
- * When the composite is the current operation, it may hold the
- * timestamp of a no-op change. We check this here rather than
- * in an override of canUndo() in UndoableCompoundTextChange simply to
- * keep all the special case checks in one place.
- */
- if (!canUndo
- && this == manager.fHistory
- .getUndoOperation(manager.fUndoContext)
- && // this is the latest operation
- this instanceof UndoableCompoundTextChange
- && this == manager.fCurrent
- && // this is the current operation
- this.fStart == -1
- && // the current operation text is not valid
- manager.fCurrent.fRedoModificationStamp != IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) {
- // but it has a redo stamp
- canUndo= manager.fCurrent.fRedoModificationStamp == docStamp;
- }
-
- }
- // if there is no timestamp to check, simply return true per the
- // 3.0.1 behavior
- return true;
- }
- return false;
- }
-
- /*
- * @see org.eclipse.core.commands.operations.IUndoableOperation#canRedo()
- */
- public boolean canRedo() {
- if (isValid()) {
- if (manager.fDocument instanceof IDocumentExtension4) {
- long docStamp= ((IDocumentExtension4) manager.fDocument)
- .getModificationStamp();
- return docStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP
- || docStamp == getUndoModificationStamp();
- }
- // if there is no timestamp to check, simply return true per the
- // 3.0.1 behavior
- return true;
- }
- return false;
- }
-
- /*
- * @see org.eclipse.core.commands.operations.IUndoableOperation#canExecute()
- */
- public boolean canExecute() {
- return manager.isConnected();
- }
-
- /*
- * @see org.eclipse.core.commands.operations.IUndoableOperation.IUndoableOperation#execute(IProgressMonitor, IAdaptable)
- */
- public IStatus execute(IProgressMonitor monitor, IAdaptable uiInfo) {
- // Text changes execute as they are typed, so executing one has no
- // effect.
- return Status.OK_STATUS;
- }
-
- /**
- * {@inheritDoc}
- * Notifies clients about the undo.
- */
- public IStatus undo(IProgressMonitor monitor, IAdaptable uiInfo) {
- if (isValid()) {
- manager.fireDocumentUndo(fStart, fPreservedText, fText, uiInfo, DocumentUndoEvent.ABOUT_TO_UNDO, false);
- undoTextChange();
- manager.fireDocumentUndo(fStart, fPreservedText, fText, uiInfo, DocumentUndoEvent.UNDONE, false);
- return Status.OK_STATUS;
- }
- return IOperationHistory.OPERATION_INVALID_STATUS;
- }
-
- /**
- * Re-applies the change described by this change.
- */
- protected void redoTextChange() {
- try {
- if (manager.fDocument instanceof IDocumentExtension4)
- ((IDocumentExtension4) manager.fDocument).replace(fStart, fEnd - fStart, fText, fRedoModificationStamp);
- else
- manager.fDocument.replace(fStart, fEnd - fStart, fText);
- } catch (BadLocationException x) {
- }
- }
-
- /**
- * Re-applies the change described by this change that was previously
- * undone. Also notifies clients about the redo.
- *
- * @param monitor the progress monitor to use if necessary
- * @param uiInfo an adaptable that can provide UI info if needed
- * @return the status
- */
- public IStatus redo(IProgressMonitor monitor, IAdaptable uiInfo) {
- if (isValid()) {
- manager.fireDocumentUndo(fStart, fText, fPreservedText, uiInfo, DocumentUndoEvent.ABOUT_TO_REDO, false);
- redoTextChange();
- manager.fireDocumentUndo(fStart, fText, fPreservedText, uiInfo, DocumentUndoEvent.REDONE, false);
- return Status.OK_STATUS;
- }
- return IOperationHistory.OPERATION_INVALID_STATUS;
- }
-
- /**
- * Update the change in response to a commit.
- */
-
- protected void updateTextChange() {
- fText= manager.fTextBuffer.toString();
- manager.fTextBuffer.setLength(0);
- fPreservedText= manager.fPreservedTextBuffer.toString();
- manager.fPreservedTextBuffer.setLength(0);
- }
-
- /**
- * Creates a new uncommitted text change depending on whether a compound
- * change is currently being executed.
- *
- * @return a new, uncommitted text change or a compound text change
- */
- protected UndoableTextChange createCurrent() {
- if (manager.fFoldingIntoCompoundChange)
- return new UndoableCompoundTextChange(manager);
- return new UndoableTextChange(manager);
- }
-
- /**
- * Commits the current change into this one.
- */
- protected void commit() {
- if (fStart < 0) {
- if (manager.fFoldingIntoCompoundChange) {
- manager.fCurrent= createCurrent();
- } else {
- reinitialize();
- }
- } else {
- updateTextChange();
- manager.fCurrent= createCurrent();
- }
- }
-
- /**
- * Updates the text from the buffers without resetting the buffers or adding
- * anything to the stack.
- */
- protected void pretendCommit() {
- if (fStart > -1) {
- fText= manager.fTextBuffer.toString();
- fPreservedText= manager.fPreservedTextBuffer.toString();
- }
- }
-
- /**
- * Attempt a commit of this change and answer true if a new fCurrent was
- * created as a result of the commit.
- *
- * @return <code>true</code> if the change was committed and created
- * a new <code>fCurrent</code>, <code>false</code> if not
- */
- protected boolean attemptCommit() {
- pretendCommit();
- if (isValid()) {
- manager.commit();
- return true;
- }
- return false;
- }
-
- /**
- * Checks whether this text change is valid for undo or redo.
- *
- * @return <code>true</code> if the change is valid for undo or redo
- */
- protected boolean isValid() {
- return fStart > -1 && fEnd > -1 && fText != null;
- }
-
- /*
- * @see java.lang.Object#toString()
- */
- public String toString() {
- String delimiter= ", "; //$NON-NLS-1$
- StringBuffer text= new StringBuffer(super.toString());
- text.append("\n"); //$NON-NLS-1$
- text.append(this.getClass().getName());
- text.append(" undo modification stamp: "); //$NON-NLS-1$
- text.append(fUndoModificationStamp);
- text.append(" redo modification stamp: "); //$NON-NLS-1$
- text.append(fRedoModificationStamp);
- text.append(" start: "); //$NON-NLS-1$
- text.append(fStart);
- text.append(delimiter);
- text.append("end: "); //$NON-NLS-1$
- text.append(fEnd);
- text.append(delimiter);
- text.append("text: '"); //$NON-NLS-1$
- text.append(fText);
- text.append('\'');
- text.append(delimiter);
- text.append("preservedText: '"); //$NON-NLS-1$
- text.append(fPreservedText);
- text.append('\'');
- return text.toString();
- }
-
- /**
- * Return the undo modification stamp
- *
- * @return the undo modification stamp for this change
- */
- protected long getUndoModificationStamp() {
- return fUndoModificationStamp;
- }
-
- /**
- * Return the redo modification stamp
- *
- * @return the redo modification stamp for this change
- */
- protected long getRedoModificationStamp() {
- return fRedoModificationStamp;
- }
-}

Back to the top