1 /*******************************************************************************
2 * Copyright (c) 2006, 2011 Wind River Systems, Inc. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * Anton Leherbauer (Wind River Systems) - initial API and implementation
10 * Markus Schorn (Wind River Systems)
11 *******************************************************************************/
12 package org.eclipse.cdt.internal.ui.text;
14 import org.eclipse.core.resources.IProject;
15 import org.eclipse.core.runtime.Assert;
16 import org.eclipse.core.runtime.CoreException;
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.core.runtime.Status;
20 import org.eclipse.core.runtime.jobs.ISchedulingRule;
21 import org.eclipse.core.runtime.jobs.Job;
22 import org.eclipse.jface.text.IDocument;
23 import org.eclipse.jface.text.ITextViewer;
24 import org.eclipse.jface.text.reconciler.DirtyRegion;
25 import org.eclipse.jface.text.reconciler.MonoReconciler;
26 import org.eclipse.swt.events.ShellAdapter;
27 import org.eclipse.swt.events.ShellEvent;
28 import org.eclipse.swt.events.ShellListener;
29 import org.eclipse.swt.widgets.Control;
30 import org.eclipse.swt.widgets.Shell;
31 import org.eclipse.ui.IEditorInput;
32 import org.eclipse.ui.IPartListener2;
33 import org.eclipse.ui.IWorkbenchPartReference;
34 import org.eclipse.ui.IWorkbenchPartSite;
35 import org.eclipse.ui.IWorkbenchWindow;
36 import org.eclipse.ui.texteditor.ITextEditor;
38 import org.eclipse.cdt.core.CCorePlugin;
39 import org.eclipse.cdt.core.index.IIndexChangeEvent;
40 import org.eclipse.cdt.core.index.IIndexChangeListener;
41 import org.eclipse.cdt.core.index.IIndexerStateEvent;
42 import org.eclipse.cdt.core.index.IIndexerStateListener;
43 import org.eclipse.cdt.core.model.CoreModel;
44 import org.eclipse.cdt.core.model.ElementChangedEvent;
45 import org.eclipse.cdt.core.model.ICElement;
46 import org.eclipse.cdt.core.model.ICElementDelta;
47 import org.eclipse.cdt.core.model.ICProject;
48 import org.eclipse.cdt.core.model.IElementChangedListener;
49 import org.eclipse.cdt.core.model.ITranslationUnit;
50 import org.eclipse.cdt.core.model.IWorkingCopy;
51 import org.eclipse.cdt.ui.CUIPlugin;
52 import org.eclipse.cdt.ui.IWorkingCopyManager;
55 * A single strategy C reconciler.
59 public class CReconciler extends MonoReconciler {
61 static class SingletonJob extends Job implements ISchedulingRule {
62 private Runnable fCode;
64 SingletonJob(String name, Runnable code) {
67 setPriority(Job.SHORT);
74 * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
77 protected IStatus run(IProgressMonitor monitor) {
78 if (!monitor.isCanceled()) {
81 return Status.OK_STATUS;
85 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule)
87 public boolean contains(ISchedulingRule rule) {
92 * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule)
94 public boolean isConflicting(ISchedulingRule rule) {
101 * Internal part listener for activating the reconciler.
103 private class PartListener implements IPartListener2 {
105 * @see org.eclipse.ui.IPartListener2#partActivated(org.eclipse.ui.IWorkbenchPartReference)
107 public void partActivated(IWorkbenchPartReference partRef) {
110 * @see org.eclipse.ui.IPartListener2#partBroughtToTop(org.eclipse.ui.IWorkbenchPartReference)
112 public void partBroughtToTop(IWorkbenchPartReference partRef) {
115 * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference)
117 public void partClosed(IWorkbenchPartReference partRef) {
120 * @see org.eclipse.ui.IPartListener2#partDeactivated(org.eclipse.ui.IWorkbenchPartReference)
122 public void partDeactivated(IWorkbenchPartReference partRef) {
125 * @see org.eclipse.ui.IPartListener2#partHidden(org.eclipse.ui.IWorkbenchPartReference)
127 public void partHidden(IWorkbenchPartReference partRef) {
128 if (partRef.getPart(false) == fTextEditor) {
129 setEditorActive(false);
133 * @see org.eclipse.ui.IPartListener2#partInputChanged(org.eclipse.ui.IWorkbenchPartReference)
135 public void partInputChanged(IWorkbenchPartReference partRef) {
138 * @see org.eclipse.ui.IPartListener2#partOpened(org.eclipse.ui.IWorkbenchPartReference)
140 public void partOpened(IWorkbenchPartReference partRef) {
143 * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference)
145 public void partVisible(IWorkbenchPartReference partRef) {
146 if (partRef.getPart(false) == fTextEditor) {
147 CReconciler.this.scheduleReconciling();
148 setEditorActive(true);
154 * Internal Shell activation listener for activating the reconciler.
156 private class ActivationListener extends ShellAdapter {
158 private Control fControl;
160 public ActivationListener(Control control) {
161 Assert.isNotNull(control);
166 * @see org.eclipse.swt.events.ShellListener#shellActivated(org.eclipse.swt.events.ShellEvent)
169 public void shellActivated(ShellEvent e) {
170 if (!fControl.isDisposed() && fControl.isVisible()) {
171 if (hasCModelChanged())
172 CReconciler.this.scheduleReconciling();
173 setEditorActive(true);
178 * @see org.eclipse.swt.events.ShellListener#shellDeactivated(org.eclipse.swt.events.ShellEvent)
181 public void shellDeactivated(ShellEvent e) {
182 if (!fControl.isDisposed() && fControl.getShell() == e.getSource()) {
183 setEditorActive(false);
189 * Internal C element changed listener
191 private class ElementChangedListener implements IElementChangedListener {
193 * @see org.eclipse.cdt.core.model.IElementChangedListener#elementChanged(org.eclipse.cdt.core.model.ElementChangedEvent)
195 public void elementChanged(ElementChangedEvent event) {
196 if (event.getType() == ElementChangedEvent.POST_CHANGE) {
197 if (isRelevantDelta(event.getDelta())) {
198 if (!fIsReconciling && isEditorActive() && fInitialProcessDone) {
199 CReconciler.this.scheduleReconciling();
201 setCModelChanged(true);
207 private boolean isRelevantDelta(ICElementDelta delta) {
208 final int flags = delta.getFlags();
209 if ((flags & ICElementDelta.F_CONTENT) != 0) {
210 if (!fIsReconciling && isRelevantElement(delta.getElement())) {
211 // mark model changed, but don't update immediately
212 fIndexerListener.ignoreChanges(false);
213 setCModelChanged(true);
214 } else if (delta.getElement() instanceof ITranslationUnit) {
215 fIndexerListener.ignoreChanges(true);
219 ICElementDelta.F_CHANGED_PATHENTRY_INCLUDE |
220 ICElementDelta.F_CHANGED_PATHENTRY_MACRO
222 if (isRelevantProject(delta.getElement().getCProject())) {
226 if ((flags & ICElementDelta.F_CHILDREN) != 0) {
227 ICElementDelta[] childDeltas= delta.getChangedChildren();
228 for (int i = 0; i < childDeltas.length; i++) {
229 if (isRelevantDelta(childDeltas[i])) {
238 private class IndexerListener implements IIndexerStateListener, IIndexChangeListener {
239 private boolean fIndexChanged;
240 private boolean fIgnoreChanges;
243 * @see org.eclipse.cdt.core.index.IIndexerStateListener#indexChanged(org.eclipse.cdt.core.index.IIndexerStateEvent)
245 public void indexChanged(IIndexerStateEvent event) {
246 if (event.indexerIsIdle()) {
247 if (fIndexChanged || hasCModelChanged()) {
248 fIndexChanged= false;
249 if (!fIsReconciling && isEditorActive() && fInitialProcessDone) {
250 CReconciler.this.scheduleReconciling();
252 setCModelChanged(true);
255 fIgnoreChanges= false;
259 public void ignoreChanges(boolean ignore) {
260 fIgnoreChanges= ignore;
264 * @see org.eclipse.cdt.core.index.IIndexChangeListener#indexChanged(org.eclipse.cdt.core.index.IIndexChangeEvent)
266 public void indexChanged(IIndexChangeEvent event) {
267 if (!fIndexChanged && isRelevantProject(event.getAffectedProject())) {
268 if (!fIgnoreChanges || event.isCleared() || event.isReloaded() || event.hasNewFile()) {
276 /** The reconciler's editor */
277 private ITextEditor fTextEditor;
278 /** The part listener */
279 private IPartListener2 fPartListener;
280 /** The shell listener */
281 private ShellListener fActivationListener;
282 /** The C element changed listener. */
283 private IElementChangedListener fCElementChangedListener;
284 /** The indexer listener */
285 private IndexerListener fIndexerListener;
286 /** Tells whether the C model might have changed. */
287 private volatile boolean fHasCModelChanged= false;
288 /** Tells whether this reconciler's editor is active. */
289 private volatile boolean fIsEditorActive= true;
290 /** Tells whether a reconcile is in progress. */
291 private volatile boolean fIsReconciling= false;
293 private boolean fInitialProcessDone= false;
294 private Job fTriggerReconcilerJob;
297 * Create a reconciler for the given editor and strategy.
299 * @param editor the text editor
300 * @param strategy the C reconciling strategy
302 public CReconciler(ITextEditor editor, CCompositeReconcilingStrategy strategy) {
303 super(strategy, false);
308 * @see org.eclipse.jface.text.reconciler.IReconciler#install(org.eclipse.jface.text.ITextViewer)
311 public void install(ITextViewer textViewer) {
312 super.install(textViewer);
314 fPartListener= new PartListener();
315 IWorkbenchPartSite site= fTextEditor.getSite();
316 IWorkbenchWindow window= site.getWorkbenchWindow();
317 window.getPartService().addPartListener(fPartListener);
319 fActivationListener= new ActivationListener(textViewer.getTextWidget());
320 Shell shell= window.getShell();
321 shell.addShellListener(fActivationListener);
323 fCElementChangedListener= new ElementChangedListener();
324 CoreModel.getDefault().addElementChangedListener(fCElementChangedListener);
326 fIndexerListener= new IndexerListener();
327 CCorePlugin.getIndexManager().addIndexerStateListener(fIndexerListener);
328 CCorePlugin.getIndexManager().addIndexChangeListener(fIndexerListener);
330 fTriggerReconcilerJob= new SingletonJob("Trigger Reconciler", new Runnable() { //$NON-NLS-1$
337 * @see org.eclipse.jface.text.reconciler.IReconciler#uninstall()
340 public void uninstall() {
341 fTriggerReconcilerJob.cancel();
343 IWorkbenchPartSite site= fTextEditor.getSite();
344 IWorkbenchWindow window= site.getWorkbenchWindow();
345 window.getPartService().removePartListener(fPartListener);
348 Shell shell= window.getShell();
349 if (shell != null && !shell.isDisposed())
350 shell.removeShellListener(fActivationListener);
351 fActivationListener= null;
353 CoreModel.getDefault().removeElementChangedListener(fCElementChangedListener);
354 fCElementChangedListener= null;
356 CCorePlugin.getIndexManager().removeIndexerStateListener(fIndexerListener);
357 CCorePlugin.getIndexManager().removeIndexChangeListener(fIndexerListener);
358 fIndexerListener= null;
362 protected void scheduleReconciling() {
363 if (!fInitialProcessDone)
365 if (fTriggerReconcilerJob.cancel()) {
366 fTriggerReconcilerJob.schedule(50);
371 * @see org.eclipse.jface.text.reconciler.AbstractReconciler#forceReconciling()
374 protected void forceReconciling() {
375 if (!fInitialProcessDone)
377 super.forceReconciling();
381 * @see org.eclipse.jface.text.reconciler.AbstractReconciler#aboutToBeReconciled()
384 protected void aboutToBeReconciled() {
385 CCompositeReconcilingStrategy strategy= (CCompositeReconcilingStrategy)getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
386 strategy.aboutToBeReconciled();
390 * @see org.eclipse.jface.text.reconciler.MonoReconciler#initialProcess()
393 protected void initialProcess() {
394 super.initialProcess();
395 fInitialProcessDone= true;
396 if (!fIsReconciling && isEditorActive() && hasCModelChanged()) {
397 CReconciler.this.scheduleReconciling();
402 * @see org.eclipse.jface.text.reconciler.MonoReconciler#process(org.eclipse.jface.text.reconciler.DirtyRegion)
405 protected void process(DirtyRegion dirtyRegion) {
406 fIsReconciling= true;
407 setCModelChanged(false);
408 super.process(dirtyRegion);
409 fIsReconciling= false;
413 * Tells whether the C Model has changed or not.
415 * @return <code>true</code> iff the C Model has changed
417 private synchronized boolean hasCModelChanged() {
418 return fHasCModelChanged;
422 * Sets whether the C Model has changed or not.
424 * @param state <code>true</code> iff the C model has changed
426 private synchronized void setCModelChanged(boolean state) {
427 fHasCModelChanged= state;
431 * Tells whether this reconciler's editor is active.
433 * @return <code>true</code> iff the editor is active
435 private synchronized boolean isEditorActive() {
436 return fIsEditorActive;
440 * Sets whether this reconciler's editor is active.
442 * @param state <code>true</code> iff the editor is active
444 private synchronized void setEditorActive(boolean active) {
445 fIsEditorActive= active;
447 fTriggerReconcilerJob.cancel();
451 public boolean isRelevantElement(ICElement element) {
452 if (!fInitialProcessDone) {
455 if (element instanceof IWorkingCopy) {
458 if (element instanceof ITranslationUnit) {
459 IEditorInput input= fTextEditor.getEditorInput();
460 IWorkingCopyManager manager= CUIPlugin.getDefault().getWorkingCopyManager();
461 IWorkingCopy copy= manager.getWorkingCopy(input);
462 if (copy == null || copy.getOriginalElement().equals(element)) {
465 return isRelevantProject(copy.getCProject());
471 private boolean isRelevantProject(ICProject affectedProject) {
472 if (affectedProject == null) {
475 IEditorInput input= fTextEditor.getEditorInput();
476 IWorkingCopyManager manager= CUIPlugin.getDefault().getWorkingCopyManager();
477 IWorkingCopy copy= manager.getWorkingCopy(input);
481 if (copy.getCProject().equals(affectedProject)) {
484 IProject project= copy.getCProject().getProject();
485 if (project == null) {
489 IProject[] referencedProjects= project.getReferencedProjects();
490 for (int i= 0; i < referencedProjects.length; i++) {
491 project= referencedProjects[i];
492 if (project.equals(affectedProject.getProject())) {
496 } catch (CoreException exc) {