From 28af61ba10ebb6016356bb28dc491a61389db4ab Mon Sep 17 00:00:00 2001 From: Doug Schaefer Date: Wed, 4 Jan 2006 20:09:35 +0000 Subject: [PATCH] Bug 113518 - Patch to improve performance of CModel at reconcile time. --- .../cdt/core/model/ElementChangedEvent.java | 14 +++ .../internal/core/model/CModelManager.java | 21 ++++ .../cdt/internal/core/model/CShiftData.java | 99 +++++++++++++++++++ .../ui/editor/CContentOutlinerProvider.java | 47 +++++++++ .../cdt/internal/ui/text/CReconciler.java | 15 +++ .../ui/text/CReconcilingStrategy.java | 45 ++++++++- .../ui/text/CSourceViewerConfiguration.java | 4 +- .../cdt/internal/ui/text/CWordFinder.java | 87 ++++++++++++++++ 8 files changed, 325 insertions(+), 7 deletions(-) create mode 100644 core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CShiftData.java create mode 100644 core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconciler.java diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ElementChangedEvent.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ElementChangedEvent.java index 0ade7b32e6e..22e10c782e5 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ElementChangedEvent.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/core/model/ElementChangedEvent.java @@ -77,6 +77,20 @@ public class ElementChangedEvent extends EventObject { * @since 2.0 */ public static final int POST_RECONCILE = 4; + + /** + * Event type constant indicating the following: + * Source text is changed somewhere in function body + * No global data affected for any C element + * but element offsets should be recalculated now. + * + * Note: usually, CShifData object is sent with + * this event as ICElementDelta + * + * @see CShiftData + */ + public static final int POST_SHIFT = 5; + /* * Event type indicating the nature of this event. * It can be a combination either: diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelManager.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelManager.java index 5d6c2c2d621..ed4138e3daa 100644 --- a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelManager.java +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CModelManager.java @@ -797,6 +797,11 @@ public class CModelManager implements IResourceChangeListener, ICDescriptorListe fire(null, eventType); } + public void fireShift(int offset, int size, int lines) { + ICElementDelta delta = new CShiftData(offset, size, lines); + fire(delta, ElementChangedEvent.POST_SHIFT); + } + /** * Fire C Model deltas, flushing them after the fact. * If the firing mode has been turned off, this has no effect. @@ -837,6 +842,9 @@ public class CModelManager implements IResourceChangeListener, ICDescriptorListe case ElementChangedEvent.POST_RECONCILE : fireReconcileDelta(listeners, listenerMask, listenerCount); break; + case ElementChangedEvent.POST_SHIFT: + fireShiftEvent(deltaToNotify, listeners, listenerMask, listenerCount); + return; } } } @@ -880,6 +888,19 @@ public class CModelManager implements IResourceChangeListener, ICDescriptorListe } } + private void fireShiftEvent(ICElementDelta deltaToNotify, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) { + + // post change deltas + if (VERBOSE) { + System.out.println("FIRING POST_SHIFT event [" + Thread.currentThread() + "]:"); //$NON-NLS-1$//$NON-NLS-2$ + System.out.println(deltaToNotify == null ? "" : deltaToNotify.toString()); //$NON-NLS-1$ + } + if (deltaToNotify != null) { + this.flush(); + notifyListeners(deltaToNotify, ElementChangedEvent.POST_SHIFT, listeners, listenerMask, listenerCount); + } + } + public void notifyListeners(ICElementDelta deltaToNotify, int eventType, IElementChangedListener[] listeners, int[] listenerMask, int listenerCount) { diff --git a/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CShiftData.java b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CShiftData.java new file mode 100644 index 00000000000..89bf981be78 --- /dev/null +++ b/core/org.eclipse.cdt.core/model/org/eclipse/cdt/internal/core/model/CShiftData.java @@ -0,0 +1,99 @@ +/** + * + */ +package org.eclipse.cdt.internal.core.model; + +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ICElementDelta; +import org.eclipse.core.resources.IResourceDelta; + +/** + * In this case, no delta for specific element passed + * Instead we'll notify Outline about offsets change. + */ +public class CShiftData implements ICElementDelta { + + public int fOffset; + public int fSize; + public int fLines; + + public CShiftData(int offset, int size, int lines) { + fOffset = offset; + fSize = size; + fLines = lines; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getAddedChildren() + */ + public ICElementDelta[] getAddedChildren() { + return new ICElementDelta[0]; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getAffectedChildren() + */ + public ICElementDelta[] getAffectedChildren() { + return new ICElementDelta[0]; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getChangedChildren() + */ + public ICElementDelta[] getChangedChildren() { + return new ICElementDelta[0]; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getElement() + */ + public ICElement getElement() { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getFlags() + */ + public int getFlags() { + return 0; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getKind() + */ + public int getKind() { + return 0; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getMovedFromElement() + */ + public ICElement getMovedFromElement() { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getMovedToElement() + */ + public ICElement getMovedToElement() { + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getRemovedChildren() + */ + public ICElementDelta[] getRemovedChildren() { + return new ICElementDelta[0]; + } + + /* (non-Javadoc) + * @see org.eclipse.cdt.core.model.ICElementDelta#getResourceDeltas() + */ + public IResourceDelta[] getResourceDeltas() { + return null; + } + + public String toString() { + return ("CShiftData: offset=" + fOffset + ", size=" + fSize + ", lines=" + fLines); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CContentOutlinerProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CContentOutlinerProvider.java index c5249fc59fb..f3faadf1ec0 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CContentOutlinerProvider.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CContentOutlinerProvider.java @@ -14,12 +14,16 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.CoreModel; import org.eclipse.cdt.core.model.ElementChangedEvent; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICElementDelta; import org.eclipse.cdt.core.model.IElementChangedListener; +import org.eclipse.cdt.core.model.ISourceRange; import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.internal.core.model.CShiftData; +import org.eclipse.cdt.internal.core.model.SourceManipulation; import org.eclipse.cdt.internal.ui.BaseCElementContentProvider; import org.eclipse.cdt.internal.ui.util.StringMatcher; import org.eclipse.cdt.ui.PreferenceConstants; @@ -30,6 +34,7 @@ import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.progress.DeferredTreeContentManager; @@ -118,6 +123,43 @@ public class CContentOutlinerProvider extends BaseCElementContentProvider { } } + /** + * Called after CEditor contents is changed. + * Existing elements can change their offset and length. + * + * @param offset + * position where source was changed + * @param size + * length of ins + * ertion (negaive for deletion) + */ + public void contentShift(CShiftData sdata) { + try { + ICElement[] el = root.getChildren(); + for (int i=0; i< el.length; i++) { + if (!(el[i] instanceof SourceManipulation)) continue; + + SourceManipulation sm = (SourceManipulation) el[i]; + ISourceRange src = sm.getSourceRange(); + int endOffset = src.getStartPos() + src.getLength(); + + // code BELOW this element changed - do nothing ! + if (sdata.fOffset > endOffset) { continue; } + + if (sdata.fOffset < src.getStartPos()) { + // code ABOVE this element changed - modify offset + sm.setIdPos(src.getIdStartPos() + sdata.fSize,src.getIdLength()); + sm.setPos(src.getStartPos() + sdata.fSize, src.getLength()); + sm.setLines(src.getStartLine() + sdata.fLines, src.getEndLine() + sdata.fLines); + } else { + // code INSIDE of this element changed - modify length + sm.setPos(src.getStartPos(), src.getLength() + sdata.fSize); + sm.setLines(src.getStartLine(), src.getEndLine() + sdata.fLines); + } + } + } catch (CModelException e) {} + } + /** * @see org.eclipse.jface.viewers.IContentProvider#dispose() */ @@ -239,6 +281,11 @@ public class CContentOutlinerProvider extends BaseCElementContentProvider { * @see org.eclipse.cdt.core.model.IElementChangedListener#elementChanged(org.eclipse.cdt.core.model.ElementChangedEvent) */ public void elementChanged(final ElementChangedEvent e) { + if (e.getType() == ElementChangedEvent.POST_SHIFT && e.getDelta() instanceof CShiftData) { + contentShift((CShiftData)(e.getDelta())); + return; + } + final ICElementDelta delta = findElement(root, e.getDelta()); if (delta != null) { contentUpdated(); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconciler.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconciler.java new file mode 100644 index 00000000000..e32c8081d4c --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconciler.java @@ -0,0 +1,15 @@ +package org.eclipse.cdt.internal.ui.text; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.reconciler.DirtyRegion; +import org.eclipse.jface.text.reconciler.Reconciler; + +public class CReconciler extends Reconciler { + + protected void process(DirtyRegion dirtyRegion) { + if(dirtyRegion != null) { + getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE).reconcile(dirtyRegion, null); + } + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconcilingStrategy.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconcilingStrategy.java index 33c5b79a6c8..40e307a9d3d 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconcilingStrategy.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconcilingStrategy.java @@ -14,6 +14,7 @@ package org.eclipse.cdt.internal.ui.text; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.internal.core.model.CModelManager; import org.eclipse.cdt.internal.ui.editor.CEditor; import org.eclipse.cdt.internal.ui.editor.IReconcilingParticipant; import org.eclipse.cdt.ui.CUIPlugin; @@ -28,12 +29,11 @@ import org.eclipse.ui.texteditor.ITextEditor; public class CReconcilingStrategy implements IReconcilingStrategy { - private ITextEditor fEditor; private IWorkingCopyManager fManager; private IProgressMonitor fProgressMonitor; - - + private String txt = null; + public CReconcilingStrategy(CEditor editor) { fEditor= editor; fManager= CUIPlugin.getDefault().getWorkingCopyManager(); @@ -62,10 +62,45 @@ public class CReconcilingStrategy implements IReconcilingStrategy { /** - * @see IReconcilingStrategy#reconcile(dirtyRegion, region) + * @see IReconcilingStrategy#reconcile(dirtyRegion, reion) */ public void reconcile(DirtyRegion dirtyRegion, IRegion region) { - reconcile(); + // consistent data needs not further checks ! + ITranslationUnit tu = fManager.getWorkingCopy(fEditor.getEditorInput()); + if (tu != null && tu.isWorkingCopy()) { + try { + if (tu.isConsistent()) return; + } catch (CModelException e) {} + } + + // bug 113518 + // local data needs not to be re-parsed + boolean needReconcile = true; + int dOff = dirtyRegion.getOffset(); + int dLen = dirtyRegion.getLength(); + IDocument doc = fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput()); + + if ((doc != null) && (!CWordFinder.isGlobal(doc, dOff))) { + String s = ""; + if (dirtyRegion.getType().charAt(2) == 'i') { // insert operation + s = dirtyRegion.getText(); + if (!CWordFinder.hasCBraces(s)) { + CModelManager.getDefault().fireShift(dOff, dLen, CWordFinder.countLFs(s)); + needReconcile = false; + } + } else { // remove operation + // check whether old document copy is relevant + if (txt != null && (txt.length() == doc.getLength() + dLen)) { + s = txt.substring(dOff, dOff + dLen); + if (!CWordFinder.hasCBraces(s)) { + CModelManager.getDefault().fireShift(dOff, -dLen, -CWordFinder.countLFs(s)); + needReconcile = false; + } + } + } + } + if (needReconcile) reconcile(); + txt = doc.get(); // save doc copy for further use } private void reconcile() { diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java index d154f02b0d2..5dc23e7a129 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CSourceViewerConfiguration.java @@ -221,7 +221,7 @@ public class CSourceViewerConfiguration extends TextSourceViewerConfiguration { */ public IReconciler getReconciler(ISourceViewer sourceViewer) { if (fEditor != null && fEditor.isEditable()) { - Reconciler reconciler= new Reconciler() { + Reconciler reconciler= new CReconciler() { protected void initialProcess() { // prevent case where getDocument() returns null // and causes exception in initialProcess() @@ -231,7 +231,7 @@ public class CSourceViewerConfiguration extends TextSourceViewerConfiguration { } }; reconciler.setDelay(1000); - reconciler.setIsIncrementalReconciler(false); +// reconciler.setIsIncrementalReconciler(false); reconciler.setReconcilingStrategy(new CReconcilingStrategy(fEditor), IDocument.DEFAULT_CONTENT_TYPE); return reconciler; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordFinder.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordFinder.java index af225c1d340..09f712b2fd7 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordFinder.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CWordFinder.java @@ -15,12 +15,18 @@ import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; + /** * This is a helper class for the text editor to be able to determine, given a * particular offset in a document, various candidates segments for things like * context help, proposals and hovering. */ public class CWordFinder { + + private static final char CBRACE_L = '{'; + private static final char CBRACE_R = '}'; + private static final char BRACE_R = ')'; + /** * This method determines for a given offset into a given document what the * region is which defines the current word. A word is defined as the set of @@ -198,5 +204,86 @@ public class CWordFinder { return null; } + /** + * This method will determine whether current offset is contained + * in any function's body or it's outside it. + * + * @param document + * The document to be examined + * @param offset + * The offset into the document + * @return + * true if there is no function body around offset + * false otherwise + * + * @param document + * @param offset + * @return + */ + public static boolean isGlobal(IDocument document, int offset) { + try { + int pos = offset; + int bracketcount = 0; + char c; + + //Find left curled bracket from our position + while (pos > 0) { + c = document.getChar(pos--); + + if (c == CBRACE_R) { + bracketcount++; // take into account nested blocks + } else if (c == CBRACE_L) { + if (bracketcount-- == 0) { + do { + c = document.getChar(pos--); + if (c == BRACE_R) return false; + } while (Character.isSpace(c)); + // container block seems to be not a function or statement body + pos++; // step back one symbol + bracketcount = 0; // let's search for upper block + } + } + } + + } catch (BadLocationException x) { /* Ignore */ } + // return true in case of unknown result or exception + return true; + } + + /** + * Searches for line feed symbols in string. + * First met '\r' or '\n' is treated as LF symbol + * + * @param s + * string to search in. + * @return number of LFs met. + */ + public static int countLFs (String s) { + int counter = 0; + char lf = 0; + char c; + for (int i=0; i -1 || s.indexOf(CBRACE_R) > -1) return true; + else return false; + } }