From 0f53c480a169bcad3d0c3b1fec164669fbafc84d Mon Sep 17 00:00:00 2001 From: Anton Leherbauer Date: Tue, 8 May 2007 10:35:51 +0000 Subject: [PATCH] Fix for 182700: [Editor] Force reconcile in case of relevant changes --- .../ui/editor/InactiveCodeHighlighting.java | 9 +- .../cdt/internal/ui/text/CReconciler.java | 441 +++++++++++++++++- .../ui/text/CReconcilingStrategy.java | 18 +- .../ui/text/CSourceViewerConfiguration.java | 8 +- 4 files changed, 454 insertions(+), 22 deletions(-) diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InactiveCodeHighlighting.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InactiveCodeHighlighting.java index 9717457f4c4..2e3e0a5dba3 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InactiveCodeHighlighting.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InactiveCodeHighlighting.java @@ -346,6 +346,10 @@ public class InactiveCodeHighlighting implements ICReconcilingListener, ITextInp * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) */ public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { + if (fEditor != null && fLineBackgroundPainter != null && !fLineBackgroundPainter.isDisposed()) { + fLineBackgroundPainter.removeHighlightPositions(fInactiveCodePositions); + fInactiveCodePositions= Collections.EMPTY_LIST; + } } /* @@ -353,11 +357,6 @@ public class InactiveCodeHighlighting implements ICReconcilingListener, ITextInp */ public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { fDocument= newInput; - if (fEditor != null && fLineBackgroundPainter != null && !fLineBackgroundPainter.isDisposed()) { - List newInactiveCodePositions= Collections.EMPTY_LIST; - fLineBackgroundPainter.replaceHighlightPositions(fInactiveCodePositions, newInactiveCodePositions); - fInactiveCodePositions= newInactiveCodePositions; - } } } 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 index 80a9c79fd8e..9078132ce79 100644 --- 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2006 Wind River Systems, Inc. and others. + * Copyright (c) 2006, 2007 Wind River Systems, Inc. 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 @@ -10,8 +10,45 @@ *******************************************************************************/ package org.eclipse.cdt.internal.ui.text; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.reconciler.DirtyRegion; import org.eclipse.jface.text.reconciler.MonoReconciler; +import org.eclipse.swt.events.ShellAdapter; +import org.eclipse.swt.events.ShellEvent; +import org.eclipse.swt.events.ShellListener; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.texteditor.ITextEditor; + +import org.eclipse.cdt.core.CCorePlugin; +import org.eclipse.cdt.core.index.IIndexChangeEvent; +import org.eclipse.cdt.core.index.IIndexChangeListener; +import org.eclipse.cdt.core.index.IIndexerStateEvent; +import org.eclipse.cdt.core.index.IIndexerStateListener; +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.ICProject; +import org.eclipse.cdt.core.model.IElementChangedListener; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.IWorkingCopyManager; /** * A single strategy C reconciler. @@ -20,13 +57,304 @@ import org.eclipse.jface.text.reconciler.MonoReconciler; */ public class CReconciler extends MonoReconciler { + static class SingletonJob extends Job implements ISchedulingRule { + private Runnable fCode; + + SingletonJob(String name, Runnable code) { + super(name); + fCode= code; + setPriority(Job.SHORT); + setRule(this); + setUser(false); + setSystem(true); + } + + /* + * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) + */ + protected IStatus run(IProgressMonitor monitor) { + if (!monitor.isCanceled()) { + fCode.run(); + } + return Status.OK_STATUS; + } + + /* + * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule) + */ + public boolean contains(ISchedulingRule rule) { + return rule == this; + } + + /* + * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule) + */ + public boolean isConflicting(ISchedulingRule rule) { + return rule == this; + } + + } + /** - * Create a reconciler for the given strategy. + * Internal part listener for activating the reconciler. + */ + private class PartListener implements IPartListener { + + /* + * @see org.eclipse.ui.IPartListener#partActivated(org.eclipse.ui.IWorkbenchPart) + */ + public void partActivated(IWorkbenchPart part) { + if (part == fTextEditor) { + if (hasCModelChanged()) + CReconciler.this.scheduleReconciling(); + setEditorActive(true); + } + } + + /* + * @see org.eclipse.ui.IPartListener#partBroughtToTop(org.eclipse.ui.IWorkbenchPart) + */ + public void partBroughtToTop(IWorkbenchPart part) { + } + + /* + * @see org.eclipse.ui.IPartListener#partClosed(org.eclipse.ui.IWorkbenchPart) + */ + public void partClosed(IWorkbenchPart part) { + } + + /* + * @see org.eclipse.ui.IPartListener#partDeactivated(org.eclipse.ui.IWorkbenchPart) + */ + public void partDeactivated(IWorkbenchPart part) { + if (part == fTextEditor) { + setEditorActive(false); + } + } + + /* + * @see org.eclipse.ui.IPartListener#partOpened(org.eclipse.ui.IWorkbenchPart) + */ + public void partOpened(IWorkbenchPart part) { + } + } + + /** + * Internal Shell activation listener for activating the reconciler. + */ + private class ActivationListener extends ShellAdapter { + + private Control fControl; + + public ActivationListener(Control control) { + Assert.isNotNull(control); + fControl= control; + } + + /* + * @see org.eclipse.swt.events.ShellListener#shellActivated(org.eclipse.swt.events.ShellEvent) + */ + public void shellActivated(ShellEvent e) { + if (!fControl.isDisposed() && fControl.isVisible()) { + if (hasCModelChanged()) + CReconciler.this.scheduleReconciling(); + setEditorActive(true); + } + } + + /* + * @see org.eclipse.swt.events.ShellListener#shellDeactivated(org.eclipse.swt.events.ShellEvent) + */ + public void shellDeactivated(ShellEvent e) { + if (!fControl.isDisposed() && fControl.getShell() == e.getSource()) { + setEditorActive(false); + } + } + } + + /** + * Internal C element changed listener + */ + private class ElementChangedListener implements IElementChangedListener { + /* + * @see org.eclipse.cdt.core.model.IElementChangedListener#elementChanged(org.eclipse.cdt.core.model.ElementChangedEvent) + */ + public void elementChanged(ElementChangedEvent event) { + if (event.getType() == ElementChangedEvent.POST_CHANGE) { + if (isRelevantDelta(event.getDelta())) { + setCModelChanged(true); + if (!fIsReconciling && isEditorActive()) { + CReconciler.this.scheduleReconciling(); + } + } + } + } + + private boolean isRelevantDelta(ICElementDelta delta) { + final int flags = delta.getFlags(); + if ((flags & ICElementDelta.F_CONTENT) != 0) { + if (isRelevantElement(delta.getElement())) { + // mark model changed, but don't update immediately + fIndexerListener.ignoreChanges(false); + setCModelChanged(true); + } else { + fIndexerListener.ignoreChanges(true); + } + } + if ((flags & ( + ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE | + ICElementDelta.F_CHANGED_PATHENTRY_MACRO + )) != 0) { + if (isRelevantProject(delta.getElement().getCProject())) { + return true; + } + } + if ((flags & ICElementDelta.F_CHILDREN) != 0) { + ICElementDelta[] childDeltas= delta.getChangedChildren(); + for (int i = 0; i < childDeltas.length; i++) { + if (isRelevantDelta(childDeltas[i])) { + return true; + } + } + } + return false; + } + } + + private class IndexerListener implements IIndexerStateListener, IIndexChangeListener { + private boolean fIndexChanged; + private boolean fIgnoreChanges; + + /* + * @see org.eclipse.cdt.core.index.IIndexerStateListener#indexChanged(org.eclipse.cdt.core.index.IIndexerStateEvent) + */ + public void indexChanged(IIndexerStateEvent event) { + if (event.indexerIsIdle()) { + if (fIndexChanged || hasCModelChanged()) { + setCModelChanged(true); + if (!fIsReconciling && isEditorActive()) { + CReconciler.this.scheduleReconciling(); + } + } + fIgnoreChanges= false; + fIndexChanged= false; + } + } + + public void ignoreChanges(boolean ignore) { + fIgnoreChanges= ignore; + } + + /* + * @see org.eclipse.cdt.core.index.IIndexChangeListener#indexChanged(org.eclipse.cdt.core.index.IIndexChangeEvent) + */ + public void indexChanged(IIndexChangeEvent event) { + if (!fIndexChanged && !fIgnoreChanges && isRelevantProject(event.getAffectedProject())) { + fIndexChanged= true; + } + } + + } + + /** The reconciler's editor */ + private ITextEditor fTextEditor; + /** The part listener */ + private IPartListener fPartListener; + /** The shell listener */ + private ShellListener fActivationListener; + /** The C element changed listener. */ + private IElementChangedListener fCElementChangedListener; + /** The indexer listener */ + private IndexerListener fIndexerListener; + /** Tells whether the C model might have changed. */ + private volatile boolean fHasCModelChanged= false; + /** Tells whether this reconciler's editor is active. */ + private volatile boolean fIsEditorActive= true; + /** Tells whether a reconcile is in progress. */ + private volatile boolean fIsReconciling= false; + + private boolean fInitialProcessDone= false; + private Job fTriggerReconcilerJob; + + /** + * Create a reconciler for the given editor and strategy. * + * @param editor the text editor * @param strategy the C reconciling strategy */ - public CReconciler(CReconcilingStrategy strategy) { + public CReconciler(ITextEditor editor, CReconcilingStrategy strategy) { super(strategy, false); + fTextEditor= editor; + } + + /* + * @see org.eclipse.jface.text.reconciler.IReconciler#install(org.eclipse.jface.text.ITextViewer) + */ + public void install(ITextViewer textViewer) { + super.install(textViewer); + + fPartListener= new PartListener(); + IWorkbenchPartSite site= fTextEditor.getSite(); + IWorkbenchWindow window= site.getWorkbenchWindow(); + window.getPartService().addPartListener(fPartListener); + + fActivationListener= new ActivationListener(textViewer.getTextWidget()); + Shell shell= window.getShell(); + shell.addShellListener(fActivationListener); + + fCElementChangedListener= new ElementChangedListener(); + CoreModel.getDefault().addElementChangedListener(fCElementChangedListener); + + fIndexerListener= new IndexerListener(); + CCorePlugin.getIndexManager().addIndexerStateListener(fIndexerListener); + CCorePlugin.getIndexManager().addIndexChangeListener(fIndexerListener); + + fTriggerReconcilerJob= new SingletonJob("Trigger Reconciler", new Runnable() { //$NON-NLS-1$ + public void run() { + forceReconciling(); + }}); + } + + /* + * @see org.eclipse.jface.text.reconciler.IReconciler#uninstall() + */ + public void uninstall() { + fTriggerReconcilerJob.cancel(); + + IWorkbenchPartSite site= fTextEditor.getSite(); + IWorkbenchWindow window= site.getWorkbenchWindow(); + window.getPartService().removePartListener(fPartListener); + fPartListener= null; + + Shell shell= window.getShell(); + if (shell != null && !shell.isDisposed()) + shell.removeShellListener(fActivationListener); + fActivationListener= null; + + CoreModel.getDefault().removeElementChangedListener(fCElementChangedListener); + fCElementChangedListener= null; + + CCorePlugin.getIndexManager().removeIndexerStateListener(fIndexerListener); + CCorePlugin.getIndexManager().removeIndexChangeListener(fIndexerListener); + fIndexerListener= null; + super.uninstall(); + } + + protected void scheduleReconciling() { + if (!fInitialProcessDone) + return; + if (fTriggerReconcilerJob.cancel()) { + fTriggerReconcilerJob.schedule(50); + } + } + + /* + * @see org.eclipse.jface.text.reconciler.AbstractReconciler#forceReconciling() + */ + protected void forceReconciling() { + if (!fInitialProcessDone) + return; + super.forceReconciling(); } /* @@ -37,4 +365,111 @@ public class CReconciler extends MonoReconciler { strategy.aboutToBeReconciled(); } + /* + * @see org.eclipse.jface.text.reconciler.MonoReconciler#initialProcess() + */ + protected void initialProcess() { + super.initialProcess(); + fInitialProcessDone= true; + } + + /* + * @see org.eclipse.jface.text.reconciler.MonoReconciler#process(org.eclipse.jface.text.reconciler.DirtyRegion) + */ + protected void process(DirtyRegion dirtyRegion) { + fIsReconciling= true; + setCModelChanged(false); + super.process(dirtyRegion); + fIsReconciling= false; + } + + /** + * Tells whether the C Model has changed or not. + * + * @return true iff the C Model has changed + */ + private synchronized boolean hasCModelChanged() { + return fHasCModelChanged; + } + + /** + * Sets whether the C Model has changed or not. + * + * @param state true iff the C model has changed + */ + private synchronized void setCModelChanged(boolean state) { + fHasCModelChanged= state; + } + + /** + * Tells whether this reconciler's editor is active. + * + * @return true iff the editor is active + */ + private synchronized boolean isEditorActive() { + return fIsEditorActive; + } + + /** + * Sets whether this reconciler's editor is active. + * + * @param state true iff the editor is active + */ + private synchronized void setEditorActive(boolean state) { + fIsEditorActive= state; + if (!state) { + fTriggerReconcilerJob.cancel(); + } + } + + public boolean isRelevantElement(ICElement element) { + if (!fInitialProcessDone) { + return false; + } + if (element instanceof IWorkingCopy) { + return false; + } + if (element instanceof ITranslationUnit) { + IEditorInput input= fTextEditor.getEditorInput(); + IWorkingCopyManager manager= CUIPlugin.getDefault().getWorkingCopyManager(); + IWorkingCopy copy= manager.getWorkingCopy(input); + if (copy.getOriginalElement().equals(element)) { + return false; + } + return isRelevantProject(copy.getCProject()); + } + return false; + } + + + private boolean isRelevantProject(ICProject affectedProject) { + if (affectedProject == null) { + return false; + } + IEditorInput input= fTextEditor.getEditorInput(); + IWorkingCopyManager manager= CUIPlugin.getDefault().getWorkingCopyManager(); + IWorkingCopy copy= manager.getWorkingCopy(input); + if (copy == null) { + return false; + } + if (copy.getCProject().equals(affectedProject)) { + return true; + } + IProject project= copy.getCProject().getProject(); + if (project == null) { + return false; + } + try { + IProject[] referencedProjects= project.getReferencedProjects(); + for (int i= 0; i < referencedProjects.length; i++) { + project= referencedProjects[i]; + if (project.equals(affectedProject.getProject())) { + return true; + } + } + } catch (CoreException exc) { + } + return false; + } + } 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 a870e69860e..a4a90598e2b 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 @@ -115,15 +115,15 @@ public class CReconcilingStrategy implements IReconcilingStrategy, IReconcilingS private void reconcile(final boolean initialReconcile) { IASTTranslationUnit ast= null; + IWorkingCopy workingCopy= fManager.getWorkingCopy(fEditor.getEditorInput()); + if (workingCopy == null) { + return; + } try { - ITranslationUnit tu = fManager.getWorkingCopy(fEditor.getEditorInput()); - if (tu != null && tu.isWorkingCopy()) { - IWorkingCopy workingCopy = (IWorkingCopy)tu; - final boolean computeAST= initialReconcile || CUIPlugin.getDefault().getASTProvider().isActive(tu); - // reconcile - synchronized (workingCopy) { - ast= workingCopy.reconcile(computeAST, true, fProgressMonitor); - } + final boolean computeAST= initialReconcile || CUIPlugin.getDefault().getASTProvider().isActive(workingCopy); + // reconcile + synchronized (workingCopy) { + ast= workingCopy.reconcile(computeAST, true, fProgressMonitor); } } catch(OperationCanceledException oce) { // document was modified while parsing @@ -155,7 +155,7 @@ public class CReconcilingStrategy implements IReconcilingStrategy, IReconcilingS if (fProgressMonitor != null && fProgressMonitor.isCanceled()) { return; } - if (ast == null && fEditor instanceof IReconcilingParticipant) { + if (ast == null && fEditor instanceof IReconcilingParticipant && workingCopy.exists()) { IReconcilingParticipant p= (IReconcilingParticipant) fEditor; p.reconciled(true); } 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 f3896552928..f6a47df8546 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 @@ -326,10 +326,8 @@ public class CSourceViewerConfiguration extends TextSourceViewerConfiguration { reconciler.setRepairer(dr, ICPartitions.C_CHARACTER); dr= new DefaultDamagerRepairer(getPreprocessorScanner(language)); - if (dr != null) { - reconciler.setDamager(new PartitionDamager(), ICPartitions.C_PREPROCESSOR); - reconciler.setRepairer(dr, ICPartitions.C_PREPROCESSOR); - } + reconciler.setDamager(new PartitionDamager(), ICPartitions.C_PREPROCESSOR); + reconciler.setRepairer(dr, ICPartitions.C_PREPROCESSOR); return reconciler; } @@ -426,7 +424,7 @@ public class CSourceViewerConfiguration extends TextSourceViewerConfiguration { if (fTextEditor != null && (fTextEditor.isEditable() || fTextEditor.getEditorInput() instanceof ExternalEditorInput)) { //Delay changed and non-incremental reconciler used due to //PR 130089 - MonoReconciler reconciler= new CReconciler(new CReconcilingStrategy(fTextEditor)); + MonoReconciler reconciler= new CReconciler(fTextEditor, new CReconcilingStrategy(fTextEditor)); reconciler.setIsIncrementalReconciler(false); reconciler.setProgressMonitor(new NullProgressMonitor()); reconciler.setDelay(500);