Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 16401 Details for
Bug 21476
[preference] Maximum number of "Undo"s
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read
this important communication.
[patch]
Undos stored on disk
fileUndoManager.patch (text/plain), 28.42 KB, created by
snowl
on 2004-12-06 16:20:35 EST
(
hide
)
Description:
Undos stored on disk
Filename:
MIME Type:
Creator:
snowl
Created:
2004-12-06 16:20:35 EST
Size:
28.42 KB
patch
obsolete
>Index: DefaultUndoManager.java >=================================================================== >RCS file: /home/eclipse/org.eclipse.jface.text/src/org/eclipse/jface/text/DefaultUndoManager.java,v >retrieving revision 1.18 >diff -u -r1.18 DefaultUndoManager.java >--- DefaultUndoManager.java 3 Dec 2004 16:14:35 -0000 1.18 >+++ DefaultUndoManager.java 6 Dec 2004 20:55:32 -0000 >@@ -12,8 +12,21 @@ > package org.eclipse.jface.text; > > >+import java.io.ByteArrayInputStream; >+import java.io.ByteArrayOutputStream; >+import java.io.File; >+import java.io.IOException; >+import java.io.ObjectInputStream; >+import java.io.ObjectOutputStream; >+import java.io.RandomAccessFile; >+import java.io.Serializable; > import java.util.ArrayList; >+import java.util.LinkedHashMap; > import java.util.List; >+import java.util.Map; >+ >+import org.eclipse.core.runtime.IPath; >+import org.eclipse.core.runtime.Platform; > import org.eclipse.swt.SWT; > import org.eclipse.swt.custom.StyledText; > import org.eclipse.swt.events.KeyEvent; >@@ -41,11 +54,278 @@ > * @see KeyListener > */ > public class DefaultUndoManager implements IUndoManager { >+ >+ /** >+ * A file that holds undo commands on disk and allow random access to >+ * commands via their offset in the file. >+ */ >+ static class PersistenceFile { >+ private RandomAccessFile fFile; >+ >+ PersistenceFile() throws IOException { >+ >+ IPath path= Platform.getLocation().append(".metadata").append(".undo"); //$NON-NLS-1$ //$NON-NLS-2$ >+ File f= path.toFile(); >+ >+ // Scrap the last session's undo log. >+ if (f.exists()) >+ f.delete(); >+ >+ fFile= new RandomAccessFile(f, "rw"); //$NON-NLS-1$ >+ } >+ >+ /** >+ * Put an object in the file and return its offset. >+ * @param s The object to store >+ * @return The offset of the object in the file. >+ * @throws IOException >+ */ >+ public synchronized Long put(Serializable s) throws IOException { >+ byte[] bs= toByteArray(s); >+ long index= fFile.length(); >+ fFile.seek(index); >+ fFile.writeInt(bs.length); >+ fFile.write(bs); >+ return new Long(index); >+ } >+ >+ /** >+ * Get the object at the givne offset. >+ * @param index The offset of the object to get. >+ * @return The object that was at the offset. >+ * @throws IOException >+ */ >+ public synchronized Serializable get(Long index) throws IOException { >+ fFile.seek(index.longValue()); >+ int l= fFile.readInt(); >+ byte[] bs= new byte[l]; >+ fFile.readFully(bs); >+ return toObject(bs); >+ } >+ >+ /** >+ * Serialize an object into a byte array. >+ * @param s The object to serialize. >+ * @return The byte array that holds the object. >+ * @throws IOException >+ */ >+ byte[] toByteArray(Serializable s) throws IOException { >+ ByteArrayOutputStream baos= new ByteArrayOutputStream(); >+ ObjectOutputStream oos= new ObjectOutputStream(baos); >+ oos.writeObject(s); >+ return baos.toByteArray(); >+ } >+ >+ /** >+ * Convert a serialized object in a byte array to an object. >+ * @param bs The byte array holdinghte serialized object. >+ * @return The object. >+ * @throws IOException >+ */ >+ Serializable toObject(byte[] bs) throws IOException { >+ try { >+ ByteArrayInputStream bais= new ByteArrayInputStream(bs); >+ ObjectInputStream ois= new ObjectInputStream(bais); >+ return (Serializable)ois.readObject(); >+ } >+ catch (ClassNotFoundException cnfe) { >+ // Can't happen. >+ throw new RuntimeException("", cnfe); //$NON-NLS-1$ >+ } >+ } >+ } >+ >+ /** >+ * Caching layer that wraps PersistenceFile to avoid disk lookups >+ * for recent changes. >+ */ >+ static class PersistenceCache { >+ private PersistenceFile fFile; >+ >+ /** >+ * LRU cache of undo commands. >+ */ >+ private LinkedHashMap lru= new LinkedHashMap(25, .75F, true) { >+ /** >+ * Shut up eclipse's warnings. >+ */ >+ private static final long serialVersionUID = 6009335074727417445L; >+ >+ /** >+ * Limit the number of entries in the cache. >+ */ >+ public boolean removeEldestEntry(Map.Entry eldest) { >+ return size() > 25; >+ } >+ }; >+ >+ PersistenceCache() { >+ try { >+ fFile= new PersistenceFile(); >+ } >+ catch (IOException ioe) { >+ ioe.printStackTrace(); >+ >+ throw new RuntimeException("", ioe); //$NON-NLS-1$ >+ } >+ } >+ >+ /** >+ * Put an object in the file and return its offset. >+ * @param s The object to store >+ * @return The offset of the object in the file. >+ * @throws IOException >+ */ >+ public synchronized Long put(Serializable s) throws IOException { >+ Long l= fFile.put(s); >+ lru.put(l, s); >+ return l; >+ } >+ >+ /** >+ * Get the object at the givne offset. >+ * @param index The offset of the object to get. >+ * @return The object that was at the offset. >+ * @throws IOException >+ */ >+ public synchronized Serializable get(Long index) throws IOException { >+ Serializable s= (Serializable) lru.get(index); >+ if (s == null) { >+ s= fFile.get(index); >+ lru.put(index, s); >+ } >+ return s; >+ } >+ >+ /** >+ * Clear the <b>cache</b>. Does <b>not</b> clear/empty the underlying >+ * file. Used to remove references that would prevent GC. >+ */ >+ public synchronized void clear() { >+ lru.clear(); >+ } >+ } >+ >+ /** >+ * Per document undo stack. >+ */ >+ class CommandStack { >+ /** >+ * Undo stack. Contains offsets in the persistence file of the undo >+ * commands. >+ */ >+ private List fIndex = new ArrayList(); >+ >+ /** >+ * Add an entry to the stack at the given position. >+ * @param a The position to add the entry to the stack. >+ * @param s The entry to add to the stack. >+ */ >+ private void add(int a, Serializable s) { >+ try { >+ Long i = persistenceCache.put(s); >+ fIndex.add(a, i); >+ } >+ catch (IOException ioe) { >+ ioe.printStackTrace(); >+ >+ // undo stack is corrupt, wipe it to avoid further damage. >+ fIndex.clear(); >+ } >+ } >+ >+ /** >+ * Add an entry to the top of the stack. >+ * @param s The entry to put on top of the stack. >+ */ >+ private void add(TextCommand s) { >+ try { >+ Long i = persistenceCache.put(s); >+ fIndex.add(i); >+ } >+ catch (IOException ioe) { >+ ioe.printStackTrace(); >+ >+ // undo stack is corrupt, wipe it to avoid further damage. >+ fIndex.clear(); >+ } >+ } >+ >+ /** >+ * Get the undo command at the requested postition in the stack. >+ * @param counter The position of the requested command. >+ * @return The command at that position. >+ */ >+ private TextCommand get(int counter) { >+ try { >+ Long i = (Long)fIndex.get(counter); >+ TextCommand tc= (TextCommand) persistenceCache.get(i); >+ >+ // Replace the transient field that may be lost during serialization. >+ tc.setUndoManager(DefaultUndoManager.this); >+ >+ return tc; >+ } >+ catch (IOException ioe) { >+ ioe.printStackTrace(); >+ >+ throw new RuntimeException("", ioe); //$NON-NLS-1$ >+ } >+ } >+ >+ /** >+ * Truncate the stack to the parameter size. Entries will be >+ * removed fro mthe otp of the stack. >+ * @param size The size of the stack after the truncation. >+ */ >+ private void truncate(int size) { >+ while (fIndex.size() > size) { >+ fIndex.remove(size); >+ } >+ } >+ >+ /** >+ * The stack's size. >+ * @return The size of the stack. >+ */ >+ private int size() { >+ return fIndex.size(); >+ } >+ } >+ >+ /** >+ * The range of chars in the document that was changed by an edit. >+ */ >+ static class Range implements Cloneable { >+ int fStart; >+ int fEnd; >+ int fOverwritten; >+ int fCursor; >+ >+ public Object clone() { >+ try { >+ return super.clone(); >+ } catch (CloneNotSupportedException clnse) { >+ throw new RuntimeException(clnse); >+ } >+ } >+ } > > /** > * Represents an undo-able edit command. > */ >- class TextCommand { >+ static class TextCommand implements Serializable { >+ >+ /** >+ * Silence Eclipse's warnings. >+ */ >+ private static final long serialVersionUID = 6009335074727417445L; >+ >+ /** >+ * Psuedo-inner class. Allow us to serialize the command without >+ * serializing the undo manager. >+ */ >+ transient DefaultUndoManager fParent; > > /** The start index of the replaced text */ > protected int fStart= -1; >@@ -55,6 +335,20 @@ > protected String fText; > /** The replaced text */ > protected String fPreservedText; >+ /** Was the text typed rather that pasted */ >+ protected boolean fTyped= false; >+ >+ TextCommand(DefaultUndoManager parent) { >+ this.fParent= parent; >+ } >+ >+ /** >+ * Used when deserializing the command. >+ * @param um The undo-manager this command is part of. >+ */ >+ void setUndoManager(DefaultUndoManager um) { >+ fParent= um; >+ } > > /** > * Re-initializes this text command. >@@ -84,7 +378,7 @@ > */ > protected void undoTextChange() { > try { >- fTextViewer.getDocument().replace(fStart, fText.length(), fPreservedText); >+ fParent.fTextViewer.getDocument().replace(fStart, fText.length(), fPreservedText); > } catch (BadLocationException x) { > } > } >@@ -95,7 +389,7 @@ > */ > protected void undo() { > undoTextChange(); >- selectAndReveal(fStart, fPreservedText == null ? 0 : fPreservedText.length()); >+ fParent.selectAndReveal(fStart, fPreservedText == null ? 0 : fPreservedText.length()); > } > > /** >@@ -105,7 +399,7 @@ > */ > protected void redoTextChange() { > try { >- fTextViewer.getDocument().replace(fStart, fEnd - fStart, fText); >+ fParent.fTextViewer.getDocument().replace(fStart, fEnd - fStart, fText); > } catch (BadLocationException x) { > } > } >@@ -116,7 +410,7 @@ > */ > protected void redo() { > redoTextChange(); >- selectAndReveal(fStart, fText == null ? 0 : fText.length()); >+ fParent.selectAndReveal(fStart, fText == null ? 0 : fText.length()); > } > > /** >@@ -125,16 +419,11 @@ > */ > protected void updateCommandStack() { > >- int length= fCommandStack.size(); >- for (int i= fCommandCounter + 1; i < length; i++) >- fCommandStack.remove(fCommandCounter + 1); >+ fParent.fCommandStack.truncate(fParent.fCommandCounter + 1); > >- fCommandStack.add(this); >+ fParent.fCommandStack.add(this); > >- while (fCommandStack.size() > fUndoLevel) >- fCommandStack.remove(0); >- >- fCommandCounter= fCommandStack.size() - 1; >+ fParent.fCommandCounter++; > } > > /** >@@ -144,7 +433,51 @@ > * @return a new, uncommitted text command or a compound text command > */ > protected TextCommand createCurrent() { >- return fFoldingIntoCompoundChange ? new CompoundTextCommand() : new TextCommand(); >+ return fParent.fFoldingIntoCompoundChange ? new CompoundTextCommand(fParent) : new TextCommand(fParent); >+ } >+ >+ /** >+ * Replaces sequential commands at the top of the stack with a single >+ * command if they are on the same line and we have started a new line. >+ */ >+ protected void collateStack() { >+ if (fParent.fCommandCounter < 1) >+ return; >+ >+ // If this previous command is collateable with this one then we >+ // haven't reached the end of a line and don't want to collate yet. >+ TextCommand previous= fParent.fCommandStack.get(fParent.fCommandCounter); >+ if (previous.isCollatable(this)) >+ return; >+ >+ // if the current command cannot be collated with any preceding ones >+ // then collate all the preceding ones that are on the same line. >+ >+ CompoundTextCommand compoundTextCommand= new CompoundTextCommand(fParent); >+ int counter= fParent.fCommandCounter; >+ >+ TextCommand lastTc= null; >+ while (counter >= 0) { >+ TextCommand top= fParent.fCommandStack.get(counter); >+ >+ if (top.isCollatable(lastTc)) { >+ counter--; >+ top.addSelf(compoundTextCommand.fCommands); >+ lastTc= top; >+ } else { >+ break; >+ } >+ } >+ >+ if (counter < fParent.fCommandCounter - 1) { >+ // Note: all the entries higher in the stack will be >+ // overwritten/truncated by updateCommandStack(). >+ fParent.fCommandStack.add(counter + 1, compoundTextCommand); >+ fParent.fCommandCounter= counter + 1; >+ >+ // Keep on collating until there is nothing left to do. >+ collateStack(); >+ } > } > > /** >@@ -156,15 +489,141 @@ > reinitialize(); > } else { > >- fText= fTextBuffer.toString(); >- fTextBuffer.setLength(0); >- fPreservedText= fPreservedTextBuffer.toString(); >- fPreservedTextBuffer.setLength(0); >+ fText= fParent.fTextBuffer.toString(); >+ fParent.fTextBuffer.setLength(0); >+ fPreservedText= fParent.fPreservedTextBuffer.toString(); >+ fParent.fPreservedTextBuffer.setLength(0); > >+ if (fParent.fAutoCollate) { >+ collateStack(); >+ } > updateCommandStack(); > } > >- fCurrent= createCurrent(); >+ fParent.fCurrent= createCurrent(); >+ } >+ >+ /** >+ * Is this a chunk that can be collated into another to form one line. >+ * >+ * @param next a TextCommand to test for collatability with this one. >+ * @return true if this TextCommand is collateable with <code>next</code> >+ */ >+ protected boolean isCollatable(TextCommand next) { >+ >+ if (next != null) { >+ Range range= new Range(); >+ range.fStart= fStart; >+ range.fEnd= fStart + fText.length(); >+ if (!next.isCollatable()) >+ return false; >+ if (!next.isOverlapping(range, false)) >+ return false; >+ } >+ >+ return isCollatable(); >+ } >+ >+ /** >+ * Is it possible that this command can be collated with others. >+ * @return true if it can be collated. >+ */ >+ protected boolean isCollatable() { >+ >+ // A deletion of text, do not allow this to be >+ // confused with eating code-complete or lost to over-zelaous >+ // collation. >+ if (fPreservedText.length() > 0 && fText.length() == 0) { >+ return false; >+ } >+ >+ String[] delimiters= fParent.fTextViewer.getDocument().getLegalLineDelimiters(); >+ int[] idxs= TextUtilities.indexOf(delimiters, fText, 0); >+ if (idxs[0] != -1 || idxs[1] != -1) { >+ return false; >+ } >+ >+ delimiters= fParent.fTextViewer.getDocument().getLegalLineDelimiters(); >+ idxs= TextUtilities.indexOf(delimiters, fPreservedText, 0); >+ if (idxs[0] != -1 || idxs[1] != -1) { >+ return false; >+ } >+ >+ return true; >+ } >+ >+ /** >+ * Does the range of chars this command overlap with the range passed >+ * in. >+ * >+ * @param range The range to test for overlap with this one. >+ * @param start The edit may have changed the positions of chars >+ * after this undo. If true compare the overlap using the original >+ * document (before the edit which created the undo) as a reference. >+ * If false use the document after the edit. >+ * @return Whether the parameter range overlaps this one. >+ */ >+ protected boolean isOverlapping(Range range, boolean start) { >+ >+ int localEnd; >+ if (start) { >+ localEnd= fStart + fPreservedText.length(); >+ } >+ else { >+ localEnd= fStart + fText.length(); >+ } >+ return ((range.fStart <= fStart && fStart <= range.fEnd) || >+ (range.fStart <= localEnd && localEnd <= range.fEnd)); >+ } >+ >+ /** >+ * Take a range and 'expand' the range to include the range covered by >+ * this undo command. In other words: return a range that is the >+ * combination of the parameter range and this undo command's range. >+ * >+ * @param range The range to combine with this undo's range. >+ * @return A range which includes both the parameter range and this undo's >+ * range. >+ */ >+ protected Range expandRange(Range range) { >+ if (range == null) { >+ range= new Range(); >+ range.fStart= fStart; >+ range.fEnd= fStart + fText.length(); >+ range.fCursor= fStart; >+ range.fOverwritten= fPreservedText.length(); >+ >+ return range; >+ } >+ >+ if (fStart < range.fStart) { >+ range.fStart= fStart; >+ } >+ >+ if (fStart + fText.length() > range.fCursor) { >+ int overlap= (fStart + fText.length() - range.fCursor); >+ >+ if (overlap > range.fOverwritten) { >+ range.fEnd= range.fEnd + overlap - range.fOverwritten; >+ range.fOverwritten= 0; >+ } else { >+ range.fOverwritten -= overlap; >+ } >+ } >+ >+ range.fOverwritten += fPreservedText.length(); >+ >+ range.fCursor= fStart; >+ >+ return range; >+ } >+ >+ /** >+ * Add this command to the parameter list. >+ * @param commands The list to add this command to. >+ */ >+ protected void addSelf(List commands) { >+ commands.add(0, this); > } > } > >@@ -172,11 +631,30 @@ > * Represents an undo-able edit command consisting of several > * individual edit commands. > */ >- class CompoundTextCommand extends TextCommand { >+ static class CompoundTextCommand extends TextCommand { >+ >+ /** >+ * Silence Eclipse warning. >+ */ >+ private static final long serialVersionUID = 6009335074727417445L; > > /** The list of individual commands */ > private List fCommands= new ArrayList(); > >+ CompoundTextCommand(DefaultUndoManager parent) { >+ super(parent); >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.jface.text.DefaultUndoManager.TextCommand#setUndoManager(org.eclipse.jface.text.DefaultUndoManager) >+ */ >+ void setUndoManager(DefaultUndoManager um) { >+ super.setUndoManager(um); >+ for (int i= 0; i < fCommands.size(); i++) { >+ ((TextCommand)fCommands.get(i)).setUndoManager(um); >+ } >+ } >+ > /** > * Adds a new individual command to this compound command. > * >@@ -191,8 +669,8 @@ > */ > protected void undo() { > ITextViewerExtension extension= null; >- if (fTextViewer instanceof ITextViewerExtension) >- extension= (ITextViewerExtension) fTextViewer; >+ if (fParent.fTextViewer instanceof ITextViewerExtension) >+ extension= (ITextViewerExtension) fParent.fTextViewer; > > if (extension != null) > extension.setRedraw(false); >@@ -225,8 +703,8 @@ > protected void redo() { > > ITextViewerExtension extension= null; >- if (fTextViewer instanceof ITextViewerExtension) >- extension= (ITextViewerExtension) fTextViewer; >+ if (fParent.fTextViewer instanceof ITextViewerExtension) >+ extension= (ITextViewerExtension) fParent.fTextViewer; > > if (extension != null) > extension.setRedraw(false); >@@ -257,7 +735,7 @@ > * @see TextCommand#updateCommandStack > */ > protected void updateCommandStack() { >- TextCommand c= new TextCommand(); >+ TextCommand c= new TextCommand(fParent); > c.fStart= fStart; > c.fEnd= fEnd; > c.fText= fText; >@@ -265,7 +743,7 @@ > > add(c); > >- if (!fFoldingIntoCompoundChange) >+ if (!fParent.fFoldingIntoCompoundChange) > super.updateCommandStack(); > } > >@@ -274,8 +752,8 @@ > */ > protected TextCommand createCurrent() { > >- if (!fFoldingIntoCompoundChange) >- return new TextCommand(); >+ if (!fParent.fFoldingIntoCompoundChange) >+ return new TextCommand(fParent); > > reinitialize(); > return this; >@@ -286,14 +764,103 @@ > */ > protected void commit() { > if (fStart < 0) { >- if (fCommands.size() > 0 && !fFoldingIntoCompoundChange) { >+ if (fCommands.size() > 0 && !fParent.fFoldingIntoCompoundChange) { > super.updateCommandStack(); >- fCurrent= createCurrent(); >+ fParent.fCurrent= createCurrent(); > return; > } > } > super.commit(); > } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.jface.text.DefaultUndoManager.TextCommand#isCollatable(org.eclipse.jface.text.DefaultUndoManager.TextCommand) >+ */ >+ protected boolean isCollatable(TextCommand next) { >+ >+ Range currentRange= null; >+ >+ if (next != null) { >+ currentRange= next.expandRange(null); >+ } >+ >+ // See if the param range is collatable with all the component >+ // commands in order. >+ for (int i= fCommands.size() - 1; i >= 0; i--) { >+ TextCommand tc= (TextCommand) fCommands.get(i); >+ >+ if (!tc.isCollatable()) { >+ return false; >+ } >+ if (currentRange != null && !tc.isOverlapping(currentRange, false)) { >+ return false; >+ } >+ >+ currentRange= tc.expandRange(currentRange); >+ } >+ >+ return true; >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.jface.text.DefaultUndoManager.TextCommand#isOverlapping(org.eclipse.jface.text.DefaultUndoManager.Range, boolean) >+ */ >+ protected boolean isOverlapping(Range range, boolean start) { >+ >+ Range localRange= (Range)range.clone(); >+ >+ for (int i= fCommands.size() - 1; i >= 0; i--) { >+ TextCommand tc= (TextCommand) fCommands.get(i); >+ >+ if (!tc.isOverlapping(localRange, start)) { >+ return false; >+ } >+ >+ localRange= tc.expandRange(localRange); >+ } >+ >+ return true; >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.jface.text.DefaultUndoManager.TextCommand#isCollatable() >+ */ >+ protected boolean isCollatable() { >+ >+ for (int i= fCommands.size() - 1; i >= 0; i--) { >+ TextCommand tc= (TextCommand) fCommands.get(i); >+ >+ if (!tc.isCollatable()) { >+ return false; >+ } >+ } >+ >+ return true; >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.jface.text.DefaultUndoManager.TextCommand#expandRange(org.eclipse.jface.text.DefaultUndoManager.Range) >+ */ >+ protected Range expandRange(Range range) { >+ for (int i= fCommands.size() - 1; i >= 0; i--) { >+ TextCommand tc= (TextCommand) fCommands.get(i); >+ >+ range= tc.expandRange(range); >+ } >+ >+ return range; >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.jface.text.DefaultUndoManager.TextCommand#addSelf(java.util.List) >+ */ >+ protected void addSelf(List commands) { >+ int length= fCommands.size(); >+ for (int i= length - 1; i >= 0; i--) { >+ TextCommand tc= (TextCommand)fCommands.get(i); >+ tc.addSelf(commands); >+ } >+ } > } > > /** >@@ -408,6 +975,8 @@ > > } > >+ private static PersistenceCache persistenceCache = new PersistenceCache(); >+ > > /** Text buffer to collect text which is inserted into the viewer */ > private StringBuffer fTextBuffer= new StringBuffer(); >@@ -423,8 +992,6 @@ > private TextInputListener fTextInputListener; > > >- /** Indicates inserting state */ >- private boolean fInserting= false; > /** Indicates overwriting state */ > private boolean fOverwriting= false; > /** Indicates whether the current change belongs to a compound change */ >@@ -433,17 +1000,21 @@ > /** The text viewer the undo manager is connected to */ > private ITextViewer fTextViewer; > >- /** Supported undo level */ >- private int fUndoLevel; > /** The list of undo-able edit commands */ >- private List fCommandStack; >+ private CommandStack fCommandStack; > /** The currently constructed edit command */ > private TextCommand fCurrent; > /** The last delete edit command */ > private TextCommand fPreviousDelete; > /** Command counter into the edit command stack */ > private int fCommandCounter= -1; >- >+ >+ /** Split words into chunks. */ >+ private boolean fChunkWords; >+ /** Split lines into chunks. */ >+ private boolean fChunkLines; >+ /** Join together chunks on line-breaks. */ >+ private boolean fAutoCollate; > > /** > * Creates a new undo manager who remembers the specified number of edit commands. >@@ -451,7 +1022,28 @@ > * @param undoLevel the length of this manager's history > */ > public DefaultUndoManager(int undoLevel) { >- setMaximalUndoLevel(undoLevel); >+ this(true, true, true); >+ } >+ >+ /** >+ * Creates a new undo manager. >+ * <p> >+ * The only valid combinations of (chunkWords, chunkLines, autoCollate) are: >+ * <ul> >+ * <li>(true, true, false) - Fine-grain undo >+ * <li>(true, true, true) - Smart undo (recommended) >+ * <li>(false, false, false) - Course-grain undo (classic Eclipse undo) >+ * </ul> >+ * Using other combinations may not produce satisfying undos. >+ * >+ * @param chunkWords Split words into chunks. >+ * @param chunkLines Split lines into chunks. >+ * @param autoCollate Join together chunks on line-breaks. >+ */ >+ public DefaultUndoManager(boolean chunkWords, boolean chunkLines, boolean autoCollate) { >+ this.fChunkLines= chunkLines; >+ this.fChunkWords= chunkWords; >+ this.fAutoCollate= autoCollate; > } > > /** >@@ -542,7 +1134,6 @@ > */ > private void commit() { > >- fInserting= false; > fOverwriting= false; > fPreviousDelete.reinitialize(); > >@@ -554,27 +1145,27 @@ > */ > private void internalRedo() { > ++fCommandCounter; >- TextCommand cmd= (TextCommand) fCommandStack.get(fCommandCounter); >+ TextCommand cmd= fCommandStack.get(fCommandCounter); > > listenToTextChanges(false); > cmd.redo(); > listenToTextChanges(true); > >- fCurrent= new TextCommand(); >+ fCurrent= new TextCommand(this); > } > > /** > * Does undo the last editing command. > */ > private void internalUndo() { >- TextCommand cmd= (TextCommand) fCommandStack.get(fCommandCounter); >+ TextCommand cmd= fCommandStack.get(fCommandCounter); > -- fCommandCounter; > > listenToTextChanges(false); > cmd.undo(); > listenToTextChanges(true); > >- fCurrent= new TextCommand(); >+ fCurrent= new TextCommand(this); > } > > /** >@@ -616,8 +1207,6 @@ > fPretendedState.cmdCounter= fCommandCounter; > } else { > int sz= Math.max(fCommandCounter, 0) + 1; >- if (sz > fUndoLevel) >- sz -= fUndoLevel; > fPretendedState.stackSize= sz; > fPretendedState.cmdCounter= sz - 1; > } >@@ -646,14 +1235,24 @@ > // text will be inserted > if ((length == 1) || isWhitespaceText(insertedText)) { > // by typing or model manipulation >- if (!fInserting || (modelStart != fCurrent.fStart + fTextBuffer.length())) { >+ if (!fCurrent.fTyped || (modelStart != fCurrent.fStart + fTextBuffer.length()) || (isWhitespaceText(insertedText) && fChunkLines)) { > commit(); >- fInserting= true; >- } >+ fCurrent.fTyped= true; >+ } else if (fTextBuffer.length() > 0 && Character.isLetterOrDigit(fTextBuffer.charAt(fTextBuffer.length() - 1))) { >+ // chunk at the end of words >+ if (!Character.isLetterOrDigit(insertedText.charAt(0)) && fChunkWords) { >+ commit(); >+ fCurrent.fTyped= true; >+ } >+ } >+ > if (fCurrent.fStart < 0) > fCurrent.fStart= fCurrent.fEnd= modelStart; >- if (length > 0) >- fTextBuffer.append(insertedText); >+ >+ fTextBuffer.append(insertedText); >+ if (isWhitespaceText(insertedText) && fChunkLines) { >+ commit(); >+ } > } else if (length > 0) { > // by pasting > commit(); >@@ -671,6 +1270,11 @@ > > // whereby selection is empty > >+ // Chunk lines >+ if (isWhitespaceText(replacedText) && fChunkLines) { >+ commit(); >+ } >+ > if (fPreviousDelete.fStart == modelStart && fPreviousDelete.fEnd == modelEnd) { > // repeated DEL > >@@ -739,6 +1343,10 @@ > fCurrent.fEnd= modelEnd; > fTextBuffer.append(insertedText); > fPreservedTextBuffer.append(replacedText); >+ >+ if (insertedText.length() == 1) { >+ fCurrent.fTyped= true; >+ } > } > } > } >@@ -747,7 +1355,7 @@ > * @see org.eclipse.jface.text.IUndoManager#setMaximalUndoLevel(int) > */ > public void setMaximalUndoLevel(int undoLevel) { >- fUndoLevel= undoLevel; >+ // Ignored > } > > /* >@@ -756,9 +1364,9 @@ > public void connect(ITextViewer textViewer) { > if (fTextViewer == null && textViewer != null) { > fTextViewer= textViewer; >- fCommandStack= new ArrayList(); >- fCurrent= new TextCommand(); >- fPreviousDelete= new TextCommand(); >+ fCommandStack= new CommandStack(); >+ fCurrent= new TextCommand(this); >+ fPreviousDelete= new TextCommand(this); > addListeners(); > } > } >@@ -773,8 +1381,11 @@ > > fCurrent= null; > if (fCommandStack != null) { >- fCommandStack.clear(); >+ fCommandStack.truncate(0); > fCommandStack= null; >+ >+ // Allow the GC to collect us. >+ persistenceCache.clear(); > } > fTextBuffer= null; > fPreservedTextBuffer= null; >@@ -787,13 +1398,12 @@ > */ > public void reset() { > if (isConnected()) { >- if (fCommandStack != null) >- fCommandStack.clear(); >+ if (fCommandStack != null) >+ fCommandStack.truncate(0); > fCommandCounter= -1; > if (fCurrent != null) > fCurrent.reinitialize(); > fFoldingIntoCompoundChange= false; >- fInserting= false; > fOverwriting= false; > fTextBuffer.setLength(0); > fPreservedTextBuffer.setLength(0);
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 21476
:
7553
| 16401