diff --git a/core/org.eclipse.cdt.ui.tests/resources/semanticHighlighting/SHTest.cpp b/core/org.eclipse.cdt.ui.tests/resources/semanticHighlighting/SHTest.cpp new file mode 100644 index 00000000000..071e3ce998b --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/resources/semanticHighlighting/SHTest.cpp @@ -0,0 +1,120 @@ +#define SIMPLE_MACRO +#define FUNCTION_MACRO(arg) globalFunc(arg) + +enum Enumeration { + enumerator +}; + +const int globalConstant = 0; +int globalVariable = 0; +static int globalStaticVariable; + +void globalFunc(int a); +static void globalStaticFunc() { +}; + +class Base1 {}; +class Base2 {}; + +class ClassContainer : Base1, Base2 { + friend void friendFunc(); + friend class FriendClass; + +public: + static int staticPubField; + const int constPubField; + const static int constStaticPubField; + int pubField; + + static int staticPubMethod(int arg) { + FUNCTION_MACRO(arg); + globalFunc(arg); + } + int pubMethod(); + + enum pubEnumeration {pubEnumerator}; + class pubClass{}; + class pubStruct{}; + class pubUnion{}; + typedef pubClass pubTypedef; + +protected: + static const int constStaticProtField = 12; + static int staticProtField; + const int constProtField; + int protField; + + static int staticProtMethod(); + int protMethod(); + + enum protEnumeration {protEnumerator}; + class protClass{}; + class protStruct{}; + class protUnion{}; + typedef protClass protTypedef; + +private: + static const int constStaticPrivField = 12; + static int staticPrivField; + const int constPrivField; + int privField; + + static int staticPrivMethod(); + int privMethod(); + + enum privEnumeration {privEnumerator}; + class privClass{}; + class privStruct{}; + class privUnion{}; + typedef privClass privTypedef; + + +}; + +template class TemplateClass { + T1 tArg1; + T2 tArg2; + TemplateClass(T1 arg1, T2 arg2) { + tArg1 = arg1; + tArg2 = arg2; + } +}; + +template class PartialInstantiatedClass : TemplateClass {}; + + +struct CppStruct { + int structField; +}; + +union CppUnion { + int unionField; +}; + +typedef CppUnion TUnion; + +namespace ns { + int namespaceField = 0; + int namespaceFunc() { + } +} + +int ClassContainer::protMethod() { + return protField; +} + +int ClassContainer::pubMethod() { + int localVar; + return pubField; +} + +int ClassContainer::staticPrivMethod() { + CppStruct st= new CppStruct(); + st.structField= 1; + CppUnion un= new CppUnion(); + un.unionField= 2; + staticPubMethod(staticPrivField); +label: + FUNCTION_MACRO(0); + return 0; +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AbstractSemanticHighlightingTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AbstractSemanticHighlightingTest.java new file mode 100644 index 00000000000..72d48ed9268 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/AbstractSemanticHighlightingTest.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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.cdt.ui.tests.text; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.source.SourceViewer; + +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.PreferenceConstants; + +import org.eclipse.cdt.internal.ui.editor.CEditor; +import org.eclipse.cdt.internal.ui.editor.SemanticHighlighting; +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager; +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingPresenter; +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightings; + +/** + * Derived from JDT. + * + * @since 4.0 + */ +public class AbstractSemanticHighlightingTest extends TestCase { + + protected static class SemanticHighlightingTestSetup extends TestSetup { + + private ICProject fCProject; + private final String fTestFilename; + + public SemanticHighlightingTestSetup(Test test, String testFilename) { + super(test); + fTestFilename= testFilename; + } + + protected void setUp() throws Exception { + super.setUp(); + fCProject= EditorTestHelper.createCProject(PROJECT, LINKED_FOLDER); + + disableAllSemanticHighlightings(); + + fEditor= (CEditor) EditorTestHelper.openInEditor(ResourceTestHelper.findFile(fTestFilename), true); + fSourceViewer= EditorTestHelper.getSourceViewer(fEditor); + assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100)); + } + + protected String getTestFilename() { + return "/SHTest/src/SHTest.cpp"; + } + + protected void tearDown () throws Exception { + EditorTestHelper.closeEditor(fEditor); + + IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore(); + + SemanticHighlighting[] semanticHighlightings= SemanticHighlightings.getSemanticHighlightings(); + for (int i= 0, n= semanticHighlightings.length; i < n; i++) { + String enabledPreferenceKey= SemanticHighlightings.getEnabledPreferenceKey(semanticHighlightings[i]); + if (!store.isDefault(enabledPreferenceKey)) + store.setToDefault(enabledPreferenceKey); + } + + if (fCProject != null) + CProjectHelper.delete(fCProject); + + super.tearDown(); + } + } + + public static final String LINKED_FOLDER= "resources/semanticHighlighting"; + + public static final String PROJECT= "SHTest"; + + private static CEditor fEditor; + + private static SourceViewer fSourceViewer; + + protected void setUp() throws Exception { + super.setUp(); + disableAllSemanticHighlightings(); + } + + protected void assertEqualPositions(Position[] expected, Position[] actual) { + assertEquals(expected.length, actual.length); + for (int i= 0, n= expected.length; i < n; i++) { + assertEquals(expected[i].isDeleted(), actual[i].isDeleted()); + assertEquals(expected[i].getOffset(), actual[i].getOffset()); + assertEquals(expected[i].getLength(), actual[i].getLength()); + } + } + + protected Position createPosition(int line, int column, int length) throws BadLocationException { + IDocument document= fSourceViewer.getDocument(); + return new Position(document.getLineOffset(line) + column, length); + } + + String toString(Position[] positions) throws BadLocationException { + StringBuffer buf= new StringBuffer(); + IDocument document= fSourceViewer.getDocument(); + buf.append("Position[] expected= new Position[] {\n"); + for (int i= 0, n= positions.length; i < n; i++) { + Position position= positions[i]; + int line= document.getLineOfOffset(position.getOffset()); + int column= position.getOffset() - document.getLineOffset(line); + buf.append("\tcreatePosition(" + line + ", " + column + ", " + position.getLength() + "),\n"); + } + buf.append("};\n"); + return buf.toString(); + } + + protected Position[] getSemanticHighlightingPositions() throws BadPositionCategoryException { + SemanticHighlightingManager manager= (SemanticHighlightingManager) new Accessor(fEditor, CEditor.class).get("fSemanticManager"); + SemanticHighlightingPresenter presenter= (SemanticHighlightingPresenter) new Accessor(manager, manager.getClass()).get("fPresenter"); + String positionCategory= (String) new Accessor(presenter, presenter.getClass()).invoke("getPositionCategory", new Object[0]); + IDocument document= fSourceViewer.getDocument(); + return document.getPositions(positionCategory); + } + + protected void setUpSemanticHighlighting(String semanticHighlighting) { + enableSemanticHighlighting(semanticHighlighting); + EditorTestHelper.forceReconcile(fSourceViewer); + assertTrue(EditorTestHelper.joinReconciler(fSourceViewer, 0, 10000, 100)); + EditorTestHelper.runEventQueue(100); + } + + private void enableSemanticHighlighting(String preferenceKey) { + IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore(); + store.setValue(getEnabledPreferenceKey(preferenceKey), true); + } + + private String getEnabledPreferenceKey(String preferenceKey) { + return PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + preferenceKey + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED_SUFFIX; + } + + private static void disableAllSemanticHighlightings() { + IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore(); + store.setValue(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED, true); + SemanticHighlighting[] semanticHilightings= SemanticHighlightings.getSemanticHighlightings(); + for (int i= 0, n= semanticHilightings.length; i < n; i++) { + SemanticHighlighting semanticHilighting= semanticHilightings[i]; + if (store.getBoolean(SemanticHighlightings.getEnabledPreferenceKey(semanticHilighting))) + store.setValue(SemanticHighlightings.getEnabledPreferenceKey(semanticHilighting), false); + } + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/Accessor.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/Accessor.java new file mode 100644 index 00000000000..c010e3b336d --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/Accessor.java @@ -0,0 +1,328 @@ +/******************************************************************************* + * Copyright (c) 2000, 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.cdt.ui.tests.text; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import junit.framework.Assert; + +/** + * Helper class for accessing classes and members which cannot + * be accessed using standard Java access control like private + * or package visible elements. + * + *

Copied from JDT.

+ * + * @since 4.0 + */ +public class Accessor extends Assert { + + /** The class to access. */ + private Class fClass; + /** The instance to access. */ + private Object fInstance; + + /** + * Creates an accessor for the given instance and + * class. Only non-inherited members that particular + * class can be accessed. + * + * @param instance the instance + * @param clazz the class + */ + public Accessor(Object instance, Class clazz) { + assertNotNull(instance); + assertNotNull(clazz); + fInstance= instance; + fClass= clazz; + } + + /** + * Creates an accessor for the given instance and + * class. Only non-inherited members that particular + * class can be accessed. + * + * @param instance the instance + * @param className the name of the class + * @param classLoader the class loader to use i.e. getClass().getClassLoader() + */ + public Accessor(Object instance, String className, ClassLoader classLoader) { + assertNotNull(instance); + assertNotNull(className); + assertNotNull(classLoader); + fInstance= instance; + try { + fClass= Class.forName(className, true, classLoader); + } catch (ClassNotFoundException e) { + fail(); + } catch (ExceptionInInitializerError e) { + fail(); + } + } + + /** + * Creates an accessor for the given class. + *

+ * In order to get the type information from the given + * arguments they must all be instanc eof Object. Use + * {@link #Accessor(String, ClassLoader, Class[], Object[])} if this + * is not the case.

+ * + * @param className the name of the class + * @param classLoader the class loader to use i.e. getClass().getClassLoader() + * @param constructorArgs the constructor arguments which must all be instance of Object + */ + public Accessor(String className, ClassLoader classLoader, Object[] constructorArgs) { + this(className, classLoader, getTypes(constructorArgs), constructorArgs); + } + + /** + * Creates an accessor for the given class. + * + * @param className the name of the class + * @param classLoader the class loader to use i.e. getClass().getClassLoader() + * @param constructorTypes the types of the constructor arguments + * @param constructorArgs the constructor arguments + */ + public Accessor(String className, ClassLoader classLoader, Class[] constructorTypes, Object[] constructorArgs) { + try { + fClass= Class.forName(className, true, classLoader); + } catch (ClassNotFoundException e) { + fail(); + } catch (ExceptionInInitializerError e) { + fail(); + } + Constructor constructor= null; + try { + constructor= fClass.getDeclaredConstructor(constructorTypes); + } catch (SecurityException e2) { + fail(); + } catch (NoSuchMethodException e2) { + fail(); + } + assertNotNull(constructor); + constructor.setAccessible(true); + try { + fInstance= constructor.newInstance(constructorArgs); + } catch (IllegalArgumentException e) { + fail(); + } catch (InvocationTargetException e) { + fail(); + } catch (InstantiationException e) { + fail(); + } catch (IllegalAccessException e) { + fail(); + } + } + + /** + * Creates an accessor for the given class. + *

+ * This constructor is used to access static stuff. + *

+ * + * @param className the name of the class + * @param classLoader the class loader to use i.e. getClass().getClassLoader() + */ + public Accessor(String className, ClassLoader classLoader) { + try { + fClass= Class.forName(className, true, classLoader); + } catch (ClassNotFoundException e) { + fail(); + } catch (ExceptionInInitializerError e) { + fail(); + } + } + + /** + * Invokes the method with the given method name and arguments. + *

+ * In order to get the type information from the given + * arguments all those arguments must be instance of Object. Use + * {@link #invoke(String, Class[], Object[])} if this + * is not the case.

+ * + * @param methodName the method name + * @param arguments the method arguments which must all be instance of Object + * @return the method return value + */ + public Object invoke(String methodName, Object[] arguments) { + return invoke(methodName, getTypes(arguments), arguments); + } + + /** + * Invokes the method with the given method name and arguments. + * + * @param methodName the method name + * @param types the argument types + * @param arguments the method arguments + * @return the method return value + */ + public Object invoke(String methodName, Class[] types, Object[] arguments) { + Method method= null; + try { + method= fClass.getDeclaredMethod(methodName, types); + } catch (SecurityException e) { + fail(); + } catch (NoSuchMethodException ex) { + fail(); + } + assertNotNull(method); + method.setAccessible(true); + try { + return method.invoke(fInstance, arguments); + } catch (IllegalArgumentException e) { + fail(); + } catch (InvocationTargetException e) { + fail(); + } catch (IllegalAccessException e) { + fail(); + } + return null; + } + + /** + * Assigns the given value to the field with the given name. + * + * @param fieldName the field name + * @param value the value to assign to the field + */ + public void set(String fieldName, Object value) { + Field field= getField(fieldName); + try { + field.set(fInstance, value); + } catch (IllegalArgumentException e) { + fail(); + } catch (IllegalAccessException e) { + fail(); + } + } + + /** + * Assigns the given value to the field with the given name. + * + * @param fieldName the field name + * @param value the value to assign to the field + */ + public void set(String fieldName, boolean value) { + Field field= getField(fieldName); + try { + field.setBoolean(fInstance, value); + } catch (IllegalArgumentException e) { + fail(); + } catch (IllegalAccessException e) { + fail(); + } + } + + /** + * Assigns the given value to the field with the given name. + * + * @param fieldName the field name + * @param value the value to assign to the field + */ + public void set(String fieldName, int value) { + Field field= getField(fieldName); + try { + field.setInt(fInstance, value); + } catch (IllegalArgumentException e) { + fail(); + } catch (IllegalAccessException e) { + fail(); + } + } + + /** + * Returns the value of the field with the given name. + * + * @param fieldName the field name + * @return the value of the field + */ + public Object get(String fieldName) { + Field field= getField(fieldName); + try { + return field.get(fInstance); + } catch (IllegalArgumentException e) { + fail(); + } catch (IllegalAccessException e) { + fail(); + } + // Unreachable code + return null; + } + + /** + * Returns the value of the field with the given name. + * + * @param fieldName the field name + * @return the value of the field + */ + public boolean getBoolean(String fieldName) { + Field field= getField(fieldName); + try { + return field.getBoolean(fInstance); + } catch (IllegalArgumentException e) { + fail(); + } catch (IllegalAccessException e) { + fail(); + } + // Unreachable code + return false; + } + + /** + * Returns the value of the field with the given name. + * + * @param fieldName the field name + * @return the value of the field + */ + public int getInt(String fieldName) { + Field field= getField(fieldName); + try { + return field.getInt(fInstance); + } catch (IllegalArgumentException e) { + fail(); + } catch (IllegalAccessException e) { + fail(); + } + // Unreachable code + return 0; + } + + private Field getField(String fieldName) { + Field field= null; + try { + field= fClass.getDeclaredField(fieldName); + } catch (SecurityException e) { + fail(); + } catch (NoSuchFieldException e) { + fail(); + } + field.setAccessible(true); + return field; + } + + private static Class[] getTypes(Object[] objects) { + if (objects == null) + return null; + + int length= objects.length; + Class[] classes= new Class[length]; + for (int i= 0; i < length; i++) { + assertNotNull(objects[i]); + classes[i]= objects[i].getClass(); + } + return classes; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/DisplayHelper.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/DisplayHelper.java new file mode 100644 index 00000000000..b22080fce72 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/DisplayHelper.java @@ -0,0 +1,621 @@ +/******************************************************************************* + * Copyright (c) 2000, 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.cdt.ui.tests.text; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import junit.framework.Assert; + +import org.eclipse.swt.widgets.Display; + +/** + * Runs the event loop of the given display until {@link #condition()} becomes + * true or no events have occurred for the supplied timeout. + * Between running the event loop, {@link Display#sleep()} is called. + *

+ * There is a caveat: the given timeouts must be long enough that the calling + * thread can enter Display.sleep() before the timeout elapses, + * otherwise, the waiter may time out before sleep is called and + * the sleeping thread may never be waken up. + *

+ * + *

Copied from org.eclipse.jdt.testplugin.util.

+ * + * @since 4.0 + */ +public abstract class DisplayHelper { + /** + * Creates a new instance. + */ + protected DisplayHelper() { + } + + /** + * Until {@link #condition()} becomes true or the timeout + * elapses, call {@link Display#sleep()} and run the event loop. + *

+ * If timeout < 0, the event loop is never driven and + * only the condition is checked. If timeout == 0, the event + * loop is driven at most once, but Display.sleep() is never + * invoked. + *

+ * + * @param display the display to run the event loop of + * @param timeout the timeout in milliseconds + * @return true if the condition became true, + * false if the timeout elapsed + */ + public final boolean waitForCondition(Display display, long timeout) { + // if the condition already holds, succeed + if (condition()) + return true; + + if (timeout < 0) + return false; + + // if driving the event loop once makes the condition hold, succeed + // without spawning a thread. + driveEventQueue(display); + if (condition()) + return true; + + // if the timeout is negative or zero, fail + if (timeout == 0) + return false; + + // repeatedly sleep until condition becomes true or timeout elapses + DisplayWaiter waiter= new DisplayWaiter(display); + DisplayWaiter.Timeout timeoutState= waiter.start(timeout); + boolean condition; + try { + do { + if (display.sleep()) + driveEventQueue(display); + condition= condition(); + } while (!condition && !timeoutState.hasTimedOut()); + } finally { + waiter.stop(); + } + return condition; + } + + /** + * Call {@link Display#sleep()} and run the event loop until the given + * timeout has elapsed. + *

+ * If timeout < 0, nothing happens. If + * timeout == 0, the event loop is driven exactly once, but + * Display.sleep() is never invoked. + *

+ * + * @param display the display to run the event loop of + * @param millis the timeout in milliseconds + */ + public static void sleep(Display display, long millis) { + new DisplayHelper() { + public boolean condition() { + return false; + } + }.waitForCondition(display, millis); + } + + /** + * Call {@link Display#sleep()} and run the event loop once if + * sleep returns before the timeout elapses. Returns + * true if any events were processed, false if + * not. + *

+ * If timeout < 0, nothing happens and false is returned. + * If timeout == 0, the event loop is driven exactly once, + * but Display.sleep() is never invoked. + *

+ * + * @param display the display to run the event loop of + * @param timeout the timeout in milliseconds + * @return true if any event was taken off the event queue, + * false if not + */ + public static boolean runEventLoop(Display display, long timeout) { + if (timeout < 0) + return false; + + if (timeout == 0) + return driveEventQueue(display); + + // repeatedly sleep until condition becomes true or timeout elapses + DisplayWaiter waiter= new DisplayWaiter(display); + DisplayWaiter.Timeout timeoutState= waiter.start(timeout); + boolean events= false; + if (display.sleep() && !timeoutState.hasTimedOut()) { + driveEventQueue(display); + events= true; + } + waiter.stop(); + return events; + } + + /** + * The condition which has to be met in order for + * {@link #waitForCondition(Display, int)} to return before the timeout + * elapses. + * + * @return true if the condition is met, false + * if the event loop should be driven some more + */ + protected abstract boolean condition(); + + /** + * Runs the event loop on the given display. + * + * @param display the display + * @return if display.readAndDispatch returned + * true at least once + */ + private static boolean driveEventQueue(Display display) { + boolean events= false; + while (display.readAndDispatch()) { + events= true; + } + return events; + } + + /** + * Until {@link #condition()} becomes true or the timeout + * elapses, call {@link Display#sleep()} and run the event loop. + *

+ * If timeout < 0, the event loop is never driven and + * only the condition is checked. If timeout == 0, the event + * loop is driven at most once, but Display.sleep() is never + * invoked. + *

+ *

+ * The condition gets rechecked every interval milliseconds, even + * if no events were read from the queue. + *

+ * + * @param display the display to run the event loop of + * @param timeout the timeout in milliseconds + * @param interval the interval to re-check the condition in milliseconds + * @return true if the condition became true, + * false if the timeout elapsed + */ + public final boolean waitForCondition(Display display, long timeout, long interval) { + // if the condition already holds, succeed + if (condition()) + return true; + + if (timeout < 0) + return false; + + // if driving the event loop once makes the condition hold, succeed + // without spawning a thread. + driveEventQueue(display); + if (condition()) + return true; + + // if the timeout is negative or zero, fail + if (timeout == 0) + return false; + + // repeatedly sleep until condition becomes true or timeout elapses + DisplayWaiter waiter= new DisplayWaiter(display, true); + long currentTimeMillis= System.currentTimeMillis(); + long finalTimeout= timeout + currentTimeMillis; + if (finalTimeout < currentTimeMillis) + finalTimeout= Long.MAX_VALUE; + boolean condition; + try { + do { + waiter.restart(interval); + if (display.sleep()) + driveEventQueue(display); + condition= condition(); + } while (!condition && finalTimeout > System.currentTimeMillis()); + } finally { + waiter.stop(); + } + return condition; + } + +} + +/** + * Implements the thread that will wait for the timeout and wake up the display + * so it does not wait forever. The thread may be restarted after it was stopped + * or timed out. + * + * @since 3.1 + */ +final class DisplayWaiter { + /** + * Timeout state of a display waiter thread. + */ + public final class Timeout { + private boolean fTimeoutState= false; + /** + * Returns true if the timeout has been reached, + * false if not. + * + * @return true if the timeout has been reached, + * false if not + */ + public boolean hasTimedOut() { + synchronized (fMutex) { + return fTimeoutState; + } + } + void setTimedOut(boolean timedOut) { + fTimeoutState= timedOut; + } + Timeout(boolean initialState) { + fTimeoutState= initialState; + } + } + + // configuration + private final Display fDisplay; + private final Object fMutex= new Object(); + private final boolean fKeepRunningOnTimeout; + + /* State -- possible transitions: + * + * STOPPED -> RUNNING + * RUNNING -> STOPPED + * RUNNING -> IDLE + * IDLE -> RUNNING + * IDLE -> STOPPED + */ + private static final int RUNNING= 1 << 1; + private static final int STOPPED= 1 << 2; + private static final int IDLE= 1 << 3; + + /** The current state. */ + private int fState; + /** The time in milliseconds (see Date) that the timeout will occur. */ + private long fNextTimeout; + /** The thread. */ + private Thread fCurrentThread; + /** The timeout state of the current thread. */ + private Timeout fCurrentTimeoutState; + + /** + * Creates a new instance on the given display and timeout. + * + * @param display the display to run the event loop of + */ + public DisplayWaiter(Display display) { + this(display, false); + } + + /** + * Creates a new instance on the given display and timeout. + * + * @param display the display to run the event loop of + * @param keepRunning true if the thread should be kept + * running after timing out + */ + public DisplayWaiter(Display display, boolean keepRunning) { + Assert.assertNotNull(display); + fDisplay= display; + fState= STOPPED; + fKeepRunningOnTimeout= keepRunning; + } + + /** + * Starts the timeout thread if it is not currently running. Nothing happens + * if a thread is already running. + * + * @param delay the delay from now in milliseconds + * @return the timeout state which can be queried for its timed out status + */ + public Timeout start(long delay) { + Assert.assertTrue(delay > 0); + synchronized (fMutex) { + switch (fState) { + case STOPPED: + startThread(); + setNextTimeout(delay); + break; + case IDLE: + unhold(); + setNextTimeout(delay); + break; + } + + return fCurrentTimeoutState; + } + } + + /** + * Sets the next timeout to current time plus delay. + * + * @param delay the delay until the next timeout occurs in milliseconds from + * now + */ + private void setNextTimeout(long delay) { + long currentTimeMillis= System.currentTimeMillis(); + long next= currentTimeMillis + delay; + if (next > currentTimeMillis) + fNextTimeout= next; + else + fNextTimeout= Long.MAX_VALUE; + } + + /** + * Starts the thread if it is not currently running; resets the timeout if + * it is. + * + * @param delay the delay from now in milliseconds + * @return the timeout state which can be queried for its timed out status + */ + public Timeout restart(long delay) { + Assert.assertTrue(delay > 0); + synchronized (fMutex) { + switch (fState) { + case STOPPED: + startThread(); + break; + case IDLE: + unhold(); + break; + } + setNextTimeout(delay); + + return fCurrentTimeoutState; + } + } + + /** + * Stops the thread if it is running. If not, nothing happens. Another + * thread may be started by calling {@link #start(long)} or + * {@link #restart(long)}. + */ + public void stop() { + synchronized (fMutex) { + if (tryTransition(RUNNING | IDLE, STOPPED)) + fMutex.notifyAll(); + } + } + + /** + * Puts the reaper thread on hold but does not stop it. It may be restarted + * by calling {@link #start(long)} or {@link #restart(long)}. + */ + public void hold() { + synchronized (fMutex) { + // nothing to do if there is no thread + if (tryTransition(RUNNING, IDLE)) + fMutex.notifyAll(); + } + } + + /** + * Transition to RUNNING and clear the timed out flag. Assume + * current state is IDLE. + */ + private void unhold() { + checkedTransition(IDLE, RUNNING); + fCurrentTimeoutState= new Timeout(false); + fMutex.notifyAll(); + } + + /** + * Start the thread. Assume the current state is STOPPED. + */ + private void startThread() { + checkedTransition(STOPPED, RUNNING); + fCurrentTimeoutState= new Timeout(false); + fCurrentThread= new Thread() { + /** + * Exception thrown when a thread notices that it has been stopped + * and a new thread has been started. + */ + final class ThreadChangedException extends Exception { + private static final long serialVersionUID= 1L; + } + + /* + * @see java.lang.Runnable#run() + */ + public void run() { + try { + run2(); + } catch (InterruptedException e) { + // ignore and end the thread - we never interrupt ourselves, + // so it must be an external entity that interrupted us + Logger.global.log(Level.FINE, "", e); + } catch (ThreadChangedException e) { + // the thread was stopped and restarted before we got out + // of a wait - we're no longer used + // we might have been notified instead of the current thread, + // so wake it up + Logger.global.log(Level.FINE, "", e); + synchronized (fMutex) { + fMutex.notifyAll(); + } + } + } + + /** + * Runs the thread. + * + * @throws InterruptedException if the thread was interrupted + * @throws ThreadChangedException if the thread changed + */ + private void run2() throws InterruptedException, ThreadChangedException { + synchronized (fMutex) { + checkThread(); + tryHold(); // wait / potential state change + assertStates(STOPPED | RUNNING); + + while (isState(RUNNING)) { + waitForTimeout(); // wait / potential state change + + if (isState(RUNNING)) + timedOut(); // state change + assertStates(STOPPED | IDLE); + + tryHold(); // wait / potential state change + assertStates(STOPPED | RUNNING); + } + assertStates(STOPPED); + } + } + + /** + * Check whether the current thread is this thread, throw an + * exception otherwise. + * + * @throws ThreadChangedException if the current thread changed + */ + private void checkThread() throws ThreadChangedException { + if (fCurrentThread != this) + throw new ThreadChangedException(); + } + + /** + * Waits until the next timeout occurs. + * + * @throws InterruptedException if the thread was interrupted + * @throws ThreadChangedException if the thread changed + */ + private void waitForTimeout() throws InterruptedException, ThreadChangedException { + long delta; + while (isState(RUNNING) && (delta = fNextTimeout - System.currentTimeMillis()) > 0) { + delta= Math.max(delta, 50); // wait at least 50ms in order to avoid timing out before the display is going to sleep + Logger.global.finest("sleeping for " + delta + "ms"); + fMutex.wait(delta); + checkThread(); + } + } + + /** + * Sets the timed out flag and wakes up the display. Transitions to + * IDLE (if in keep-running mode) or + * STOPPED. + */ + private void timedOut() { + Logger.global.finer("timed out"); + fCurrentTimeoutState.setTimedOut(true); + fDisplay.wake(); // wake up call! + if (fKeepRunningOnTimeout) + checkedTransition(RUNNING, IDLE); + else + checkedTransition(RUNNING, STOPPED); + } + + /** + * Waits while the state is IDLE, then returns. The + * state must not be RUNNING when calling this + * method. The state is either STOPPED or + * RUNNING when the method returns. + * + * @throws InterruptedException if the thread was interrupted + * @throws ThreadChangedException if the thread has changed while on + * hold + */ + private void tryHold() throws InterruptedException, ThreadChangedException { + while (isState(IDLE)) { + fMutex.wait(0); + checkThread(); + } + assertStates(STOPPED | RUNNING); + } + }; + + fCurrentThread.start(); + } + + /** + * Transitions to nextState if the current state is one of + * possibleStates. Returns true if the + * transition happened, false otherwise. + * + * @param possibleStates the states which trigger a transition + * @param nextState the state to transition to + * @return true if the transition happened, + * false otherwise + */ + private boolean tryTransition(int possibleStates, int nextState) { + if (isState(possibleStates)) { + Logger.global.finer(name(fState) + " > " + name(nextState) + " (" + name(possibleStates) + ")"); + fState= nextState; + return true; + } + Logger.global.finest("noTransition" + name(fState) + " !> " + name(nextState) + " (" + name(possibleStates) + ")"); + return false; + } + + /** + * Checks the possibleStates and throws an assertion if it is + * not met, then transitions to nextState. + * + * @param possibleStates the allowed states + * @param nextState the state to transition to + */ + private void checkedTransition(int possibleStates, int nextState) { + assertStates(possibleStates); + Logger.global.finer(name(fState) + " > " + name(nextState)); + fState= nextState; + } + + /** + * Implements state consistency checking. + * + * @param states the allowed states + * @throws junit.framework.AssertionFailedError if the current state is not + * in states + */ + private void assertStates(int states) { + Assert.assertTrue("illegal state", isState(states)); + } + + /** + * Answers true if the current state is in the given + * states. + * + * @param states the possible states + * @return true if the current state is in the given states, + * false otherwise + */ + private boolean isState(int states) { + return (states & fState) == fState; + } + + /** + * Pretty print the given states. + * + * @param states the states + * @return a string representation of the states + */ + private String name(int states) { + StringBuffer buf= new StringBuffer(); + boolean comma= false; + if ((states & RUNNING) == RUNNING) { + buf.append("RUNNING"); + comma= true; + } + if ((states & STOPPED) == STOPPED) { + if (comma) + buf.append(","); + buf.append("STOPPED"); + comma= true; + } + if ((states & IDLE) == IDLE) { + if (comma) + buf.append(","); + buf.append("IDLE"); + } + return buf.toString(); + } + +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/EditorTestHelper.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/EditorTestHelper.java new file mode 100644 index 00000000000..996da0ff02a --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/EditorTestHelper.java @@ -0,0 +1,508 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.text; + + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Logger; + +import junit.framework.Assert; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobManager; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IWidgetTokenKeeper; +import org.eclipse.jface.text.IWidgetTokenOwner; +import org.eclipse.jface.text.reconciler.AbstractReconciler; +import org.eclipse.jface.text.reconciler.IReconcilingStrategy; +import org.eclipse.jface.text.reconciler.MonoReconciler; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IEditorReference; +import org.eclipse.ui.IViewReference; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartSite; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PartInitException; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.WorkbenchException; +import org.eclipse.ui.dialogs.IOverwriteQuery; +import org.eclipse.ui.ide.IDE; +import org.eclipse.ui.texteditor.AbstractTextEditor; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.ui.wizards.datatransfer.FileSystemStructureProvider; +import org.eclipse.ui.wizards.datatransfer.IImportStructureProvider; +import org.eclipse.ui.wizards.datatransfer.ImportOperation; + +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.testplugin.CProjectHelper; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.PreferenceConstants; +import org.eclipse.cdt.ui.testplugin.CTestPlugin; + +import org.eclipse.cdt.internal.ui.text.CReconcilingStrategy; + + +/** + * Copied from org.eclipse.jdt.text.tests.performance. + * + * @since 4.0 + */ +public class EditorTestHelper { + + private static class ImportOverwriteQuery implements IOverwriteQuery { + public String queryOverwrite(String file) { + return ALL; + } + } + + public static final String TEXT_EDITOR_ID= "org.eclipse.ui.DefaultTextEditor"; + + public static final String C_EDITOR_ID= "org.eclipse.cdt.ui.editor.CEditor"; + + public static final String RESOURCE_PERSPECTIVE_ID= "org.eclipse.ui.resourcePerspective"; + + public static final String C_PERSPECTIVE_ID= "org.eclipse.cdt.ui.CPerspective"; + + public static final String OUTLINE_VIEW_ID= "org.eclipse.ui.views.ContentOutline"; + + public static final String C_VIEW_ID= "org.eclipse.cdt.ui.CView"; + + public static final String NAVIGATOR_VIEW_ID= "org.eclipse.ui.views.ResourceNavigator"; + + public static final String INTRO_VIEW_ID= "org.eclipse.ui.internal.introview"; + + public static IEditorPart openInEditor(IFile file, boolean runEventLoop) throws PartInitException { + IEditorPart part= IDE.openEditor(getActivePage(), file); + if (runEventLoop) + runEventQueue(part); + return part; + } + + public static IEditorPart openInEditor(IFile file, String editorId, boolean runEventLoop) throws PartInitException { + IEditorPart part= IDE.openEditor(getActivePage(), file, editorId); + if (runEventLoop) + runEventQueue(part); + return part; + } + + public static AbstractTextEditor[] openInEditor(IFile[] files, String editorId) throws PartInitException { + AbstractTextEditor editors[]= new AbstractTextEditor[files.length]; + for (int i= 0; i < files.length; i++) { + editors[i]= (AbstractTextEditor) openInEditor(files[i], editorId, true); + joinReconciler(getSourceViewer(editors[i]), 100, 10000, 100); + } + return editors; + } + + public static IDocument getDocument(ITextEditor editor) { + IDocumentProvider provider= editor.getDocumentProvider(); + IEditorInput input= editor.getEditorInput(); + return provider.getDocument(input); + } + + public static void revertEditor(ITextEditor editor, boolean runEventQueue) { + editor.doRevertToSaved(); + if (runEventQueue) + runEventQueue(editor); + } + + public static void closeEditor(IEditorPart editor) { + IWorkbenchPartSite site; + IWorkbenchPage page; + if (editor != null && (site= editor.getSite()) != null && (page= site.getPage()) != null) + page.closeEditor(editor, false); + } + + public static void closeAllEditors() { + IWorkbenchWindow[] windows= PlatformUI.getWorkbench().getWorkbenchWindows(); + for (int i= 0; i < windows.length; i++) { + IWorkbenchPage[] pages= windows[i].getPages(); + for (int j= 0; j < pages.length; j++) { + IEditorReference[] editorReferences= pages[j].getEditorReferences(); + for (int k= 0; k < editorReferences.length; k++) + closeEditor(editorReferences[k].getEditor(false)); + } + } + } + + /** + * Runs the event queue on the current display until it is empty. + */ + public static void runEventQueue() { + IWorkbenchWindow window= getActiveWorkbenchWindow(); + if (window != null) + runEventQueue(window.getShell()); + } + + public static void runEventQueue(IWorkbenchPart part) { + runEventQueue(part.getSite().getShell()); + } + + public static void runEventQueue(Shell shell) { + runEventQueue(shell.getDisplay()); + } + + public static void runEventQueue(Display display) { + while (display.readAndDispatch()) { + // do nothing + } + } + + /** + * Runs the event queue on the current display and lets it sleep until the + * timeout elapses. + * + * @param millis the timeout in milliseconds + */ + public static void runEventQueue(long millis) { + runEventQueue(getActiveDisplay(), millis); + } + + public static void runEventQueue(IWorkbenchPart part, long millis) { + runEventQueue(part.getSite().getShell(), millis); + } + + public static void runEventQueue(Shell shell, long millis) { + runEventQueue(shell.getDisplay(), millis); + } + + public static void runEventQueue(Display display, long minTime) { + if (display != null) { + DisplayHelper.sleep(display, minTime); + } else { + sleep((int) minTime); + } + } + + public static IWorkbenchWindow getActiveWorkbenchWindow() { + return PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + } + + public static void forceFocus() { + IWorkbenchWindow window= getActiveWorkbenchWindow(); + if (window == null) { + IWorkbenchWindow[] wbWindows= PlatformUI.getWorkbench().getWorkbenchWindows(); + if (wbWindows.length == 0) + return; + window= wbWindows[0]; + } + Shell shell= window.getShell(); + if (shell != null && !shell.isDisposed()) { + shell.forceActive(); + shell.forceFocus(); + } + } + + public static IWorkbenchPage getActivePage() { + IWorkbenchWindow window= getActiveWorkbenchWindow(); + return window != null ? window.getActivePage() : null; + } + + public static Display getActiveDisplay() { + IWorkbenchWindow window= getActiveWorkbenchWindow(); + return window != null ? window.getShell().getDisplay() : null; + } + + public static void joinBackgroundActivities(AbstractTextEditor editor) throws CoreException { + joinBackgroundActivities(getSourceViewer(editor)); + } + + public static void joinBackgroundActivities(SourceViewer sourceViewer) throws CoreException { + joinBackgroundActivities(); + joinReconciler(sourceViewer, 500, 0, 500); + } + + public static void joinBackgroundActivities() throws CoreException { + // Join Building + Logger.global.entering("EditorTestHelper", "joinBackgroundActivities"); + Logger.global.finer("join builder"); + boolean interrupted= true; + while (interrupted) { + try { + Platform.getJobManager().join(ResourcesPlugin.FAMILY_AUTO_BUILD, null); + interrupted= false; + } catch (InterruptedException e) { + interrupted= true; + } + } + // Join indexing + Logger.global.finer("join indexer"); +// new SearchEngine().searchAllTypeNames( +// null, +// "XXXXXXXXX".toCharArray(), // make sure we search a concrete name. This is faster according to Kent +// SearchPattern.R_EXACT_MATCH, +// IJavaSearchConstants.CLASS, +// SearchEngine.createJavaSearchScope(new ICElement[0]), +// new Requestor(), +// IJavaSearchConstants.WAIT_UNTIL_READY_TO_SEARCH, +// null); + // Join jobs + joinJobs(0, 0, 500); + Logger.global.exiting("EditorTestHelper", "joinBackgroundActivities"); + } + + public static boolean joinJobs(long minTime, long maxTime, long intervalTime) { + Logger.global.entering("EditorTestHelper", "joinJobs"); + runEventQueue(minTime); + + DisplayHelper helper= new DisplayHelper() { + public boolean condition() { + return allJobsQuiet(); + } + }; + boolean quiet= helper.waitForCondition(getActiveDisplay(), maxTime > 0 ? maxTime : Long.MAX_VALUE, intervalTime); + Logger.global.exiting("EditorTestHelper", "joinJobs", new Boolean(quiet)); + return quiet; + } + + public static void sleep(int intervalTime) { + try { + Thread.sleep(intervalTime); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public static boolean allJobsQuiet() { + IJobManager jobManager= Platform.getJobManager(); + Job[] jobs= jobManager.find(null); + for (int i= 0; i < jobs.length; i++) { + Job job= jobs[i]; + int state= job.getState(); + if (state == Job.RUNNING || state == Job.WAITING) { + Logger.global.finest(job.toString()); + return false; + } + } + return true; + } + + public static boolean isViewShown(String viewId) { + return getActivePage().findViewReference(viewId) != null; + } + + public static boolean showView(String viewId, boolean show) throws PartInitException { + IWorkbenchPage activePage= getActivePage(); + IViewReference view= activePage.findViewReference(viewId); + boolean shown= view != null; + if (shown != show) + if (show) + activePage.showView(viewId); + else + activePage.hideView(view); + return shown; + } + + public static void bringToTop() { + getActiveWorkbenchWindow().getShell().forceActive(); + } + + public static void forceReconcile(SourceViewer sourceViewer) { + Accessor reconcilerAccessor= new Accessor(getReconciler(sourceViewer), AbstractReconciler.class); + reconcilerAccessor.invoke("forceReconciling", new Object[0]); + } + + public static boolean joinReconciler(SourceViewer sourceViewer, long minTime, long maxTime, long intervalTime) { + Logger.global.entering("EditorTestHelper", "joinReconciler"); + runEventQueue(minTime); + + AbstractReconciler reconciler= getReconciler(sourceViewer); + if (reconciler == null) + return true; + final Accessor backgroundThreadAccessor= getBackgroundThreadAccessor(reconciler); + final Accessor cReconcilerAccessor; + if (reconciler instanceof MonoReconciler) { + IReconcilingStrategy strategy= reconciler.getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE); + if (strategy instanceof CReconcilingStrategy) { + cReconcilerAccessor= new Accessor(strategy, CReconcilingStrategy.class); + } else { + cReconcilerAccessor= null; + } + } else { + cReconcilerAccessor= null; + } + DisplayHelper helper= new DisplayHelper() { + public boolean condition() { + return !isRunning(cReconcilerAccessor, backgroundThreadAccessor); + } + }; + boolean finished= helper.waitForCondition(getActiveDisplay(), maxTime > 0 ? maxTime : Long.MAX_VALUE, intervalTime); + Logger.global.exiting("EditorTestHelper", "joinReconciler", new Boolean(finished)); + return finished; + } + + public static AbstractReconciler getReconciler(SourceViewer sourceViewer) { + return (AbstractReconciler) new Accessor(sourceViewer, SourceViewer.class).get("fReconciler"); + } + + public static SourceViewer getSourceViewer(AbstractTextEditor editor) { + SourceViewer sourceViewer= (SourceViewer) new Accessor(editor, AbstractTextEditor.class).invoke("getSourceViewer", new Object[0]); + return sourceViewer; + } + + private static Accessor getBackgroundThreadAccessor(AbstractReconciler reconciler) { + Object backgroundThread= new Accessor(reconciler, AbstractReconciler.class).get("fThread"); + return new Accessor(backgroundThread, backgroundThread.getClass()); + } + + private static boolean isRunning(Accessor cReconcilerAccessor, Accessor backgroundThreadAccessor) { + return (cReconcilerAccessor != null ? !isInitialProcessDone(cReconcilerAccessor) : false) || isDirty(backgroundThreadAccessor) || isActive(backgroundThreadAccessor); + } + + private static boolean isInitialProcessDone(Accessor cReconcilerAccessor) { + return ((Boolean) cReconcilerAccessor.get("fInitialProcessDone")).booleanValue(); + } + + private static boolean isDirty(Accessor backgroundThreadAccessor) { + return ((Boolean) backgroundThreadAccessor.invoke("isDirty", new Object[0])).booleanValue(); + } + + private static boolean isActive(Accessor backgroundThreadAccessor) { + return ((Boolean) backgroundThreadAccessor.invoke("isActive", new Object[0])).booleanValue(); + } + + public static String showPerspective(String perspective) throws WorkbenchException { + String shownPerspective= getActivePage().getPerspective().getId(); + if (!perspective.equals(shownPerspective)) { + IWorkbench workbench= PlatformUI.getWorkbench(); + IWorkbenchWindow activeWindow= workbench.getActiveWorkbenchWindow(); + workbench.showPerspective(perspective, activeWindow); + } + return shownPerspective; + } + + public static void closeAllPopUps(SourceViewer sourceViewer) { + IWidgetTokenKeeper tokenKeeper= new IWidgetTokenKeeper() { + public boolean requestWidgetToken(IWidgetTokenOwner owner) { + return true; + } + }; + sourceViewer.requestWidgetToken(tokenKeeper, Integer.MAX_VALUE); + sourceViewer.releaseWidgetToken(tokenKeeper); + } + + public static void resetFolding() { + CUIPlugin.getDefault().getPreferenceStore().setToDefault(PreferenceConstants.EDITOR_FOLDING_ENABLED); + } + + public static boolean enableFolding(boolean value) { + IPreferenceStore preferenceStore= CUIPlugin.getDefault().getPreferenceStore(); + boolean oldValue= preferenceStore.getBoolean(PreferenceConstants.EDITOR_FOLDING_ENABLED); + if (value != oldValue) + preferenceStore.setValue(PreferenceConstants.EDITOR_FOLDING_ENABLED, value); + return oldValue; + } + + public static ICProject createCProject(String project, String externalSourceFolder) throws CoreException { + return createCProject(project, externalSourceFolder, false); + } + + public static ICProject createCProject(String project, String externalSourceFolder, boolean linkSourceFolder) throws CoreException { + ICProject cProject= CProjectHelper.createCProject(project, "bin"); + IFolder folder; + if (linkSourceFolder) + folder= ResourceHelper.createLinkedFolder((IProject) cProject.getUnderlyingResource(), new Path("src"), CTestPlugin.getDefault(), new Path(externalSourceFolder)); + else { + folder= ((IProject) cProject.getUnderlyingResource()).getFolder("src"); + importFilesFromDirectory(FileTool.getFileInPlugin(CTestPlugin.getDefault(), new Path(externalSourceFolder)), folder.getFullPath(), null); + } + Assert.assertNotNull(folder); + Assert.assertTrue(folder.exists()); + CProjectHelper.addCContainer(cProject, "src"); + return cProject; + } + + public static IFile[] findFiles(IResource resource) throws CoreException { + List files= new ArrayList(); + findFiles(resource, files); + return (IFile[]) files.toArray(new IFile[files.size()]); + } + + private static void findFiles(IResource resource, List files) throws CoreException { + if (resource instanceof IFile) { + files.add(resource); + return; + } + if (resource instanceof IContainer) { + IResource[] resources= ((IContainer) resource).members(); + for (int i= 0; i < resources.length; i++) + findFiles(resources[i], files); + } + } + + public static boolean setDialogEnabled(String id, boolean enabled) { +// boolean wasEnabled= OptionalMessageDialog.isDialogEnabled(id); +// if (wasEnabled != enabled) +// OptionalMessageDialog.setDialogEnabled(id, enabled); +// return wasEnabled; + return false; + } + + public static void importFilesFromDirectory(File rootDir, IPath destPath, IProgressMonitor monitor) throws CoreException { + try { + IImportStructureProvider structureProvider= FileSystemStructureProvider.INSTANCE; + List files= new ArrayList(100); + addFiles(rootDir, files); + ImportOperation op= new ImportOperation(destPath, rootDir, structureProvider, new ImportOverwriteQuery(), files); + op.setCreateContainerStructure(false); + op.run(monitor); + } catch (Exception x) { + throw newCoreException(x); + } + } + + private static CoreException newCoreException(Throwable x) { + return new CoreException(new Status(IStatus.ERROR, CTestPlugin.PLUGIN_ID, -1, "", x)); + } + + private static void addFiles(File dir, List collection) throws IOException { + File[] files= dir.listFiles(); + List subDirs= new ArrayList(2); + for (int i= 0; i < files.length; i++) { + if (files[i].isFile()) { + collection.add(files[i]); + } else if (files[i].isDirectory()) { + subDirs.add(files[i]); + } + } + Iterator iter= subDirs.iterator(); + while (iter.hasNext()) { + File subDir= (File)iter.next(); + addFiles(subDir, collection); + } + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/FileTool.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/FileTool.java new file mode 100644 index 00000000000..3c491e70d46 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/FileTool.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * Copyright (c) 2000, 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.cdt.ui.tests.text; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Reader; +import java.io.Writer; +import java.net.URL; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; + +import org.eclipse.core.filebuffers.FileBuffers; + +/** + * Copied from org.eclipse.core.filebuffers.tests. + * + * @since 4.0 + */ +public class FileTool { + + private final static int MAX_RETRY= 5; + + /** + * A buffer. + */ + private static byte[] buffer = new byte[8192]; + + /** + * Unzips the given zip file to the given destination directory + * extracting only those entries the pass through the given + * filter. + * + * @param zipFile the zip file to unzip + * @param dstDir the destination directory + * @throws IOException in case of problem + */ + public static void unzip(ZipFile zipFile, File dstDir) throws IOException { + unzip(zipFile, dstDir, dstDir, 0); + } + + private static void unzip(ZipFile zipFile, File rootDstDir, File dstDir, int depth) throws IOException { + + Enumeration entries = zipFile.entries(); + + try { + while(entries.hasMoreElements()){ + ZipEntry entry = (ZipEntry)entries.nextElement(); + if(entry.isDirectory()){ + continue; + } + String entryName = entry.getName(); + File file = new File(dstDir, changeSeparator(entryName, '/', File.separatorChar)); + file.getParentFile().mkdirs(); + InputStream src = null; + OutputStream dst = null; + try { + src = zipFile.getInputStream(entry); + dst = new FileOutputStream(file); + transferData(src, dst); + } finally { + if(dst != null){ + try { + dst.close(); + } catch(IOException e){ + } + } + if(src != null){ + try { + src.close(); + } catch(IOException e){ + } + } + } + } + } finally { + try { + zipFile.close(); + } catch(IOException e){ + } + } + } + + /** + * Returns the given file path with its separator + * character changed from the given old separator to the + * given new separator. + * + * @param path a file path + * @param oldSeparator a path separator character + * @param newSeparator a path separator character + * @return the file path with its separator character + * changed from the given old separator to the given new + * separator + */ + public static String changeSeparator(String path, char oldSeparator, char newSeparator){ + return path.replace(oldSeparator, newSeparator); + } + + /** + * Copies all bytes in the given source file to + * the given destination file. + * + * @param source the given source file + * @param destination the given destination file + * @throws IOException in case of error + */ + public static void transferData(File source, File destination) throws IOException { + destination.getParentFile().mkdirs(); + InputStream is = null; + OutputStream os = null; + try { + is = new FileInputStream(source); + os = new FileOutputStream(destination); + transferData(is, os); + } finally { + if(os != null) + os.close(); + if(is != null) + is.close(); + } + } + + /** + * Copies all bytes in the given source stream to + * the given destination stream. Neither streams + * are closed. + * + * @param source the given source stream + * @param destination the given destination stream + * @throws IOException in case of error + */ + public static void transferData(InputStream source, OutputStream destination) throws IOException { + int bytesRead = 0; + while(bytesRead != -1){ + bytesRead = source.read(buffer, 0, buffer.length); + if(bytesRead != -1){ + destination.write(buffer, 0, bytesRead); + } + } + } + + /** + * Copies the given source file to the given destination file. + * + * @param src the given source file + * @param dst the given destination file + * @throws IOException in case of error + */ + public static void copy(File src, File dst) throws IOException { + if(src.isDirectory()){ + String[] srcChildren = src.list(); + for(int i = 0; i < srcChildren.length; ++i){ + File srcChild= new File(src, srcChildren[i]); + File dstChild= new File(dst, srcChildren[i]); + copy(srcChild, dstChild); + } + } else + transferData(src, dst); + } + + public static File getFileInPlugin(Plugin plugin, IPath path) { + try { + URL installURL= plugin.getBundle().getEntry(path.toString()); + URL localURL= Platform.asLocalURL(installURL); + return new File(localURL.getFile()); + } catch (IOException e) { + return null; + } + } + + public static File createTempFileInPlugin(Plugin plugin, IPath path) { + IPath stateLocation= plugin.getStateLocation(); + stateLocation= stateLocation.append(path); + return stateLocation.toFile(); + } + + public static StringBuffer read(String fileName) throws IOException { + return read(new FileReader(fileName)); + } + + public static StringBuffer read(Reader reader) throws IOException { + StringBuffer s= new StringBuffer(); + try { + char[] charBuffer= new char[8196]; + int chars= reader.read(charBuffer); + while (chars != -1) { + s.append(charBuffer, 0, chars); + chars= reader.read(charBuffer); + } + } finally { + try { + reader.close(); + } catch (IOException e) { + } + } + return s; + } + + public static void write(String fileName, StringBuffer content) throws IOException { + Writer writer= new FileWriter(fileName); + try { + writer.write(content.toString()); + } finally { + try { + writer.close(); + } catch (IOException e) { + } + } + } + + public static void delete(IPath path) throws CoreException { + File file= FileBuffers.getSystemFileAtLocation(path); + delete(file); + } + + public static void delete(File file) throws CoreException { + if (file.exists()) { + for (int i= 0; i < MAX_RETRY; i++) { + if (file.delete()) + i= MAX_RETRY; + else { + try { + Thread.sleep(1000); // sleep a second + } catch (InterruptedException e) { + } + } + } + } + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/ResourceHelper.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/ResourceHelper.java new file mode 100644 index 00000000000..017539b5e7c --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/ResourceHelper.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2000, 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.cdt.ui.tests.text; + +import java.io.File; +import java.io.InputStream; + +import org.eclipse.core.filebuffers.manipulation.ContainerCreator; +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IFolder; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Plugin; + +import org.eclipse.cdt.ui.testplugin.CTestPlugin; + +/** + * Copied from org.eclipse.core.filebuffers.tests. + * + * @since 4.0 + */ +public class ResourceHelper { + + private final static IProgressMonitor NULL_MONITOR= new NullProgressMonitor(); + private static final int MAX_RETRY= 5; + + public static IProject createProject(String projectName) throws CoreException { + + IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); + IProject project= root.getProject(projectName); + if (!project.exists()) + project.create(NULL_MONITOR); + else + project.refreshLocal(IResource.DEPTH_INFINITE, null); + + if (!project.isOpen()) + project.open(NULL_MONITOR); + + return project; + } + + public static void deleteProject(String projectName) throws CoreException { + IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot(); + IProject project= root.getProject(projectName); + if (project.exists()) + delete(project); + } + + public static void delete(final IProject project) throws CoreException { + delete(project, true); + } + + public static void delete(final IProject project, boolean deleteContent) throws CoreException { + for (int i= 0; i < MAX_RETRY; i++) { + try { + project.delete(deleteContent, true, NULL_MONITOR); + i= MAX_RETRY; + } catch (CoreException x) { + if (i == MAX_RETRY - 1) { + CTestPlugin.getDefault().getLog().log(x.getStatus()); +// throw x; + } + try { + Thread.sleep(1000); // sleep a second + } catch (InterruptedException e) { + } + } + } + } + + public static IFolder createFolder(String portableFolderPath) throws CoreException { + ContainerCreator creator= new ContainerCreator(ResourcesPlugin.getWorkspace(), new Path(portableFolderPath)); + IContainer container= creator.createContainer(NULL_MONITOR); + if (container instanceof IFolder) + return (IFolder) container; + return null; + } + + public static IFile createFile(IFolder folder, String name, String contents) throws CoreException { + return createFile(folder.getFile(name), name, contents); + } + + public static IFile createFile(IProject project, String name, String contents) throws CoreException { + return createFile(project.getFile(name), name, contents); + } + + private static IFile createFile(IFile file, String name, String contents) throws CoreException { + if (contents == null) + contents= ""; + InputStream inputStream= new java.io.StringBufferInputStream(contents); + file.create(inputStream, true, NULL_MONITOR); + return file; + } + + public static IFile createLinkedFile(IContainer container, IPath linkPath, File linkedFileTarget) throws CoreException { + IFile iFile= container.getFile(linkPath); + iFile.createLink(new Path(linkedFileTarget.getAbsolutePath()), IResource.ALLOW_MISSING_LOCAL, NULL_MONITOR); + return iFile; + } + + public static IFile createLinkedFile(IContainer container, IPath linkPath, Plugin plugin, IPath linkedFileTargetPath) throws CoreException { + File file= FileTool.getFileInPlugin(plugin, linkedFileTargetPath); + IFile iFile= container.getFile(linkPath); + iFile.createLink(new Path(file.getAbsolutePath()), IResource.ALLOW_MISSING_LOCAL, NULL_MONITOR); + return iFile; + } + + public static IFolder createLinkedFolder(IContainer container, IPath linkPath, File linkedFolderTarget) throws CoreException { + IFolder folder= container.getFolder(linkPath); + folder.createLink(new Path(linkedFolderTarget.getAbsolutePath()), IResource.ALLOW_MISSING_LOCAL, NULL_MONITOR); + return folder; + } + + public static IFolder createLinkedFolder(IContainer container, IPath linkPath, Plugin plugin, IPath linkedFolderTargetPath) throws CoreException { + File file= FileTool.getFileInPlugin(plugin, linkedFolderTargetPath); + IFolder iFolder= container.getFolder(linkPath); + iFolder.createLink(new Path(file.getAbsolutePath()), IResource.ALLOW_MISSING_LOCAL, NULL_MONITOR); + return iFolder; + } + + public static IProject createLinkedProject(String projectName, Plugin plugin, IPath linkPath) throws CoreException { + IWorkspace workspace= ResourcesPlugin.getWorkspace(); + IProject project= workspace.getRoot().getProject(projectName); + + IProjectDescription desc= workspace.newProjectDescription(projectName); + File file= FileTool.getFileInPlugin(plugin, linkPath); + IPath projectLocation= new Path(file.getAbsolutePath()); + if (Platform.getLocation().equals(projectLocation)) + projectLocation= null; + desc.setLocation(projectLocation); + + project.create(desc, NULL_MONITOR); + if (!project.isOpen()) + project.open(NULL_MONITOR); + + return project; + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/ResourceTestHelper.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/ResourceTestHelper.java new file mode 100644 index 00000000000..a1e7aa57ea9 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/ResourceTestHelper.java @@ -0,0 +1,252 @@ +/******************************************************************************* + * Copyright (c) 2000, 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.cdt.ui.tests.text; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.zip.ZipException; +import java.util.zip.ZipFile; + +import junit.framework.Assert; + +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IProjectDescription; +import org.eclipse.core.resources.IWorkspace; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.IncrementalProjectBuilder; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Plugin; +import org.eclipse.core.runtime.Preferences; + +/** + * Copied from org.eclipse.jdt.text.tests.performance. + * + * @since 4.0 + */ +public class ResourceTestHelper { + + public static final int FAIL_IF_EXISTS= 0; + + public static final int OVERWRITE_IF_EXISTS= 1; + + public static final int SKIP_IF_EXISTS= 2; + + private static final int DELETE_MAX_RETRY= 5; + + private static final long DELETE_RETRY_DELAY= 1000; + + public static void replicate(String src, String destPrefix, String destSuffix, int n, int ifExists) throws CoreException { + for (int i= 0; i < n; i++) + copy(src, destPrefix + i + destSuffix, ifExists); + } + + public static void copy(String src, String dest) throws CoreException { + copy(src, dest, FAIL_IF_EXISTS); + } + + public static void copy(String src, String dest, int ifExists) throws CoreException { + if (handleExisting(dest, ifExists)) + getFile(src).copy(new Path(dest), true, null); + } + + private static boolean handleExisting(String dest, int ifExists) throws CoreException { + IFile destFile= getFile(dest); + switch (ifExists) { + case FAIL_IF_EXISTS: + if (destFile.exists()) + throw new IllegalArgumentException("Destination file exists: " + dest); + return true; + case OVERWRITE_IF_EXISTS: + if (destFile.exists()) + delete(destFile); + return true; + case SKIP_IF_EXISTS: + if (destFile.exists()) + return false; + return true; + default: + throw new IllegalArgumentException(); + } + } + + private static IFile getFile(String path) { + return getRoot().getFile(new Path(path)); + } + + public static void delete(String file) throws CoreException { + delete(getFile(file)); + } + + private static void delete(IFile file) throws CoreException { + CoreException x= null; + for (int i= 0; i < DELETE_MAX_RETRY; i++) { + try { + file.delete(true, null); + return; + } catch (CoreException x0) { + x= x0; + try { + Thread.sleep(DELETE_RETRY_DELAY); + } catch (InterruptedException x1) { + // should not happen + } + } + } + throw x; + } + + public static void delete(String prefix, String suffix, int n) throws CoreException { + for (int i= 0; i < n; i++) + delete(prefix + i + suffix); + } + + public static IFile findFile(String pathStr) { + IFile file= getFile(pathStr); + Assert.assertTrue(file != null && file.exists()); + return file; + } + + public static IFile[] findFiles(String prefix, String suffix, int i, int n) { + List files= new ArrayList(n); + for (int j= i; j < i + n; j++) { + String path= prefix + j + suffix; + files.add(findFile(path)); + } + return (IFile[]) files.toArray(new IFile[files.size()]); + } + + public static StringBuffer read(String src) throws IOException, CoreException { + return FileTool.read(new InputStreamReader(getFile(src).getContents())); + } + + public static void write(String dest, final String content) throws IOException, CoreException { + InputStream stream= new InputStream() { + private Reader fReader= new StringReader(content); + public int read() throws IOException { + return fReader.read(); + } + }; + getFile(dest).create(stream, true, null); + } + + + public static void replicate(String src, String destPrefix, String destSuffix, int n, String srcName, String destNamePrefix, int ifExists) throws IOException, CoreException { + StringBuffer s= read(src); + List positions= identifierPositions(s, srcName); + for (int j= 0; j < n; j++) { + String dest= destPrefix + j + destSuffix; + if (handleExisting(dest, ifExists)) { + StringBuffer c= new StringBuffer(s.toString()); + replacePositions(c, srcName.length(), destNamePrefix + j, positions); + write(dest, c.toString()); + } + } + } + + public static void copy(String src, String dest, String srcName, String destName, int ifExists) throws IOException, CoreException { + if (handleExisting(dest, ifExists)) { + StringBuffer buf= read(src); + List positions= identifierPositions(buf, srcName); + replacePositions(buf, srcName.length(), destName, positions); + write(dest, buf.toString()); + } + } + + private static void replacePositions(StringBuffer c, int origLength, String string, List positions) { + int offset= 0; + for (Iterator iter= positions.iterator(); iter.hasNext();) { + int position= ((Integer) iter.next()).intValue(); + c.replace(offset + position, offset + position + origLength, string); + offset += string.length() - origLength; + } + } + + private static List identifierPositions(StringBuffer buffer, String identifier) { + List positions= new ArrayList(); + int i= -1; + while (true) { + i= buffer.indexOf(identifier, i + 1); + if (i == -1) + break; + if (i > 0 && Character.isJavaIdentifierPart(buffer.charAt(i - 1))) + continue; + if (i < buffer.length() - 1 && Character.isJavaIdentifierPart(buffer.charAt(i + identifier.length()))) + continue; + positions.add(new Integer(i)); + } + return positions; + } + + private static IWorkspaceRoot getRoot() { + return ResourcesPlugin.getWorkspace().getRoot(); + } + + public static void incrementalBuild() throws CoreException { + ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.INCREMENTAL_BUILD, null); + } + + public static void fullBuild() throws CoreException { + ResourcesPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, null); + } + + public static boolean disableAutoBuilding() { + return setAutoBuilding(false); + } + + public static boolean enableAutoBuilding() { + return setAutoBuilding(true); + } + + public static boolean setAutoBuilding(boolean value) { + Preferences preferences= ResourcesPlugin.getPlugin().getPluginPreferences(); + boolean oldValue= preferences.getBoolean(ResourcesPlugin.PREF_AUTO_BUILDING); + if (value != oldValue) + preferences.setValue(ResourcesPlugin.PREF_AUTO_BUILDING, value); + return oldValue; + } + + public static IProject createExistingProject(String projectName) throws CoreException { + IWorkspace workspace= ResourcesPlugin.getWorkspace(); + IProject project= workspace.getRoot().getProject(projectName); + IProjectDescription description= workspace.newProjectDescription(projectName); + description.setLocation(null); + + project.create(description, null); + project.open(null); + return project; + } + + public static IProject createProjectFromZip(Plugin installationPlugin, String projectZip, String projectName) throws IOException, ZipException, CoreException { + String workspacePath= ResourcesPlugin.getWorkspace().getRoot().getLocation().toString() + "/"; + FileTool.unzip(new ZipFile(FileTool.getFileInPlugin(installationPlugin, new Path(projectZip))), new File(workspacePath)); + return createExistingProject(projectName); + } + + public static IProject getProject(String projectName) { + IWorkspace workspace= ResourcesPlugin.getWorkspace(); + return workspace.getRoot().getProject(projectName); + } + + public static boolean projectExists(String projectName) { + return getProject(projectName).exists(); + } +} diff --git a/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/SemanticHighlightingTest.java b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/SemanticHighlightingTest.java new file mode 100644 index 00000000000..0196a034be6 --- /dev/null +++ b/core/org.eclipse.cdt.ui.tests/ui/org/eclipse/cdt/ui/tests/text/SemanticHighlightingTest.java @@ -0,0 +1,391 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ +package org.eclipse.cdt.ui.tests.text; + +import junit.framework.Test; +import junit.framework.TestSuite; + +import org.eclipse.jface.text.Position; + +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightings; + +/** + * Semantic highlighting tests. + * + *

Derived from JDT.

+ * + * @since 4.0 + */ +public class SemanticHighlightingTest extends AbstractSemanticHighlightingTest { + + private static final Class THIS= SemanticHighlightingTest.class; + + public static Test suite() { + return new SemanticHighlightingTestSetup(new TestSuite(THIS), "/SHTest/src/SHTest.cpp"); + } + +// public void testStaticConstFieldHighlighting() throws Exception { +// setUpSemanticHighlighting(SemanticHighlightings.STATIC_CONST_FIELD); +// Position[] expected= new Position[] { +// createPosition(6, 18, 16), +// createPosition(35, 37, 16), +// }; +// Position[] actual= getSemanticHighlightingPositions(); +//// System.out.println(toString(actual)); +// assertEqualPositions(expected, actual); +// } + + public void testStaticFieldHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.STATIC_FIELD); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(23, 15, 14), + createPosition(25, 21, 19), + createPosition(41, 21, 20), + createPosition(42, 15, 15), + createPosition(56, 21, 20), + createPosition(57, 15, 15), + createPosition(115, 20, 15), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testFieldHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.FIELD); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(24, 14, 13), + createPosition(26, 8, 8), + createPosition(43, 15, 14), + createPosition(44, 8, 9), + createPosition(58, 15, 14), + createPosition(59, 8, 9), + createPosition(74, 7, 5), + createPosition(75, 7, 5), + createPosition(77, 8, 5), + createPosition(78, 8, 5), + createPosition(86, 8, 11), + createPosition(90, 8, 10), + createPosition(102, 11, 9), + createPosition(107, 11, 8), + createPosition(112, 7, 11), + createPosition(114, 7, 10), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testMethodDeclarationHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.METHOD_DECLARATION); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(28, 15, 15), + createPosition(32, 8, 9), + createPosition(46, 15, 16), + createPosition(47, 8, 10), + createPosition(61, 15, 16), + createPosition(62, 8, 10), + createPosition(76, 4, 13), + createPosition(101, 4, 26), + createPosition(101, 20, 10), + createPosition(105, 4, 25), + createPosition(105, 20, 9), + createPosition(110, 4, 32), + createPosition(110, 20, 16), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testMethodInvocationHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.METHOD_INVOCATION); + Position[] expected= new Position[] { + createPosition(111, 22, 9), + createPosition(113, 21, 8), + createPosition(115, 4, 15), + }; + Position[] actual= getSemanticHighlightingPositions(); +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + // public void testStaticMethodInvocationHighlighting() throws Exception { +// setUpSemanticHighlighting(SemanticHighlightings.STATIC_METHOD_INVOCATION); +// Position[] expected= new Position[] { +// }; +// Position[] actual= getSemanticHighlightingPositions(); +// System.out.println(toString(actual)); +// assertEqualPositions(expected, actual); +// } + + /* + */ +// public void testVirtualMethodInvocationHighlighting() throws Exception { +// setUpSemanticHighlighting(SemanticHighlightings.VIRTUAL_METHOD_INVOCATION); +// Position[] expected= new Position[] { +// createPosition(11, 2, 14), +// }; +// Position[] actual= getSemanticHighlightingPositions(); +//// System.out.println(toString(actual)); +// assertEqualPositions(expected, actual); +// } + +// public void testInheritedMethodInvocationHighlighting() throws Exception { +// setUpSemanticHighlighting(SemanticHighlightings.INHERITED_METHOD_INVOCATION); +// Position[] expected= new Position[] { +// createPosition(12, 2, 8), +// createPosition(15, 17, 8), +// }; +// Position[] actual= getSemanticHighlightingPositions(); +//// System.out.println(toString(actual)); +// assertEqualPositions(expected, actual); +// } + + public void testLocalVariableDeclarationHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.LOCAL_VARIABLE_DECLARATION); + Position[] expected= new Position[] { + createPosition(106, 8, 8), + createPosition(111, 14, 2), + createPosition(113, 13, 2), + }; + Position[] actual= getSemanticHighlightingPositions(); +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testLocalVariableHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.LOCAL_VARIABLE); + Position[] expected= new Position[] { + createPosition(112, 4, 2), + createPosition(114, 4, 2), + }; + Position[] actual= getSemanticHighlightingPositions(); +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testParameterVariableHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.PARAMETER_VARIABLE); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(11, 20, 1), + createPosition(28, 35, 3), + createPosition(29, 8, 19), + createPosition(30, 19, 3), + createPosition(76, 21, 4), + createPosition(76, 30, 4), + createPosition(77, 16, 4), + createPosition(78, 16, 4), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testTemplateParameterHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.TEMPLATE_PARAMETER); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + +// public void testTemplateArgumentHighlighting() throws Exception { +// setUpSemanticHighlighting(SemanticHighlightings.TEMPLATE_ARGUMENT); +// Position[] actual= getSemanticHighlightingPositions(); +// Position[] expected= new Position[] { +// createPosition(41, 8, 6), +// }; +//// System.out.println(toString(actual)); +// assertEqualPositions(expected, actual); +// } + + public void testEnumHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.ENUM); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(3, 5, 11), + createPosition(34, 9, 14), + createPosition(49, 9, 15), + createPosition(64, 9, 15), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testClassHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.CLASS); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(15, 6, 5), + createPosition(16, 6, 5), + createPosition(18, 6, 14), + createPosition(18, 23, 5), + createPosition(18, 30, 5), + createPosition(20, 17, 11), + createPosition(35, 10, 8), + createPosition(36, 10, 9), + createPosition(37, 10, 8), + createPosition(38, 12, 8), + createPosition(50, 10, 9), + createPosition(51, 10, 10), + createPosition(52, 10, 9), + createPosition(53, 12, 9), + createPosition(65, 10, 9), + createPosition(66, 10, 10), + createPosition(67, 10, 9), + createPosition(68, 12, 9), + createPosition(73, 22, 13), + createPosition(82, 19, 24), + createPosition(82, 46, 13), + createPosition(82, 63, 5), + createPosition(85, 7, 9), + createPosition(89, 6, 8), + createPosition(93, 8, 8), + createPosition(111, 4, 9), + createPosition(113, 4, 8), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testFunctionDeclarationHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.FUNCTION_DECLARATION); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(11, 5, 10), + createPosition(12, 12, 16), + createPosition(19, 16, 10), + createPosition(28, 15, 15), + createPosition(32, 8, 9), + createPosition(46, 15, 16), + createPosition(47, 8, 10), + createPosition(61, 15, 16), + createPosition(62, 8, 10), + createPosition(76, 4, 13), + createPosition(97, 8, 13), + createPosition(101, 4, 26), + createPosition(101, 20, 10), + createPosition(105, 4, 25), + createPosition(105, 20, 9), + createPosition(110, 4, 32), + createPosition(110, 20, 16), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testFunctionInvocationHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.FUNCTION_INVOCATION); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(29, 8, 19), + createPosition(30, 8, 10), + createPosition(111, 22, 9), + createPosition(113, 21, 8), + createPosition(115, 4, 15), + createPosition(117, 4, 17), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + + public void testGlobalVariableHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.GLOBAL_VARIABLE); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(7, 10, 14), + createPosition(8, 4, 14), + createPosition(9, 11, 20), + createPosition(96, 8, 14), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testMacroSubstitutionHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.MACRO_SUBSTITUTION); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(29, 8, 19), + createPosition(117, 4, 17), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testTypedefHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.TYPEDEF); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(38, 21, 10), + createPosition(53, 22, 11), + createPosition(68, 22, 11), + createPosition(93, 17, 6), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testNamespaceHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.NAMESPACE); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(95, 10, 2), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testLabelHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.LABEL); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(116, 0, 5), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testEnumeratorHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.ENUMERATOR); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(4, 4, 10), + createPosition(34, 25, 13), + createPosition(49, 26, 14), + createPosition(64, 26, 14), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + + public void testProblemHighlighting() throws Exception { + setUpSemanticHighlighting(SemanticHighlightings.PROBLEM); + Position[] actual= getSemanticHighlightingPositions(); + Position[] expected= new Position[] { + createPosition(73, 9, 2), + createPosition(73, 12, 2), + createPosition(74, 4, 2), + createPosition(75, 4, 2), + createPosition(76, 18, 2), + createPosition(76, 27, 2), + createPosition(82, 9, 2), + createPosition(82, 60, 2), + }; +// System.out.println(toString(actual)); + assertEqualPositions(expected, actual); + } + +} diff --git a/core/org.eclipse.cdt.ui/.options b/core/org.eclipse.cdt.ui/.options index b895bdddd2b..4917fd522fb 100644 --- a/core/org.eclipse.cdt.ui/.options +++ b/core/org.eclipse.cdt.ui/.options @@ -3,3 +3,8 @@ org.eclipse.cdt.ui/debug=true # Reports contentAssist activity org.eclipse.cdt.ui/debug/contentassist=false +# Prints debug information related to the AST provider +org.eclipse.cdt.ui/debug/ASTProvider=false + +# Enables all semantic highlighting types +org.eclipse.cdt.ui/debug/SemanticHighlighting=false diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index fea81f68d18..347fbc3c9d9 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -621,12 +621,12 @@ class="org.eclipse.cdt.internal.ui.preferences.IndexerPreferencePage" id="org.eclipse.cdt.ui.preferences.IndexerPreferencePage" name="%indexerPrefName"/> - + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/util/SimplePositionTracker.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/util/SimplePositionTracker.java new file mode 100644 index 00000000000..d3f75f5469b --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/corext/util/SimplePositionTracker.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2006 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.corext.util; + +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IPositionUpdater; + +import org.eclipse.cdt.internal.core.PositionTracker; + +/** + * A simple general purpose position tracker. + * + * @since 4.0 + */ +public class SimplePositionTracker extends PositionTracker implements + IPositionUpdater { + + private IDocument fDocument; + + /* + * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent) + */ + public void update(DocumentEvent event) { + String text = event.getText(); + int insertLen = text != null ? text.length() : 0; + update(event.getOffset(), event.getLength(), insertLen); + } + + private void update(int offset, int deleteLen, int insertLen) { + if (insertLen > deleteLen) { + insert(offset + deleteLen, insertLen - deleteLen); + } else if (insertLen < deleteLen) { + delete(offset+insertLen, deleteLen - insertLen); + } + } + + /** + * Start tracking on the given document. + * + * @param doc + */ + public synchronized void startTracking(IDocument doc) { + stopTracking(); + fDocument= doc; + if (fDocument != null) { + fDocument.addPositionUpdater(this); + } + } + + /** + * Stop tracking. + */ + public synchronized void stopTracking() { + if (fDocument != null) { + fDocument.removePositionUpdater(this); + fDocument= null; + } + } + + /** + * Destroy the tracker. + */ + public void dispose() { + stopTracking(); + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/LineBackgroundPainter.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/LineBackgroundPainter.java index 5944c133c32..829c5e912e1 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/LineBackgroundPainter.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/LineBackgroundPainter.java @@ -127,7 +127,7 @@ public class LineBackgroundPainter implements IPainter, LineBackgroundListener { /** * Set highlight positions. It is assumed that all positions - * are up-to-date with respect to the text viewers document. + * are up-to-date with respect to the text viewer document. * * @param positions a list of Positions */ @@ -142,6 +142,51 @@ public class LineBackgroundPainter implements IPainter, LineBackgroundListener { } } + /** + * Add highlight positions. It is assumed that all positions + * are up-to-date with respect to the text viewer document. + * + * @param positions a list of Positions + */ + public void addHighlightPositions(List positions) { + boolean isActive= fIsActive; + deactivate(isActive); + fPositions.addAll(positions); + if (isActive) { + activate(true); + } + } + + /** + * Remove highlight positions by identity. + * + * @param positions a list of Positions + */ + public void removeHighlightPositions(List positions) { + boolean isActive= fIsActive; + deactivate(isActive); + fPositions.removeAll(positions); + if (isActive) { + activate(true); + } + } + + /** + * Replace given highlight positions in one step. + * + * @param removePositions a list of Positions to remove + * @param addPositions a list of Positions to add + */ + public void replaceHighlightPositions(List removePositions, List addPositions) { + boolean isActive= fIsActive; + deactivate(isActive); + fPositions.removeAll(removePositions); + fPositions.addAll(addPositions); + if (isActive) { + activate(true); + } + } + /** * Trigger redraw of managed positions. */ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java new file mode 100644 index 00000000000..de841841a41 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ASTProvider.java @@ -0,0 +1,640 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.editor; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.Assert; +import org.eclipse.ui.IPartListener2; +import org.eclipse.ui.IWindowListener; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchPartReference; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.ISourceReference; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.CUIPlugin; + + +/** + * Provides a shared AST for clients. The shared AST is + * the AST of the active CEditor's input element. + * Cloned from JDT. + * + * @since 4.0 + */ +public final class ASTProvider { + + /** + * Wait flag. + */ + public static final class WAIT_FLAG { + + String fName; + + private WAIT_FLAG(String name) { + fName= name; + } + + /* + * @see java.lang.Object#toString() + */ + public String toString() { + return fName; + } + } + + /** + * Wait flag indicating that a client requesting an AST + * wants to wait until an AST is ready. + *

+ * An AST will be created by this AST provider if the shared + * AST is not for the given C element. + *

+ */ + public static final WAIT_FLAG WAIT_YES= new WAIT_FLAG("wait yes"); //$NON-NLS-1$ + + /** + * Wait flag indicating that a client requesting an AST + * only wants to wait for the shared AST of the active editor. + *

+ * No AST will be created by the AST provider. + *

+ */ + public static final WAIT_FLAG WAIT_ACTIVE_ONLY= new WAIT_FLAG("wait active only"); //$NON-NLS-1$ + + /** + * Wait flag indicating that a client requesting an AST + * only wants the already available shared AST. + *

+ * No AST will be created by the AST provider. + *

+ */ + public static final WAIT_FLAG WAIT_NO= new WAIT_FLAG("don't wait"); //$NON-NLS-1$ + + public static int PARSE_MODE_FULL= ILanguage.AST_SKIP_IF_NO_BUILD_INFO; + public static int PARSE_MODE_FAST= ILanguage.AST_SKIP_IF_NO_BUILD_INFO | ILanguage.AST_SKIP_INDEXED_HEADERS; + public static int PARSE_MODE_FAST_INDEX= ILanguage.AST_SKIP_IF_NO_BUILD_INFO | ILanguage.AST_SKIP_INDEXED_HEADERS | ILanguage.AST_USE_INDEX; + public static int PARSE_MODE_INDEX= ILanguage.AST_SKIP_IF_NO_BUILD_INFO | ILanguage.AST_USE_INDEX; + + public static int PARSE_MODE= PARSE_MODE_FULL; + + /** + * Tells whether this class is in debug mode. + */ + private static final boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.cdt.ui/debug/ASTProvider")); //$NON-NLS-1$//$NON-NLS-2$ + + /** + * Internal activation listener. + */ + private class ActivationListener implements IPartListener2, IWindowListener { + + /* + * @see org.eclipse.ui.IPartListener2#partActivated(org.eclipse.ui.IWorkbenchPartReference) + */ + public void partActivated(IWorkbenchPartReference ref) { + if (isCEditor(ref) && !isActiveEditor(ref)) + activeEditorChanged(ref.getPart(true)); + } + + /* + * @see org.eclipse.ui.IPartListener2#partBroughtToTop(org.eclipse.ui.IWorkbenchPartReference) + */ + public void partBroughtToTop(IWorkbenchPartReference ref) { + if (isCEditor(ref) && !isActiveEditor(ref)) + activeEditorChanged(ref.getPart(true)); + } + + /* + * @see org.eclipse.ui.IPartListener2#partClosed(org.eclipse.ui.IWorkbenchPartReference) + */ + public void partClosed(IWorkbenchPartReference ref) { + if (isActiveEditor(ref)) { + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "closed active editor: " + ref.getTitle()); //$NON-NLS-1$ //$NON-NLS-2$ + + activeEditorChanged(null); + } + } + + /* + * @see org.eclipse.ui.IPartListener2#partDeactivated(org.eclipse.ui.IWorkbenchPartReference) + */ + public void partDeactivated(IWorkbenchPartReference ref) { + } + + /* + * @see org.eclipse.ui.IPartListener2#partOpened(org.eclipse.ui.IWorkbenchPartReference) + */ + public void partOpened(IWorkbenchPartReference ref) { + if (isCEditor(ref) && !isActiveEditor(ref)) + activeEditorChanged(ref.getPart(true)); + } + + /* + * @see org.eclipse.ui.IPartListener2#partHidden(org.eclipse.ui.IWorkbenchPartReference) + */ + public void partHidden(IWorkbenchPartReference ref) { + } + + /* + * @see org.eclipse.ui.IPartListener2#partVisible(org.eclipse.ui.IWorkbenchPartReference) + */ + public void partVisible(IWorkbenchPartReference ref) { + if (isCEditor(ref) && !isActiveEditor(ref)) + activeEditorChanged(ref.getPart(true)); + } + + /* + * @see org.eclipse.ui.IPartListener2#partInputChanged(org.eclipse.ui.IWorkbenchPartReference) + */ + public void partInputChanged(IWorkbenchPartReference ref) { + if (isCEditor(ref) && isActiveEditor(ref)) + activeEditorChanged(ref.getPart(true)); + } + + /* + * @see org.eclipse.ui.IWindowListener#windowActivated(org.eclipse.ui.IWorkbenchWindow) + */ + public void windowActivated(IWorkbenchWindow window) { + IWorkbenchPartReference ref= window.getPartService().getActivePartReference(); + if (isCEditor(ref) && !isActiveEditor(ref)) + activeEditorChanged(ref.getPart(true)); + } + + /* + * @see org.eclipse.ui.IWindowListener#windowDeactivated(org.eclipse.ui.IWorkbenchWindow) + */ + public void windowDeactivated(IWorkbenchWindow window) { + } + + /* + * @see org.eclipse.ui.IWindowListener#windowClosed(org.eclipse.ui.IWorkbenchWindow) + */ + public void windowClosed(IWorkbenchWindow window) { + if (fActiveEditor != null && fActiveEditor.getSite() != null && window == fActiveEditor.getSite().getWorkbenchWindow()) { + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "closed active editor: " + fActiveEditor.getTitle()); //$NON-NLS-1$ //$NON-NLS-2$ + + activeEditorChanged(null); + } + window.getPartService().removePartListener(this); + } + + /* + * @see org.eclipse.ui.IWindowListener#windowOpened(org.eclipse.ui.IWorkbenchWindow) + */ + public void windowOpened(IWorkbenchWindow window) { + window.getPartService().addPartListener(this); + } + + private boolean isActiveEditor(IWorkbenchPartReference ref) { + return ref != null && isActiveEditor(ref.getPart(false)); + } + + private boolean isActiveEditor(IWorkbenchPart part) { + return part != null && (part == fActiveEditor); + } + + private boolean isCEditor(IWorkbenchPartReference ref) { + if (ref == null) + return false; + + String id= ref.getId(); + + return CUIPlugin.EDITOR_ID.equals(id) || ref.getPart(false) instanceof CEditor; + } + } + + private static final String DEBUG_PREFIX= "ASTProvider > "; //$NON-NLS-1$ + + + private ICElement fReconcilingCElement; + private ICElement fActiveCElement; + private IASTTranslationUnit fAST; + private ActivationListener fActivationListener; + private Object fReconcileLock= new Object(); + private Object fWaitLock= new Object(); + private boolean fIsReconciling; + private IWorkbenchPart fActiveEditor; + + + /** + * Returns the C plug-in's AST provider. + * + * @return the AST provider + */ + public static ASTProvider getASTProvider() { + return CUIPlugin.getDefault().getASTProvider(); + } + + /** + * Creates a new AST provider. + */ + public ASTProvider() { + install(); + } + + /** + * Installs this AST provider. + */ + void install() { + // Create and register activation listener + fActivationListener= new ActivationListener(); + PlatformUI.getWorkbench().addWindowListener(fActivationListener); + + // Ensure existing windows get connected + IWorkbenchWindow[] windows= PlatformUI.getWorkbench().getWorkbenchWindows(); + for (int i= 0, length= windows.length; i < length; i++) + windows[i].getPartService().addPartListener(fActivationListener); + } + + private void activeEditorChanged(IWorkbenchPart editor) { + + ICElement cElement= null; + if (editor instanceof CEditor) { + cElement= ((CEditor)editor).getInputCElement(); + } + + synchronized (this) { + fActiveEditor= editor; + fActiveCElement= cElement; + cache(null, cElement); + } + + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "active editor is: " + toString(cElement)); //$NON-NLS-1$ //$NON-NLS-2$ + + synchronized (fReconcileLock) { + if (fIsReconciling && (fReconcilingCElement == null || !fReconcilingCElement.equals(cElement))) { + fIsReconciling= false; + fReconcilingCElement= null; + } else if (cElement == null) { + fIsReconciling= false; + fReconcilingCElement= null; + } + } + } + + /** + * Returns whether the given translation unit AST is + * cached by this AST provided. + * + * @param ast the translation unit AST + * @return true if the given AST is the cached one + */ + public boolean isCached(IASTTranslationUnit ast) { + return ast != null && fAST == ast; + } + + /** + * Returns whether this AST provider is active on the given + * translation unit. + * + * @param tu the translation unit + * @return true if the given translation unit is the active one + */ + public boolean isActive(ITranslationUnit tu) { + return tu != null && tu.equals(fActiveCElement); + } + + /** + * Informs that reconciling for the given element is about to be started. + * + * @param cElement the C element + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#aboutToBeReconciled() + */ + void aboutToBeReconciled(ICElement cElement) { + + if (cElement == null) + return; + + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "about to reconcile: " + toString(cElement)); //$NON-NLS-1$ //$NON-NLS-2$ + + synchronized (fReconcileLock) { + fIsReconciling= true; + fReconcilingCElement= cElement; + } + cache(null, cElement); + } + + /** + * Disposes the cached AST. + */ + private synchronized void disposeAST() { + + if (fAST == null) + return; + + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "disposing AST: " + toString(fAST) + " for: " + toString(fActiveCElement)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + fAST= null; + + cache(null, null); + } + + /** + * Returns a string for the given C element used for debugging. + * + * @param cElement the translation unit AST + * @return a string used for debugging + */ + private String toString(ICElement cElement) { + if (cElement == null) + return "null"; //$NON-NLS-1$ + else + return cElement.getElementName(); + + } + + /** + * Returns a string for the given AST used for debugging. + * + * @param ast the translation unit AST + * @return a string used for debugging + */ + private String toString(IASTTranslationUnit ast) { + if (ast == null) + return "null"; //$NON-NLS-1$ + + IASTNode[] nodes= ast.getDeclarations(); + if (nodes != null && nodes.length > 0) + return nodes[0].getRawSignature(); + else + return "AST without any declaration"; //$NON-NLS-1$ + } + + /** + * Caches the given translation unit AST for the given C element. + * + * @param ast + * @param cElement + */ + private synchronized void cache(IASTTranslationUnit ast, ICElement cElement) { + + if (fActiveCElement != null && !fActiveCElement.equals(cElement)) { + if (DEBUG && cElement != null) // don't report call from disposeAST() + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "don't cache AST for inactive: " + toString(cElement)); //$NON-NLS-1$ //$NON-NLS-2$ + return; + } + + if (DEBUG && (cElement != null || ast != null)) // don't report call from disposeAST() + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "caching AST: " + toString(ast) + " for: " + toString(cElement)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + if (fAST != null) + disposeAST(); + + fAST= ast; + + // Signal AST change + synchronized (fWaitLock) { + fWaitLock.notifyAll(); + } + } + + /** + * Returns a shared translation unit AST for the given + * C element. + *

+ * Clients are not allowed to modify the AST and must + * synchronize all access to its nodes. + *

+ * + * @param cElement the C element + * @param waitFlag {@link #WAIT_YES}, {@link #WAIT_NO} or {@link #WAIT_ACTIVE_ONLY} + * @param progressMonitor the progress monitor or null + * @return the AST or null if the AST is not available + */ + public IASTTranslationUnit getAST(ICElement cElement, WAIT_FLAG waitFlag, IProgressMonitor progressMonitor) { + if (cElement == null) + return null; + + Assert.isTrue(cElement instanceof ITranslationUnit); + + if (progressMonitor != null && progressMonitor.isCanceled()) + return null; + + boolean isActiveElement; + synchronized (this) { + isActiveElement= cElement.equals(fActiveCElement); + if (isActiveElement) { + if (fAST != null) { + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "returning cached AST:" + toString(fAST) + " for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + return fAST; + } + if (waitFlag == WAIT_NO) { + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "returning null (WAIT_NO) for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$ + + return null; + + } + } + } + if (isActiveElement && isReconciling(cElement)) { + try { + final ICElement activeElement= fReconcilingCElement; + + // Wait for AST + synchronized (fWaitLock) { + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "waiting for AST for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$ + + fWaitLock.wait(); + } + + // Check whether active element is still valid + synchronized (this) { + if (activeElement == fActiveCElement && fAST != null) { + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "...got AST for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$ + + return fAST; + } + } + return getAST(cElement, waitFlag, progressMonitor); + } catch (InterruptedException e) { + return null; // thread has been interrupted don't compute AST + } + } else if (waitFlag == WAIT_NO || (waitFlag == WAIT_ACTIVE_ONLY && !(isActiveElement && fAST == null))) + return null; + + if (isActiveElement) + aboutToBeReconciled(cElement); + + if (DEBUG) + System.err.println(getThreadName() + " - " + DEBUG_PREFIX + "creating AST for " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$ + + IASTTranslationUnit ast= null; + try { + ast= createAST(cElement, progressMonitor); + if (progressMonitor != null && progressMonitor.isCanceled()) + ast= null; + else if (DEBUG && ast != null) + System.err.println(getThreadName() + " - " + DEBUG_PREFIX + "created AST for: " + cElement.getElementName()); //$NON-NLS-1$ //$NON-NLS-2$ + } finally { + if (isActiveElement) { + if (fAST != null) { + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "Ignore created AST for " + cElement.getElementName() + "- AST from reconciler is newer"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + reconciled(fAST, cElement, null); + } else + reconciled(ast, cElement, null); + } + } + + return ast; + } + + /** + * Tells whether the given C element is the one + * reported as currently being reconciled. + * + * @param cElement the C element + * @return true if reported as currently being reconciled + */ + private boolean isReconciling(ICElement cElement) { + synchronized (fReconcileLock) { + return cElement != null && cElement.equals(fReconcilingCElement) && fIsReconciling; + } + } + + /** + * Creates a new translation unit AST. + * + * @param cElement the C element for which to create the AST + * @param progressMonitor the progress monitor + * @return AST + */ + IASTTranslationUnit createAST(ICElement cElement, final IProgressMonitor progressMonitor) { + if (!hasSource(cElement)) + return null; + + if (progressMonitor != null && progressMonitor.isCanceled()) + return null; + + if (!(cElement instanceof ITranslationUnit)) + return null; + + final ITranslationUnit tu= (ITranslationUnit)cElement; + final IASTTranslationUnit root[]= new IASTTranslationUnit[1]; + + SafeRunner.run(new ISafeRunnable() { + public void run() throws CoreException { + try { + if (progressMonitor != null && progressMonitor.isCanceled()) { + root[0]= null; + } else { + root[0]= tu.getLanguage().getASTTranslationUnit(tu, PARSE_MODE); + } + } catch (OperationCanceledException ex) { + root[0]= null; + } + } + public void handleException(Throwable ex) { + IStatus status= new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, IStatus.OK, "Error in CDT Core during AST creation", ex); //$NON-NLS-1$ + CUIPlugin.getDefault().getLog().log(status); + } + }); + + return root[0]; + } + + /** + * Checks whether the given C element has accessible source. + * + * @param cElement the C element to test + * @return true if the element has source + */ + private boolean hasSource(ICElement cElement) { + if (cElement == null || !cElement.exists()) + return false; + + try { + return cElement instanceof ISourceReference /* && ((ISourceReference)cElement).getSource() != null */; + } catch (Exception ex) { + IStatus status= new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, IStatus.OK, "Error in CDT Core during AST creation", ex); //$NON-NLS-1$ + CUIPlugin.getDefault().getLog().log(status); + } + return false; + } + + /** + * Disposes this AST provider. + */ + public void dispose() { + + // Dispose activation listener + PlatformUI.getWorkbench().removeWindowListener(fActivationListener); + fActivationListener= null; + + disposeAST(); + + synchronized (fWaitLock) { + fWaitLock.notifyAll(); + } + } + + /* + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#reconciled(org.eclipse.cdt.core.dom.IASTTranslationUnit) + */ + void reconciled(IASTTranslationUnit ast, ICElement cElement, IProgressMonitor progressMonitor) { + + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + "reconciled: " + toString(cElement) + ", AST: " + toString(ast)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + + synchronized (fReconcileLock) { + + fIsReconciling= progressMonitor != null && progressMonitor.isCanceled(); + if (cElement == null || !cElement.equals(fReconcilingCElement)) { + + if (DEBUG) + System.out.println(getThreadName() + " - " + DEBUG_PREFIX + " ignoring AST of out-dated editor"); //$NON-NLS-1$ //$NON-NLS-2$ + + // Signal - threads might wait for wrong element + synchronized (fWaitLock) { + fWaitLock.notifyAll(); + } + + return; + } + + cache(ast, cElement); + } + } + + private static String getThreadName() { + String name= Thread.currentThread().getName(); + if (name != null) + return name; + else + return Thread.currentThread().toString(); + } + +} + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java index 93af6339b4a..792d2bca15e 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java @@ -21,6 +21,8 @@ import java.util.ResourceBundle; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ListenerList; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; @@ -119,6 +121,8 @@ import com.ibm.icu.text.BreakIterator; import org.eclipse.cdt.core.CCorePlugin; import org.eclipse.cdt.core.CCorePreferenceConstants; +import org.eclipse.cdt.core.IPositionConverter; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ISourceRange; @@ -132,6 +136,8 @@ import org.eclipse.cdt.ui.actions.ShowInCViewAction; import org.eclipse.cdt.ui.text.ICPartitions; import org.eclipse.cdt.ui.text.folding.ICFoldingStructureProvider; +import org.eclipse.cdt.internal.corext.util.SimplePositionTracker; + import org.eclipse.cdt.internal.ui.ICHelpContextIds; import org.eclipse.cdt.internal.ui.IContextMenuConstants; import org.eclipse.cdt.internal.ui.actions.AddBlockCommentAction; @@ -150,6 +156,7 @@ import org.eclipse.cdt.internal.ui.text.CTextTools; import org.eclipse.cdt.internal.ui.text.CWordIterator; import org.eclipse.cdt.internal.ui.text.DocumentCharacterIterator; import org.eclipse.cdt.internal.ui.text.HTMLTextPresenter; +import org.eclipse.cdt.internal.ui.text.ICReconcilingListener; import org.eclipse.cdt.internal.ui.text.c.hover.SourceViewerInformationControl; import org.eclipse.cdt.internal.ui.text.contentassist.ContentAssistPreference; import org.eclipse.cdt.internal.ui.util.CUIHelp; @@ -158,9 +165,8 @@ import org.eclipse.cdt.internal.ui.util.CUIHelp; /** * C specific text editor. */ -public class CEditor extends TextEditor implements ISelectionChangedListener, IShowInSource , IReconcilingParticipant{ +public class CEditor extends TextEditor implements ISelectionChangedListener, IShowInSource, IReconcilingParticipant, ICReconcilingListener { - /** * The information provider used to present focusable information * shells. @@ -554,6 +560,19 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS */ private FoldingActionGroup fFoldingGroup; + /** + * AST reconciling listeners. + * @since 4.0 + */ + private ListenerList fReconcilingListeners= new ListenerList(ListenerList.IDENTITY); + + /** + * Semantic highlighting manager + * @since 4.0 + */ + private SemanticHighlightingManager fSemanticManager; + + /** * Default constructor. */ @@ -590,11 +609,6 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS if (fCEditorErrorTickUpdater != null) { fCEditorErrorTickUpdater.updateEditorImage(getInputCElement()); } - - if (getSourceViewer() != null) { - CSourceViewerDecorationSupport decoSupport = (CSourceViewerDecorationSupport) getSourceViewerDecorationSupport(getSourceViewer()); - decoSupport.editorInputChanged(input); - } } /** @@ -732,6 +746,16 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS return; } + if (SemanticHighlightings.affectsEnablement(getPreferenceStore(), event)) { + if (isSemanticHighlightingEnabled()) { + installSemanticHighlighting(); + fSemanticManager.refresh(); + } else { + uninstallSemanticHighlighting(); + } + return; + } + IContentAssistant c= asv.getContentAssistant(); if (c instanceof ContentAssistant) { ContentAssistPreference.changeConfiguration((ContentAssistant) c, getPreferenceStore(), event); @@ -1211,7 +1235,10 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS if (isTabConversionEnabled()) startTabConversion(); - + + if (isSemanticHighlightingEnabled()) + installSemanticHighlighting(); + } /* @@ -1234,12 +1261,12 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS } /* - * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#getSourceViewerDecorationSupport(org.eclipse.jface.text.source.ISourceViewer) + * @see org.eclipse.ui.texteditor.AbstractDecoratedTextEditor#getSourceViewerDecorationSupport(ISourceViewer) */ protected SourceViewerDecorationSupport getSourceViewerDecorationSupport( ISourceViewer viewer) { if (fSourceViewerDecorationSupport == null) { - fSourceViewerDecorationSupport= new CSourceViewerDecorationSupport(viewer, getOverviewRuler(), getAnnotationAccess(), getSharedColors()); + fSourceViewerDecorationSupport= new CSourceViewerDecorationSupport(this, viewer, getOverviewRuler(), getAnnotationAccess(), getSharedColors()); configureSourceViewerDecorationSupport(fSourceViewerDecorationSupport); } return fSourceViewerDecorationSupport; @@ -1557,11 +1584,10 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS getOverviewRuler(), isOverviewRulerVisible()); - CSourceViewerDecorationSupport decoSupport = (CSourceViewerDecorationSupport) getSourceViewerDecorationSupport(sourceViewer); - decoSupport.editorInputChanged(getEditorInput()); - CUIHelp.setHelp(this, sourceViewer.getTextWidget(), ICHelpContextIds.CEDITOR_VIEW); + getSourceViewerDecorationSupport(sourceViewer); + return sourceViewer; } @@ -1701,16 +1727,36 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS } - /* (non-Javadoc) + /* * @see org.eclipse.cdt.internal.ui.editor.IReconcilingParticipant#reconciled() */ public void reconciled(boolean somethingHasChanged) { - // Do nothing the outliner is listener to the - // CoreModel WorkingCopy changes instead. - // It will allow more fined grained. - if (somethingHasChanged && getSourceViewer() != null) { - CSourceViewerDecorationSupport decoSupport = (CSourceViewerDecorationSupport) getSourceViewerDecorationSupport(getSourceViewer()); - decoSupport.editorInputChanged(getEditorInput()); + if (getSourceViewer() == null) { + return; + } + // this method must be called in a background thread + assert getSourceViewer().getTextWidget().getDisplay().getThread() != Thread.currentThread(); + + if (fReconcilingListeners.size() > 0) { + // create AST and notify ICReconcilingListeners + ICElement cElement= getInputCElement(); + if (cElement == null) { + return; + } + + aboutToBeReconciled(); + + // track changes to the document while parsing + IDocument doc= getDocumentProvider().getDocument(getEditorInput()); + SimplePositionTracker positionTracker= new SimplePositionTracker(); + positionTracker.startTracking(doc); + + try { + IASTTranslationUnit ast= CUIPlugin.getDefault().getASTProvider().createAST(cElement, null); + reconciled(ast, positionTracker, null); + } finally { + positionTracker.stopTracking(); + } } } @@ -2099,4 +2145,101 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IS } } } + + /* + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#aboutToBeReconciled() + * @since 4.0 + */ + public void aboutToBeReconciled() { + + // Notify AST provider + CUIPlugin.getDefault().getASTProvider().aboutToBeReconciled(getInputCElement()); + + // Notify listeners + Object[] listeners = fReconcilingListeners.getListeners(); + for (int i = 0, length= listeners.length; i < length; ++i) { + ((ICReconcilingListener)listeners[i]).aboutToBeReconciled(); + } + } + + /* + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#reconciled(IASTTranslationUnit, IPositionConverter, IProgressMonitor) + * @since 4.0 + */ + public void reconciled(IASTTranslationUnit ast, IPositionConverter positionTracker, IProgressMonitor progressMonitor) { + + CUIPlugin cuiPlugin= CUIPlugin.getDefault(); + if (cuiPlugin == null) + return; + + // Always notify AST provider + cuiPlugin.getASTProvider().reconciled(ast, getInputCElement(), progressMonitor); + + // Notify listeners + Object[] listeners = fReconcilingListeners.getListeners(); + for (int i = 0, length= listeners.length; i < length; ++i) { + ((ICReconcilingListener)listeners[i]).reconciled(ast, positionTracker, progressMonitor); + } + + } + + /** + * Adds the given listener. + * Has no effect if an identical listener was not already registered. + * + * @param listener The reconcile listener to be added + * @since 4.0 + */ + final void addReconcileListener(ICReconcilingListener listener) { + synchronized (fReconcilingListeners) { + fReconcilingListeners.add(listener); + } + } + + /** + * Removes the given listener. + * Has no effect if an identical listener was not already registered. + * + * @param listener the reconcile listener to be removed + * @since 4.0 + */ + final void removeReconcileListener(ICReconcilingListener listener) { + synchronized (fReconcilingListeners) { + fReconcilingListeners.remove(listener); + } + } + + /** + * @return true if Semantic Highlighting is enabled. + * + * @since 4.0 + */ + private boolean isSemanticHighlightingEnabled() { + return SemanticHighlightings.isEnabled(getPreferenceStore()); + } + + /** + * Install Semantic Highlighting. + * + * @since 4.0 + */ + private void installSemanticHighlighting() { + if (fSemanticManager == null) { + fSemanticManager= new SemanticHighlightingManager(); + fSemanticManager.install(this, (CSourceViewer) getSourceViewer(), CUIPlugin.getDefault().getTextTools().getColorManager(), getPreferenceStore()); + } + } + + /** + * Uninstall Semantic Highlighting. + * + * @since 4.0 + */ + private void uninstallSemanticHighlighting() { + if (fSemanticManager != null) { + fSemanticManager.uninstall(); + fSemanticManager= null; + } + } + } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties index 5d3c50b0cf2..c9586e17895 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditorMessages.properties @@ -1,5 +1,5 @@ ######################################### -# Copyright (c) 2005 IBM Corporation and others. +# Copyright (c) 2005, 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 @@ -8,6 +8,7 @@ # Contributors: # IBM Corporation - initial API and implementation # QNX Software System +# Anton Leherbauer (Wind River Systems) ######################################### AddIncludeOnSelection.description=Add include statement on selection @@ -201,3 +202,33 @@ ShowToolTip.label=Show T&ooltip Description ToggleComment_error_title=Comment/Uncomment ToggleComment_error_message=An error occurred while commenting/uncommenting. + +InactiveCodeHighlighting_job= Inactive Code Highlighting + +Reconciling_job= Reconciling + +SemanticHighlighting_job= Semantic Highlighting +SemanticHighlighting_field= Fields +SemanticHighlighting_staticField= Static fields +SemanticHighlighting_staticConstField= Constants +SemanticHighlighting_methodDeclaration= Method declarations +SemanticHighlighting_staticMethodInvocation= Static method invocations +SemanticHighlighting_virtualMethodInvocation= Virtual method invocations +SemanticHighlighting_inheritedMethodInvocation= Inherited method invocations +SemanticHighlighting_localVariableDeclaration= Local variable declarations +SemanticHighlighting_localVariable= Local variable references +SemanticHighlighting_globalVariable= Global variables +SemanticHighlighting_parameterVariable= Parameter variables +SemanticHighlighting_TemplateVariables= Template variables +SemanticHighlighting_method= Methods +SemanticHighlighting_classes= Classes +SemanticHighlighting_enums= Enums +SemanticHighlighting_templateArguments= Template arguments +SemanticHighlighting_functionDeclaration= Function declarations +SemanticHighlighting_functionInvocation= Function invocations +SemanticHighlighting_macroSubstitions= Macro references +SemanticHighlighting_macroDefintion= Macro definitions +SemanticHighlighting_typeDef= Typedefs +SemanticHighlighting_namespace= Namespaces +SemanticHighlighting_label= Labels +SemanticHighlighting_problem= Problems diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CSourceViewerDecorationSupport.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CSourceViewerDecorationSupport.java index 16e628d4272..af1fab9c60d 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CSourceViewerDecorationSupport.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CSourceViewerDecorationSupport.java @@ -10,23 +10,9 @@ *******************************************************************************/ package org.eclipse.cdt.internal.ui.editor; -import java.util.ArrayList; -import java.util.Collections; -import java.util.EmptyStackException; -import java.util.Iterator; -import java.util.List; -import java.util.Stack; - -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.Job; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceConverter; -import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextViewerExtension2; -import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.IAnnotationAccess; import org.eclipse.jface.text.source.IOverviewRuler; import org.eclipse.jface.text.source.ISharedTextColors; @@ -34,24 +20,8 @@ import org.eclipse.jface.text.source.ISourceViewer; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.RGB; -import org.eclipse.ui.IEditorInput; import org.eclipse.ui.texteditor.SourceViewerDecorationSupport; -import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.IPositionConverter; -import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; -import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElifStatement; -import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElseStatement; -import org.eclipse.cdt.core.dom.ast.IASTPreprocessorEndifStatement; -import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfStatement; -import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfdefStatement; -import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfndefStatement; -import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; -import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; -import org.eclipse.cdt.core.model.ILanguage; -import org.eclipse.cdt.core.model.ITranslationUnit; -import org.eclipse.cdt.ui.CUIPlugin; - import org.eclipse.cdt.internal.ui.LineBackgroundPainter; /** @@ -64,69 +34,10 @@ import org.eclipse.cdt.internal.ui.LineBackgroundPainter; * * @since 4.0 */ -public class CSourceViewerDecorationSupport - extends SourceViewerDecorationSupport { +public class CSourceViewerDecorationSupport extends SourceViewerDecorationSupport { - /** - * This job takes the current translation unit and produces an - * AST in the background. Upon completion, {@link #inactiveCodePositionsChanged} - * is called in the display thread. - */ - private class UpdateJob extends Job { - - /** - * @param name - */ - public UpdateJob(String name) { - super(name); - setSystem(true); - setPriority(Job.DECORATE); - } - - /* - * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor) - */ - protected IStatus run(IProgressMonitor monitor) { - IStatus result = Status.OK_STATUS; - if (fASTTranslationUnit == null && fTranslationUnit != null) { - try { - fParseTimeStamp = System.currentTimeMillis(); - int style = ILanguage.AST_SKIP_IF_NO_BUILD_INFO | ILanguage.AST_SKIP_INDEXED_HEADERS; - fASTTranslationUnit = fTranslationUnit.getLanguage().getASTTranslationUnit(fTranslationUnit, style); - } catch (CoreException exc) { - result = exc.getStatus(); - } - } - if (monitor.isCanceled() || fViewer == null) { - result = Status.CANCEL_STATUS; - } else { - final List inactiveCodePositions = collectInactiveCodePositions(fASTTranslationUnit); - Runnable updater = new Runnable() { - public void run() { - inactiveCodePositionsChanged(inactiveCodePositions); - } - }; - if (fViewer != null && !monitor.isCanceled()) { - fViewer.getTextWidget().getDisplay().asyncExec(updater); - } - } - return result; - } - - } - - /** - * Implementation of IRegion that can be reused - * by setting the offset and the length. - */ - private static class ReusableRegion extends Position implements IRegion { - public ReusableRegion(int offset, int length) { - super(offset, length); - } - public ReusableRegion(IRegion region) { - super(region.getOffset(), region.getLength()); - } - } + /** The key to use for the {@link LineBackgroundPainter} */ + private static final String INACTIVE_CODE_KEY = "inactiveCode"; //$NON-NLS-1$ /** The preference key for the inactive code highlight color */ private String fInactiveCodeColorKey; @@ -144,14 +55,10 @@ public class CSourceViewerDecorationSupport private String fCLPEnableKey; /** The source viewer (duplicate of private base class member) */ protected ISourceViewer fViewer; - /** The current translation unit */ - private ITranslationUnit fTranslationUnit; - /** The corresponding AST translation unit */ - private IASTTranslationUnit fASTTranslationUnit; - /** The time stamp when the parsing was initiated */ - private long fParseTimeStamp; - /** The background job doing the AST parsing */ - private Job fUpdateJob; + /** The editor we are associated with */ + private CEditor fEditor; + /** The inactive code highlighting */ + private InactiveCodeHighlighting fInactiveCodeHighlighting; /** * Inherited constructor. @@ -162,55 +69,22 @@ public class CSourceViewerDecorationSupport * @param sharedTextColors */ CSourceViewerDecorationSupport( + CEditor editor, ISourceViewer sourceViewer, IOverviewRuler overviewRuler, IAnnotationAccess annotationAccess, ISharedTextColors sharedTextColors) { super(sourceViewer, overviewRuler, annotationAccess, sharedTextColors); - // we have to save our own reference, because super class members are all private + fEditor = editor; + // we have to save our own references, because super class members are all private fViewer = sourceViewer; fSharedColors = sharedTextColors; } - /** - * Notify that the associated editor got a new input. - * This is currently also used to notify of a reconcilation - * to update the inactive code while editing. - * - * @param input the new editor input - */ - void editorInputChanged(IEditorInput input) { - if (fUpdateJob != null) { - fUpdateJob.cancel(); - } - fTranslationUnit = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(input); - fASTTranslationUnit = null; - if (isInactiveCodePositionsActive()) { - updateInactiveCodePositions(); - } - } - - /** - * Schedule update of the AST in the background. - */ - private void updateInactiveCodePositions() { - if (fUpdateJob == null) { - fUpdateJob = new UpdateJob("Update Inactive Code Positions"); //$NON-NLS-1$ - } - if (fUpdateJob.getState() == Job.NONE) { - fUpdateJob.schedule(); - } - } - /* * @see org.eclipse.ui.texteditor.SourceViewerDecorationSupport#dispose() */ public void dispose() { - if (fUpdateJob != null) { - fUpdateJob.cancel(); - } - fTranslationUnit = null; - fASTTranslationUnit = null; super.dispose(); } @@ -229,7 +103,7 @@ public class CSourceViewerDecorationSupport updateCLPColor(); } else if (p.equals(fInactiveCodeEnableKey)) { if (isInactiveCodePositionsActive()) { - showInactiveCodePositions(); + showInactiveCodePositions(true); } else { hideInactiveCodePositions(); } @@ -244,7 +118,7 @@ public class CSourceViewerDecorationSupport */ private void updateInactiveCodeColor() { if (fLineBackgroundPainter != null) { - fLineBackgroundPainter.setDefaultColor(getColor(fInactiveCodeColorKey)); + fLineBackgroundPainter.setBackgroundColor(INACTIVE_CODE_KEY, getColor(fInactiveCodeColorKey)); if (isInactiveCodePositionsActive()) { fLineBackgroundPainter.redraw(); } @@ -342,7 +216,7 @@ public class CSourceViewerDecorationSupport showCLP(); } if (isInactiveCodePositionsActive()) { - showInactiveCodePositions(); + showInactiveCodePositions(false); } } @@ -350,6 +224,10 @@ public class CSourceViewerDecorationSupport * @see org.eclipse.ui.texteditor.SourceViewerDecorationSupport#uninstall() */ public void uninstall() { + if (fInactiveCodeHighlighting != null) { + fInactiveCodeHighlighting.dispose(); + fInactiveCodeHighlighting= null; + } uninstallLineBackgroundPainter(); super.uninstall(); } @@ -361,7 +239,7 @@ public class CSourceViewerDecorationSupport if (fLineBackgroundPainter == null) { if (fViewer instanceof ITextViewerExtension2) { fLineBackgroundPainter = new LineBackgroundPainter(fViewer); - fLineBackgroundPainter.setDefaultColor(getColor(fInactiveCodeColorKey)); + fLineBackgroundPainter.setBackgroundColor(INACTIVE_CODE_KEY, getColor(fInactiveCodeColorKey)); fLineBackgroundPainter.setCursorLineColor(getColor(fCLPColorKey)); fLineBackgroundPainter.enableCursorLine(isCLPActive()); ((ITextViewerExtension2)fViewer).addPainter(fLineBackgroundPainter); @@ -385,10 +263,18 @@ public class CSourceViewerDecorationSupport /** * Show inactive code positions. + * + * @param refresh trigger a refresh of the positions */ - private void showInactiveCodePositions() { + private void showInactiveCodePositions(boolean refresh) { installLineBackgroundPainter(); - updateInactiveCodePositions(); + if (fLineBackgroundPainter != null) { + fInactiveCodeHighlighting= new InactiveCodeHighlighting(fLineBackgroundPainter, INACTIVE_CODE_KEY); + fInactiveCodeHighlighting.install(fEditor); + if (refresh) { + fInactiveCodeHighlighting.refresh(); + } + } } /** @@ -396,133 +282,16 @@ public class CSourceViewerDecorationSupport */ private void hideInactiveCodePositions() { if (fLineBackgroundPainter != null) { + if (fInactiveCodeHighlighting != null) { + fInactiveCodeHighlighting.dispose(); + fInactiveCodeHighlighting= null; + } if (!isCLPActive()) { uninstallLineBackgroundPainter(); - } else { - fLineBackgroundPainter.setHighlightPositions(Collections.EMPTY_LIST); } } } - private void inactiveCodePositionsChanged(List inactiveCodePositions) { - if (fLineBackgroundPainter != null) { - if (!inactiveCodePositions.isEmpty()) { - IPositionConverter pt = CCorePlugin.getPositionTrackerManager().findPositionConverter(fTranslationUnit.getPath(), fParseTimeStamp); - if (pt != null) { - List convertedPositions = new ArrayList(inactiveCodePositions.size()); - for (Iterator iter = inactiveCodePositions.iterator(); iter - .hasNext();) { - IRegion pos = (IRegion) iter.next(); - convertedPositions.add(new ReusableRegion(pt.historicToActual(pos))); - } - inactiveCodePositions = convertedPositions; - } - } - fLineBackgroundPainter.setHighlightPositions(inactiveCodePositions); - } - } - - /** - * Collect source positions of preprocessor-hidden branches - * in the given translation unit. - * - * @param translationUnit the {@link IASTTranslationUnit}, may be null - * @return a {@link List} of {@link IRegion}s - */ - private static List collectInactiveCodePositions(IASTTranslationUnit translationUnit) { - if (translationUnit == null) { - return Collections.EMPTY_LIST; - } - String fileName = translationUnit.getFilePath(); - if (fileName == null) { - return Collections.EMPTY_LIST; - } - List positions = new ArrayList(); - int inactiveCodeStart = -1; - boolean inInactiveCode = false; - Stack inactiveCodeStack = new Stack(); - // TLETODO [performance] This does not look very efficient - IASTPreprocessorStatement[] preprocStmts = translationUnit.getAllPreprocessorStatements(); - for (int i = 0; i < preprocStmts.length; i++) { - IASTPreprocessorStatement statement = preprocStmts[i]; - if (!fileName.equals(statement.getContainingFilename())) { - // preprocessor directive is from a different file - continue; - } - if (statement instanceof IASTPreprocessorIfStatement) { - IASTPreprocessorIfStatement ifStmt = (IASTPreprocessorIfStatement)statement; - inactiveCodeStack.push(Boolean.valueOf(inInactiveCode)); - if (!ifStmt.taken()) { - if (!inInactiveCode) { - IASTNodeLocation nodeLocation = ifStmt.getNodeLocations()[0]; - inactiveCodeStart = nodeLocation.getNodeOffset(); - inInactiveCode = true; - } - } - } else if (statement instanceof IASTPreprocessorIfdefStatement) { - IASTPreprocessorIfdefStatement ifdefStmt = (IASTPreprocessorIfdefStatement)statement; - inactiveCodeStack.push(Boolean.valueOf(inInactiveCode)); - if (!ifdefStmt.taken()) { - if (!inInactiveCode) { - IASTNodeLocation nodeLocation = ifdefStmt.getNodeLocations()[0]; - inactiveCodeStart = nodeLocation.getNodeOffset(); - inInactiveCode = true; - } - } - } else if (statement instanceof IASTPreprocessorIfndefStatement) { - IASTPreprocessorIfndefStatement ifndefStmt = (IASTPreprocessorIfndefStatement)statement; - inactiveCodeStack.push(Boolean.valueOf(inInactiveCode)); - if (!ifndefStmt.taken()) { - if (!inInactiveCode) { - IASTNodeLocation nodeLocation = ifndefStmt.getNodeLocations()[0]; - inactiveCodeStart = nodeLocation.getNodeOffset(); - inInactiveCode = true; - } - } - } else if (statement instanceof IASTPreprocessorElseStatement) { - IASTPreprocessorElseStatement elseStmt = (IASTPreprocessorElseStatement)statement; - if (!elseStmt.taken() && !inInactiveCode) { - IASTNodeLocation nodeLocation = elseStmt.getNodeLocations()[0]; - inactiveCodeStart = nodeLocation.getNodeOffset(); - inInactiveCode = true; - } else if (elseStmt.taken() && inInactiveCode) { - IASTNodeLocation nodeLocation = elseStmt.getNodeLocations()[0]; - int inactiveCodeEnd = nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); - positions.add(new ReusableRegion(inactiveCodeStart, inactiveCodeEnd - inactiveCodeStart)); - inInactiveCode = false; - } - } else if (statement instanceof IASTPreprocessorElifStatement) { - IASTPreprocessorElifStatement elifStmt = (IASTPreprocessorElifStatement)statement; - if (!elifStmt.taken() && !inInactiveCode) { - IASTNodeLocation nodeLocation = elifStmt.getNodeLocations()[0]; - inactiveCodeStart = nodeLocation.getNodeOffset(); - inInactiveCode = true; - } else if (elifStmt.taken() && inInactiveCode) { - IASTNodeLocation nodeLocation = elifStmt.getNodeLocations()[0]; - int inactiveCodeEnd = nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); - positions.add(new ReusableRegion(inactiveCodeStart, inactiveCodeEnd - inactiveCodeStart)); - inInactiveCode = false; - } - } else if (statement instanceof IASTPreprocessorEndifStatement) { - IASTPreprocessorEndifStatement endifStmt = (IASTPreprocessorEndifStatement)statement; - try { - boolean wasInInactiveCode = ((Boolean)inactiveCodeStack.pop()).booleanValue(); - if (inInactiveCode && !wasInInactiveCode) { - IASTNodeLocation nodeLocation = endifStmt.getNodeLocations()[0]; - int inactiveCodeEnd = nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); - positions.add(new ReusableRegion(inactiveCodeStart, inactiveCodeEnd - inactiveCodeStart)); - } - inInactiveCode = wasInInactiveCode; - } - catch( EmptyStackException e) {} - } - } - if (inInactiveCode) { - // handle dangling #if? - } - return positions; - } - /* * @see org.eclipse.ui.texteditor.SourceViewerDecorationSupport#setCursorLinePainterPreferenceKeys(java.lang.String, java.lang.String) */ @@ -543,4 +312,5 @@ public class CSourceViewerDecorationSupport fInactiveCodeEnableKey = enableKey; fInactiveCodeColorKey = colorKey; } + } 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 new file mode 100644 index 00000000000..094f01c7d7e --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/InactiveCodeHighlighting.java @@ -0,0 +1,294 @@ +/******************************************************************************* + * Copyright (c) 2006 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.editor; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.EmptyStackException; +import java.util.List; +import java.util.Stack; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.TypedPosition; +import org.eclipse.swt.widgets.Display; + +import org.eclipse.cdt.core.IPositionConverter; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElifStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorElseStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorEndifStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfdefStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorIfndefStatement; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.ui.LineBackgroundPainter; +import org.eclipse.cdt.internal.ui.text.ICReconcilingListener; + +/** + * TLETODO Document InactiveCodeHighlighting. + * + * @since 4.0 + */ +public class InactiveCodeHighlighting implements ICReconcilingListener { + + /** + * Implementation of IRegion that can be reused + * by setting the offset and the length. + */ + private static class HighlightPosition extends TypedPosition implements IRegion { + public HighlightPosition(int offset, int length, String type) { + super(offset, length, type); + } + public HighlightPosition(IRegion region, String type) { + super(region.getOffset(), region.getLength(), type); + } + } + + + /** The line background painter */ + private LineBackgroundPainter fLineBackgroundPainter; + /** The key for inactive code positions in the background painter */ + private String fHighlightKey; + /** The current translation unit */ + private ITranslationUnit fTranslationUnit; + /** The background job doing the AST parsing */ + private Job fUpdateJob; + /** The lock for job manipulation */ + private Object fJobLock = new Object(); + /** The editor this is installed on */ + private CEditor fEditor; + /** The list of currently highlighted positions */ + private List fInactiveCodePositions= Collections.EMPTY_LIST; + + /** + * @param lineBackgroundPainter + */ + public InactiveCodeHighlighting(LineBackgroundPainter lineBackgroundPainter, String highlightKey) { + fLineBackgroundPainter= lineBackgroundPainter; + fHighlightKey= highlightKey; + } + + /** + * Schedule update of the inactive code positions in the background. + */ + private void scheduleJob() { + synchronized (fJobLock) { + if (fUpdateJob == null) { + fUpdateJob = new Job(CEditorMessages.getString("InactiveCodeHighlighting_job")) { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + IStatus result = Status.OK_STATUS; + if (fTranslationUnit != null) { + IASTTranslationUnit ast= CUIPlugin.getDefault().getASTProvider().getAST(fTranslationUnit, ASTProvider.WAIT_YES, monitor); + reconciled(ast, null, monitor); + } + if (monitor.isCanceled()) { + result = Status.CANCEL_STATUS; + } + return result; + } + }; + fUpdateJob.setPriority(Job.DECORATE); + } + if (fUpdateJob.getState() == Job.NONE) { + // schedule later if AST is not available yet + fUpdateJob.schedule(); + } + } + } + + /** + * @param editor + */ + public void install(CEditor editor) { + assert fEditor == null; + fEditor= editor; + fEditor.addReconcileListener(this); + ICElement cElement= fEditor.getInputCElement(); + if (cElement instanceof ITranslationUnit) { + fTranslationUnit = (ITranslationUnit)cElement; + } else { + fTranslationUnit = null; + } + } + + /** + * + */ + public void uninstall() { + if (fLineBackgroundPainter != null) { + fLineBackgroundPainter.removeHighlightPositions(fInactiveCodePositions); + fInactiveCodePositions= Collections.EMPTY_LIST; + } + if (fEditor != null) { + fEditor.removeReconcileListener(this); + fEditor= null; + fTranslationUnit= null; + } + } + + public void dispose() { + uninstall(); + fLineBackgroundPainter= null; + } + + /** + * Force refresh. + */ + public void refresh() { + scheduleJob(); + } + + /* + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#aboutToBeReconciled() + */ + public void aboutToBeReconciled() { + } + + /* + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#reconciled(IASTTranslationUnit, IPositionConverter, IProgressMonitor) + */ + public void reconciled(IASTTranslationUnit ast, final IPositionConverter positionTracker, IProgressMonitor progressMonitor) { + if (progressMonitor != null && progressMonitor.isCanceled()) { + return; + } + final List newInactiveCodePositions = collectInactiveCodePositions(ast); + if (positionTracker != null) { + for (int i = 0, sz = newInactiveCodePositions.size(); i < sz; i++) { + IRegion pos = (IRegion) newInactiveCodePositions.get(i); + newInactiveCodePositions.set(i, new HighlightPosition(positionTracker.historicToActual(pos), fHighlightKey)); + } + } + Runnable updater = new Runnable() { + public void run() { + if (fEditor != null && fLineBackgroundPainter != null) { + fLineBackgroundPainter.replaceHighlightPositions(fInactiveCodePositions, newInactiveCodePositions); + fInactiveCodePositions= newInactiveCodePositions; + } + } + }; + if (fEditor != null) { + Display.getDefault().asyncExec(updater); + } + } + + /** + * Collect source positions of preprocessor-hidden branches + * in the given translation unit. + * + * @param translationUnit the {@link IASTTranslationUnit}, may be null + * @return a {@link List} of {@link IRegion}s + */ + private List collectInactiveCodePositions(IASTTranslationUnit translationUnit) { + if (translationUnit == null) { + return Collections.EMPTY_LIST; + } + String fileName = translationUnit.getFilePath(); + if (fileName == null) { + return Collections.EMPTY_LIST; + } + List positions = new ArrayList(); + int inactiveCodeStart = -1; + boolean inInactiveCode = false; + Stack inactiveCodeStack = new Stack(); + + IASTPreprocessorStatement[] preprocStmts = translationUnit.getAllPreprocessorStatements(); + + for (int i = 0; i < preprocStmts.length; i++) { + IASTPreprocessorStatement statement = preprocStmts[i]; + if (!fileName.equals(statement.getContainingFilename())) { + // preprocessor directive is from a different file + continue; + } + if (statement instanceof IASTPreprocessorIfStatement) { + IASTPreprocessorIfStatement ifStmt = (IASTPreprocessorIfStatement)statement; + inactiveCodeStack.push(Boolean.valueOf(inInactiveCode)); + if (!ifStmt.taken()) { + if (!inInactiveCode) { + IASTNodeLocation nodeLocation = ifStmt.getNodeLocations()[0]; + inactiveCodeStart = nodeLocation.getNodeOffset(); + inInactiveCode = true; + } + } + } else if (statement instanceof IASTPreprocessorIfdefStatement) { + IASTPreprocessorIfdefStatement ifdefStmt = (IASTPreprocessorIfdefStatement)statement; + inactiveCodeStack.push(Boolean.valueOf(inInactiveCode)); + if (!ifdefStmt.taken()) { + if (!inInactiveCode) { + IASTNodeLocation nodeLocation = ifdefStmt.getNodeLocations()[0]; + inactiveCodeStart = nodeLocation.getNodeOffset(); + inInactiveCode = true; + } + } + } else if (statement instanceof IASTPreprocessorIfndefStatement) { + IASTPreprocessorIfndefStatement ifndefStmt = (IASTPreprocessorIfndefStatement)statement; + inactiveCodeStack.push(Boolean.valueOf(inInactiveCode)); + if (!ifndefStmt.taken()) { + if (!inInactiveCode) { + IASTNodeLocation nodeLocation = ifndefStmt.getNodeLocations()[0]; + inactiveCodeStart = nodeLocation.getNodeOffset(); + inInactiveCode = true; + } + } + } else if (statement instanceof IASTPreprocessorElseStatement) { + IASTPreprocessorElseStatement elseStmt = (IASTPreprocessorElseStatement)statement; + if (!elseStmt.taken() && !inInactiveCode) { + IASTNodeLocation nodeLocation = elseStmt.getNodeLocations()[0]; + inactiveCodeStart = nodeLocation.getNodeOffset(); + inInactiveCode = true; + } else if (elseStmt.taken() && inInactiveCode) { + IASTNodeLocation nodeLocation = elseStmt.getNodeLocations()[0]; + int inactiveCodeEnd = nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); + positions.add(new HighlightPosition(inactiveCodeStart, inactiveCodeEnd - inactiveCodeStart, fHighlightKey)); + inInactiveCode = false; + } + } else if (statement instanceof IASTPreprocessorElifStatement) { + IASTPreprocessorElifStatement elifStmt = (IASTPreprocessorElifStatement)statement; + if (!elifStmt.taken() && !inInactiveCode) { + IASTNodeLocation nodeLocation = elifStmt.getNodeLocations()[0]; + inactiveCodeStart = nodeLocation.getNodeOffset(); + inInactiveCode = true; + } else if (elifStmt.taken() && inInactiveCode) { + IASTNodeLocation nodeLocation = elifStmt.getNodeLocations()[0]; + int inactiveCodeEnd = nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); + positions.add(new HighlightPosition(inactiveCodeStart, inactiveCodeEnd - inactiveCodeStart, fHighlightKey)); + inInactiveCode = false; + } + } else if (statement instanceof IASTPreprocessorEndifStatement) { + IASTPreprocessorEndifStatement endifStmt = (IASTPreprocessorEndifStatement)statement; + try { + boolean wasInInactiveCode = ((Boolean)inactiveCodeStack.pop()).booleanValue(); + if (inInactiveCode && !wasInInactiveCode) { + IASTNodeLocation nodeLocation = endifStmt.getNodeLocations()[0]; + int inactiveCodeEnd = nodeLocation.getNodeOffset() + nodeLocation.getNodeLength(); + positions.add(new HighlightPosition(inactiveCodeStart, inactiveCodeEnd - inactiveCodeStart, fHighlightKey)); + } + inInactiveCode = wasInInactiveCode; + } + catch( EmptyStackException e) {} + } + } + if (inInactiveCode) { + // handle dangling #if? + } + return positions; + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlighting.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlighting.java new file mode 100644 index 00000000000..74d27ce6f9f --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlighting.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.editor; + +import org.eclipse.swt.graphics.RGB; + +/** + * Semantic highlighting. + * Cloned from JDT. + * + * @since 4.0 + */ +public abstract class SemanticHighlighting { + + /** + * @return the preference key, will be augmented by a prefix and a suffix for each preference + */ + public abstract String getPreferenceKey(); + + /** + * @return the default text color + */ + public RGB getDefaultTextColor() { + return new RGB(0, 0, 0); + } + + /** + * @return true if the text attribute bold is set by default + */ + public boolean isBoldByDefault() { + return false; + } + + /** + * @return true if the text attribute italic is set by default + */ + public boolean isItalicByDefault() { + return false; + } + + /** + * @return true if the text attribute strikethrough is set by default + */ + public boolean isStrikethroughByDefault() { + return false; + } + + /** + * @return true if the text attribute underline is set by default + * @since 3.1 + */ + public boolean isUnderlineByDefault() { + return false; + } + + /** + * @return true if the text attribute italic is enabled by default + */ + public abstract boolean isEnabledByDefault(); + + /** + * @return the display name + */ + public abstract String getDisplayName(); + + /** + * Returns true iff the semantic highlighting consumes the semantic token. + *

+ * NOTE: Implementors are not allowed to keep a reference on the token or on any object + * retrieved from the token. + *

+ * + * @param token the semantic token for a {@link org.eclipse.cdt.core.dom.ast.IASTName} + * @return true iff the semantic highlighting consumes the semantic token + */ + public abstract boolean consumes(SemanticToken token); + + /** + * Returns true iff the semantic highlighting consumes the + * semantic token. + *

+ * NOTE: Implementors are not allowed to keep a reference on the token or on + * any object retrieved from the token. + *

+ * @param token the semantic token for a + * {@link org.eclipse.cdt.core.dom.ast.IASTLiteralExpression} + * @return true iff the semantic highlighting consumes the + * semantic token + */ + public boolean consumesLiteral(SemanticToken token) { + return false; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingManager.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingManager.java new file mode 100644 index 00000000000..3f3fa7240de --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingManager.java @@ -0,0 +1,632 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.editor; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.resource.StringConverter; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextAttribute; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyleRange; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.RGB; + +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.ui.text.CPresentationReconciler; +import org.eclipse.cdt.internal.ui.text.CSourceViewerConfiguration; +import org.eclipse.cdt.internal.ui.text.IColorManager; +import org.eclipse.cdt.internal.ui.text.IColorManagerExtension; + +/** + * Semantic highlighting manager. + * Cloned from JDT. + * + * @since 4.0 + */ +public class SemanticHighlightingManager implements IPropertyChangeListener { + + /** + * Highlighting style. + */ + static class HighlightingStyle { + + /** Text attribute */ + private TextAttribute fTextAttribute; + /** Enabled state */ + private boolean fIsEnabled; + + /** + * Initialize with the given text attribute. + * @param textAttribute The text attribute + * @param isEnabled the enabled state + */ + public HighlightingStyle(TextAttribute textAttribute, boolean isEnabled) { + setTextAttribute(textAttribute); + setEnabled(isEnabled); + } + + /** + * @return Returns the text attribute. + */ + public TextAttribute getTextAttribute() { + return fTextAttribute; + } + + /** + * @param textAttribute The background to set. + */ + public void setTextAttribute(TextAttribute textAttribute) { + fTextAttribute= textAttribute; + } + + /** + * @return the enabled state + */ + public boolean isEnabled() { + return fIsEnabled; + } + + /** + * @param isEnabled the new enabled state + */ + public void setEnabled(boolean isEnabled) { + fIsEnabled= isEnabled; + } + } + + /** + * Highlighted Positions. + */ + static class HighlightedPosition extends Position { + + /** Highlighting of the position */ + private HighlightingStyle fStyle; + + /** Lock object */ + private Object fLock; + + /** + * Initialize the styled positions with the given offset, length and foreground color. + * + * @param offset The position offset + * @param length The position length + * @param highlighting The position's highlighting + * @param lock The lock object + */ + public HighlightedPosition(int offset, int length, HighlightingStyle highlighting, Object lock) { + super(offset, length); + fStyle= highlighting; + fLock= lock; + } + + /** + * @return Returns a corresponding style range. + */ + public StyleRange createStyleRange() { + int len= 0; + if (fStyle.isEnabled()) + len= getLength(); + + TextAttribute textAttribute= fStyle.getTextAttribute(); + int style= textAttribute.getStyle(); + int fontStyle= style & (SWT.ITALIC | SWT.BOLD | SWT.NORMAL); + StyleRange styleRange= new StyleRange(getOffset(), len, textAttribute.getForeground(), textAttribute.getBackground(), fontStyle); + styleRange.strikeout= (style & TextAttribute.STRIKETHROUGH) != 0; + styleRange.underline= (style & TextAttribute.UNDERLINE) != 0; + + return styleRange; + } + + /** + * Uses reference equality for the highlighting. + * + * @param off The offset + * @param len The length + * @param highlighting The highlighting + * @return true iff the given offset, length and highlighting are equal to the internal ones. + */ + public boolean isEqual(int off, int len, HighlightingStyle highlighting) { + synchronized (fLock) { + return !isDeleted() && getOffset() == off && getLength() == len && fStyle == highlighting; + } + } + + /** + * Is this position contained in the given range (inclusive)? Synchronizes on position updater. + * + * @param off The range offset + * @param len The range length + * @return true iff this position is not delete and contained in the given range. + */ + public boolean isContained(int off, int len) { + synchronized (fLock) { + return !isDeleted() && off <= getOffset() && off + len >= getOffset() + getLength(); + } + } + + public void update(int off, int len) { + synchronized (fLock) { + super.setOffset(off); + super.setLength(len); + } + } + + /* + * @see org.eclipse.jface.text.Position#setLength(int) + */ + public void setLength(int length) { + synchronized (fLock) { + super.setLength(length); + } + } + + /* + * @see org.eclipse.jface.text.Position#setOffset(int) + */ + public void setOffset(int offset) { + synchronized (fLock) { + super.setOffset(offset); + } + } + + /* + * @see org.eclipse.jface.text.Position#delete() + */ + public void delete() { + synchronized (fLock) { + super.delete(); + } + } + + /* + * @see org.eclipse.jface.text.Position#undelete() + */ + public void undelete() { + synchronized (fLock) { + super.undelete(); + } + } + + /** + * @return Returns the highlighting. + */ + public HighlightingStyle getHighlighting() { + return fStyle; + } + } + + /** + * Highlighted ranges. + */ + public static class HighlightedRange extends Region { + /** The highlighting key as returned by {@link SemanticHighlighting#getPreferenceKey()}. */ + private String fKey; + + /** + * Initialize with the given offset, length and highlighting key. + * + * @param offset + * @param length + * @param key the highlighting key as returned by {@link SemanticHighlighting#getPreferenceKey()} + */ + public HighlightedRange(int offset, int length, String key) { + super(offset, length); + fKey= key; + } + + /** + * @return the highlighting key as returned by {@link SemanticHighlighting#getPreferenceKey()} + */ + public String getKey() { + return fKey; + } + + /* + * @see org.eclipse.jface.text.Region#equals(java.lang.Object) + */ + public boolean equals(Object o) { + return super.equals(o) && o instanceof HighlightedRange && fKey.equals(((HighlightedRange)o).getKey()); + } + + /* + * @see org.eclipse.jface.text.Region#hashCode() + */ + public int hashCode() { + return super.hashCode() | fKey.hashCode(); + } + } + + /** Semantic highlighting presenter */ + private SemanticHighlightingPresenter fPresenter; + /** Semantic highlighting reconciler */ + private SemanticHighlightingReconciler fReconciler; + + /** Semantic highlightings */ + private SemanticHighlighting[] fSemanticHighlightings; + /** Highlightings */ + private HighlightingStyle[] fHighlightings; + + /** The editor */ + private CEditor fEditor; + /** The source viewer */ + private CSourceViewer fSourceViewer; + /** The color manager */ + private IColorManager fColorManager; + /** The preference store */ + private IPreferenceStore fPreferenceStore; + /** The source viewer configuration */ + private CSourceViewerConfiguration fConfiguration; + /** The presentation reconciler */ + private CPresentationReconciler fPresentationReconciler; + + /** The hard-coded ranges */ + private HighlightedRange[][] fHardcodedRanges; + + /** + * Install the semantic highlighting on the given editor infrastructure + * + * @param editor The C editor + * @param sourceViewer The source viewer + * @param colorManager The color manager + * @param preferenceStore The preference store + */ + public void install(CEditor editor, CSourceViewer sourceViewer, IColorManager colorManager, IPreferenceStore preferenceStore) { + fEditor= editor; + fSourceViewer= sourceViewer; + fColorManager= colorManager; + fPreferenceStore= preferenceStore; + if (fEditor != null) { + fConfiguration= new CSourceViewerConfiguration(CUIPlugin.getDefault().getTextTools(), fEditor); +// fConfiguration= new CSourceViewerConfiguration(colorManager, preferenceStore, editor, ICPartitions.C_PARTITIONING); + fPresentationReconciler= (CPresentationReconciler) fConfiguration.getPresentationReconciler(sourceViewer); + } else { + fConfiguration= null; + fPresentationReconciler= null; + } + + fPreferenceStore.addPropertyChangeListener(this); + + if (isEnabled()) + enable(); + } + + /** + * Install the semantic highlighting on the given source viewer infrastructure. No reconciliation will be performed. + * + * @param sourceViewer the source viewer + * @param colorManager the color manager + * @param preferenceStore the preference store + * @param hardcodedRanges the hard-coded ranges to be highlighted + */ + public void install(CSourceViewer sourceViewer, IColorManager colorManager, IPreferenceStore preferenceStore, HighlightedRange[][] hardcodedRanges) { + fHardcodedRanges= hardcodedRanges; + install(null, sourceViewer, colorManager, preferenceStore); + } + + /** + * Enable semantic highlighting. + */ + private void enable() { + initializeHighlightings(); + + fPresenter= new SemanticHighlightingPresenter(); + fPresenter.install(fSourceViewer, fPresentationReconciler); + + if (fEditor != null) { + fReconciler= new SemanticHighlightingReconciler(); + fReconciler.install(fEditor, fSourceViewer, fPresenter, fSemanticHighlightings, fHighlightings); + } else { + fPresenter.updatePresentation(null, createHardcodedPositions(), new HighlightedPosition[0]); + } + } + + /** + * Computes the hard-coded positions from the hard-coded ranges + * + * @return the hard-coded positions + */ + private HighlightedPosition[] createHardcodedPositions() { + List positions= new ArrayList(); + for (int i= 0; i < fHardcodedRanges.length; i++) { + HighlightedRange range= null; + HighlightingStyle hl= null; + for (int j= 0; j < fHardcodedRanges[i].length; j++ ) { + hl= getHighlighting(fHardcodedRanges[i][j].getKey()); + if (hl.isEnabled()) { + range= fHardcodedRanges[i][j]; + break; + } + } + + if (range != null) + positions.add(fPresenter.createHighlightedPosition(range.getOffset(), range.getLength(), hl)); + } + return (HighlightedPosition[]) positions.toArray(new HighlightedPosition[positions.size()]); + } + + /** + * Returns the highlighting corresponding to the given key. + * + * @param key the highlighting key as returned by {@link SemanticHighlighting#getPreferenceKey()} + * @return the corresponding highlighting + */ + private HighlightingStyle getHighlighting(String key) { + for (int i= 0; i < fSemanticHighlightings.length; i++) { + SemanticHighlighting semanticHighlighting= fSemanticHighlightings[i]; + if (key.equals(semanticHighlighting.getPreferenceKey())) + return fHighlightings[i]; + } + return null; + } + + /** + * Uninstall the semantic highlighting + */ + public void uninstall() { + disable(); + + if (fPreferenceStore != null) { + fPreferenceStore.removePropertyChangeListener(this); + fPreferenceStore= null; + } + + fEditor= null; + fSourceViewer= null; + fColorManager= null; + fConfiguration= null; + fPresentationReconciler= null; + fHardcodedRanges= null; + } + + /** + * Disable semantic highlighting. + */ + private void disable() { + if (fReconciler != null) { + fReconciler.uninstall(); + fReconciler= null; + } + + if (fPresenter != null) { + fPresenter.uninstall(); + fPresenter= null; + } + + if (fSemanticHighlightings != null) + disposeHighlightings(); + } + + /** + * @return true iff semantic highlighting is enabled in the preferences + */ + private boolean isEnabled() { + return SemanticHighlightings.isEnabled(fPreferenceStore); + } + + /** + * Initialize semantic highlightings. + */ + private void initializeHighlightings() { + fSemanticHighlightings= SemanticHighlightings.getSemanticHighlightings(); + fHighlightings= new HighlightingStyle[fSemanticHighlightings.length]; + + for (int i= 0, n= fSemanticHighlightings.length; i < n; i++) { + SemanticHighlighting semanticHighlighting= fSemanticHighlightings[i]; + String colorKey= SemanticHighlightings.getColorPreferenceKey(semanticHighlighting); + addColor(colorKey); + + String boldKey= SemanticHighlightings.getBoldPreferenceKey(semanticHighlighting); + int style= fPreferenceStore.getBoolean(boldKey) ? SWT.BOLD : SWT.NORMAL; + + String italicKey= SemanticHighlightings.getItalicPreferenceKey(semanticHighlighting); + if (fPreferenceStore.getBoolean(italicKey)) + style |= SWT.ITALIC; + + String strikethroughKey= SemanticHighlightings.getStrikethroughPreferenceKey(semanticHighlighting); + if (fPreferenceStore.getBoolean(strikethroughKey)) + style |= TextAttribute.STRIKETHROUGH; + + String underlineKey= SemanticHighlightings.getUnderlinePreferenceKey(semanticHighlighting); + if (fPreferenceStore.getBoolean(underlineKey)) + style |= TextAttribute.UNDERLINE; + + boolean isEnabled= fPreferenceStore.getBoolean(SemanticHighlightings.getEnabledPreferenceKey(semanticHighlighting)); + + fHighlightings[i]= new HighlightingStyle(new TextAttribute(fColorManager.getColor(PreferenceConverter.getColor(fPreferenceStore, colorKey)), null, style), isEnabled); + } + } + + /** + * Dispose the semantic highlightings. + */ + private void disposeHighlightings() { + for (int i= 0, n= fSemanticHighlightings.length; i < n; i++) + removeColor(SemanticHighlightings.getColorPreferenceKey(fSemanticHighlightings[i])); + + fSemanticHighlightings= null; + fHighlightings= null; + } + + /* + * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent) + */ + public void propertyChange(PropertyChangeEvent event) { + handlePropertyChangeEvent(event); + } + + /** + * Handle the given property change event + * + * @param event The event + */ + private void handlePropertyChangeEvent(PropertyChangeEvent event) { + if (fPreferenceStore == null) + return; // Uninstalled during event notification + + if (fConfiguration != null) + fConfiguration.handlePropertyChangeEvent(event); + + if (SemanticHighlightings.affectsEnablement(fPreferenceStore, event)) { + if (isEnabled()) + enable(); + else + disable(); + } + + if (!isEnabled()) + return; + + boolean refreshNeeded= false; + + for (int i= 0, n= fSemanticHighlightings.length; i < n; i++) { + SemanticHighlighting semanticHighlighting= fSemanticHighlightings[i]; + + String colorKey= SemanticHighlightings.getColorPreferenceKey(semanticHighlighting); + if (colorKey.equals(event.getProperty())) { + adaptToTextForegroundChange(fHighlightings[i], event); + fPresenter.highlightingStyleChanged(fHighlightings[i]); + refreshNeeded= true; + continue; + } + + String boldKey= SemanticHighlightings.getBoldPreferenceKey(semanticHighlighting); + if (boldKey.equals(event.getProperty())) { + adaptToTextStyleChange(fHighlightings[i], event, SWT.BOLD); + fPresenter.highlightingStyleChanged(fHighlightings[i]); + refreshNeeded= true; + continue; + } + + String italicKey= SemanticHighlightings.getItalicPreferenceKey(semanticHighlighting); + if (italicKey.equals(event.getProperty())) { + adaptToTextStyleChange(fHighlightings[i], event, SWT.ITALIC); + fPresenter.highlightingStyleChanged(fHighlightings[i]); + refreshNeeded= true; + continue; + } + + String strikethroughKey= SemanticHighlightings.getStrikethroughPreferenceKey(semanticHighlighting); + if (strikethroughKey.equals(event.getProperty())) { + adaptToTextStyleChange(fHighlightings[i], event, TextAttribute.STRIKETHROUGH); + fPresenter.highlightingStyleChanged(fHighlightings[i]); + refreshNeeded= true; + continue; + } + + String underlineKey= SemanticHighlightings.getUnderlinePreferenceKey(semanticHighlighting); + if (underlineKey.equals(event.getProperty())) { + adaptToTextStyleChange(fHighlightings[i], event, TextAttribute.UNDERLINE); + fPresenter.highlightingStyleChanged(fHighlightings[i]); + refreshNeeded= true; + continue; + } + + String enabledKey= SemanticHighlightings.getEnabledPreferenceKey(semanticHighlighting); + if (enabledKey.equals(event.getProperty())) { + adaptToEnablementChange(fHighlightings[i], event); + fPresenter.highlightingStyleChanged(fHighlightings[i]); + refreshNeeded= true; + continue; + } + } + + if (refreshNeeded && fReconciler != null) + fReconciler.refresh(); + } + + private void adaptToEnablementChange(HighlightingStyle highlighting, PropertyChangeEvent event) { + Object value= event.getNewValue(); + boolean eventValue; + if (value instanceof Boolean) + eventValue= ((Boolean) value).booleanValue(); + else if (IPreferenceStore.TRUE.equals(value)) + eventValue= true; + else + eventValue= false; + highlighting.setEnabled(eventValue); + } + + private void adaptToTextForegroundChange(HighlightingStyle highlighting, PropertyChangeEvent event) { + RGB rgb= null; + + Object value= event.getNewValue(); + if (value instanceof RGB) + rgb= (RGB) value; + else if (value instanceof String) + rgb= StringConverter.asRGB((String) value); + + if (rgb != null) { + + String property= event.getProperty(); + Color color= fColorManager.getColor(property); + + if ((color == null || !rgb.equals(color.getRGB())) && fColorManager instanceof IColorManagerExtension) { + IColorManagerExtension ext= (IColorManagerExtension) fColorManager; + ext.unbindColor(property); + ext.bindColor(property, rgb); + color= fColorManager.getColor(property); + } + + TextAttribute oldAttr= highlighting.getTextAttribute(); + highlighting.setTextAttribute(new TextAttribute(color, oldAttr.getBackground(), oldAttr.getStyle())); + } + } + + private void adaptToTextStyleChange(HighlightingStyle highlighting, PropertyChangeEvent event, int styleAttribute) { + boolean eventValue= false; + Object value= event.getNewValue(); + if (value instanceof Boolean) + eventValue= ((Boolean) value).booleanValue(); + else if (IPreferenceStore.TRUE.equals(value)) + eventValue= true; + + TextAttribute oldAttr= highlighting.getTextAttribute(); + boolean activeValue= (oldAttr.getStyle() & styleAttribute) == styleAttribute; + + if (activeValue != eventValue) + highlighting.setTextAttribute(new TextAttribute(oldAttr.getForeground(), oldAttr.getBackground(), eventValue ? oldAttr.getStyle() | styleAttribute : oldAttr.getStyle() & ~styleAttribute)); + } + + private void addColor(String colorKey) { + if (fColorManager != null && colorKey != null && fColorManager.getColor(colorKey) == null) { + RGB rgb= PreferenceConverter.getColor(fPreferenceStore, colorKey); + if (fColorManager instanceof IColorManagerExtension) { + IColorManagerExtension ext= (IColorManagerExtension) fColorManager; + ext.unbindColor(colorKey); + ext.bindColor(colorKey, rgb); + } + } + } + + private void removeColor(String colorKey) { + if (fColorManager instanceof IColorManagerExtension) + ((IColorManagerExtension) fColorManager).unbindColor(colorKey); + } + + /** + * Force refresh of highlighting. + */ + public void refresh() { + if (fReconciler != null) { + fReconciler.refresh(); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingPresenter.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingPresenter.java new file mode 100644 index 00000000000..e8206619f0b --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingPresenter.java @@ -0,0 +1,777 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.editor; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.BadPositionCategoryException; +import org.eclipse.jface.text.DocumentEvent; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IDocumentListener; +import org.eclipse.jface.text.IPositionUpdater; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ISynchronizable; +import org.eclipse.jface.text.ITextInputListener; +import org.eclipse.jface.text.ITextPresentationListener; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.swt.custom.StyleRange; + +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightedPosition; +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightingStyle; +import org.eclipse.cdt.internal.ui.text.CPresentationReconciler; + + +/** + * Semantic highlighting presenter - UI thread implementation. + * Cloned from JDT. + * + * @since 4.0 + */ +public class SemanticHighlightingPresenter implements ITextPresentationListener, ITextInputListener, IDocumentListener { + + /** + * Semantic highlighting position updater. + */ + private class HighlightingPositionUpdater implements IPositionUpdater { + + /** The position category. */ + private final String fCategory; + + /** + * Creates a new updater for the given category. + * + * @param category the new category. + */ + public HighlightingPositionUpdater(String category) { + fCategory= category; + } + + /* + * @see org.eclipse.jface.text.IPositionUpdater#update(org.eclipse.jface.text.DocumentEvent) + */ + public void update(DocumentEvent event) { + + int eventOffset= event.getOffset(); + int eventOldLength= event.getLength(); + int eventEnd= eventOffset + eventOldLength; + + try { + Position[] positions= event.getDocument().getPositions(fCategory); + + for (int i= 0; i != positions.length; i++) { + + HighlightedPosition position= (HighlightedPosition) positions[i]; + + // Also update deleted positions because they get deleted by the background thread and removed/invalidated only in the UI runnable +// if (position.isDeleted()) +// continue; + + int offset= position.getOffset(); + int length= position.getLength(); + int end= offset + length; + + if (offset > eventEnd) + updateWithPrecedingEvent(position, event); + else if (end < eventOffset) + updateWithSucceedingEvent(position, event); + else if (offset <= eventOffset && end >= eventEnd) + updateWithIncludedEvent(position, event); + else if (offset <= eventOffset) + updateWithOverEndEvent(position, event); + else if (end >= eventEnd) + updateWithOverStartEvent(position, event); + else + updateWithIncludingEvent(position, event); + } + } catch (BadPositionCategoryException e) { + // ignore and return + } + } + + /** + * Update the given position with the given event. The event precedes the position. + * + * @param position The position + * @param event The event + */ + private void updateWithPrecedingEvent(HighlightedPosition position, DocumentEvent event) { + String newText= event.getText(); + int eventNewLength= newText != null ? newText.length() : 0; + int deltaLength= eventNewLength - event.getLength(); + + position.setOffset(position.getOffset() + deltaLength); + } + + /** + * Update the given position with the given event. The event succeeds the position. + * + * @param position The position + * @param event The event + */ + private void updateWithSucceedingEvent(HighlightedPosition position, DocumentEvent event) { + } + + /** + * Update the given position with the given event. The event is included by the position. + * + * @param position The position + * @param event The event + */ + private void updateWithIncludedEvent(HighlightedPosition position, DocumentEvent event) { + int eventOffset= event.getOffset(); + String newText= event.getText(); + if (newText == null) + newText= ""; //$NON-NLS-1$ + int eventNewLength= newText.length(); + + int deltaLength= eventNewLength - event.getLength(); + + int offset= position.getOffset(); + int length= position.getLength(); + int end= offset + length; + + int includedLength= 0; + while (includedLength < eventNewLength && Character.isJavaIdentifierPart(newText.charAt(includedLength))) + includedLength++; + if (includedLength == eventNewLength) + position.setLength(length + deltaLength); + else { + int newLeftLength= eventOffset - offset + includedLength; + + int excludedLength= eventNewLength; + while (excludedLength > 0 && Character.isJavaIdentifierPart(newText.charAt(excludedLength - 1))) + excludedLength--; + int newRightOffset= eventOffset + excludedLength; + int newRightLength= end + deltaLength - newRightOffset; + + if (newRightLength == 0) { + position.setLength(newLeftLength); + } else { + if (newLeftLength == 0) { + position.update(newRightOffset, newRightLength); + } else { + position.setLength(newLeftLength); + addPositionFromUI(newRightOffset, newRightLength, position.getHighlighting()); + } + } + } + } + + /** + * Update the given position with the given event. The event overlaps with the end of the position. + * + * @param position The position + * @param event The event + */ + private void updateWithOverEndEvent(HighlightedPosition position, DocumentEvent event) { + String newText= event.getText(); + if (newText == null) + newText= ""; //$NON-NLS-1$ + int eventNewLength= newText.length(); + + int includedLength= 0; + while (includedLength < eventNewLength && Character.isJavaIdentifierPart(newText.charAt(includedLength))) + includedLength++; + position.setLength(event.getOffset() - position.getOffset() + includedLength); + } + + /** + * Update the given position with the given event. The event overlaps with the start of the position. + * + * @param position The position + * @param event The event + */ + private void updateWithOverStartEvent(HighlightedPosition position, DocumentEvent event) { + int eventOffset= event.getOffset(); + int eventEnd= eventOffset + event.getLength(); + + String newText= event.getText(); + if (newText == null) + newText= ""; //$NON-NLS-1$ + int eventNewLength= newText.length(); + + int excludedLength= eventNewLength; + while (excludedLength > 0 && Character.isJavaIdentifierPart(newText.charAt(excludedLength - 1))) + excludedLength--; + int deleted= eventEnd - position.getOffset(); + int inserted= eventNewLength - excludedLength; + position.update(eventOffset + excludedLength, position.getLength() - deleted + inserted); + } + + /** + * Update the given position with the given event. The event includes the position. + * + * @param position The position + * @param event The event + */ + private void updateWithIncludingEvent(HighlightedPosition position, DocumentEvent event) { + position.delete(); + position.update(event.getOffset(), 0); + } + } + + /** Position updater */ + private IPositionUpdater fPositionUpdater= new HighlightingPositionUpdater(getPositionCategory()); + + /** The source viewer this semantic highlighting reconciler is installed on */ + private CSourceViewer fSourceViewer; + /** The background presentation reconciler */ + private CPresentationReconciler fPresentationReconciler; + + /** UI's current highlighted positions - can contain null elements */ + private List fPositions= new ArrayList(); + /** UI position lock */ + private Object fPositionLock= new Object(); + + /** true iff the current reconcile is canceled. */ + private boolean fIsCanceled= false; + + /** + * Creates and returns a new highlighted position with the given offset, length and highlighting. + *

+ * NOTE: Also called from background thread. + *

+ * + * @param offset The offset + * @param length The length + * @param highlighting The highlighting + * @return The new highlighted position + */ + public HighlightedPosition createHighlightedPosition(int offset, int length, HighlightingStyle highlighting) { + // TODO: reuse deleted positions + return new HighlightedPosition(offset, length, highlighting, fPositionUpdater); + } + + /** + * Adds all current positions to the given list. + *

+ * NOTE: Called from background thread. + *

+ * + * @param list The list + */ + public void addAllPositions(List list) { + synchronized (fPositionLock) { + list.addAll(fPositions); + } + } + + /** + * Create a text presentation in the background. + *

+ * NOTE: Called from background thread. + *

+ * + * @param addedPositions the added positions + * @param removedPositions the removed positions + * @return the text presentation or null, if reconciliation should be canceled + */ + public TextPresentation createPresentation(List addedPositions, List removedPositions) { + CSourceViewer sourceViewer= fSourceViewer; + CPresentationReconciler presentationReconciler= fPresentationReconciler; + if (sourceViewer == null || presentationReconciler == null) + return null; + + if (isCanceled()) + return null; + + IDocument document= sourceViewer.getDocument(); + if (document == null) + return null; + + int minStart= Integer.MAX_VALUE; + int maxEnd= Integer.MIN_VALUE; + for (int i= 0, n= removedPositions.size(); i < n; i++) { + Position position= (Position) removedPositions.get(i); + int offset= position.getOffset(); + minStart= Math.min(minStart, offset); + maxEnd= Math.max(maxEnd, offset + position.getLength()); + } + for (int i= 0, n= addedPositions.size(); i < n; i++) { + Position position= (Position) addedPositions.get(i); + int offset= position.getOffset(); + minStart= Math.min(minStart, offset); + maxEnd= Math.max(maxEnd, offset + position.getLength()); + } + + if (minStart < maxEnd) + try { + return presentationReconciler.createRepairDescription(new Region(minStart, maxEnd - minStart), document); + } catch (RuntimeException e) { + // Assume concurrent modification from UI thread + } + + return null; + } + + /** + * Create a runnable for updating the presentation. + *

+ * NOTE: Called from background thread. + *

+ * @param textPresentation the text presentation + * @param addedPositions the added positions + * @param removedPositions the removed positions + * @return the runnable or null, if reconciliation should be canceled + */ + public Runnable createUpdateRunnable(final TextPresentation textPresentation, List addedPositions, List removedPositions) { + if (fSourceViewer == null || textPresentation == null) + return null; + + // TODO: do clustering of positions and post multiple fast runnables + final HighlightedPosition[] added= new SemanticHighlightingManager.HighlightedPosition[addedPositions.size()]; + addedPositions.toArray(added); + final SemanticHighlightingManager.HighlightedPosition[] removed= new SemanticHighlightingManager.HighlightedPosition[removedPositions.size()]; + removedPositions.toArray(removed); + + if (isCanceled()) + return null; + + Runnable runnable= new Runnable() { + public void run() { + updatePresentation(textPresentation, added, removed); + } + }; + return runnable; + } + + /** + * Invalidate the presentation of the positions based on the given added positions and the existing deleted positions. + * Also unregisters the deleted positions from the document and patches the positions of this presenter. + *

+ * NOTE: Indirectly called from background thread by UI runnable. + *

+ * @param textPresentation the text presentation or null, if the presentation should computed in the UI thread + * @param addedPositions the added positions + * @param removedPositions the removed positions + */ + public void updatePresentation(TextPresentation textPresentation, HighlightedPosition[] addedPositions, HighlightedPosition[] removedPositions) { + if (fSourceViewer == null) + return; + +// checkOrdering("added positions: ", Arrays.asList(addedPositions)); //$NON-NLS-1$ +// checkOrdering("removed positions: ", Arrays.asList(removedPositions)); //$NON-NLS-1$ +// checkOrdering("old positions: ", fPositions); //$NON-NLS-1$ + + // TODO: double-check consistency with document.getPositions(...) + // TODO: reuse removed positions + if (isCanceled()) + return; + + IDocument document= fSourceViewer.getDocument(); + if (document == null) + return; + + String positionCategory= getPositionCategory(); + + List removedPositionsList= Arrays.asList(removedPositions); + + try { + synchronized (fPositionLock) { + List oldPositions= fPositions; + int newSize= Math.max(fPositions.size() + addedPositions.length - removedPositions.length, 10); + + /* + * The following loop is a kind of merge sort: it merges two List, each + * sorted by position.offset, into one new list. The first of the two is the + * previous list of positions (oldPositions), from which any deleted positions get + * removed on the fly. The second of two is the list of added positions. The result + * is stored in newPositions. + */ + List newPositions= new ArrayList(newSize); + Position position= null; + Position addedPosition= null; + for (int i= 0, j= 0, n= oldPositions.size(), m= addedPositions.length; i < n || position != null || j < m || addedPosition != null;) { + // loop variant: i + j < old(i + j) + + // a) find the next non-deleted Position from the old list + while (position == null && i < n) { + position= (Position) oldPositions.get(i++); + if (position.isDeleted() || contain(removedPositionsList, position)) { + document.removePosition(positionCategory, position); + position= null; + } + } + + // b) find the next Position from the added list + if (addedPosition == null && j < m) { + addedPosition= addedPositions[j++]; + document.addPosition(positionCategory, addedPosition); + } + + // c) merge: add the next of position/addedPosition with the lower offset + if (position != null) { + if (addedPosition != null) + if (position.getOffset() <= addedPosition.getOffset()) { + newPositions.add(position); + position= null; + } else { + newPositions.add(addedPosition); + addedPosition= null; + } + else { + newPositions.add(position); + position= null; + } + } else if (addedPosition != null) { + newPositions.add(addedPosition); + addedPosition= null; + } + } + fPositions= newPositions; + } + } catch (BadPositionCategoryException e) { + // Should not happen + CUIPlugin.getDefault().log(e); + } catch (BadLocationException e) { + // Should not happen + CUIPlugin.getDefault().log(e); + } +// checkOrdering("new positions: ", fPositions); //$NON-NLS-1$ + + if (textPresentation != null) + fSourceViewer.changeTextPresentation(textPresentation, false); + else + fSourceViewer.invalidateTextPresentation(); + } + +// private void checkOrdering(String s, List positions) { +// Position previous= null; +// for (int i= 0, n= positions.size(); i < n; i++) { +// Position current= (Position) positions.get(i); +// if (previous != null && previous.getOffset() + previous.getLength() > current.getOffset()) +// return; +// } +// } + + /** + * Returns true iff the positions contain the position. + * @param positions the positions, must be ordered by offset but may overlap + * @param position the position + * @return true iff the positions contain the position + */ + private boolean contain(List positions, Position position) { + return indexOf(positions, position) != -1; + } + + /** + * Returns index of the position in the positions, -1 if not found. + * @param positions the positions, must be ordered by offset but may overlap + * @param position the position + * @return the index + */ + private int indexOf(List positions, Position position) { + int index= computeIndexAtOffset(positions, position.getOffset()); + int size= positions.size(); + while (index < size) { + if (positions.get(index) == position) + return index; + index++; + } + return -1; + } + + /** + * Insert the given position in fPositions, s.t. the offsets remain in linear order. + * + * @param position The position for insertion + */ + private void insertPosition(Position position) { + int i= computeIndexAfterOffset(fPositions, position.getOffset()); + fPositions.add(i, position); + } + + /** + * Returns the index of the first position with an offset greater than the given offset. + * + * @param positions the positions, must be ordered by offset and must not overlap + * @param offset the offset + * @return the index of the last position with an offset greater than the given offset + */ + private int computeIndexAfterOffset(List positions, int offset) { + int i= -1; + int j= positions.size(); + while (j - i > 1) { + int k= (i + j) >> 1; + Position position= (Position) positions.get(k); + if (position.getOffset() > offset) + j= k; + else + i= k; + } + return j; + } + + /** + * Returns the index of the first position with an offset equal or greater than the given offset. + * + * @param positions the positions, must be ordered by offset and must not overlap + * @param offset the offset + * @return the index of the last position with an offset equal or greater than the given offset + */ + private int computeIndexAtOffset(List positions, int offset) { + int i= -1; + int j= positions.size(); + while (j - i > 1) { + int k= (i + j) >> 1; + Position position= (Position) positions.get(k); + if (position.getOffset() >= offset) + j= k; + else + i= k; + } + return j; + } + + /* + * @see org.eclipse.jface.text.ITextPresentationListener#applyTextPresentation(org.eclipse.jface.text.TextPresentation) + */ + public void applyTextPresentation(TextPresentation textPresentation) { + IRegion region= textPresentation.getExtent(); + int i= computeIndexAtOffset(fPositions, region.getOffset()), n= computeIndexAtOffset(fPositions, region.getOffset() + region.getLength()); + if (n - i > 2) { + List ranges= new ArrayList(n - i); + for (; i < n; i++) { + HighlightedPosition position= (HighlightedPosition) fPositions.get(i); + if (!position.isDeleted()) + ranges.add(position.createStyleRange()); + } + StyleRange[] array= new StyleRange[ranges.size()]; + array= (StyleRange[]) ranges.toArray(array); + textPresentation.replaceStyleRanges(array); + } else { + for (; i < n; i++) { + HighlightedPosition position= (HighlightedPosition) fPositions.get(i); + if (!position.isDeleted()) + textPresentation.replaceStyleRange(position.createStyleRange()); + } + } + } + + /* + * @see org.eclipse.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) + */ + public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { + setCanceled(true); + releaseDocument(oldInput); + resetState(); + } + + /* + * @see org.eclipse.jface.text.ITextInputListener#inputDocumentChanged(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IDocument) + */ + public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { + manageDocument(newInput); + } + + /* + * @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent) + */ + public void documentAboutToBeChanged(DocumentEvent event) { + setCanceled(true); + } + + /* + * @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent) + */ + public void documentChanged(DocumentEvent event) { + } + + /** + * @return Returns true iff the current reconcile is canceled. + *

+ * NOTE: Also called from background thread. + *

+ */ + public boolean isCanceled() { + IDocument document= fSourceViewer != null ? fSourceViewer.getDocument() : null; + if (document == null) + return fIsCanceled; + + synchronized (getLockObject(document)) { + return fIsCanceled; + } + } + + /** + * Set whether or not the current reconcile is canceled. + * + * @param isCanceled true iff the current reconcile is canceled + */ + public void setCanceled(boolean isCanceled) { + IDocument document= fSourceViewer != null ? fSourceViewer.getDocument() : null; + if (document == null) { + fIsCanceled= isCanceled; + return; + } + + synchronized (getLockObject(document)) { + fIsCanceled= isCanceled; + } + } + + /** + * @param document the document + * @return the document's lock object + */ + private Object getLockObject(IDocument document) { + if (document instanceof ISynchronizable) { + Object lock= ((ISynchronizable)document).getLockObject(); + if (lock != null) + return lock; + } + return document; + } + + /** + * Install this presenter on the given source viewer and background presentation + * reconciler. + * + * @param sourceViewer the source viewer + * @param backgroundPresentationReconciler the background presentation reconciler, + * can be null, in that case {@link SemanticHighlightingPresenter#createPresentation(List, List)} + * should not be called + */ + public void install(CSourceViewer sourceViewer, CPresentationReconciler backgroundPresentationReconciler) { + fSourceViewer= sourceViewer; + fPresentationReconciler= backgroundPresentationReconciler; + + fSourceViewer.addTextPresentationListener(this); +// fSourceViewer.prependTextPresentationListener(this); + fSourceViewer.addTextInputListener(this); + manageDocument(fSourceViewer.getDocument()); + } + + /** + * Uninstall this presenter. + */ + public void uninstall() { + setCanceled(true); + + if (fSourceViewer != null) { + fSourceViewer.removeTextPresentationListener(this); + releaseDocument(fSourceViewer.getDocument()); + invalidateTextPresentation(); + resetState(); + + fSourceViewer.removeTextInputListener(this); + fSourceViewer= null; + } + } + + /** + * Invalidate text presentation of positions with the given highlighting. + * + * @param highlighting The highlighting + */ + public void highlightingStyleChanged(HighlightingStyle highlighting) { + for (int i= 0, n= fPositions.size(); i < n; i++) { + HighlightedPosition position= (HighlightedPosition) fPositions.get(i); + if (position.getHighlighting() == highlighting) + fSourceViewer.invalidateTextPresentation(position.getOffset(), position.getLength()); + } + } + + /** + * Invalidate text presentation of all positions. + */ + private void invalidateTextPresentation() { + for (int i= 0, n= fPositions.size(); i < n; i++) { + Position position= (Position) fPositions.get(i); + fSourceViewer.invalidateTextPresentation(position.getOffset(), position.getLength()); + } + } + + /** + * Add a position with the given range and highlighting unconditionally, only from UI thread. + * The position will also be registered on the document. The text presentation is not invalidated. + * + * @param offset The range offset + * @param length The range length + * @param highlighting + */ + private void addPositionFromUI(int offset, int length, HighlightingStyle highlighting) { + Position position= createHighlightedPosition(offset, length, highlighting); + synchronized (fPositionLock) { + insertPosition(position); + } + + IDocument document= fSourceViewer.getDocument(); + if (document == null) + return; + String positionCategory= getPositionCategory(); + try { + document.addPosition(positionCategory, position); + } catch (BadLocationException e) { + // Should not happen + CUIPlugin.getDefault().log(e); + } catch (BadPositionCategoryException e) { + // Should not happen + CUIPlugin.getDefault().log(e); + } + } + + /** + * Reset to initial state. + */ + private void resetState() { + synchronized (fPositionLock) { + fPositions.clear(); + } + } + + /** + * Start managing the given document. + * + * @param document The document + */ + private void manageDocument(IDocument document) { + if (document != null) { + document.addPositionCategory(getPositionCategory()); + document.addPositionUpdater(fPositionUpdater); + document.addDocumentListener(this); + } + } + + /** + * Stop managing the given document. + * + * @param document The document + */ + private void releaseDocument(IDocument document) { + if (document != null) { + document.removeDocumentListener(this); + document.removePositionUpdater(fPositionUpdater); + try { + document.removePositionCategory(getPositionCategory()); + } catch (BadPositionCategoryException e) { + // Should not happen + CUIPlugin.getDefault().log(e); + } + } + } + + /** + * @return The semantic reconciler position's category. + */ + private String getPositionCategory() { + return toString(); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingReconciler.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingReconciler.java new file mode 100644 index 00000000000..ec674943c2d --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightingReconciler.java @@ -0,0 +1,449 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.editor; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbenchPartSite; + +import org.eclipse.cdt.core.IPositionConverter; +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTMacroExpansion; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.model.ICElement; +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightedPosition; +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightingManager.HighlightingStyle; +import org.eclipse.cdt.internal.ui.text.ICReconcilingListener; + + +/** + * Semantic highlighting reconciler - Background thread implementation. + * Cloned from JDT. + * + * @since 4.0 + */ +public class SemanticHighlightingReconciler implements ICReconcilingListener { + + /** + * Collects positions from the AST. + */ + private class PositionCollector extends ASTVisitor { + { + shouldVisitNames= true; + shouldVisitDeclarations= true; + shouldVisitExpressions= true; + } + + /** The semantic token */ + private SemanticToken fToken= new SemanticToken(); + private String fFilePath; + private IPositionConverter fPositionTracker; + + /** + * @param filePath + * @param positionTracker + */ + public PositionCollector(String filePath, IPositionConverter positionTracker) { + fFilePath= filePath; + fPositionTracker= positionTracker; + } + + /* + * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTDeclaration) + */ + public int visit(IASTDeclaration declaration) { + if (!fFilePath.equals(declaration.getContainingFilename())) { + return PROCESS_SKIP; + } + return PROCESS_CONTINUE; + } + + /* + * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTExpression) + */ + public int visit(IASTExpression expression) { + if (!fFilePath.equals(expression.getContainingFilename())) { + return PROCESS_SKIP; + } + IASTNodeLocation[] nodeLocations= expression.getNodeLocations(); + if (nodeLocations.length > 0 && nodeLocations[0] instanceof IASTMacroExpansion) { + if (visitNode(expression)) { + return PROCESS_SKIP; + } + } + return PROCESS_CONTINUE; + } + + /* + * @see org.eclipse.cdt.core.dom.ast.ASTVisitor#visit(org.eclipse.cdt.core.dom.ast.IASTName) + */ + public int visit(IASTName node) { + if (!fFilePath.equals(node.getContainingFilename())) { + return PROCESS_SKIP; + } + visitNode(node); + return PROCESS_CONTINUE; + } + + private boolean visitNode(IASTNode node) { + boolean consumed= false; + fToken.update(node); + for (int i= 0, n= fJobSemanticHighlightings.length; i < n; ++i) { + SemanticHighlighting semanticHighlighting= fJobSemanticHighlightings[i]; + if (fJobHighlightings[i].isEnabled() && semanticHighlighting.consumes(fToken)) { + IASTNodeLocation[] nodeLocations= node.getNodeLocations(); + if (nodeLocations.length > 0) { + addNodeLocations(nodeLocations, fJobHighlightings[i]); + } + consumed= true; + break; + } + } + fToken.clear(); + return consumed; + } + + /** + * Add all node locations for the given highlighting. + * + * @param nodeLocations The node locations + * @param highlighting The highlighting + */ + private void addNodeLocations(IASTNodeLocation[] nodeLocations, HighlightingStyle highlighting) { + for (int j = 0; j < nodeLocations.length; j++) { + IASTNodeLocation nodeLocation = nodeLocations[j]; + if (nodeLocation instanceof IASTMacroExpansion) { + IASTMacroExpansion macroExpansion= (IASTMacroExpansion)nodeLocation; + IASTNodeLocation[] expansionLocations = macroExpansion.getExpansionLocations(); + addNodeLocations(expansionLocations, highlighting); + } else { + int offset= nodeLocation.getNodeOffset(); + int length= nodeLocation.getNodeLength(); + if (fPositionTracker != null) { + IRegion actualPos= fPositionTracker.historicToActual(new Region(offset, length)); + offset= actualPos.getOffset(); + length= actualPos.getLength(); + } + if (offset > -1 && length > 0) { + addPosition(offset, length, highlighting); + } + } + } + } + + /** + * Add a position with the given range and highlighting iff it does not exist already. + * + * @param offset The range offset + * @param length The range length + * @param highlighting The highlighting + */ + private void addPosition(int offset, int length, HighlightingStyle highlighting) { + boolean isExisting= false; + // TODO: use binary search + for (int i= 0, n= fRemovedPositions.size(); i < n; i++) { + HighlightedPosition position= (HighlightedPosition) fRemovedPositions.get(i); + if (position == null) + continue; + if (position.isEqual(offset, length, highlighting)) { + isExisting= true; + fRemovedPositions.set(i, null); + fNOfRemovedPositions--; + break; + } + } + + if (!isExisting) { + Position position= fJobPresenter.createHighlightedPosition(offset, length, highlighting); + fAddedPositions.add(position); + } + } + + } + + /** The C editor this semantic highlighting reconciler is installed on */ + private CEditor fEditor; + /** The semantic highlighting presenter */ + private SemanticHighlightingPresenter fPresenter; + /** Semantic highlightings */ + private SemanticHighlighting[] fSemanticHighlightings; + /** Highlightings */ + private HighlightingStyle[] fHighlightings; + + /** Background job's added highlighted positions */ + private List fAddedPositions= new ArrayList(); + /** Background job's removed highlighted positions */ + private List fRemovedPositions= new ArrayList(); + /** Number of removed positions */ + private int fNOfRemovedPositions; + + /** Background job */ + private Job fJob; + /** Background job lock */ + private final Object fJobLock= new Object(); + /** Reconcile operation lock. */ + private final Object fReconcileLock= new Object(); + /** + * true if any thread is executing + * reconcile, false otherwise. + */ + private boolean fIsReconciling= false; + + /** The semantic highlighting presenter - cache for background thread, only valid during {@link #reconciled(IASTTranslationUnit, boolean, IProgressMonitor)} */ + private SemanticHighlightingPresenter fJobPresenter; + /** Semantic highlightings - cache for background thread, only valid during {@link #reconciled(IASTTranslationUnit, boolean, IProgressMonitor)} */ + private SemanticHighlighting[] fJobSemanticHighlightings; + /** Highlightings - cache for background thread, only valid during {@link #reconciled(IASTTranslationUnit, boolean, IProgressMonitor)} */ + private HighlightingStyle[] fJobHighlightings; + + /* + * @see org.eclipse.cdt.internal.ui.text.java.ICReconcilingListener#aboutToBeReconciled() + */ + public void aboutToBeReconciled() { + // Do nothing + } + + /* + * @see org.eclipse.cdt.internal.ui.text.ICReconcilingListener#reconciled(IASTTranslationUnit, IPositionConverter, IProgressMonitor) + */ + public void reconciled(IASTTranslationUnit ast, IPositionConverter positionTracker, IProgressMonitor progressMonitor) { + // ensure at most one thread can be reconciling at any time + synchronized (fReconcileLock) { + if (fIsReconciling) + return; + else + fIsReconciling= true; + } + fJobPresenter= fPresenter; + fJobSemanticHighlightings= fSemanticHighlightings; + fJobHighlightings= fHighlightings; + + try { + if (fJobPresenter == null || fJobSemanticHighlightings == null || fJobHighlightings == null) + return; + + fJobPresenter.setCanceled(progressMonitor != null && progressMonitor.isCanceled()); + + if (ast == null || fJobPresenter.isCanceled()) + return; + + PositionCollector collector= new PositionCollector(ast.getFilePath(), positionTracker); + IASTNode[] subtrees= getAffectedSubtrees(ast); + if (subtrees.length == 0) + return; + + startReconcilingPositions(); + + if (!fJobPresenter.isCanceled()) + reconcilePositions(subtrees, collector); + + TextPresentation textPresentation= null; + if (!fJobPresenter.isCanceled()) + textPresentation= fJobPresenter.createPresentation(fAddedPositions, fRemovedPositions); + + if (!fJobPresenter.isCanceled()) + updatePresentation(textPresentation, fAddedPositions, fRemovedPositions); + + stopReconcilingPositions(); + } finally { + fJobPresenter= null; + fJobSemanticHighlightings= null; + fJobHighlightings= null; + synchronized (fReconcileLock) { + fIsReconciling= false; + } + } + } + + /** + * @param node Root node + * @return Array of subtrees that may be affected by past document changes + */ + private IASTNode[] getAffectedSubtrees(IASTNode node) { + return new IASTNode[] { node }; + } + + /** + * Start reconciling positions. + */ + private void startReconcilingPositions() { + fJobPresenter.addAllPositions(fRemovedPositions); + fNOfRemovedPositions= fRemovedPositions.size(); + } + + /** + * Reconcile positions based on the AST subtrees + * + * @param subtrees the AST subtrees + */ + private void reconcilePositions(IASTNode[] subtrees, ASTVisitor visitor) { + // FIXME: remove positions not covered by subtrees + for (int i= 0, n= subtrees.length; i < n; i++) { + subtrees[i].accept(visitor); + } + List oldPositions= fRemovedPositions; + List newPositions= new ArrayList(fNOfRemovedPositions); + for (int i= 0, n= oldPositions.size(); i < n; i ++) { + Object current= oldPositions.get(i); + if (current != null) + newPositions.add(current); + } + fRemovedPositions= newPositions; + } + + /** + * Update the presentation. + * + * @param textPresentation the text presentation + * @param addedPositions the added positions + * @param removedPositions the removed positions + */ + private void updatePresentation(TextPresentation textPresentation, List addedPositions, List removedPositions) { + Runnable runnable= fJobPresenter.createUpdateRunnable(textPresentation, addedPositions, removedPositions); + if (runnable == null) + return; + + CEditor editor= fEditor; + if (editor == null) + return; + + IWorkbenchPartSite site= editor.getSite(); + if (site == null) + return; + + Shell shell= site.getShell(); + if (shell == null || shell.isDisposed()) + return; + + Display display= shell.getDisplay(); + if (display == null || display.isDisposed()) + return; + + display.asyncExec(runnable); + } + + /** + * Stop reconciling positions. + */ + private void stopReconcilingPositions() { + fRemovedPositions.clear(); + fNOfRemovedPositions= 0; + fAddedPositions.clear(); + } + + /** + * Install this reconciler on the given editor, presenter and highlightings. + * @param editor the editor + * @param sourceViewer the source viewer + * @param presenter the semantic highlighting presenter + * @param semanticHighlightings the semantic highlightings + * @param highlightings the highlightings + */ + public void install(CEditor editor, ISourceViewer sourceViewer, SemanticHighlightingPresenter presenter, SemanticHighlighting[] semanticHighlightings, HighlightingStyle[] highlightings) { + fPresenter= presenter; + fSemanticHighlightings= semanticHighlightings; + fHighlightings= highlightings; + + fEditor= editor; + + if (fEditor != null) { + fEditor.addReconcileListener(this); + } + } + + /** + * Uninstall this reconciler from the editor + */ + public void uninstall() { + if (fPresenter != null) + fPresenter.setCanceled(true); + + if (fEditor != null) { + fEditor.removeReconcileListener(this); + fEditor= null; + } + + fSemanticHighlightings= null; + fHighlightings= null; + fPresenter= null; + } + + /** + * Schedule a background job for retrieving the AST and reconciling the Semantic Highlighting model. + */ + private void scheduleJob() { + final ICElement element= fEditor.getInputCElement(); + + synchronized (fJobLock) { + final Job oldJob= fJob; + if (fJob != null) { + fJob.cancel(); + fJob= null; + } + + if (element != null) { + fJob= new Job(CEditorMessages.getString("SemanticHighlighting_job")) { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + if (oldJob != null) { + try { + oldJob.join(); + } catch (InterruptedException e) { + CUIPlugin.getDefault().log(e); + return Status.CANCEL_STATUS; + } + } + if (monitor.isCanceled()) + return Status.CANCEL_STATUS; + IASTTranslationUnit ast= CUIPlugin.getDefault().getASTProvider().getAST(element, ASTProvider.WAIT_YES, monitor); + reconciled(ast, null, monitor); + synchronized (fJobLock) { + // allow the job to be gc'ed + if (fJob == this) + fJob= null; + } + return Status.OK_STATUS; + } + }; +// fJob.setSystem(true); + fJob.setPriority(Job.DECORATE); + fJob.schedule(); + } + } + } + + /** + * Refreshes the highlighting. + */ + public void refresh() { + scheduleJob(); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightings.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightings.java new file mode 100644 index 00000000000..0c8b5da9f75 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticHighlightings.java @@ -0,0 +1,2055 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.editor; + +import org.eclipse.core.runtime.Platform; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.util.PropertyChangeEvent; +import org.eclipse.swt.graphics.RGB; + +import org.eclipse.cdt.core.dom.ast.DOMException; +import org.eclipse.cdt.core.dom.ast.IASTMacroExpansion; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.IEnumeration; +import org.eclipse.cdt.core.dom.ast.IEnumerator; +import org.eclipse.cdt.core.dom.ast.IField; +import org.eclipse.cdt.core.dom.ast.IFunction; +import org.eclipse.cdt.core.dom.ast.ILabel; +import org.eclipse.cdt.core.dom.ast.IParameter; +import org.eclipse.cdt.core.dom.ast.IProblemBinding; +import org.eclipse.cdt.core.dom.ast.IScope; +import org.eclipse.cdt.core.dom.ast.ITypedef; +import org.eclipse.cdt.core.dom.ast.IVariable; +import org.eclipse.cdt.core.dom.ast.c.ICFunctionScope; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPASTQualifiedName; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPBlockScope; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPClassType; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPFunctionScope; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPMethod; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPNamespace; +import org.eclipse.cdt.core.dom.ast.cpp.ICPPTemplateParameter; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.PreferenceConstants; + +/** + * Semantic highlightings. + * Cloned from JDT. + * + * @since 4.0 + */ +public class SemanticHighlightings { + + private static final RGB RGB_BLACK = new RGB(0, 0, 0); + + /** + * A named preference part that controls the highlighting of static const fields. + */ + public static final String STATIC_CONST_FIELD="staticConstField"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of static fields. + */ + public static final String STATIC_FIELD="staticField"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of fields. + */ + public static final String FIELD="field"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of method declarations. + */ + public static final String METHOD_DECLARATION="methodDeclaration"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of static method invocations. + */ + public static final String STATIC_METHOD_INVOCATION="staticMethodInvocation"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of inherited method invocations. + */ + public static final String INHERITED_METHOD_INVOCATION="inheritedMethodInvocation"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of virtual method invocations. + */ + public static final String VIRTUAL_METHOD_INVOCATION="virtualMethodInvocation"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of function declarations. + */ + public static final String FUNCTION_DECLARATION="functionDeclaration"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of static method invocations. + */ + public static final String FUNCTION_INVOCATION="functionInvocation"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of local variables. + */ + public static final String LOCAL_VARIABLE_DECLARATION="localVariableDeclaration"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of local variables. + */ + public static final String LOCAL_VARIABLE="localVariable"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of global variables. + */ + public static final String GLOBAL_VARIABLE="globalVariable"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of parameter variables. + */ + public static final String PARAMETER_VARIABLE="parameterVariable"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of template parameters. + */ + public static final String TEMPLATE_PARAMETER="templateParameter"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of method invocations. + */ + public static final String METHOD_INVOCATION="method"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of classes. + */ + public static final String CLASS="class"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of enums. + */ + public static final String ENUM="enum"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of template arguments. + */ + public static final String TEMPLATE_ARGUMENT="templateArgument"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of macro substitutions + * (=references). + */ + public static final String MACRO_SUBSTITUTION="macroSubstitution"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of macro definitions. + */ + public static final String MACRO_DEFINITION="macroDefinition"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of typedefs. + */ + public static final String TYPEDEF="typedef"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of namespaces. + */ + public static final String NAMESPACE="namespace"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of labels. + */ + public static final String LABEL="label"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of enumerators. + */ + public static final String ENUMERATOR="enumerator"; //$NON-NLS-1$ + + /** + * A named preference part that controls the highlighting of problems. + */ + public static final String PROBLEM="problem"; //$NON-NLS-1$ + + /** Init debugging mode */ + private static final boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.cdt.ui/debug/SemanticHighlighting")); //$NON-NLS-1$//$NON-NLS-2$ + + /** + * Semantic highlightings + */ + private static SemanticHighlighting[] fgSemanticHighlightings; + + /** + * Semantic highlighting for static const fields. + */ +// private static final class StaticConstFieldHighlighting extends SemanticHighlighting { +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() +// */ +// public String getPreferenceKey() { +// return STATIC_CONST_FIELD; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() +// */ +// public RGB getDefaultTextColor() { +// return RGB_BLACK; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() +// */ +// public boolean isBoldByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() +// */ +// public boolean isItalicByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() +// */ +// public boolean isEnabledByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() +// */ +// public String getDisplayName() { +// return CEditorMessages.getString("SemanticHighlighting_staticConstField"); //$NON-NLS-1$ +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) +// */ +// public boolean consumes(SemanticToken token) { +// IBinding binding= token.getBinding(); +// if (binding instanceof ICPPField) { +// ICPPField field= (ICPPField)binding; +// try { +// // TLETODO [semanticHighlighting] need access to const storage class +// return field.isStatic() /* && field.isConst() */; +// } catch (DOMException exc) { +// CUIPlugin.getDefault().log(exc.getStatus()); +// } catch (Error e) /* PDOMNotImplementedError */ { +// // ignore +// } +// } +// return false; +// } +// } + + /** + * Semantic highlighting for static fields. + */ + private static final class StaticFieldHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return STATIC_FIELD; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return new RGB(0, 0, 192); + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_staticField"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof IField) { + try { + return ((IField)binding).isStatic(); + } catch (DOMException exc) { + CUIPlugin.getDefault().log(exc.getStatus()); + } catch (Error e) /* PDOMNotImplementedError */ { + // ignore + } + } + return false; + } + } + + /** + * Semantic highlighting for fields. + */ + private static final class FieldHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return FIELD; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return new RGB(0, 0, 192); + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_field"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof IField) { + try { + return !((IField)binding).isStatic(); + } catch (DOMException exc) { + CUIPlugin.getDefault().log(exc.getStatus()); + } catch (Error e) /* PDOMNotImplementedError */ { + // ignore + } + return true; + } + return false; + } + } + + /** + * Semantic highlighting for method declarations. + */ + private static final class MethodDeclarationHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return METHOD_DECLARATION; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_methodDeclaration"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IASTNode node= token.getNode(); + if (node instanceof IASTName) { + IASTName name= (IASTName)node; + if (name.isDeclaration() || name.isDefinition()) { + IBinding binding= token.getBinding(); + if (binding instanceof ICPPMethod) { + return true; + } + } + } + return false; + } + } + + /** + * Semantic highlighting for static method invocations. + */ +// private static final class StaticMethodInvocationHighlighting extends SemanticHighlighting { +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() +// */ +// public String getPreferenceKey() { +// return STATIC_METHOD_INVOCATION; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() +// */ +// public RGB getDefaultTextColor() { +// return RGB_BLACK; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() +// */ +// public boolean isBoldByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() +// */ +// public boolean isItalicByDefault() { +// return true; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() +// */ +// public boolean isEnabledByDefault() { +// return true; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() +// */ +// public String getDisplayName() { +// return CEditorMessages.getString("SemanticHighlighting_staticMethodInvocation"); //$NON-NLS-1$ +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) +// */ +// public boolean consumes(SemanticToken token) { +// IASTNode node= token.getNode(); +// if (node instanceof IASTName) { +// IASTName name= (IASTName)node; +// if (name.isReference()) { +// IBinding binding= token.getBinding(); +// if (binding instanceof ICPPMethod) { +// try { +// return ((ICPPMethod)binding).isStatic(); +// } catch (DOMException exc) { +// CUIPlugin.getDefault().log(exc.getStatus()); +// } catch (Error e) /* PDOMNotImplementedError */ { +// // ignore +// } +// } +// } +// } +// return false; +// } +// } + + /** + * Semantic highlighting for virtual method invocations. + */ +// private static final class VirtualMethodInvocationHighlighting extends SemanticHighlighting { +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() +// */ +// public String getPreferenceKey() { +// return VIRTUAL_METHOD_INVOCATION; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() +// */ +// public RGB getDefaultTextColor() { +// return RGB_BLACK; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() +// */ +// public boolean isBoldByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() +// */ +// public boolean isItalicByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() +// */ +// public boolean isEnabledByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() +// */ +// public String getDisplayName() { +// return CEditorMessages.getString("SemanticHighlighting_virtualMethodInvocation"); //$NON-NLS-1$ +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) +// */ +// public boolean consumes(SemanticToken token) { +// IASTName node= token.getNode(); +// if (node.isReference()) { +// IBinding binding= token.getBinding(); +// if (binding instanceof ICPPMethod) { +// try { +// // TLETODO [semanticHighlighting] need proper check for virtual method +// return ((ICPPMethod)binding).isVirtual(); +// } catch (DOMException exc) { +// CUIPlugin.getDefault().log(exc.getStatus()); +// } catch (Error e) /* PDOMNotImplementedError */ { +// // ignore +// } +// } +// } +// return false; +// } +// } + + /** + * Semantic highlighting for inherited method invocations. + */ +// private static final class InheritedMethodInvocationHighlighting extends SemanticHighlighting { +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() +// */ +// public String getPreferenceKey() { +// return INHERITED_METHOD_INVOCATION; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() +// */ +// public RGB getDefaultTextColor() { +// return RGB_BLACK; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() +// */ +// public boolean isBoldByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() +// */ +// public boolean isItalicByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() +// */ +// public boolean isEnabledByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() +// */ +// public String getDisplayName() { +// return CEditorMessages.getString("SemanticHighlighting_inheritedMethodInvocation"); //$NON-NLS-1$ +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) +// */ +// public boolean consumes(SemanticToken token) { +// // TLETODO [semanticHighlighting] inherited method invocation +// return false; +// } +// } + + /** + * Semantic highlighting for method invocations. + */ + private static final class MethodInvocationHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return METHOD_INVOCATION; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_method"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IASTNode node= token.getNode(); + if (node instanceof IASTName) { + IASTName name= (IASTName)node; + if (name.isReference()) { + IBinding binding= token.getBinding(); + if (binding instanceof ICPPMethod) { + return true; + } + } + } + return false; + } + + /** + * Extracts the method binding from the token's simple name. The method + * binding is either the token's binding (if the parent of token is a + * method call or declaration) or the constructor binding of a class + * instance creation if the node is the type name of a class instance + * creation. + * + * @param token the token to extract the method binding from + * @return the corresponding method binding, or null + */ +// private IBinding getMethodBinding(SemanticToken token) { +// IBinding binding= null; +// // work around: https://bugs.eclipse.org/bugs/show_bug.cgi?id=62605 +// IASTNode node= token.getNode(); +// IASTNode parent= node.getParent(); +// while (isTypePath(node, parent)) { +// node= parent; +// parent= parent.getParent(); +// } +// +// if (parent != null && node.getLocationInParent() == ClassInstanceCreation.TYPE_PROPERTY) +// binding= ((ClassInstanceCreation) parent).resolveConstructorBinding(); +// else +// binding= token.getBinding(); +// return binding; +// } + + /** + * Returns true if the given child/parent nodes are valid + * sub nodes of a Type IASTNode. + * @param child the child node + * @param parent the parent node + * @return true if the nodes may be the sub nodes of a type node, false otherwise + */ +// private boolean isTypePath(IASTNode child, IASTNode parent) { +// if (parent instanceof Type) { +// StructuralPropertyDescriptor location= child.getLocationInParent(); +// return location == ParameterizedType.TYPE_PROPERTY || location == SimpleType.NAME_PROPERTY; +// } else if (parent instanceof QualifiedName) { +// StructuralPropertyDescriptor location= child.getLocationInParent(); +// return location == QualifiedName.NAME_PROPERTY; +// } +// return false; +// } + } + + /** + * Semantic highlighting for function declarations. + */ + private static final class FunctionDeclarationHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return FUNCTION_DECLARATION; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_functionDeclaration"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IASTNode node= token.getNode(); + if (node instanceof IASTName) { + IASTName name= (IASTName)node; + if (name.isDeclaration()) { + IBinding binding= token.getBinding(); + if (binding instanceof IFunction) { + return true; + } + } + } + return false; + } + } + + /** + * Semantic highlighting for function invocations. + */ + private static final class FunctionInvocationHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return FUNCTION_INVOCATION; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_functionInvocation"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IASTNode node= token.getNode(); + if (node instanceof IASTName) { + IASTName name= (IASTName)node; + if (name.isReference()) { + IBinding binding= token.getBinding(); + return binding instanceof IFunction; + } + } + return false; + } + } + + /** + * Semantic highlighting for local variable declarations. + */ + private static final class LocalVariableDeclarationHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return LOCAL_VARIABLE_DECLARATION; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return new RGB(128, 0, 0); + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_localVariableDeclaration"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IASTNode node= token.getNode(); + if (node instanceof IASTName) { + IASTName name= (IASTName)node; + if (name.isDeclaration()) { + IBinding binding= token.getBinding(); + if (binding instanceof IVariable + && !(binding instanceof IField) + && !(binding instanceof IParameter)) { + try { + IScope scope= binding.getScope(); + if (LocalVariableHighlighting.isLocalScope(scope)) { + return true; + } + } catch (DOMException exc) { + CUIPlugin.getDefault().log(exc.getStatus()); + } catch (Error e) /* PDOMNotImplementedError */ { + // ignore + } + } + } + } + return false; + } + +} + + /** + * Semantic highlighting for local variables. + */ + private static final class LocalVariableHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return LOCAL_VARIABLE; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_localVariable"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IASTNode node= token.getNode(); + if (node instanceof IASTName) { + IASTName name= (IASTName)node; + if (name.isReference()) { + IBinding binding= token.getBinding(); + if (binding instanceof IVariable + && !(binding instanceof IField) + && !(binding instanceof IParameter)) { + try { + IScope scope= binding.getScope(); + if (isLocalScope(scope)) { + return true; + } + } catch (DOMException exc) { + CUIPlugin.getDefault().log(exc.getStatus()); + } catch (Error e) /* PDOMNotImplementedError */ { + // ignore + } + } + } + } + return false; + } + + public static boolean isLocalScope(IScope scope) { + while (scope != null) { + if (scope instanceof ICPPFunctionScope || + scope instanceof ICPPBlockScope || + scope instanceof ICFunctionScope) { + return true; + } + try { + scope= scope.getParent(); + } catch (DOMException e) { + scope= null; + } + } + return false; + } +} + + /** + * Semantic highlighting for global variables. + */ + private static final class GlobalVariableHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return GLOBAL_VARIABLE; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_globalVariable"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof IVariable + && !(binding instanceof IField) + && !(binding instanceof IParameter)) { + try { + IScope scope= binding.getScope(); + if (!LocalVariableHighlighting.isLocalScope(scope)) { + return true; + } + } catch (DOMException exc) { + CUIPlugin.getDefault().log(exc.getStatus()); + } catch (Error e) /* PDOMNotImplementedError */ { + // ignore + } + } + return false; + } + } + + /** + * Semantic highlighting for parameter variables. + */ + private static final class ParameterVariableHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return PARAMETER_VARIABLE; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_parameterVariable"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof IParameter) { + return true; + } + return false; + } + } + + /** + * Semantic highlighting for template parameters. + */ + private static final class TemplateParameterHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return TEMPLATE_PARAMETER; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return new RGB(100, 70, 50); + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_templateParameter"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IASTNode node= token.getNode(); + if (node instanceof IASTName) { + IBinding binding= token.getBinding(); + if (binding instanceof ICPPTemplateParameter) { + return true; + } + // template parameters are resolved as problems?? +// if (node.getParent() instanceof ICPPASTNamedTypeSpecifier) { +// if (binding instanceof IProblemBinding) { +// return true; +// } +// } + } + return false; + } + } + + /** + * Semantic highlighting for classes. + */ + private static final class ClassHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return CLASS; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return new RGB(0, 80, 50); + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_classes"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IASTNode node= token.getNode(); + if (node instanceof IASTName) { + IBinding binding= token.getBinding(); + if (binding instanceof ICPPClassType) { + IASTName name= (IASTName)node; + if (name.isReference()) { + if (node.getParent() instanceof ICPPASTQualifiedName) { + ICPPASTQualifiedName qName= (ICPPASTQualifiedName)node.getParent(); + if (qName.getLastName() == name) { + return true; + } + } else { + return true; + } + } else { + return true; + } + } + } + return false; + } + } + + /** + * Semantic highlighting for enums. + */ + private static final class EnumHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return ENUM; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return new RGB(100, 70, 50); + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_enums"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof IEnumeration) { + return true; + } + return false; + } + } + + /** + * Semantic highlighting for template arguments. + */ +// private static final class TemplateArgumentHighlighting extends SemanticHighlighting { +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() +// */ +// public String getPreferenceKey() { +// return TEMPLATE_ARGUMENT; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() +// */ +// public RGB getDefaultTextColor() { +// return new RGB(13, 100, 0); +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() +// */ +// public boolean isBoldByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() +// */ +// public boolean isItalicByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() +// */ +// public boolean isEnabledByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() +// */ +// public String getDisplayName() { +// return CEditorMessages.getString("SemanticHighlighting_templateArguments"); //$NON-NLS-1$ +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) +// */ +// public boolean consumes(SemanticToken token) { +// IBinding binding= token.getBinding(); +// if (binding instanceof ICPPTemplateParameter) { +// return true; +// } +// return false; +// } +// } + + /** + * Semantic highlighting for macro substitutions (references). + */ + private static final class MacroSubstitutionHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return MACRO_SUBSTITUTION; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_macroSubstitution"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IASTNode node= token.getNode(); + if (!(node instanceof IASTName)) { + IASTNodeLocation[] nodeLocations= node.getNodeLocations(); + if (nodeLocations.length == 1 && nodeLocations[0] instanceof IASTMacroExpansion) { + return true; + } + } + return false; + } + } + + /** + * Semantic highlighting for macro definitions. + */ +// private static final class MacroDefinitionHighlighting extends SemanticHighlighting { +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() +// */ +// public String getPreferenceKey() { +// return MACRO_DEFINITION; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() +// */ +// public RGB getDefaultTextColor() { +// return RGB_BLACK; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() +// */ +// public boolean isBoldByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() +// */ +// public boolean isItalicByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() +// */ +// public boolean isEnabledByDefault() { +// return false; +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() +// */ +// public String getDisplayName() { +// return CEditorMessages.getString("SemanticHighlighting_macroDefinition"); //$NON-NLS-1$ +// } +// +// /* +// * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) +// */ +// public boolean consumes(SemanticToken token) { +// IASTNode node= token.getNode(); +// if (node instanceof IASTName && node.getParent() instanceof IASTPreprocessorMacroDefinition) { +// return true; +// } +// return false; +// } +// } + + /** + * Semantic highlighting for typedefs. + */ + private static final class TypedefHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return TYPEDEF; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_typeDef"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof ITypedef) { + return true; + } + return false; + } + } + + /** + * Semantic highlighting for namespaces. + */ + private static final class NamespaceHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return NAMESPACE; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_namespace"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof ICPPNamespace) { + return true; + } + return false; + } + } + + /** + * Semantic highlighting for labels. + */ + private static final class LabelHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return LABEL; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_label"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof ILabel) { + return true; + } + return false; + } + } + + /** + * Semantic highlighting for enumerators. + */ + private static final class EnumeratorHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return ENUMERATOR; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return RGB_BLACK; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_enumerator"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof IEnumerator) { + return true; + } + return false; + } + } + + /** + * Semantic highlighting for problems. + */ + private static final class ProblemHighlighting extends SemanticHighlighting { + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getPreferenceKey() + */ + public String getPreferenceKey() { + return PROBLEM; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextColor() + */ + public RGB getDefaultTextColor() { + return new RGB(224, 0, 0); + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDefaultTextStyleBold() + */ + public boolean isBoldByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isStrikethroughByDefault() + */ + public boolean isStrikethroughByDefault() { + return true; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isItalicByDefault() + */ + public boolean isItalicByDefault() { + return false; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#isEnabledByDefault() + */ + public boolean isEnabledByDefault() { + return DEBUG; + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#getDisplayName() + */ + public String getDisplayName() { + return CEditorMessages.getString("SemanticHighlighting_problem"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.editor.SemanticHighlighting#consumes(org.eclipse.cdt.internal.ui.editor.SemanticToken) + */ + public boolean consumes(SemanticToken token) { + IBinding binding= token.getBinding(); + if (binding instanceof IProblemBinding) { + return true; + } + return false; + } + } + + /** + * A named preference that controls the given semantic highlighting's color. + * + * @param semanticHighlighting the semantic highlighting + * @return the color preference key + */ + public static String getColorPreferenceKey(SemanticHighlighting semanticHighlighting) { + return PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + semanticHighlighting.getPreferenceKey() + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_COLOR_SUFFIX; + } + + /** + * A named preference that controls if the given semantic highlighting has the text attribute bold. + * + * @param semanticHighlighting the semantic highlighting + * @return the bold preference key + */ + public static String getBoldPreferenceKey(SemanticHighlighting semanticHighlighting) { + return PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + semanticHighlighting.getPreferenceKey() + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_BOLD_SUFFIX; + } + + /** + * A named preference that controls if the given semantic highlighting has the text attribute italic. + * + * @param semanticHighlighting the semantic highlighting + * @return the italic preference key + */ + public static String getItalicPreferenceKey(SemanticHighlighting semanticHighlighting) { + return PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + semanticHighlighting.getPreferenceKey() + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ITALIC_SUFFIX; + } + + /** + * A named preference that controls if the given semantic highlighting has the text attribute strikethrough. + * + * @param semanticHighlighting the semantic highlighting + * @return the strikethrough preference key + */ + public static String getStrikethroughPreferenceKey(SemanticHighlighting semanticHighlighting) { + return PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + semanticHighlighting.getPreferenceKey() + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_STRIKETHROUGH_SUFFIX; + } + + /** + * A named preference that controls if the given semantic highlighting has the text attribute underline. + * + * @param semanticHighlighting the semantic highlighting + * @return the underline preference key + */ + public static String getUnderlinePreferenceKey(SemanticHighlighting semanticHighlighting) { + return PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + semanticHighlighting.getPreferenceKey() + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_UNDERLINE_SUFFIX; + } + + /** + * A named preference that controls if the given semantic highlighting is enabled. + * + * @param semanticHighlighting the semantic highlighting + * @return the enabled preference key + */ + public static String getEnabledPreferenceKey(SemanticHighlighting semanticHighlighting) { + return PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX + semanticHighlighting.getPreferenceKey() + PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED_SUFFIX; + } + + /** + * @return The semantic highlightings, the order defines the precedence of matches, the first match wins. + */ + public static SemanticHighlighting[] getSemanticHighlightings() { + if (fgSemanticHighlightings == null) + fgSemanticHighlightings= new SemanticHighlighting[] { + new MacroSubstitutionHighlighting(), // before all others! + new FunctionDeclarationHighlighting(), + new FunctionInvocationHighlighting(), + new ClassHighlighting(), +// new StaticConstFieldHighlighting(), + new StaticFieldHighlighting(), + new FieldHighlighting(), + new MethodDeclarationHighlighting(), +// TLETODO [semanticHighlighting] Static method invocations +// new StaticMethodInvocationHighlighting(), +// TLETODO [semanticHighlighting] Virtual method invocations +// new VirtualMethodInvocationHighlighting(), +// TLETODO [semanticHighlighting] Inherited method invocations +// new InheritedMethodInvocationHighlighting(), + new ParameterVariableHighlighting(), // before local variables + new LocalVariableDeclarationHighlighting(), + new LocalVariableHighlighting(), + new GlobalVariableHighlighting(), +// TLETODO [semanticHighlighting] Template parameter highlighting + new TemplateParameterHighlighting(), // before template arguments! + new MethodInvocationHighlighting(), // before types to get ctors +// TLETODO [semanticHighlighting] Template argument highlighting +// new TemplateArgumentHighlighting(), // before other types + new EnumHighlighting(), +// TLETODO [semanticHighlighting] Macro definition highlighting +// new MacroDefinitionHighlighting(), + new TypedefHighlighting(), + new NamespaceHighlighting(), + new LabelHighlighting(), + new EnumeratorHighlighting(), + new ProblemHighlighting(), + }; + return fgSemanticHighlightings; + } + + /** + * Initialize default preferences in the given preference store. + * @param store The preference store + */ + public static void initDefaults(IPreferenceStore store) { + store.setDefault(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED, DEBUG); + + SemanticHighlighting[] semanticHighlightings= getSemanticHighlightings(); + for (int i= 0, n= semanticHighlightings.length; i < n; i++) { + SemanticHighlighting semanticHighlighting= semanticHighlightings[i]; + store.setDefault(SemanticHighlightings.getEnabledPreferenceKey(semanticHighlighting), DEBUG || semanticHighlighting.isEnabledByDefault()); + PreferenceConverter.setDefault(store, SemanticHighlightings.getColorPreferenceKey(semanticHighlighting), semanticHighlighting.getDefaultTextColor()); + store.setDefault(SemanticHighlightings.getBoldPreferenceKey(semanticHighlighting), semanticHighlighting.isBoldByDefault()); + store.setDefault(SemanticHighlightings.getItalicPreferenceKey(semanticHighlighting), semanticHighlighting.isItalicByDefault()); + store.setDefault(SemanticHighlightings.getStrikethroughPreferenceKey(semanticHighlighting), semanticHighlighting.isStrikethroughByDefault()); + store.setDefault(SemanticHighlightings.getUnderlinePreferenceKey(semanticHighlighting), DEBUG || semanticHighlighting.isUnderlineByDefault()); + } + } + + /** + * Tests whether event in store affects the + * enablement of semantic highlighting. + * + * @param store the preference store where event was observed + * @param event the property change under examination + * @return true if event changed semantic + * highlighting enablement, false if it did not + */ + public static boolean affectsEnablement(IPreferenceStore store, PropertyChangeEvent event) { + if (event.getProperty().equals(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED)) { + return true; + } + String relevantKey= null; + SemanticHighlighting[] highlightings= getSemanticHighlightings(); + for (int i= 0; i < highlightings.length; i++) { + if (event.getProperty().equals(getEnabledPreferenceKey(highlightings[i]))) { + relevantKey= event.getProperty(); + break; + } + } + if (relevantKey == null) + return false; + + for (int i= 0; i < highlightings.length; i++) { + String key= getEnabledPreferenceKey(highlightings[i]); + if (key.equals(relevantKey)) + continue; + if (store.getBoolean(key)) + return false; // another is still enabled or was enabled before + } + + // all others are disabled, so toggling relevantKey affects the enablement + return true; + } + + /** + * Tests whether semantic highlighting is currently enabled. + * + * @param store the preference store to consult + * @return true if semantic highlighting is enabled, + * false if it is not + */ + public static boolean isEnabled(IPreferenceStore store) { + if (!store.getBoolean(PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED)) { + return false; + } + SemanticHighlighting[] highlightings= getSemanticHighlightings(); + boolean enable= false; + for (int i= 0; i < highlightings.length; i++) { + String enabledKey= getEnabledPreferenceKey(highlightings[i]); + if (store.getBoolean(enabledKey)) { + enable= true; + break; + } + } + + return enable; + } + + /** + * Do not instantiate + */ + private SemanticHighlightings() { + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticToken.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticToken.java new file mode 100644 index 00000000000..1bac7aed700 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SemanticToken.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.editor; + +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; + +/** + * Semantic token. + * Cloned from JDT. + * + * @since 4.0 + */ +public final class SemanticToken { + + /** AST node */ + private IASTNode fNode; + + /** Binding */ + private IBinding fBinding; + /** Is the binding resolved? */ + private boolean fIsBindingResolved= false; + + /** AST root */ + private IASTTranslationUnit fRoot; + private boolean fIsRootResolved= false; + + /** + * @return Returns the binding, can be null. + */ + public IBinding getBinding() { + if (!fIsBindingResolved) { + fIsBindingResolved= true; + if (fNode instanceof IASTName) + fBinding= ((IASTName)fNode).resolveBinding(); + } + + return fBinding; + } + + /** + * @return the AST node + */ + public IASTNode getNode() { + return fNode; + } + + /** + * @return the AST root + */ + public IASTTranslationUnit getRoot() { + if (!fIsRootResolved) { + fIsRootResolved= true; + if (fNode != null) { + fRoot= fNode.getTranslationUnit(); + } + } + return fRoot; + } + + /** + * Update this token with the given AST node. + *

+ * NOTE: Allowed to be used by {@link SemanticHighlightingReconciler} only. + *

+ * + * @param node the AST node + */ + void update(IASTNode node) { + clear(); + fNode= node; + } + + /** + * Clears this token. + *

+ * NOTE: Allowed to be used by {@link SemanticHighlightingReconciler} only. + *

+ */ + void clear() { + fNode= null; + fBinding= null; + fIsBindingResolved= false; + fRoot= null; + fIsRootResolved= false; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/WorkInProgressPreferencePage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/WorkInProgressPreferencePage.java new file mode 100644 index 00000000000..f931265a16c --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/WorkInProgressPreferencePage.java @@ -0,0 +1,167 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 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.cdt.internal.ui.preferences; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.PreferenceConstants; + +/** + * Preference page for work in progress. + */ +public class WorkInProgressPreferencePage extends PreferencePage implements IWorkbenchPreferencePage { + + private List fCheckBoxes; + private List fRadioButtons; + private List fTextControls; + + /** + * creates a new preference page. + */ + public WorkInProgressPreferencePage() { + setPreferenceStore(getPreferenceStore()); + fRadioButtons= new ArrayList(); + fCheckBoxes= new ArrayList(); + fTextControls= new ArrayList(); + } + + Button addCheckBox(Composite parent, String label, String key) { + GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL); + + Button button= new Button(parent, SWT.CHECK); + button.setText(label); + button.setData(key); + button.setLayoutData(gd); + + button.setSelection(getPreferenceStore().getBoolean(key)); + + fCheckBoxes.add(button); + return button; + } + + /* + * @see PreferencePage#createControl(Composite) + */ + public void createControl(Composite parent) { + super.createControl(parent); + PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), "WORK_IN_PROGRESS_PREFERENCE_PAGE"); //$NON-NLS-1$ + } + + protected Control createContents(Composite parent) { + initializeDialogUnits(parent); + + Composite result= new Composite(parent, SWT.NONE); + GridLayout layout= new GridLayout(); + layout.marginHeight= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); + layout.marginWidth= 0; + layout.verticalSpacing= convertVerticalDLUsToPixels(10); + layout.horizontalSpacing= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + result.setLayout(layout); + + // Add your controls here + addCheckBox(result, "Semantic Highlighting", PreferenceConstants.EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED); //$NON-NLS-1$ + applyDialogFont(result); + return result; + } + + + /* + * @see org.eclipse.ui.IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) + */ + public void init(IWorkbench workbench) { + } + + protected void createSpacer(Composite composite, int columnSpan) { + Label label= new Label(composite, SWT.NONE); + GridData gd= new GridData(); + gd.horizontalSpan= columnSpan; + label.setLayoutData(gd); + } + + /* + * @see org.eclipse.jface.preference.PreferencePage#doGetPreferenceStore() + */ + protected IPreferenceStore doGetPreferenceStore() { + return CUIPlugin.getDefault().getPreferenceStore(); + } + + /* + * @see PreferencePage#performDefaults() + */ + protected void performDefaults() { + IPreferenceStore store= getPreferenceStore(); + for (int i= 0; i < fCheckBoxes.size(); i++) { + Button button= (Button) fCheckBoxes.get(i); + String key= (String) button.getData(); + button.setSelection(store.getDefaultBoolean(key)); + } + for (int i= 0; i < fRadioButtons.size(); i++) { + Button button= (Button) fRadioButtons.get(i); + String[] info= (String[]) button.getData(); + button.setSelection(info[1].equals(store.getDefaultString(info[0]))); + } + for (int i= 0; i < fTextControls.size(); i++) { + Text text= (Text) fTextControls.get(i); + String key= (String) text.getData(); + text.setText(store.getDefaultString(key)); + } + + super.performDefaults(); + } + + /* + * @see IPreferencePage#performOk() + */ + public boolean performOk() { + IPreferenceStore store= getPreferenceStore(); + for (int i= 0; i < fCheckBoxes.size(); i++) { + Button button= (Button) fCheckBoxes.get(i); + String key= (String) button.getData(); + store.setValue(key, button.getSelection()); + } + for (int i= 0; i < fRadioButtons.size(); i++) { + Button button= (Button) fRadioButtons.get(i); + if (button.getSelection()) { + String[] info= (String[]) button.getData(); + store.setValue(info[0], info[1]); + } + } + for (int i= 0; i < fTextControls.size(); i++) { + Text text= (Text) fTextControls.get(i); + String key= (String) text.getData(); + store.setValue(key, text.getText()); + } + + CUIPlugin.getDefault().savePluginPreferences(); + return super.performOk(); + } + + public static void initDefaults(IPreferenceStore store) { + // Initialize your defaults here + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CPresentationReconciler.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CPresentationReconciler.java new file mode 100644 index 00000000000..ab8c580cda7 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CPresentationReconciler.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.text; + +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.TextPresentation; +import org.eclipse.jface.text.presentation.PresentationReconciler; + + +/** + * Presentation reconciler, adding functionality for operation without a viewer. + * Cloned from JDT. + * + * @since 4.0 + */ +public class CPresentationReconciler extends PresentationReconciler { + + /** Last used document */ + private IDocument fLastDocument; + + /** + * Constructs a "repair description" for the given damage and returns + * this description as a text presentation. + *

+ * NOTE: Should not be used if this reconciler is installed on a viewer. + *

+ * + * @param damage the damage to be repaired + * @param document the document whose presentation must be repaired + * @return the presentation repair description as text presentation + */ + public TextPresentation createRepairDescription(IRegion damage, IDocument document) { + if (document != fLastDocument) { + setDocumentToDamagers(document); + setDocumentToRepairers(document); + fLastDocument= document; + } + return createPresentation(damage, document); + } +} 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 5c4e06bbe97..054e2bbf0c7 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * QNX Software System + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.ui.text; @@ -24,15 +25,18 @@ import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.reconciler.DirtyRegion; import org.eclipse.jface.text.reconciler.IReconcilingStrategy; +import org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension; import org.eclipse.ui.texteditor.ITextEditor; -public class CReconcilingStrategy implements IReconcilingStrategy { +public class CReconcilingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension { private ITextEditor fEditor; private IWorkingCopyManager fManager; private IProgressMonitor fProgressMonitor; private String txt = null; + // used by tests + protected boolean fInitialProcessDone; public CReconcilingStrategy(CEditor editor) { fEditor= editor; @@ -123,5 +127,16 @@ public class CReconcilingStrategy implements IReconcilingStrategy { } catch(CModelException e) { } - } + } + + /* + * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#initialReconcile() + */ + public void initialReconcile() { + if (fEditor instanceof IReconcilingParticipant) { + IReconcilingParticipant p= (IReconcilingParticipant) fEditor; + p.reconciled(true); + } + fInitialProcessDone= 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 5148df68d1a..aa4d425b94b 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * QNX Software System + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.ui.text; @@ -33,7 +34,6 @@ import org.eclipse.jface.text.information.IInformationPresenter; import org.eclipse.jface.text.information.IInformationProvider; import org.eclipse.jface.text.information.InformationPresenter; import org.eclipse.jface.text.presentation.IPresentationReconciler; -import org.eclipse.jface.text.presentation.PresentationReconciler; import org.eclipse.jface.text.reconciler.IReconciler; import org.eclipse.jface.text.reconciler.MonoReconciler; import org.eclipse.jface.text.rules.DefaultDamagerRepairer; @@ -163,7 +163,7 @@ public class CSourceViewerConfiguration extends TextSourceViewerConfiguration { */ public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer) { - PresentationReconciler reconciler= new PresentationReconciler(); + CPresentationReconciler reconciler= new CPresentationReconciler(); reconciler.setDocumentPartitioning(getConfiguredDocumentPartitioning(sourceViewer)); RuleBasedScanner scanner = null; @@ -529,5 +529,14 @@ public class CSourceViewerConfiguration extends TextSourceViewerConfiguration { return settings; } + + /** + * Adapt to the given preference change event. + * + * @param event + */ + public void handlePropertyChangeEvent(PropertyChangeEvent event) { + fTextTools.adaptToPreferenceChange(event); + } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/ICReconcilingListener.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/ICReconcilingListener.java new file mode 100644 index 00000000000..e7adee0131e --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/ICReconcilingListener.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2000, 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 + * Anton Leherbauer (Wind River Systems) - Adapted for CDT + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.cdt.core.IPositionConverter; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; + + +/** + * Interface of an object listening to (AST-) reconciling. + * Inspired by JDT. + * + * @since 4.0 + */ +public interface ICReconcilingListener { + + /** + * Called before reconciling is started. + */ + void aboutToBeReconciled(); + + /** + * Called after reconciling has been finished. + * + * @param ast + * the translation unit AST or null if the working + * copy was consistent or reconciliation has been cancelled + * @param positionTracker + * the position tracker to map AST positions to current document + * positions; may be null which means positions + * can be considered up-to-date + * @param progressMonitor + * the progress monitor + */ + void reconciled(IASTTranslationUnit ast, IPositionConverter positionTracker, IProgressMonitor progressMonitor); +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java index 6d8fc3b5374..045dc052fd9 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPlugin.java @@ -68,6 +68,7 @@ import org.eclipse.cdt.internal.ui.ICStatusConstants; import org.eclipse.cdt.internal.ui.IContextMenuConstants; import org.eclipse.cdt.internal.ui.ResourceAdapterFactory; import org.eclipse.cdt.internal.ui.buildconsole.BuildConsoleManager; +import org.eclipse.cdt.internal.ui.editor.ASTProvider; import org.eclipse.cdt.internal.ui.editor.CDocumentProvider; import org.eclipse.cdt.internal.ui.editor.CustomBufferFactory; import org.eclipse.cdt.internal.ui.editor.ExternalSearchDocumentProvider; @@ -110,9 +111,8 @@ public class CUIPlugin extends AbstractUIPlugin { private CEditorTextHoverDescriptor[] fCEditorTextHoverDescriptors; /** - * The extension point registry for the org.eclipse.jdt.ui.javaFoldingStructureProvider + * The extension point registry for the org.eclipse.cdt.ui.foldingStructureProviders * extension point. - * */ private CFoldingStructureProviderRegistry fFoldingStructureProviderRegistry; @@ -338,17 +338,23 @@ public class CUIPlugin extends AbstractUIPlugin { private CElementAdapterFactory fCElementAdapterFactory; /** - * The template context type registry for the java editor. + * The template context type registry for the C editor. * @since 3.0 */ private ContributionContextTypeRegistry fContextTypeRegistry; /** - * The template store for the java editor. + * The template store for the C editor. * @since 3.0 */ private TemplateStore fTemplateStore; + /** + * The AST provider. + * @since 4.0 + */ + private ASTProvider fASTProvider; + public CUIPlugin() { fgCPlugin = this; @@ -708,9 +714,9 @@ public class CUIPlugin extends AbstractUIPlugin { } /** - * Returns the template context type registry for the java plugin. + * Returns the template context type registry for the C plugin. * - * @return the template context type registry for the java plugin + * @return the template context type registry for the C plugin * @since 3.0 */ public ContextTypeRegistry getTemplateContextRegistry() { @@ -722,9 +728,9 @@ public class CUIPlugin extends AbstractUIPlugin { } /** - * Returns the template store for the java editor templates. + * Returns the template store for the C editor templates. * - * @return the template store for the java editor templates + * @return the template store for the C editor templates * @since 3.0 */ public TemplateStore getTemplateStore() { @@ -739,4 +745,17 @@ public class CUIPlugin extends AbstractUIPlugin { return fTemplateStore; } + /** + * Returns the AST provider. + * + * @return the AST provider + * @since 4.0 + */ + public synchronized ASTProvider getASTProvider() { + if (fASTProvider == null) + fASTProvider= new ASTProvider(); + + return fASTProvider; + } + } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPreferenceInitializer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPreferenceInitializer.java index f1e77638b2d..a4ac05a7ca4 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPreferenceInitializer.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/CUIPreferenceInitializer.java @@ -11,11 +11,14 @@ package org.eclipse.cdt.ui; import org.eclipse.cdt.internal.ui.cview.CView; +import org.eclipse.cdt.internal.ui.editor.SemanticHighlightings; import org.eclipse.cdt.internal.ui.preferences.BuildConsolePreferencePage; import org.eclipse.cdt.internal.ui.preferences.CEditorPreferencePage; import org.eclipse.cdt.internal.ui.preferences.CParserPreferencePage; import org.eclipse.cdt.internal.ui.preferences.CPluginPreferencePage; import org.eclipse.cdt.internal.ui.preferences.CodeAssistPreferencePage; +import org.eclipse.cdt.internal.ui.preferences.WorkInProgressPreferencePage; + import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.ui.editors.text.EditorsUI; @@ -41,7 +44,9 @@ public class CUIPreferenceInitializer extends AbstractPreferenceInitializer { CParserPreferencePage.initDefaults(store); CEditorPreferencePage.initDefaults(store); CodeAssistPreferencePage.initDefaults(store); - + SemanticHighlightings.initDefaults(store); + WorkInProgressPreferencePage.initDefaults(store); + // We need to do this remove any keys that might have been // in the CUIPlugin store prior to the move of the CEditor setting // All of those settings are now in the workbench "All TextEditor" preference Page. diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java index bc7bfc0c19f..8aad2f38231 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/PreferenceConstants.java @@ -12,6 +12,7 @@ package org.eclipse.cdt.ui; import org.eclipse.cdt.internal.ui.text.ICColorConstants; + import org.eclipse.jface.action.Action; import org.eclipse.jface.preference.IPreferenceStore; import org.eclipse.jface.preference.PreferenceConverter; @@ -432,6 +433,86 @@ public class PreferenceConstants { */ public static final String EDITOR_SHOW_TEXT_HOVER_AFFORDANCE= "PreferenceConstants.EDITOR_SHOW_TEXT_HOVER_AFFORDANCE"; //$NON-NLS-1$ + /** + * A named preference prefix for semantic highlighting preferences. + * + * @since 4.0 + */ + public static final String EDITOR_SEMANTIC_HIGHLIGHTING_PREFIX="semanticHighlighting."; //$NON-NLS-1$ + + /** + * A named preference suffix that controls a semantic highlighting's color. + *

+ * Value is of type String. A RGB color value encoded as a string + * using class PreferenceConverter + *

+ * + * @see org.eclipse.jface.resource.StringConverter + * @see org.eclipse.jface.preference.PreferenceConverter + * @since 4.0 + */ + public static final String EDITOR_SEMANTIC_HIGHLIGHTING_COLOR_SUFFIX=".color"; //$NON-NLS-1$ + + /** + * A named preference suffix that controls if semantic highlighting has the text attribute bold. + *

+ * Value is of type Boolean: true if bold. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_SEMANTIC_HIGHLIGHTING_BOLD_SUFFIX=".bold"; //$NON-NLS-1$ + + /** + * A named preference suffix that controls if semantic highlighting has the text attribute italic. + *

+ * Value is of type Boolean: true if italic. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_SEMANTIC_HIGHLIGHTING_ITALIC_SUFFIX=".italic"; //$NON-NLS-1$ + + /** + * A named preference suffix that controls if semantic highlighting has the text attribute strikethrough. + *

+ * Value is of type Boolean: true if strikethrough. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_SEMANTIC_HIGHLIGHTING_STRIKETHROUGH_SUFFIX=".strikethrough"; //$NON-NLS-1$ + + /** + * A named preference suffix that controls if semantic highlighting has the text attribute underline. + *

+ * Value is of type Boolean: true if underline. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_SEMANTIC_HIGHLIGHTING_UNDERLINE_SUFFIX=".underline"; //$NON-NLS-1$ + + /** + * A named preference suffix that controls if semantic highlighting is enabled. + *

+ * Value is of type Boolean: true if enabled. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED_SUFFIX=".enabled"; //$NON-NLS-1$ + + /** + * A named preference key that controls if semantic highlighting is enabled. + *

+ * Value is of type Boolean: true if enabled. + *

+ * + * @since 4.0 + */ + public static final String EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED= "semanticHighlighting.enabled"; //$NON-NLS-1$ + /** * Initializes the given preference store with the default values.