org.eclipse.ui.themes
+ * extension contributed by this plug-in.
+ */
+public interface ICThemeConstants {
+ String ID_PREFIX= CUIPlugin.PLUGIN_ID + "."; //$NON-NLS-1$
+
+ /**
+ * A theme constant that holds the background color used in the code assist selection dialog.
+ */
+ public final String CODEASSIST_PROPOSALS_BACKGROUND= ID_PREFIX + PreferenceConstants.CODEASSIST_PROPOSALS_BACKGROUND;
+
+ /**
+ * A theme constant that holds the foreground color used in the code assist selection dialog.
+ */
+ public final String CODEASSIST_PROPOSALS_FOREGROUND= ID_PREFIX + PreferenceConstants.CODEASSIST_PROPOSALS_FOREGROUND;
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CDocumentProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CDocumentProvider.java
index 417a9bef8a0..28414c00816 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CDocumentProvider.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CDocumentProvider.java
@@ -8,6 +8,7 @@
* Contributors:
* QNX Software Systems - Initial API and implementation
* Anton Leherbauer (Wind River Systems)
+ * Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.editor;
@@ -54,12 +55,14 @@ import org.eclipse.ui.texteditor.IMarkerUpdater;
import org.eclipse.ui.texteditor.MarkerAnnotation;
import org.eclipse.ui.texteditor.MarkerUtilities;
import org.eclipse.ui.texteditor.ResourceMarkerAnnotationModel;
+import org.eclipse.ui.texteditor.spelling.SpellingAnnotation;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICModelMarker;
import org.eclipse.cdt.core.model.IProblemRequestor;
import org.eclipse.cdt.core.model.ITranslationUnit;
import org.eclipse.cdt.core.model.IWorkingCopy;
+import org.eclipse.cdt.core.parser.IPersistableProblem;
import org.eclipse.cdt.core.parser.IProblem;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.PreferenceConstants;
@@ -68,6 +71,7 @@ import org.eclipse.cdt.ui.text.ICPartitions;
import org.eclipse.cdt.internal.core.model.IBufferFactory;
import org.eclipse.cdt.internal.ui.text.IProblemRequestorExtension;
+import org.eclipse.cdt.internal.ui.text.spelling.CoreSpellingProblem;
import org.eclipse.cdt.internal.ui.util.EditorUtility;
/**
@@ -85,17 +89,17 @@ public class CDocumentProvider extends TextFileDocumentProvider {
* Annotation representing an IProblem
.
*/
static protected class ProblemAnnotation extends Annotation implements ICAnnotation {
-
private static final String INDEXER_ANNOTATION_TYPE= "org.eclipse.cdt.ui.indexmarker"; //$NON-NLS-1$
private ITranslationUnit fTranslationUnit;
private List fOverlaids;
private IProblem fProblem;
- public ProblemAnnotation(IProblem problem, ITranslationUnit cu) {
+ public ProblemAnnotation(IProblem problem, ITranslationUnit tu) {
fProblem= problem;
- fTranslationUnit= cu;
- setType(INDEXER_ANNOTATION_TYPE);
+ fTranslationUnit= tu;
+ setType(problem instanceof CoreSpellingProblem ?
+ SpellingAnnotation.TYPE : INDEXER_ANNOTATION_TYPE);
}
/*
@@ -109,7 +113,7 @@ public class CDocumentProvider extends TextFileDocumentProvider {
* @see ICAnnotation#getArguments()
*/
public String[] getArguments() {
- return isProblem() ? new String[]{fProblem.getArguments()} : null;
+ return isProblem() ? fProblem.getArguments() : null;
}
/*
@@ -175,6 +179,15 @@ public class CDocumentProvider extends TextFileDocumentProvider {
public ITranslationUnit getTranslationUnit() {
return fTranslationUnit;
}
+
+ /*
+ * @see org.eclipse.jdt.internal.ui.javaeditor.IJavaAnnotation#getMarkerType()
+ */
+ public String getMarkerType() {
+ if (fProblem instanceof IPersistableProblem)
+ return ((IPersistableProblem) fProblem).getMarkerType();
+ return null;
+ }
}
/**
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CMarkerAnnotation.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CMarkerAnnotation.java
index ae32fb8e6b4..38c665efa7a 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CMarkerAnnotation.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CMarkerAnnotation.java
@@ -169,4 +169,12 @@ public class CMarkerAnnotation extends MarkerAnnotation implements IProblemAnnot
}
return null;
}
+
+ public String getMarkerType() {
+ IMarker marker= getMarker();
+ if (marker == null || !marker.exists())
+ return null;
+
+ return MarkerUtilities.getMarkerType(getMarker());
+ }
}
\ No newline at end of file
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICAnnotation.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICAnnotation.java
index 5b1251d78e9..8ae5f978923 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICAnnotation.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/ICAnnotation.java
@@ -72,7 +72,7 @@ public interface ICAnnotation {
* Adds the given annotation to the list of
* annotations which are overlaid by this annotations.
*
- * @param annotation the problem annoation
+ * @param annotation the problem annotation
*/
void addOverlaid(ICAnnotation annotation);
@@ -80,7 +80,7 @@ public interface ICAnnotation {
* Removes the given annotation from the list of
* annotations which are overlaid by this annotation.
*
- * @param annotation the problem annoation
+ * @param annotation the problem annotation
*/
void removeOverlaid(ICAnnotation annotation);
@@ -101,4 +101,13 @@ public interface ICAnnotation {
String[] getArguments();
int getId();
+
+ /**
+ * Returns the marker type associated to this problem or null if no marker type
+ * can be evaluated. See also {@link org.eclipse.cdt.ui.text.IProblemLocation#getMarkerType()}.
+ *
+ * @return the type of the marker which would be associated to the problem or
+ * null if no marker type can be evaluated.
+ */
+ String getMarkerType();
}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java
index 188e179d1c1..4fc80f44320 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.java
@@ -9,6 +9,7 @@
* IBM Corporation - initial API and implementation
* Anton Leherbauer (Wind River Systems)
* Markus Schorn (Wind River Systems)
+ * Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.preferences;
@@ -223,6 +224,36 @@ public final class PreferencesMessages extends NLS {
public static String TodoTaskInputDialog_error_entryExists;
public static String TodoTaskInputDialog_error_noSpace;
+ public static String SpellingPreferencePage_empty_threshold;
+ public static String SpellingPreferencePage_invalid_threshold;
+ public static String SpellingPreferencePage_ignore_digits_label;
+ public static String SpellingPreferencePage_ignore_mixed_label;
+ public static String SpellingPreferencePage_ignore_sentence_label;
+ public static String SpellingPreferencePage_ignore_upper_label;
+ public static String SpellingPreferencePage_ignore_url_label;
+ public static String SpellingPreferencePage_ignore_non_letters_label;
+ public static String SpellingPreferencePage_ignore_single_letters_label;
+ public static String SpellingPreferencePage_ignore_string_literals_label;
+ public static String SpellingPreferencePage_proposals_threshold;
+ public static String SpellingPreferencePage_problems_threshold;
+ public static String SpellingPreferencePage_dictionary_label;
+ public static String SpellingPreferencePage_encoding_label;
+ public static String SpellingPreferencePage_workspace_dictionary_label;
+ public static String SpellingPreferencePage_browse_label;
+ public static String SpellingPreferencePage_dictionary_error;
+ public static String SpellingPreferencePage_dictionary_none;
+ public static String SpellingPreferencePage_locale_error;
+ public static String SpellingPreferencePage_filedialog_title;
+ public static String SpellingPreferencePage_filter_dictionary_label;
+ public static String SpellingPreferencePage_filter_all_label;
+ public static String SpellingPreferencePage_enable_contentassist_label;
+ public static String SpellingPreferencePage_group_user;
+ public static String SpellingPreferencePage_group_dictionary;
+ public static String SpellingPreferencePage_group_dictionaries;
+ public static String SpellingPreferencePage_group_advanced;
+ public static String SpellingPreferencePage_user_dictionary_description;
+ public static String SpellingPreferencePage_variables;
+
public static String LanguageMappings_missingLanguageTitle;
public static String WorkspaceLanguagesPreferencePage_description;
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties
index 0412c738fca..b83a4eda436 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/PreferencesMessages.properties
@@ -9,6 +9,7 @@
# IBM Corporation - initial API and implementation
# Anton Leherbauer (Wind River Systems)
# Markus Schorn (Wind River Systems)
+# Sergey Prigogin (Google)
###############################################################################
CEditorPreferencePage_link=C/C++ Editor Preferences. Note that some preferences may be set on the Text Editors preference page.
@@ -257,7 +258,36 @@ PathEntryVariablesBlock_addVariableButton = &New...
PathEntryVariablesBlock_editVariableButton = Edi&t...
PathEntryVariablesBlock_removeVariableButton = &Remove
-
+# Spelling
+SpellingPreferencePage_empty_threshold= A positive integer must be specified.
+SpellingPreferencePage_invalid_threshold=''{0}'' is not a valid positive integer.
+SpellingPreferencePage_ignore_digits_label=Ignore &words with digits
+SpellingPreferencePage_ignore_mixed_label=Ignore &mixed case words
+SpellingPreferencePage_ignore_sentence_label=Ignore &sentence capitalization
+SpellingPreferencePage_ignore_upper_label=Ignore u&pper case words
+SpellingPreferencePage_ignore_url_label=Ignore &internet addresses
+SpellingPreferencePage_ignore_single_letters_label=I&gnore single letters
+SpellingPreferencePage_ignore_string_literals_label=Ignore string &literals
+SpellingPreferencePage_ignore_non_letters_label=Ignore &non-letters at word boundaries
+SpellingPreferencePage_proposals_threshold= Ma&ximum number of correction proposals:
+SpellingPreferencePage_problems_threshold= Maximum number of problems repor&ted per file:
+SpellingPreferencePage_dictionary_label=Plat&form dictionary:
+SpellingPreferencePage_workspace_dictionary_label=Use&r defined dictionary:
+SpellingPreferencePage_browse_label=&Browse...
+SpellingPreferencePage_dictionary_error=The dictionary file must be read/write accessible.
+SpellingPreferencePage_dictionary_none=none
+SpellingPreferencePage_encoding_label=En&coding:
+SpellingPreferencePage_locale_error=There is no dictionary available for this language.
+SpellingPreferencePage_filedialog_title=Select User Dictionary
+SpellingPreferencePage_filter_dictionary_label=Dictionary Files
+SpellingPreferencePage_filter_all_label=All Files
+SpellingPreferencePage_enable_contentassist_label=Ma&ke dictionary available to content assist
+SpellingPreferencePage_group_user=Options
+SpellingPreferencePage_group_dictionary=Dictionary
+SpellingPreferencePage_group_dictionaries=Dictionaries
+SpellingPreferencePage_group_advanced=Advanced
+SpellingPreferencePage_variables=&Variables...
+SpellingPreferencePage_user_dictionary_description=The user dictionary is a text file with one word on each line
# Language settings
LanguageMappings_missingLanguageTitle = Missing Languages
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/SpellingConfigurationBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/SpellingConfigurationBlock.java
new file mode 100644
index 00000000000..d77712cb931
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/SpellingConfigurationBlock.java
@@ -0,0 +1,641 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.preferences;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+
+import org.eclipse.core.variables.IStringVariableManager;
+import org.eclipse.core.variables.VariablesPlugin;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.ResourcesPlugin;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Combo;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.FileDialog;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+import org.eclipse.jface.dialogs.DialogPage;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceStore;
+import org.eclipse.jface.window.Window;
+
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer;
+
+import org.eclipse.ui.ide.dialogs.EncodingFieldEditor;
+
+import org.eclipse.cdt.ui.PreferenceConstants;
+
+import org.eclipse.cdt.internal.ui.ICHelpContextIds;
+import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener;
+import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
+import org.eclipse.cdt.internal.ui.dialogs.StatusUtil;
+import org.eclipse.cdt.internal.ui.text.spelling.SpellCheckEngine;
+import org.eclipse.cdt.internal.ui.util.PixelConverter;
+import org.eclipse.cdt.internal.ui.util.SWTUtil;
+import org.eclipse.cdt.internal.ui.wizards.indexwizards.StringVariableSelectionDialog;
+
+/**
+ * Options configuration block for spell check related settings.
+ */
+public class SpellingConfigurationBlock extends OptionsConfigurationBlock {
+ /**
+ * Tells whether content assist proposal block should be shown.
+ * Currently the spelling engine cannot return word proposals but
+ * only correction proposals and hence this is disabled.
+ */
+ private static final boolean SUPPORT_CONTENT_ASSIST_PROPOSALS= false;
+
+ /** Preference keys for the preferences in this block */
+ private static final Key PREF_SPELLING_IGNORE_DIGITS= getCDTUIKey(PreferenceConstants.SPELLING_IGNORE_DIGITS);
+ private static final Key PREF_SPELLING_IGNORE_MIXED= getCDTUIKey(PreferenceConstants.SPELLING_IGNORE_MIXED);
+ private static final Key PREF_SPELLING_IGNORE_SENTENCE= getCDTUIKey(PreferenceConstants.SPELLING_IGNORE_SENTENCE);
+ private static final Key PREF_SPELLING_IGNORE_UPPER= getCDTUIKey(PreferenceConstants.SPELLING_IGNORE_UPPER);
+ private static final Key PREF_SPELLING_IGNORE_STRING_LITERALS= getCDTUIKey(PreferenceConstants.SPELLING_IGNORE_STRING_LITERALS);
+ private static final Key PREF_SPELLING_IGNORE_SINGLE_LETTERS= getCDTUIKey(PreferenceConstants.SPELLING_IGNORE_SINGLE_LETTERS);
+ private static final Key PREF_SPELLING_IGNORE_NON_LETTERS= getCDTUIKey(PreferenceConstants.SPELLING_IGNORE_NON_LETTERS);
+ private static final Key PREF_SPELLING_IGNORE_URLS= getCDTUIKey(PreferenceConstants.SPELLING_IGNORE_URLS);
+ private static final Key PREF_SPELLING_LOCALE= getCDTUIKey(PreferenceConstants.SPELLING_LOCALE);
+ private static final Key PREF_SPELLING_PROPOSAL_THRESHOLD= getCDTUIKey(PreferenceConstants.SPELLING_PROPOSAL_THRESHOLD);
+ private static final Key PREF_SPELLING_PROBLEMS_THRESHOLD= getCDTUIKey(PreferenceConstants.SPELLING_PROBLEMS_THRESHOLD);
+ private static final Key PREF_SPELLING_USER_DICTIONARY= getCDTUIKey(PreferenceConstants.SPELLING_USER_DICTIONARY);
+ private static final Key PREF_SPELLING_USER_DICTIONARY_ENCODING= getCDTUIKey(PreferenceConstants.SPELLING_USER_DICTIONARY_ENCODING);
+ private static final Key PREF_SPELLING_ENABLE_CONTENTASSIST= getCDTUIKey(PreferenceConstants.SPELLING_ENABLE_CONTENTASSIST);
+
+ /**
+ * The value for no platform dictionary.
+ */
+ private static final String PREF_VALUE_NO_LOCALE= ""; //$NON-NLS-1$
+
+ /**
+ * Creates a selection dependency between a master and a slave control.
+ *
+ * @param master The master button that controls the state of the slave
+ * @param slave The slave control that is enabled only if the master is
+ * selected
+ */
+ protected static void createSelectionDependency(final Button master, final Control slave) {
+ master.addSelectionListener(new SelectionListener() {
+ public void widgetDefaultSelected(SelectionEvent event) {
+ // Do nothing
+ }
+
+ public void widgetSelected(SelectionEvent event) {
+ slave.setEnabled(master.getSelection());
+ }
+ });
+ slave.setEnabled(master.getSelection());
+ }
+
+ /**
+ * Returns the locale codes for the locale list.
+ *
+ * @param locales
+ * The list of locales
+ * @return Array of locale codes for the list
+ */
+ protected static String[] getDictionaryCodes(final Set locales) {
+ int index= 0;
+ Locale locale= null;
+
+ final String[] codes= new String[locales.size() + 1];
+ for (final Iterator iterator= locales.iterator(); iterator.hasNext();) {
+ locale= (Locale)iterator.next();
+ codes[index++]= locale.toString();
+ }
+ codes[index++]= PREF_VALUE_NO_LOCALE;
+ return codes;
+ }
+
+ /**
+ * Returns the display labels for the locale list.
+ *
+ * @param locales The list of locales
+ * @return Array of display labels for the list
+ */
+ protected static String[] getDictionaryLabels(final Set locales) {
+ int index= 0;
+ Locale locale= null;
+
+ final String[] labels= new String[locales.size() + 1];
+ for (final Iterator iterator= locales.iterator(); iterator.hasNext();) {
+
+ locale= (Locale)iterator.next();
+ labels[index++]= locale.getDisplayName();
+ }
+ labels[index++]= PreferencesMessages.SpellingPreferencePage_dictionary_none;
+ return labels;
+ }
+
+ /**
+ * Validates that the file with the specified absolute path exists and can
+ * be opened.
+ *
+ * @param path The path of the file to validate
+ * @return a status without error if the path is valid
+ */
+ protected static IStatus validateAbsoluteFilePath(String path) {
+ final StatusInfo status= new StatusInfo();
+ IStringVariableManager variableManager= VariablesPlugin.getDefault().getStringVariableManager();
+ try {
+ path= variableManager.performStringSubstitution(path);
+ if (path.length() > 0) {
+
+ final File file= new File(path);
+ if (!file.exists() && (!file.isAbsolute() || !file.getParentFile().canWrite()))
+ status.setError(PreferencesMessages.SpellingPreferencePage_dictionary_error);
+ else if (file.exists() && (!file.isFile() || !file.isAbsolute() || !file.canRead() || !file.canWrite()))
+ status.setError(PreferencesMessages.SpellingPreferencePage_dictionary_error);
+ }
+ } catch (CoreException e) {
+ status.setError(e.getLocalizedMessage());
+ }
+ return status;
+ }
+
+ /**
+ * Validates that the specified locale is available.
+ *
+ * @param localeString the locale to validate
+ * @return The status of the validation
+ */
+ private static IStatus validateLocale(final String localeString) {
+ if (PREF_VALUE_NO_LOCALE.equals(localeString))
+ return new StatusInfo();
+
+ Locale locale= SpellCheckEngine.convertToLocale(localeString);
+
+ if (SpellCheckEngine.findClosestLocale(locale) != null)
+ return new StatusInfo();
+
+ return new StatusInfo(IStatus.ERROR, PreferencesMessages.SpellingPreferencePage_locale_error);
+ }
+
+ /**
+ * Validates that the specified number is positive.
+ *
+ * @param number the number to validate
+ * @return The status of the validation
+ */
+ protected static IStatus validatePositiveNumber(final String number) {
+ final StatusInfo status= new StatusInfo();
+ if (number.length() == 0) {
+ status.setError(PreferencesMessages.SpellingPreferencePage_empty_threshold);
+ } else {
+ try {
+ final int value= Integer.parseInt(number);
+ if (value < 0) {
+ status.setError(PreferencesMessages.bind(PreferencesMessages.SpellingPreferencePage_invalid_threshold, number));
+ }
+ } catch (NumberFormatException exception) {
+ status.setError(PreferencesMessages.bind(PreferencesMessages.SpellingPreferencePage_invalid_threshold, number));
+ }
+ }
+ return status;
+ }
+
+ /** The dictionary path field */
+ private Text fDictionaryPath= null;
+
+ /** The status for the workspace dictionary file */
+ private IStatus fFileStatus= new StatusInfo();
+
+ /** The status for the proposal threshold */
+ private IStatus fThresholdStatus= new StatusInfo();
+
+ /** The status for the encoding field editor */
+ private IStatus fEncodingFieldEditorStatus= new StatusInfo();
+
+ /** The encoding field editor. */
+ private EncodingFieldEditor fEncodingEditor;
+ /** The encoding field editor's parent. */
+ private Composite fEncodingEditorParent;
+
+ /**
+ * All controls
+ */
+ private Control[] fAllControls;
+
+ /**
+ * All previously enabled controls
+ */
+ private Control[] fEnabledControls;
+
+ /**
+ * Creates a new spelling configuration block.
+ *
+ * @param context the status change listener
+ * @param project the Java project
+ * @param container the preference container
+ */
+ public SpellingConfigurationBlock(final IStatusChangeListener context, final IProject project, IWorkbenchPreferenceContainer container) {
+ super(context, project, getAllKeys(), container);
+
+ IStatus status= validateAbsoluteFilePath(getValue(PREF_SPELLING_USER_DICTIONARY));
+ if (status.getSeverity() != IStatus.OK)
+ setValue(PREF_SPELLING_USER_DICTIONARY, ""); //$NON-NLS-1$
+
+ status= validateLocale(getValue(PREF_SPELLING_LOCALE));
+ if (status.getSeverity() != IStatus.OK)
+ setValue(PREF_SPELLING_LOCALE, SpellCheckEngine.getDefaultLocale().toString());
+ }
+
+ protected Combo addComboBox(Composite parent, String label, Key key, String[] values, String[] valueLabels, int indent) {
+ ControlData data= new ControlData(key, values);
+
+ GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ gd.horizontalIndent= indent;
+
+ Label labelControl= new Label(parent, SWT.LEFT | SWT.WRAP);
+ labelControl.setText(label);
+ labelControl.setLayoutData(gd);
+
+ Combo comboBox= new Combo(parent, SWT.READ_ONLY);
+ comboBox.setItems(valueLabels);
+ comboBox.setData(data);
+ gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ gd.horizontalSpan= 2;
+ comboBox.setLayoutData(gd);
+ comboBox.addSelectionListener(getSelectionListener());
+
+ fLabels.put(comboBox, labelControl);
+
+ String currValue= getValue(key);
+
+ Locale locale= SpellCheckEngine.convertToLocale(currValue);
+ locale= SpellCheckEngine.findClosestLocale(locale);
+ if (locale != null)
+ currValue= locale.toString();
+
+ comboBox.select(data.getSelection(currValue));
+
+ fComboBoxes.add(comboBox);
+ return comboBox;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#createContents(org.eclipse.swt.widgets.Composite)
+ */
+ protected Control createContents(final Composite parent) {
+ Composite composite= new Composite(parent, SWT.NONE);
+ composite.setLayout(new GridLayout());
+
+ List allControls= new ArrayList();
+ final PixelConverter converter= new PixelConverter(parent);
+
+ final String[] trueFalse= new String[] { IPreferenceStore.TRUE, IPreferenceStore.FALSE };
+
+ Group user= new Group(composite, SWT.NONE);
+ user.setText(PreferencesMessages.SpellingPreferencePage_group_user);
+ user.setLayout(new GridLayout());
+ user.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ allControls.add(user);
+
+ String label= PreferencesMessages.SpellingPreferencePage_ignore_digits_label;
+ Control slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_DIGITS, trueFalse, 0);
+ allControls.add(slave);
+
+ label= PreferencesMessages.SpellingPreferencePage_ignore_mixed_label;
+ slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_MIXED, trueFalse, 0);
+ allControls.add(slave);
+
+ label= PreferencesMessages.SpellingPreferencePage_ignore_sentence_label;
+ slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_SENTENCE, trueFalse, 0);
+ allControls.add(slave);
+
+ label= PreferencesMessages.SpellingPreferencePage_ignore_upper_label;
+ slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_UPPER, trueFalse, 0);
+ allControls.add(slave);
+
+ label= PreferencesMessages.SpellingPreferencePage_ignore_url_label;
+ slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_URLS, trueFalse, 0);
+ allControls.add(slave);
+
+ label= PreferencesMessages.SpellingPreferencePage_ignore_non_letters_label;
+ slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_NON_LETTERS, trueFalse, 0);
+ allControls.add(slave);
+
+ label= PreferencesMessages.SpellingPreferencePage_ignore_single_letters_label;
+ slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_SINGLE_LETTERS, trueFalse, 0);
+ allControls.add(slave);
+
+ label= PreferencesMessages.SpellingPreferencePage_ignore_string_literals_label;
+ slave= addCheckBox(user, label, PREF_SPELLING_IGNORE_STRING_LITERALS, trueFalse, 0);
+ allControls.add(slave);
+
+ final Set locales= SpellCheckEngine.getLocalesWithInstalledDictionaries();
+ boolean hasPlaformDictionaries= locales.size() > 0;
+
+ final Group engine= new Group(composite, SWT.NONE);
+ if (hasPlaformDictionaries)
+ engine.setText(PreferencesMessages.SpellingPreferencePage_group_dictionaries);
+ else
+ engine.setText(PreferencesMessages.SpellingPreferencePage_group_dictionary);
+ engine.setLayout(new GridLayout(4, false));
+ engine.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ allControls.add(engine);
+
+ if (hasPlaformDictionaries) {
+ label= PreferencesMessages.SpellingPreferencePage_dictionary_label;
+ Combo combo= addComboBox(engine, label, PREF_SPELLING_LOCALE, getDictionaryCodes(locales), getDictionaryLabels(locales), 0);
+ combo.setEnabled(locales.size() > 0);
+ allControls.add(combo);
+ allControls.add(fLabels.get(combo));
+
+ new Label(engine, SWT.NONE); // placeholder
+ }
+
+ label= PreferencesMessages.SpellingPreferencePage_workspace_dictionary_label;
+ fDictionaryPath= addTextField(engine, label, PREF_SPELLING_USER_DICTIONARY, 0, 0);
+ GridData gd= (GridData) fDictionaryPath.getLayoutData();
+ gd.grabExcessHorizontalSpace= true;
+ gd.widthHint= converter.convertWidthInCharsToPixels(40);
+ allControls.add(fDictionaryPath);
+ allControls.add(fLabels.get(fDictionaryPath));
+
+ Composite buttons=new Composite(engine, SWT.NONE);
+ buttons.setLayout(new GridLayout(2,true));
+ buttons.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_END));
+
+ Button button= new Button(buttons, SWT.PUSH);
+ button.setText(PreferencesMessages.SpellingPreferencePage_browse_label);
+ button.addSelectionListener(new SelectionAdapter() {
+
+ public void widgetSelected(final SelectionEvent event) {
+ handleBrowseButtonSelected();
+ }
+ });
+ SWTUtil.setButtonDimensionHint(button);
+ button.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
+ allControls.add(button);
+
+ button=new Button(buttons, SWT.PUSH);
+ button.setText(PreferencesMessages.SpellingPreferencePage_variables);
+ button.addSelectionListener(new SelectionAdapter() {
+ public void widgetSelected(SelectionEvent e) {
+ handleVariablesButtonSelected();
+ }
+ });
+ SWTUtil.setButtonDimensionHint(button);
+ button.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL));
+ allControls.add(button);
+
+ // Description for user dictionary
+ new Label(engine, SWT.NONE); // filler
+ Label description= new Label(engine, SWT.NONE);
+ gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ gd.horizontalSpan= 3;
+ description.setLayoutData(gd);
+ description.setText(PreferencesMessages.SpellingPreferencePage_user_dictionary_description);
+ allControls.add(description);
+
+ createEncodingFieldEditor(engine, allControls);
+
+ Group advanced= new Group(composite, SWT.NONE);
+ advanced.setText(PreferencesMessages.SpellingPreferencePage_group_advanced);
+ advanced.setLayout(new GridLayout(3, false));
+ advanced.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
+ allControls.add(advanced);
+
+ label= PreferencesMessages.SpellingPreferencePage_problems_threshold;
+ int digits= 4;
+ Text text= addTextField(advanced, label, PREF_SPELLING_PROBLEMS_THRESHOLD, 0, converter.convertWidthInCharsToPixels(digits+1));
+ text.setTextLimit(digits);
+ allControls.add(text);
+ allControls.add(fLabels.get(text));
+
+ label= PreferencesMessages.SpellingPreferencePage_proposals_threshold;
+ digits= 3;
+ text= addTextField(advanced, label, PREF_SPELLING_PROPOSAL_THRESHOLD, 0, converter.convertWidthInCharsToPixels(digits+1));
+ text.setTextLimit(digits);
+ gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING);
+ allControls.add(text);
+ allControls.add(fLabels.get(text));
+
+ if (SUPPORT_CONTENT_ASSIST_PROPOSALS) {
+ label= PreferencesMessages.SpellingPreferencePage_enable_contentassist_label;
+ button= addCheckBox(advanced, label, PREF_SPELLING_ENABLE_CONTENTASSIST, trueFalse, 0);
+ allControls.add(button);
+ }
+
+ fAllControls= (Control[]) allControls.toArray(new Control[allControls.size()]);
+
+ PlatformUI.getWorkbench().getHelpSystem().setHelp(composite, ICHelpContextIds.SPELLING_CONFIGURATION_BLOCK);
+ return composite;
+ }
+
+ /**
+ * Creates the encoding field editor.
+ *
+ * @param composite the parent composite
+ * @param allControls list with all controls
+ */
+ private void createEncodingFieldEditor(Composite composite, List allControls) {
+ Label filler= new Label(composite, SWT.NONE);
+ GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ gd.horizontalSpan= 4;
+ filler.setLayoutData(gd);
+
+ Label label= new Label(composite, SWT.NONE);
+ label.setText(PreferencesMessages.SpellingPreferencePage_encoding_label);
+ label.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
+ allControls.add(label);
+
+ fEncodingEditorParent= new Composite(composite, SWT.NONE);
+ GridLayout layout= new GridLayout(2, false);
+ layout.marginWidth= 0;
+ layout.marginHeight= 0;
+ fEncodingEditorParent.setLayout(layout);
+ gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
+ gd.horizontalSpan= 3;
+ fEncodingEditorParent.setLayoutData(gd);
+
+ fEncodingEditor= new EncodingFieldEditor(PREF_SPELLING_USER_DICTIONARY_ENCODING.getName(), "", null, fEncodingEditorParent); //$NON-NLS-1$
+
+ PreferenceStore store= new PreferenceStore();
+ String defaultEncoding= ResourcesPlugin.getEncoding();
+ store.setDefault(PREF_SPELLING_USER_DICTIONARY_ENCODING.getName(), defaultEncoding);
+ String encoding= getValue(PREF_SPELLING_USER_DICTIONARY_ENCODING);
+ if (encoding != null && encoding.length() > 0)
+ store.setValue(PREF_SPELLING_USER_DICTIONARY_ENCODING.getName(), encoding);
+
+ fEncodingEditor.setPreferenceStore(store);
+
+ // Redirect status messages from the field editor to the status change listener
+ DialogPage fakePage= new DialogPage() {
+ public void createControl(Composite c) {
+ }
+ public void setErrorMessage(String newMessage) {
+ StatusInfo status= new StatusInfo();
+ if (newMessage != null)
+ status.setError(newMessage);
+ fEncodingFieldEditorStatus= status;
+ fContext.statusChanged(StatusUtil.getMostSevere(new IStatus[] { fThresholdStatus, fFileStatus, fEncodingFieldEditorStatus }));
+ }
+ };
+ fEncodingEditor.setPage(fakePage);
+
+ fEncodingEditor.load();
+
+ if (encoding == null || encoding.equals(defaultEncoding) || encoding.length() == 0)
+ fEncodingEditor.loadDefault();
+ }
+
+ private static Key[] getAllKeys() {
+ if (SUPPORT_CONTENT_ASSIST_PROPOSALS)
+ return new Key[] { PREF_SPELLING_USER_DICTIONARY, PREF_SPELLING_USER_DICTIONARY_ENCODING, PREF_SPELLING_IGNORE_DIGITS, PREF_SPELLING_IGNORE_MIXED, PREF_SPELLING_IGNORE_SENTENCE, PREF_SPELLING_IGNORE_UPPER, PREF_SPELLING_IGNORE_URLS, PREF_SPELLING_IGNORE_NON_LETTERS, PREF_SPELLING_IGNORE_SINGLE_LETTERS, PREF_SPELLING_LOCALE, PREF_SPELLING_PROPOSAL_THRESHOLD, PREF_SPELLING_PROBLEMS_THRESHOLD, PREF_SPELLING_ENABLE_CONTENTASSIST, PREF_SPELLING_IGNORE_STRING_LITERALS };
+ else
+ return new Key[] { PREF_SPELLING_USER_DICTIONARY, PREF_SPELLING_USER_DICTIONARY_ENCODING, PREF_SPELLING_IGNORE_DIGITS, PREF_SPELLING_IGNORE_MIXED, PREF_SPELLING_IGNORE_SENTENCE, PREF_SPELLING_IGNORE_UPPER, PREF_SPELLING_IGNORE_URLS, PREF_SPELLING_IGNORE_NON_LETTERS, PREF_SPELLING_IGNORE_SINGLE_LETTERS, PREF_SPELLING_LOCALE, PREF_SPELLING_PROPOSAL_THRESHOLD, PREF_SPELLING_PROBLEMS_THRESHOLD, PREF_SPELLING_IGNORE_STRING_LITERALS };
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#getFullBuildDialogStrings(boolean)
+ */
+ protected final String[] getFullBuildDialogStrings(final boolean workspace) {
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#performOk()
+ */
+ public boolean performOk() {
+ fEncodingEditor.store();
+ if (fEncodingEditor.presentsDefaultValue())
+ setValue(PREF_SPELLING_USER_DICTIONARY_ENCODING, ""); //$NON-NLS-1$
+ else
+ setValue(PREF_SPELLING_USER_DICTIONARY_ENCODING, fEncodingEditor.getPreferenceStore().getString(PREF_SPELLING_USER_DICTIONARY_ENCODING.getName()));
+ return super.performOk();
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#performApply()
+ */
+ public boolean performApply() {
+ fEncodingEditor.store();
+ if (fEncodingEditor.presentsDefaultValue())
+ setValue(PREF_SPELLING_USER_DICTIONARY_ENCODING, ""); //$NON-NLS-1$
+ else
+ setValue(PREF_SPELLING_USER_DICTIONARY_ENCODING, fEncodingEditor.getPreferenceStore().getString(PREF_SPELLING_USER_DICTIONARY_ENCODING.getName()));
+ return super.performApply();
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#performDefaults()
+ */
+ public void performDefaults() {
+ super.performDefaults();
+
+ setValue(PREF_SPELLING_USER_DICTIONARY_ENCODING, ""); //$NON-NLS-1$
+
+ fEncodingEditor.getPreferenceStore().setValue(fEncodingEditor.getPreferenceName(), ResourcesPlugin.getEncoding());
+ fEncodingEditor.load();
+
+ fEncodingEditor.loadDefault();
+ }
+
+ protected void handleVariablesButtonSelected() {
+ StringVariableSelectionDialog dialog= new StringVariableSelectionDialog(fDictionaryPath.getShell());
+ if (dialog.open() == Window.OK)
+ fDictionaryPath.setText(fDictionaryPath.getText() + dialog.getVariableExpression());
+ }
+
+ /**
+ * Handles selections of the browse button.
+ */
+ protected void handleBrowseButtonSelected() {
+ final FileDialog dialog= new FileDialog(fDictionaryPath.getShell(), SWT.OPEN);
+ dialog.setText(PreferencesMessages.SpellingPreferencePage_filedialog_title);
+ dialog.setFilterPath(fDictionaryPath.getText());
+
+ final String path= dialog.open();
+ if (path != null)
+ fDictionaryPath.setText(path);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#validateSettings(java.lang.String,java.lang.String)
+ */
+ protected void validateSettings(final Key key, final String oldValue, final String newValue) {
+ if (key == null || PREF_SPELLING_PROPOSAL_THRESHOLD.equals(key))
+ fThresholdStatus= validatePositiveNumber(getValue(PREF_SPELLING_PROPOSAL_THRESHOLD));
+ else
+ fThresholdStatus= new StatusInfo();
+
+ if (key == null || PREF_SPELLING_PROBLEMS_THRESHOLD.equals(key)) {
+ IStatus status= validatePositiveNumber(getValue(PREF_SPELLING_PROBLEMS_THRESHOLD));
+ fThresholdStatus= StatusUtil.getMostSevere(new IStatus[] {fThresholdStatus, status});
+ }
+
+ if (key == null || PREF_SPELLING_USER_DICTIONARY.equals(key))
+ fFileStatus= validateAbsoluteFilePath(getValue(PREF_SPELLING_USER_DICTIONARY));
+
+ fContext.statusChanged(StatusUtil.getMostSevere(new IStatus[] { fThresholdStatus, fFileStatus, fEncodingFieldEditorStatus }));
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#updateCheckBox(org.eclipse.swt.widgets.Button)
+ */
+ protected void updateCheckBox(Button curr) {
+ super.updateCheckBox(curr);
+ Event event= new Event();
+ event.type= SWT.Selection;
+ event.display= curr.getDisplay();
+ event.widget= curr;
+ curr.notifyListeners(SWT.Selection, event);
+ }
+
+ /**
+ * Sets the enabled state.
+ *
+ * @param enabled the new state
+ */
+ protected void setEnabled(boolean enabled) {
+ fEncodingEditor.setEnabled(enabled, fEncodingEditorParent);
+
+ if (enabled && fEnabledControls != null) {
+ for (int i= fEnabledControls.length - 1; i >= 0; i--)
+ fEnabledControls[i].setEnabled(true);
+ fEnabledControls= null;
+ }
+ if (!enabled && fEnabledControls == null) {
+ List enabledControls= new ArrayList();
+ for (int i= fAllControls.length - 1; i >= 0; i--) {
+ Control control= fAllControls[i];
+ if (control.isEnabled()) {
+ enabledControls.add(control);
+ control.setEnabled(false);
+ }
+ }
+ fEnabledControls= (Control[]) enabledControls.toArray(new Control[enabledControls.size()]);
+ }
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/SpellingPreferenceBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/SpellingPreferenceBlock.java
new file mode 100644
index 00000000000..d49a7a45c9f
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/SpellingPreferenceBlock.java
@@ -0,0 +1,121 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.preferences;
+
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+
+import org.eclipse.ui.texteditor.spelling.IPreferenceStatusMonitor;
+import org.eclipse.ui.texteditor.spelling.ISpellingPreferenceBlock;
+
+import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener;
+
+/**
+ * Spelling preference block
+ */
+public class SpellingPreferenceBlock implements ISpellingPreferenceBlock {
+
+ private class NullStatusChangeListener implements IStatusChangeListener {
+ /*
+ * @see org.eclipse.cdt.internal.ui.wizards.IStatusChangeListener#statusChanged(org.eclipse.core.runtime.IStatus)
+ */
+ public void statusChanged(IStatus status) {
+ }
+ }
+
+ private class StatusChangeListenerAdapter implements IStatusChangeListener {
+ private IPreferenceStatusMonitor fMonitor;
+
+ private IStatus fStatus;
+
+ public StatusChangeListenerAdapter(IPreferenceStatusMonitor monitor) {
+ super();
+ fMonitor= monitor;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.wizards.IStatusChangeListener#statusChanged(org.eclipse.core.runtime.IStatus)
+ */
+ public void statusChanged(IStatus status) {
+ fStatus= status;
+ fMonitor.statusChanged(status);
+ }
+
+ public IStatus getStatus() {
+ return fStatus;
+ }
+ }
+
+ private SpellingConfigurationBlock fBlock= new SpellingConfigurationBlock(new NullStatusChangeListener(), null, null);
+
+ private SpellingPreferenceBlock.StatusChangeListenerAdapter fStatusMonitor;
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingPreferenceBlock#createControl(org.eclipse.swt.widgets.Composite)
+ */
+ public Control createControl(Composite parent) {
+ return fBlock.createContents(parent);
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingPreferenceBlock#initialize(org.eclipse.ui.texteditor.spelling.IPreferenceStatusMonitor)
+ */
+ public void initialize(IPreferenceStatusMonitor statusMonitor) {
+ fStatusMonitor= new StatusChangeListenerAdapter(statusMonitor);
+ fBlock.fContext= fStatusMonitor;
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingPreferenceBlock#canPerformOk()
+ */
+ public boolean canPerformOk() {
+ return fStatusMonitor == null || fStatusMonitor.getStatus() == null || !fStatusMonitor.getStatus().matches(IStatus.ERROR);
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingPreferenceBlock#performOk()
+ */
+ public void performOk() {
+ fBlock.performOk();
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingPreferenceBlock#performDefaults()
+ */
+ public void performDefaults() {
+ fBlock.performDefaults();
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingPreferenceBlock#performRevert()
+ */
+ public void performRevert() {
+ fBlock.performRevert();
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingPreferenceBlock#dispose()
+ */
+ public void dispose() {
+ fBlock.dispose();
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingPreferenceBlock#setEnabled(boolean)
+ */
+ public void setEnabled(boolean enabled) {
+ fBlock.setEnabled(enabled);
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompositeReconcilingStrategy.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompositeReconcilingStrategy.java
new file mode 100644
index 00000000000..abeb6828808
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CCompositeReconcilingStrategy.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * 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.internal.ui.text;
+
+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.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import org.eclipse.cdt.internal.ui.text.spelling.CSpellingReconcileStrategy;
+
+/**
+ * Reconciling strategy for C/C++ code. This is a composite strategy containing
+ * the regular C/C++ model reconciler and the comment spelling strategy.
+ */
+public class CCompositeReconcilingStrategy extends CompositeReconcilingStrategy {
+ private ITextEditor fEditor;
+ private CReconcilingStrategy fCStrategy;
+
+ /**
+ * Creates a new C/C++ reconciling strategy.
+ *
+ * @param viewer the source viewer
+ * @param editor the editor of the strategy's reconciler
+ * @param documentPartitioning the document partitioning this strategy uses for configuration
+ */
+ public CCompositeReconcilingStrategy(ISourceViewer viewer, ITextEditor editor, String documentPartitioning) {
+ fEditor= editor;
+ fCStrategy= new CReconcilingStrategy(editor);
+ setReconcilingStrategies(new IReconcilingStrategy[] {
+ fCStrategy,
+ new CSpellingReconcileStrategy(viewer, editor)
+ });
+ }
+
+ /**
+ * Returns the problem requestor for the editor's input element.
+ *
+ * @return the problem requestor for the editor's input element
+ */
+ private IProblemRequestorExtension getProblemRequestorExtension() {
+ IDocumentProvider p = fEditor.getDocumentProvider();
+ if (p == null) {
+ return null;
+ }
+ IAnnotationModel m = p.getAnnotationModel(fEditor.getEditorInput());
+ if (m instanceof IProblemRequestorExtension)
+ return (IProblemRequestorExtension) m;
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.CompositeReconcilingStrategy#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion, org.eclipse.jface.text.IRegion)
+ */
+ public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
+ IProblemRequestorExtension e= getProblemRequestorExtension();
+ if (e != null) {
+ try {
+ e.beginReportingSequence();
+ super.reconcile(dirtyRegion, subRegion);
+ } finally {
+ e.endReportingSequence();
+ }
+ } else {
+ super.reconcile(dirtyRegion, subRegion);
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.CompositeReconcilingStrategy#reconcile(org.eclipse.jface.text.IRegion)
+ */
+ public void reconcile(IRegion partition) {
+ IProblemRequestorExtension e= getProblemRequestorExtension();
+ if (e != null) {
+ try {
+ e.beginReportingSequence();
+ super.reconcile(partition);
+ } finally {
+ e.endReportingSequence();
+ }
+ } else {
+ super.reconcile(partition);
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.CompositeReconcilingStrategy#initialReconcile()
+ */
+ public void initialReconcile() {
+ IProblemRequestorExtension e = getProblemRequestorExtension();
+ if (e != null) {
+ try {
+ e.beginReportingSequence();
+ super.initialReconcile();
+ } finally {
+ e.endReportingSequence();
+ }
+ } else {
+ super.initialReconcile();
+ }
+ }
+
+ /**
+ * Called before reconciling is started.
+ */
+ public void aboutToBeReconciled() {
+ fCStrategy.aboutToBeReconciled();
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconciler.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconciler.java
index 04ba294e1bd..20fd4e7c058 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconciler.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CReconciler.java
@@ -294,7 +294,7 @@ public class CReconciler extends MonoReconciler {
* @param editor the text editor
* @param strategy the C reconciling strategy
*/
- public CReconciler(ITextEditor editor, CReconcilingStrategy strategy) {
+ public CReconciler(ITextEditor editor, CCompositeReconcilingStrategy strategy) {
super(strategy, false);
fTextEditor= editor;
}
@@ -373,7 +373,7 @@ public class CReconciler extends MonoReconciler {
* @see org.eclipse.jface.text.reconciler.AbstractReconciler#aboutToBeReconciled()
*/
protected void aboutToBeReconciled() {
- CReconcilingStrategy strategy= (CReconcilingStrategy)getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
+ CCompositeReconcilingStrategy strategy= (CCompositeReconcilingStrategy)getReconcilingStrategy(IDocument.DEFAULT_CONTENT_TYPE);
strategy.aboutToBeReconciled();
}
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 2ecaabfb23c..f62719ea8c9 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
@@ -9,7 +9,7 @@
* IBM Corporation - initial API and implementation
* QNX Software System
* Anton Leherbauer (Wind River Systems)
- * Sergey Prigogin, Google
+ * Sergey Prigogin (Google)
* Markus Schorn (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.internal.ui.text;
@@ -43,6 +43,7 @@ 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.quickassist.IQuickAssistAssistant;
import org.eclipse.jface.text.reconciler.IReconciler;
import org.eclipse.jface.text.reconciler.MonoReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
@@ -85,6 +86,7 @@ import org.eclipse.cdt.internal.ui.text.c.hover.CEditorTextHoverDescriptor;
import org.eclipse.cdt.internal.ui.text.c.hover.CEditorTextHoverProxy;
import org.eclipse.cdt.internal.ui.text.contentassist.CContentAssistProcessor;
import org.eclipse.cdt.internal.ui.text.contentassist.ContentAssistPreference;
+import org.eclipse.cdt.internal.ui.text.correction.CCorrectionAssistant;
import org.eclipse.cdt.internal.ui.typehierarchy.THInformationControl;
import org.eclipse.cdt.internal.ui.typehierarchy.THInformationProvider;
@@ -93,6 +95,7 @@ import org.eclipse.cdt.internal.ui.typehierarchy.THInformationProvider;
* Configuration for an SourceViewer
which shows C/C++ code.
*/
public class CSourceViewerConfiguration extends TextSourceViewerConfiguration {
+ private CTextTools fTextTools;
private ITextEditor fTextEditor;
/**
@@ -371,6 +374,16 @@ public class CSourceViewerConfiguration extends TextSourceViewerConfiguration {
return settings;
}
+
+ /*
+ * @see org.eclipse.jface.text.source.SourceViewerConfiguration#getQuickAssistAssistant(org.eclipse.jface.text.source.ISourceViewer)
+ * @since 5.0
+ */
+ public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) {
+ if (getEditor() != null)
+ return new CCorrectionAssistant(getEditor());
+ return null;
+ }
/*
* @see org.eclipse.ui.editors.text.TextSourceViewerConfiguration#getReconciler(org.eclipse.jface.text.source.ISourceViewer)
@@ -379,7 +392,9 @@ public class CSourceViewerConfiguration extends TextSourceViewerConfiguration {
if (fTextEditor != null) {
//Delay changed and non-incremental reconciler used due to
//PR 130089
- MonoReconciler reconciler= new CReconciler(fTextEditor, new CReconcilingStrategy(fTextEditor));
+ CCompositeReconcilingStrategy strategy=
+ new CCompositeReconcilingStrategy(sourceViewer, fTextEditor, getConfiguredDocumentPartitioning(sourceViewer));
+ MonoReconciler reconciler= new CReconciler(fTextEditor, strategy);
reconciler.setIsIncrementalReconciler(false);
reconciler.setProgressMonitor(new NullProgressMonitor());
reconciler.setDelay(500);
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CompositeReconcilingStrategy.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CompositeReconcilingStrategy.java
new file mode 100644
index 00000000000..92507baf4f1
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CompositeReconcilingStrategy.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * 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.text;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+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;
+
+/**
+ * A reconciling strategy consisting of a sequence of internal reconciling strategies.
+ * By default, all requests are passed on to the contained strategies.
+ */
+public class CompositeReconcilingStrategy implements IReconcilingStrategy, IReconcilingStrategyExtension {
+
+ /** The list of internal reconciling strategies. */
+ private IReconcilingStrategy[] fStrategies;
+
+ /**
+ * Creates a new, empty composite reconciling strategy.
+ */
+ public CompositeReconcilingStrategy() {
+ }
+
+ /**
+ * Sets the reconciling strategies for this composite strategy.
+ *
+ * @param strategies the strategies to be set or null
+ */
+ public void setReconcilingStrategies(IReconcilingStrategy[] strategies) {
+ fStrategies= strategies;
+ }
+
+ /**
+ * Returns the previously set strategies or null
.
+ *
+ * @return the contained strategies or null
+ */
+ public IReconcilingStrategy[] getReconcilingStrategies() {
+ return fStrategies;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#setDocument(org.eclipse.jface.text.IDocument)
+ */
+ public void setDocument(IDocument document) {
+ if (fStrategies == null)
+ return;
+
+ for (int i= 0; i < fStrategies.length; i++)
+ fStrategies[i].setDocument(document);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.reconciler.DirtyRegion, org.eclipse.jface.text.IRegion)
+ */
+ public void reconcile(DirtyRegion dirtyRegion, IRegion subRegion) {
+ if (fStrategies == null)
+ return;
+
+ for (int i= 0; i < fStrategies.length; i++)
+ fStrategies[i].reconcile(dirtyRegion, subRegion);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.IRegion)
+ */
+ public void reconcile(IRegion partition) {
+ if (fStrategies == null)
+ return;
+
+ for (int i= 0; i < fStrategies.length; i++)
+ fStrategies[i].reconcile(partition);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#setProgressMonitor(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void setProgressMonitor(IProgressMonitor monitor) {
+ if (fStrategies == null)
+ return;
+
+ for (int i=0; i < fStrategies.length; i++) {
+ if (fStrategies[i] instanceof IReconcilingStrategyExtension) {
+ IReconcilingStrategyExtension extension= (IReconcilingStrategyExtension) fStrategies[i];
+ extension.setProgressMonitor(monitor);
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.IReconcilingStrategyExtension#initialReconcile()
+ */
+ public void initialReconcile() {
+ if (fStrategies == null)
+ return;
+
+ for (int i = 0; i < fStrategies.length; i++) {
+ if (fStrategies[i] instanceof IReconcilingStrategyExtension) {
+ IReconcilingStrategyExtension extension= (IReconcilingStrategyExtension) fStrategies[i];
+ extension.initialReconcile();
+ }
+ }
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/IHtmlTagConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/IHtmlTagConstants.java
new file mode 100644
index 00000000000..83daaa06654
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/IHtmlTagConstants.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * 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.internal.ui.text;
+
+/**
+ * Html tag constants.
+ */
+public interface IHtmlTagConstants {
+ /** Html tag close prefix */
+ public static final String HTML_CLOSE_PREFIX= ""; //$NON-NLS-1$
+
+ /** Html entity characters */
+ public static final char[] HTML_ENTITY_CHARACTERS= new char[] { '<', '>', ' ', '&', '^', '~', '\"' };
+
+ /**
+ * Html entity start.
+ */
+ public static final char HTML_ENTITY_START= '&';
+ /**
+ * Html entity end.
+ */
+ public static final char HTML_ENTITY_END= ';';
+
+ /** Html entity codes */
+ public static final String[] HTML_ENTITY_CODES= new String[] { "<", ">", " ", "&", "ˆ", "˜", """ }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+
+ /** Html general tags */
+ public static final String[] HTML_GENERAL_TAGS= new String[] { "a", "b", "blockquote", "br", "code", "dd", "dl", "dt", "em", "hr", "h1", "h2", "h3", "h4", "h5", "h6", "i", "li", "nl", "ol", "p", "pre", "q", "strong", "tbody", "td", "th", "tr", "tt", "ul" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ //$NON-NLS-13$ //$NON-NLS-14$ //$NON-NLS-15$ //$NON-NLS-16$ //$NON-NLS-17$ //$NON-NLS-18$ //$NON-NLS-19$ //$NON-NLS-20$ //$NON-NLS-21$ //$NON-NLS-22$ //$NON-NLS-23$ //$NON-NLS-24$ //$NON-NLS-25$ //$NON-NLS-26$ //$NON-NLS-27$ //$NON-NLS-28$ //$NON-NLS-29$ //$NON-NLS-30$
+
+ /** Html tag postfix */
+ public static final char HTML_TAG_POSTFIX= '>';
+
+ /** Html tag prefix */
+ public static final char HTML_TAG_PREFIX= '<';
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CCorrectionAssistant.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CCorrectionAssistant.java
new file mode 100644
index 00000000000..8ac35af436c
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CCorrectionAssistant.java
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.widgets.Shell;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.preference.PreferenceConverter;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DefaultInformationControl;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IInformationControl;
+import org.eclipse.jface.text.IInformationControlCreator;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
+import org.eclipse.jface.text.quickassist.QuickAssistAssistant;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISourceViewer;
+
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+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.ITranslationUnit;
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.PreferenceConstants;
+
+import org.eclipse.cdt.internal.core.model.ASTCache;
+
+import org.eclipse.cdt.internal.ui.editor.ASTProvider;
+import org.eclipse.cdt.internal.ui.text.CTextTools;
+import org.eclipse.cdt.internal.ui.text.HTMLTextPresenter;
+import org.eclipse.cdt.internal.ui.text.IColorManager;
+
+public class CCorrectionAssistant extends QuickAssistAssistant {
+ private ITextViewer fViewer;
+ private ITextEditor fEditor;
+ private Position fPosition;
+ private Annotation[] fCurrentAnnotations;
+
+ private QuickAssistLightBulbUpdater fLightBulbUpdater;
+
+ /**
+ * Constructor for CCorrectionAssistant.
+ */
+ public CCorrectionAssistant(ITextEditor editor) {
+ super();
+ Assert.isNotNull(editor);
+ fEditor= editor;
+
+ CCorrectionProcessor processor= new CCorrectionProcessor(this);
+
+ setQuickAssistProcessor(processor);
+
+ setInformationControlCreator(getInformationControlCreator());
+
+ CTextTools textTools= CUIPlugin.getDefault().getTextTools();
+ IColorManager manager= textTools.getColorManager();
+
+ IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore();
+
+ Color c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_FOREGROUND, manager);
+ setProposalSelectorForeground(c);
+
+ c= getColor(store, PreferenceConstants.CODEASSIST_PROPOSALS_BACKGROUND, manager);
+ setProposalSelectorBackground(c);
+ }
+
+ public IEditorPart getEditor() {
+ return fEditor;
+ }
+
+
+ private IInformationControlCreator getInformationControlCreator() {
+ return new IInformationControlCreator() {
+ public IInformationControl createInformationControl(Shell parent) {
+ return new DefaultInformationControl(parent, new HTMLTextPresenter());
+ }
+ };
+ }
+
+ private static Color getColor(IPreferenceStore store, String key, IColorManager manager) {
+ RGB rgb= PreferenceConverter.getColor(store, key);
+ return manager.getColor(rgb);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.IContentAssistant#install(org.eclipse.jface.text.ITextViewer)
+ */
+ public void install(ISourceViewer sourceViewer) {
+ super.install(sourceViewer);
+ fViewer= sourceViewer;
+
+ fLightBulbUpdater= new QuickAssistLightBulbUpdater(fEditor, sourceViewer);
+ fLightBulbUpdater.install();
+ }
+
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ContentAssistant#uninstall()
+ */
+ public void uninstall() {
+ if (fLightBulbUpdater != null) {
+ fLightBulbUpdater.uninstall();
+ fLightBulbUpdater= null;
+ }
+ super.uninstall();
+ }
+
+ /**
+ * Show completions at caret position. If current
+ * position does not contain quick fixes look for
+ * next quick fix on same line by moving from left
+ * to right and restarting at end of line if the
+ * beginning of the line is reached.
+ *
+ * @see IQuickAssistAssistant#showPossibleQuickAssists()
+ */
+ public String showPossibleQuickAssists() {
+ fPosition= null;
+ fCurrentAnnotations= null;
+
+ if (fViewer == null || fViewer.getDocument() == null)
+ // Let superclass deal with this
+ return super.showPossibleQuickAssists();
+
+
+ ArrayList resultingAnnotations= new ArrayList(20);
+ try {
+ Point selectedRange= fViewer.getSelectedRange();
+ int currOffset= selectedRange.x;
+ int currLength= selectedRange.y;
+ boolean goToClosest= (currLength == 0);
+
+ int newOffset= collectQuickFixableAnnotations(fEditor, currOffset, goToClosest, resultingAnnotations);
+ if (newOffset != currOffset) {
+ storePosition(currOffset, currLength);
+ fViewer.setSelectedRange(newOffset, 0);
+ fViewer.revealRange(newOffset, 0);
+ }
+ } catch (BadLocationException e) {
+ CUIPlugin.getDefault().log(e);
+ }
+ fCurrentAnnotations= (Annotation[]) resultingAnnotations.toArray(new Annotation[resultingAnnotations.size()]);
+
+ return super.showPossibleQuickAssists();
+ }
+
+
+ private static IRegion getRegionOfInterest(ITextEditor editor, int invocationLocation) throws BadLocationException {
+ IDocumentProvider documentProvider= editor.getDocumentProvider();
+ if (documentProvider == null) {
+ return null;
+ }
+ IDocument document= documentProvider.getDocument(editor.getEditorInput());
+ if (document == null) {
+ return null;
+ }
+ return document.getLineInformationOfOffset(invocationLocation);
+ }
+
+ public static int collectQuickFixableAnnotations(ITextEditor editor, int invocationLocation, boolean goToClosest, ArrayList resultingAnnotations) throws BadLocationException {
+ IAnnotationModel model= CUIPlugin.getDefault().getDocumentProvider().getAnnotationModel(editor.getEditorInput());
+ if (model == null) {
+ return invocationLocation;
+ }
+
+ ensureUpdatedAnnotations(editor);
+
+ Iterator iter= model.getAnnotationIterator();
+ if (goToClosest) {
+ IRegion lineInfo= getRegionOfInterest(editor, invocationLocation);
+ if (lineInfo == null) {
+ return invocationLocation;
+ }
+ int rangeStart= lineInfo.getOffset();
+ int rangeEnd= rangeStart + lineInfo.getLength();
+
+ ArrayList allAnnotations= new ArrayList();
+ ArrayList allPositions= new ArrayList();
+ int bestOffset= Integer.MAX_VALUE;
+ while (iter.hasNext()) {
+ Annotation annot= (Annotation) iter.next();
+ if (CCorrectionProcessor.isQuickFixableType(annot)) {
+ Position pos= model.getPosition(annot);
+ if (pos != null && isInside(pos.offset, rangeStart, rangeEnd)) { // inside our range?
+ allAnnotations.add(annot);
+ allPositions.add(pos);
+ bestOffset= processAnnotation(annot, pos, invocationLocation, bestOffset);
+ }
+ }
+ }
+ if (bestOffset == Integer.MAX_VALUE) {
+ return invocationLocation;
+ }
+ for (int i= 0; i < allPositions.size(); i++) {
+ Position pos= (Position) allPositions.get(i);
+ if (isInside(bestOffset, pos.offset, pos.offset + pos.length)) {
+ resultingAnnotations.add(allAnnotations.get(i));
+ }
+ }
+ return bestOffset;
+ } else {
+ while (iter.hasNext()) {
+ Annotation annot= (Annotation) iter.next();
+ if (CCorrectionProcessor.isQuickFixableType(annot)) {
+ Position pos= model.getPosition(annot);
+ if (pos != null && isInside(invocationLocation, pos.offset, pos.offset + pos.length)) {
+ resultingAnnotations.add(annot);
+ }
+ }
+ }
+ return invocationLocation;
+ }
+ }
+
+ private static void ensureUpdatedAnnotations(ITextEditor editor) {
+ Object inputElement= editor.getEditorInput().getAdapter(ICElement.class);
+ if (inputElement instanceof ITranslationUnit) {
+ final ASTProvider astProvider= CUIPlugin.getDefault().getASTProvider();
+ astProvider.runOnAST((ITranslationUnit) inputElement, ASTProvider.WAIT_ACTIVE_ONLY, null, new ASTCache.ASTRunnable() {
+ public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) {
+ return Status.OK_STATUS;
+ }
+ });
+ }
+ }
+
+ private static int processAnnotation(Annotation annot, Position pos, int invocationLocation, int bestOffset) {
+ int posBegin= pos.offset;
+ int posEnd= posBegin + pos.length;
+ if (isInside(invocationLocation, posBegin, posEnd)) { // covers invocation location?
+ return invocationLocation;
+ } else if (bestOffset != invocationLocation) {
+ int newClosestPosition= computeBestOffset(posBegin, invocationLocation, bestOffset);
+ if (newClosestPosition != -1) {
+ if (newClosestPosition != bestOffset) { // new best
+ if (CCorrectionProcessor.hasCorrections(annot)) { // only jump to it if there are proposals
+ return newClosestPosition;
+ }
+ }
+ }
+ }
+ return bestOffset;
+ }
+
+
+ private static boolean isInside(int offset, int start, int end) {
+ return offset == start || offset == end || (offset > start && offset < end); // make sure to handle 0-length ranges
+ }
+
+ /**
+ * Computes and returns the invocation offset given a new
+ * position, the initial offset and the best invocation offset
+ * found so far.
+ *
+ * The closest offset to the left of the initial offset is the
+ * best. If there is no offset on the left, the closest on the
+ * right is the best.
+ * @return -1 is returned if the given offset is not closer or the new best offset
+ */
+ private static int computeBestOffset(int newOffset, int invocationLocation, int bestOffset) {
+ if (newOffset <= invocationLocation) {
+ if (bestOffset > invocationLocation) {
+ return newOffset; // closest was on the right, prefer on the left
+ } else if (bestOffset <= newOffset) {
+ return newOffset; // we are closer or equal
+ }
+ return -1; // further away
+ }
+
+ if (newOffset <= bestOffset)
+ return newOffset; // we are closer or equal
+
+ return -1; // further away
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ContentAssistant#possibleCompletionsClosed()
+ */
+ protected void possibleCompletionsClosed() {
+ super.possibleCompletionsClosed();
+ restorePosition();
+ }
+
+ private void storePosition(int currOffset, int currLength) {
+ fPosition= new Position(currOffset, currLength);
+ }
+
+ private void restorePosition() {
+ if (fPosition != null && !fPosition.isDeleted() && fViewer.getDocument() != null) {
+ fViewer.setSelectedRange(fPosition.offset, fPosition.length);
+ fViewer.revealRange(fPosition.offset, fPosition.length);
+ }
+ fPosition= null;
+ }
+
+ /**
+ * Returns true if the last invoked completion was called with an updated offset.
+ */
+ public boolean isUpdatedOffset() {
+ return fPosition != null;
+ }
+
+ /**
+ * Returns the annotations at the current offset
+ */
+ public Annotation[] getAnnotationsAtOffset() {
+ return fCurrentAnnotations;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CCorrectionProcessor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CCorrectionProcessor.java
new file mode 100644
index 00000000000..4b17a1d4be9
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CCorrectionProcessor.java
@@ -0,0 +1,489 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.contentassist.ContentAssistEvent;
+import org.eclipse.jface.text.contentassist.ICompletionListener;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.jface.text.quickassist.IQuickAssistProcessor;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IMarkerHelpRegistry;
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.ide.IDE;
+import org.eclipse.ui.texteditor.SimpleMarkerAnnotation;
+
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.text.ICCompletionProposal;
+import org.eclipse.cdt.ui.text.IProblemLocation;
+import org.eclipse.cdt.ui.text.IQuickFixProcessor;
+
+import org.eclipse.cdt.internal.ui.editor.ICAnnotation;
+import org.eclipse.cdt.internal.ui.text.contentassist.CCompletionProposal;
+import org.eclipse.cdt.internal.ui.text.contentassist.CCompletionProposalComparator;
+
+public class CCorrectionProcessor implements IQuickAssistProcessor {
+ private static final String QUICKFIX_PROCESSOR_CONTRIBUTION_ID= "quickFixProcessors"; //$NON-NLS-1$
+ private static final String QUICKASSIST_PROCESSOR_CONTRIBUTION_ID= "quickAssistProcessors"; //$NON-NLS-1$
+
+ private static ContributedProcessorDescriptor[] fgContributedAssistProcessors= null;
+ private static ContributedProcessorDescriptor[] fgContributedCorrectionProcessors= null;
+
+ private static ContributedProcessorDescriptor[] getProcessorDescriptors(String contributionId, boolean testMarkerTypes) {
+ IConfigurationElement[] elements= Platform.getExtensionRegistry().getConfigurationElementsFor(CUIPlugin.PLUGIN_ID, contributionId);
+ ArrayList res= new ArrayList(elements.length);
+
+ for (int i= 0; i < elements.length; i++) {
+ ContributedProcessorDescriptor desc= new ContributedProcessorDescriptor(elements[i], testMarkerTypes);
+ IStatus status= desc.checkSyntax();
+ if (status.isOK()) {
+ res.add(desc);
+ } else {
+ CUIPlugin.getDefault().log(status);
+ }
+ }
+ return (ContributedProcessorDescriptor[]) res.toArray(new ContributedProcessorDescriptor[res.size()]);
+ }
+
+ private static ContributedProcessorDescriptor[] getCorrectionProcessors() {
+ if (fgContributedCorrectionProcessors == null) {
+ fgContributedCorrectionProcessors= getProcessorDescriptors(QUICKFIX_PROCESSOR_CONTRIBUTION_ID, true);
+ }
+ return fgContributedCorrectionProcessors;
+ }
+
+ private static ContributedProcessorDescriptor[] getAssistProcessors() {
+ if (fgContributedAssistProcessors == null) {
+ fgContributedAssistProcessors= getProcessorDescriptors(QUICKASSIST_PROCESSOR_CONTRIBUTION_ID, false);
+ }
+ return fgContributedAssistProcessors;
+ }
+
+ public static boolean hasCorrections(ITranslationUnit cu, int problemId, String markerType) {
+ ContributedProcessorDescriptor[] processors= getCorrectionProcessors();
+ SafeHasCorrections collector= new SafeHasCorrections(cu, problemId);
+ for (int i= 0; i < processors.length; i++) {
+ if (processors[i].canHandleMarkerType(markerType)) {
+ collector.process(processors[i]);
+ if (collector.hasCorrections()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean isQuickFixableType(Annotation annotation) {
+ return (annotation instanceof ICAnnotation || annotation instanceof SimpleMarkerAnnotation) && !annotation.isMarkedDeleted();
+ }
+
+ public static boolean hasCorrections(Annotation annotation) {
+ if (annotation instanceof ICAnnotation) {
+ ICAnnotation cAnnotation= (ICAnnotation) annotation;
+ int problemId= cAnnotation.getId();
+ if (problemId != -1) {
+ ITranslationUnit cu= cAnnotation.getTranslationUnit();
+ if (cu != null) {
+ return hasCorrections(cu, problemId, cAnnotation.getMarkerType());
+ }
+ }
+ }
+ if (annotation instanceof SimpleMarkerAnnotation) {
+ return hasCorrections(((SimpleMarkerAnnotation) annotation).getMarker());
+ }
+ return false;
+ }
+
+ private static boolean hasCorrections(IMarker marker) {
+ if (marker == null || !marker.exists())
+ return false;
+
+ IMarkerHelpRegistry registry= IDE.getMarkerHelpRegistry();
+ return registry != null && registry.hasResolutions(marker);
+ }
+
+ public static boolean hasAssists(CorrectionContext context) {
+ ContributedProcessorDescriptor[] processors= getAssistProcessors();
+ SafeHasAssist collector= new SafeHasAssist(context);
+
+ for (int i= 0; i < processors.length; i++) {
+ collector.process(processors[i]);
+ if (collector.hasAssists()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private CCorrectionAssistant fAssistant;
+ private String fErrorMessage;
+
+ /*
+ * Constructor for CCorrectionProcessor.
+ */
+ public CCorrectionProcessor(CCorrectionAssistant assistant) {
+ fAssistant= assistant;
+ fAssistant.addCompletionListener(new ICompletionListener() {
+
+ public void assistSessionEnded(ContentAssistEvent event) {
+ fAssistant.setStatusLineVisible(false);
+ }
+
+ public void assistSessionStarted(ContentAssistEvent event) {
+ fAssistant.setStatusLineVisible(true);
+ }
+
+ public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
+ if (proposal instanceof IStatusLineProposal) {
+ IStatusLineProposal statusLineProposal= (IStatusLineProposal)proposal;
+ String message= statusLineProposal.getStatusMessage();
+ if (message != null) {
+ fAssistant.setStatusMessage(message);
+ } else {
+ fAssistant.setStatusMessage(""); //$NON-NLS-1$
+ }
+ } else {
+ fAssistant.setStatusMessage(""); //$NON-NLS-1$
+ }
+ }
+ });
+ }
+
+ /*
+ * @see IContentAssistProcessor#computeCompletionProposals(ITextViewer, int)
+ */
+ public ICompletionProposal[] computeQuickAssistProposals(IQuickAssistInvocationContext quickAssistContext) {
+ ISourceViewer viewer= quickAssistContext.getSourceViewer();
+ int documentOffset= quickAssistContext.getOffset();
+
+ IEditorPart part= fAssistant.getEditor();
+
+ ITranslationUnit cu= CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(part.getEditorInput());
+ IAnnotationModel model= CUIPlugin.getDefault().getDocumentProvider().getAnnotationModel(part.getEditorInput());
+
+ int length= viewer != null ? viewer.getSelectedRange().y : 0;
+ CorrectionContext context= new CorrectionContext(cu, viewer, documentOffset, length);
+
+ Annotation[] annotations= fAssistant.getAnnotationsAtOffset();
+
+ fErrorMessage= null;
+
+ ICompletionProposal[] res= null;
+ if (model != null && annotations != null) {
+ ArrayList proposals= new ArrayList(10);
+ IStatus status= collectProposals(context, model, annotations, true, !fAssistant.isUpdatedOffset(), proposals);
+ res= (ICompletionProposal[]) proposals.toArray(new ICompletionProposal[proposals.size()]);
+ if (!status.isOK()) {
+ fErrorMessage= status.getMessage();
+ CUIPlugin.getDefault().log(status);
+ }
+ }
+
+ if (res == null || res.length == 0) {
+ return new ICompletionProposal[]
+ { new CCompletionProposal("", 0, 0, null, CorrectionMessages.NoCorrectionProposal_description, 0) }; //$NON-NLS-1$
+ }
+ if (res.length > 1) {
+ Arrays.sort(res, new CCompletionProposalComparator());
+ }
+ return res;
+ }
+
+ public static IStatus collectProposals(CorrectionContext context, IAnnotationModel model, Annotation[] annotations, boolean addQuickFixes, boolean addQuickAssists, Collection proposals) {
+ ArrayList problems= new ArrayList();
+
+ // collect problem locations and corrections from marker annotations
+ for (int i= 0; i < annotations.length; i++) {
+ Annotation curr= annotations[i];
+ if (curr instanceof ICAnnotation) {
+ ProblemLocation problemLocation= getProblemLocation((ICAnnotation) curr, model);
+ if (problemLocation != null) {
+ problems.add(problemLocation);
+ }
+ } else if (addQuickFixes && curr instanceof SimpleMarkerAnnotation) {
+ // don't collect if annotation is already a C annotation
+ collectMarkerProposals((SimpleMarkerAnnotation) curr, proposals);
+ }
+ }
+ MultiStatus resStatus= null;
+
+ IProblemLocation[] problemLocations= (IProblemLocation[]) problems.toArray(new IProblemLocation[problems.size()]);
+ if (addQuickFixes) {
+ IStatus status= collectCorrections(context, problemLocations, proposals);
+ if (!status.isOK()) {
+ resStatus= new MultiStatus(CUIPlugin.PLUGIN_ID, IStatus.ERROR, CorrectionMessages.CCorrectionProcessor_error_quickfix_message, null);
+ resStatus.add(status);
+ }
+ }
+ if (addQuickAssists) {
+ IStatus status= collectAssists(context, problemLocations, proposals);
+ if (!status.isOK()) {
+ if (resStatus == null) {
+ resStatus= new MultiStatus(CUIPlugin.PLUGIN_ID, IStatus.ERROR, CorrectionMessages.CCorrectionProcessor_error_quickassist_message, null);
+ }
+ resStatus.add(status);
+ }
+ }
+ if (resStatus != null) {
+ return resStatus;
+ }
+ return Status.OK_STATUS;
+ }
+
+ private static ProblemLocation getProblemLocation(ICAnnotation cAnnotation, IAnnotationModel model) {
+ int problemId= cAnnotation.getId();
+ if (problemId != -1) {
+ Position pos= model.getPosition((Annotation) cAnnotation);
+ if (pos != null) {
+ return new ProblemLocation(pos.getOffset(), pos.getLength(), cAnnotation); // java problems all handled by the quick assist processors
+ }
+ }
+ return null;
+ }
+
+ private static void collectMarkerProposals(SimpleMarkerAnnotation annotation, Collection proposals) {
+ IMarker marker= annotation.getMarker();
+ IMarkerResolution[] res= IDE.getMarkerHelpRegistry().getResolutions(marker);
+ if (res.length > 0) {
+ for (int i= 0; i < res.length; i++) {
+ proposals.add(new MarkerResolutionProposal(res[i], marker));
+ }
+ }
+ }
+
+ private static abstract class SafeCorrectionProcessorAccess implements ISafeRunnable {
+ private MultiStatus fMulti= null;
+ private ContributedProcessorDescriptor fDescriptor;
+
+ public void process(ContributedProcessorDescriptor[] desc) {
+ for (int i= 0; i < desc.length; i++) {
+ fDescriptor= desc[i];
+ SafeRunner.run(this);
+ }
+ }
+
+ public void process(ContributedProcessorDescriptor desc) {
+ fDescriptor= desc;
+ SafeRunner.run(this);
+ }
+
+ public void run() throws Exception {
+ safeRun(fDescriptor);
+ }
+
+ protected abstract void safeRun(ContributedProcessorDescriptor processor) throws Exception;
+
+ public void handleException(Throwable exception) {
+ if (fMulti == null) {
+ fMulti= new MultiStatus(CUIPlugin.PLUGIN_ID, IStatus.OK, CorrectionMessages.CCorrectionProcessor_error_status, null);
+ }
+ fMulti.merge(new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, IStatus.ERROR, CorrectionMessages.CCorrectionProcessor_error_status, exception));
+ }
+
+ public IStatus getStatus() {
+ if (fMulti == null) {
+ return Status.OK_STATUS;
+ }
+ return fMulti;
+ }
+ }
+
+ private static class SafeCorrectionCollector extends SafeCorrectionProcessorAccess {
+ private final CorrectionContext fContext;
+ private final Collection fProposals;
+ private IProblemLocation[] fLocations;
+
+ public SafeCorrectionCollector(CorrectionContext context, Collection proposals) {
+ fContext= context;
+ fProposals= proposals;
+ }
+
+ public void setProblemLocations(IProblemLocation[] locations) {
+ fLocations= locations;
+ }
+
+ public void safeRun(ContributedProcessorDescriptor desc) throws Exception {
+ IQuickFixProcessor curr= (IQuickFixProcessor) desc.getProcessor(fContext.getTranslationUnit());
+ if (curr != null) {
+ ICCompletionProposal[] res= curr.getCorrections(fContext, fLocations);
+ if (res != null) {
+ for (int k= 0; k < res.length; k++) {
+ fProposals.add(res[k]);
+ }
+ }
+ }
+ }
+ }
+
+ private static class SafeAssistCollector extends SafeCorrectionProcessorAccess {
+ private final CorrectionContext fContext;
+ private final IProblemLocation[] fLocations;
+ private final Collection fProposals;
+
+ public SafeAssistCollector(CorrectionContext context, IProblemLocation[] locations, Collection proposals) {
+ fContext= context;
+ fLocations= locations;
+ fProposals= proposals;
+ }
+
+ public void safeRun(ContributedProcessorDescriptor desc) throws Exception {
+ IQuickFixProcessor curr= (IQuickFixProcessor) desc.getProcessor(fContext.getTranslationUnit());
+ if (curr != null) {
+ ICCompletionProposal[] res= curr.getCorrections(fContext, fLocations);
+ if (res != null) {
+ for (int k= 0; k < res.length; k++) {
+ fProposals.add(res[k]);
+ }
+ }
+ }
+ }
+ }
+
+ private static class SafeHasAssist extends SafeCorrectionProcessorAccess {
+ private final CorrectionContext fContext;
+ private boolean fHasAssists;
+
+ public SafeHasAssist(CorrectionContext context) {
+ fContext= context;
+ fHasAssists= false;
+ }
+
+ public boolean hasAssists() {
+ return fHasAssists;
+ }
+
+ public void safeRun(ContributedProcessorDescriptor desc) throws Exception {
+ IQuickAssistProcessor processor= (IQuickAssistProcessor) desc.getProcessor(fContext.getTranslationUnit());
+ if (processor != null && processor.canAssist(fContext)) {
+ fHasAssists= true;
+ }
+ }
+ }
+
+ private static class SafeHasCorrections extends SafeCorrectionProcessorAccess {
+ private final ITranslationUnit fCu;
+ private final int fProblemId;
+ private boolean fHasCorrections;
+
+ public SafeHasCorrections(ITranslationUnit cu, int problemId) {
+ fCu= cu;
+ fProblemId= problemId;
+ fHasCorrections= false;
+ }
+
+ public boolean hasCorrections() {
+ return fHasCorrections;
+ }
+
+ public void safeRun(ContributedProcessorDescriptor desc) throws Exception {
+ IQuickFixProcessor processor= (IQuickFixProcessor) desc.getProcessor(fCu);
+ if (processor != null && processor.hasCorrections(fCu, fProblemId)) {
+ fHasCorrections= true;
+ }
+ }
+ }
+
+ public static IStatus collectCorrections(CorrectionContext context, IProblemLocation[] locations, Collection proposals) {
+ ContributedProcessorDescriptor[] processors= getCorrectionProcessors();
+ SafeCorrectionCollector collector= new SafeCorrectionCollector(context, proposals);
+ for (int i= 0; i < processors.length; i++) {
+ ContributedProcessorDescriptor curr= processors[i];
+ IProblemLocation[] handled= getHandledProblems(locations, curr);
+ if (handled != null) {
+ collector.setProblemLocations(handled);
+ collector.process(curr);
+ }
+ }
+ return collector.getStatus();
+ }
+
+ private static IProblemLocation[] getHandledProblems(IProblemLocation[] locations, ContributedProcessorDescriptor processor) {
+ // implementation tries to avoid creating a new array
+ boolean allHandled= true;
+ ArrayList res= null;
+ for (int i= 0; i < locations.length; i++) {
+ IProblemLocation curr= locations[i];
+ if (processor.canHandleMarkerType(curr.getMarkerType())) {
+ if (!allHandled) { // first handled problem
+ if (res == null) {
+ res= new ArrayList(locations.length - i);
+ }
+ res.add(curr);
+ }
+ } else if (allHandled) {
+ if (i > 0) { // first non handled problem
+ res= new ArrayList(locations.length - i);
+ for (int k= 0; k < i; k++) {
+ res.add(locations[k]);
+ }
+ }
+ allHandled= false;
+ }
+ }
+ if (allHandled) {
+ return locations;
+ }
+ if (res == null) {
+ return null;
+ }
+ return (IProblemLocation[]) res.toArray(new IProblemLocation[res.size()]);
+ }
+
+ public static IStatus collectAssists(CorrectionContext context, IProblemLocation[] locations, Collection proposals) {
+ ContributedProcessorDescriptor[] processors= getAssistProcessors();
+ SafeAssistCollector collector= new SafeAssistCollector(context, locations, proposals);
+ collector.process(processors);
+
+ return collector.getStatus();
+ }
+
+ /*
+ * @see IContentAssistProcessor#getErrorMessage()
+ */
+ public String getErrorMessage() {
+ return fErrorMessage;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.quickassist.IQuickAssistProcessor#canFix(org.eclipse.jface.text.source.Annotation)
+ */
+ public boolean canFix(Annotation annotation) {
+ return hasCorrections(annotation);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.quickassist.IQuickAssistProcessor#canAssist(org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext)
+ */
+ public boolean canAssist(IQuickAssistInvocationContext invocationContext) {
+ if (invocationContext instanceof CorrectionContext)
+ return hasAssists((CorrectionContext) invocationContext);
+ return false;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/ContributedProcessorDescriptor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/ContributedProcessorDescriptor.java
new file mode 100644
index 00000000000..9d677090430
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/ContributedProcessorDescriptor.java
@@ -0,0 +1,123 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IStatus;
+
+import org.eclipse.core.expressions.EvaluationContext;
+import org.eclipse.core.expressions.EvaluationResult;
+import org.eclipse.core.expressions.Expression;
+import org.eclipse.core.expressions.ExpressionConverter;
+import org.eclipse.core.expressions.ExpressionTagNames;
+
+import org.eclipse.cdt.core.model.ICModelMarker;
+import org.eclipse.cdt.core.model.ICProject;
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.ui.CUIPlugin;
+
+import org.eclipse.cdt.internal.ui.dialogs.StatusInfo;
+
+public final class ContributedProcessorDescriptor {
+ private final IConfigurationElement fConfigurationElement;
+ private Object fProcessorInstance;
+ private Boolean fStatus;
+ private boolean fLastResult;
+ private final Set fHandledMarkerTypes;
+
+ private static final String ID= "id"; //$NON-NLS-1$
+ private static final String CLASS= "class"; //$NON-NLS-1$
+
+ private static final String HANDLED_MARKER_TYPES= "handledMarkerTypes"; //$NON-NLS-1$
+ private static final String MARKER_TYPE= "markerType"; //$NON-NLS-1$
+
+ public ContributedProcessorDescriptor(IConfigurationElement element, boolean testMarkerTypes) {
+ fConfigurationElement= element;
+ fProcessorInstance= null;
+ fStatus= null; // undefined
+ if (fConfigurationElement.getChildren(ExpressionTagNames.ENABLEMENT).length == 0) {
+ fStatus= Boolean.TRUE;
+ }
+ fHandledMarkerTypes= testMarkerTypes ? getHandledMarkerTypes(element) : null;
+ }
+
+ private Set getHandledMarkerTypes(IConfigurationElement element) {
+ HashSet map= new HashSet(7);
+ IConfigurationElement[] children= element.getChildren(HANDLED_MARKER_TYPES);
+ for (int i= 0; i < children.length; i++) {
+ IConfigurationElement[] types= children[i].getChildren(MARKER_TYPE);
+ for (int k= 0; k < types.length; k++) {
+ String attribute= types[k].getAttribute(ID);
+ if (attribute != null) {
+ map.add(attribute);
+ }
+ }
+ }
+ if (map.isEmpty()) {
+ map.add(ICModelMarker.TASK_MARKER);
+ }
+ return map;
+ }
+
+ public IStatus checkSyntax() {
+ IConfigurationElement[] children= fConfigurationElement.getChildren(ExpressionTagNames.ENABLEMENT);
+ if (children.length > 1) {
+ String id= fConfigurationElement.getAttribute(ID);
+ return new StatusInfo(IStatus.ERROR, "Only one < enablement > element allowed. Disabling " + id); //$NON-NLS-1$
+ }
+ return new StatusInfo(IStatus.OK, "Syntactically correct quick assist/fix processor"); //$NON-NLS-1$
+ }
+
+ private boolean matches(ITranslationUnit cunit) {
+ if (fStatus != null) {
+ return fStatus.booleanValue();
+ }
+
+ IConfigurationElement[] children= fConfigurationElement.getChildren(ExpressionTagNames.ENABLEMENT);
+ if (children.length == 1) {
+ try {
+ ExpressionConverter parser= ExpressionConverter.getDefault();
+ Expression expression= parser.perform(children[0]);
+ EvaluationContext evalContext= new EvaluationContext(null, cunit);
+ evalContext.addVariable("compilationUnit", cunit); //$NON-NLS-1$
+ ICProject cProject= cunit.getCProject();
+ String[] natures= cProject.getProject().getDescription().getNatureIds();
+ evalContext.addVariable("projectNatures", Arrays.asList(natures)); //$NON-NLS-1$
+ fLastResult= !(expression.evaluate(evalContext) != EvaluationResult.TRUE);
+ return fLastResult;
+ } catch (CoreException e) {
+ CUIPlugin.getDefault().log(e);
+ }
+ }
+ fStatus= Boolean.FALSE;
+ return false;
+ }
+
+ public Object getProcessor(ITranslationUnit cunit) throws CoreException {
+ if (matches(cunit)) {
+ if (fProcessorInstance == null) {
+ fProcessorInstance= fConfigurationElement.createExecutableExtension(CLASS);
+ }
+ return fProcessorInstance;
+ }
+ return null;
+ }
+
+ public boolean canHandleMarkerType(String markerType) {
+ return fHandledMarkerTypes == null || fHandledMarkerTypes.contains(markerType);
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionCommandHandler.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionCommandHandler.java
new file mode 100644
index 00000000000..11d3529ae0e
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionCommandHandler.java
@@ -0,0 +1,142 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.eclipse.core.commands.AbstractHandler;
+import org.eclipse.core.commands.ExecutionEvent;
+import org.eclipse.core.commands.ExecutionException;
+
+import org.eclipse.jface.bindings.TriggerSequence;
+import org.eclipse.jface.viewers.ISelection;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension;
+import org.eclipse.jface.text.contentassist.ICompletionProposalExtension2;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationModel;
+
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.keys.IBindingService;
+
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.ui.CUIPlugin;
+
+import org.eclipse.cdt.internal.ui.editor.CEditor;
+
+/**
+ * Handler to be used to run a quick fix or assist by keyboard shortcut
+ */
+public class CorrectionCommandHandler extends AbstractHandler {
+ private final CEditor fEditor;
+ private final String fId;
+ private final boolean fIsAssist;
+
+ public CorrectionCommandHandler(CEditor editor, String id, boolean isAssist) {
+ fEditor= editor;
+ fId= id;
+ fIsAssist= isAssist;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent)
+ */
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ ISelection selection= fEditor.getSelectionProvider().getSelection();
+ ITranslationUnit cu= CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(fEditor.getEditorInput());
+ IAnnotationModel model= CUIPlugin.getDefault().getDocumentProvider().getAnnotationModel(fEditor.getEditorInput());
+ if (selection instanceof ITextSelection && cu != null && model != null) {
+ ICompletionProposal proposal= findCorrection(fId, fIsAssist, (ITextSelection) selection, cu, model);
+ if (proposal != null) {
+ invokeProposal(proposal, ((ITextSelection) selection).getOffset());
+ }
+ }
+ return null;
+ }
+
+ private ICompletionProposal findCorrection(String id, boolean isAssist, ITextSelection selection, ITranslationUnit cu, IAnnotationModel model) {
+ CorrectionContext context= new CorrectionContext(cu, selection.getOffset(), selection.getLength());
+ Collection proposals= new ArrayList(10);
+ if (isAssist) {
+ CCorrectionProcessor.collectAssists(context, new ProblemLocation[0], proposals);
+ } else {
+ try {
+ boolean goToClosest= selection.getLength() == 0;
+ Annotation[] annotations= getAnnotations(selection.getOffset(), goToClosest);
+ CCorrectionProcessor.collectProposals(context, model, annotations, true, false, proposals);
+ } catch (BadLocationException e) {
+ return null;
+ }
+ }
+ for (Iterator iter= proposals.iterator(); iter.hasNext();) {
+ Object curr= iter.next();
+ if (curr instanceof ICommandAccess) {
+ if (id.equals(((ICommandAccess) curr).getCommandId())) {
+ return (ICompletionProposal) curr;
+ }
+ }
+ }
+ return null;
+ }
+
+ private Annotation[] getAnnotations(int offset, boolean goToClosest) throws BadLocationException {
+ ArrayList resultingAnnotations= new ArrayList();
+ CCorrectionAssistant.collectQuickFixableAnnotations(fEditor, offset, goToClosest, resultingAnnotations);
+ return (Annotation[]) resultingAnnotations.toArray(new Annotation[resultingAnnotations.size()]);
+ }
+
+ private IDocument getDocument() {
+ return CUIPlugin.getDefault().getDocumentProvider().getDocument(fEditor.getEditorInput());
+ }
+
+ private void invokeProposal(ICompletionProposal proposal, int offset) {
+ if (proposal instanceof ICompletionProposalExtension2) {
+ ITextViewer viewer= fEditor.getViewer();
+ if (viewer != null) {
+ ((ICompletionProposalExtension2) proposal).apply(viewer, (char) 0, 0, offset);
+ return;
+ }
+ } else if (proposal instanceof ICompletionProposalExtension) {
+ IDocument document= getDocument();
+ if (document != null) {
+ ((ICompletionProposalExtension) proposal).apply(document, (char) 0, offset);
+ return;
+ }
+ }
+ IDocument document= getDocument();
+ if (document != null) {
+ proposal.apply(document);
+ }
+ }
+
+ public static String getShortCutString(String proposalId) {
+ if (proposalId != null) {
+ IBindingService bindingService= (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class);
+ if (bindingService != null) {
+ TriggerSequence[] activeBindingsFor= bindingService.getActiveBindingsFor(proposalId);
+ if (activeBindingsFor.length > 0) {
+ return activeBindingsFor[0].format();
+ }
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionCommandInstaller.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionCommandInstaller.java
new file mode 100644
index 00000000000..436458bc783
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionCommandInstaller.java
@@ -0,0 +1,80 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.ui.IWorkbench;
+import org.eclipse.ui.LegacyHandlerSubmissionExpression;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.commands.ICommandService;
+import org.eclipse.ui.handlers.IHandlerActivation;
+import org.eclipse.ui.handlers.IHandlerService;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+
+import org.eclipse.cdt.internal.ui.editor.CEditor;
+
+public class CorrectionCommandInstaller {
+ /**
+ * All correction commands must start with the following prefix.
+ */
+ public static final String COMMAND_PREFIX= "org.eclipse.jdt.ui.correction."; //$NON-NLS-1$
+
+ /**
+ * Commands for quick assist must have the following suffix.
+ */
+ public static final String ASSIST_SUFFIX= ".assist"; //$NON-NLS-1$
+
+ private List fCorrectionHandlerActivations;
+
+ public CorrectionCommandInstaller() {
+ fCorrectionHandlerActivations= null;
+ }
+
+ public void registerCommands(CEditor editor) {
+ IWorkbench workbench= PlatformUI.getWorkbench();
+ ICommandService commandService= (ICommandService) workbench.getAdapter(ICommandService.class);
+ IHandlerService handlerService= (IHandlerService) workbench.getAdapter(IHandlerService.class);
+ if (commandService == null || handlerService == null) {
+ return;
+ }
+
+ if (fCorrectionHandlerActivations != null) {
+ CUIPlugin.getDefault().logErrorMessage("correction handler activations not released"); //$NON-NLS-1$
+ }
+ fCorrectionHandlerActivations= new ArrayList();
+
+ Collection definedCommandIds= commandService.getDefinedCommandIds();
+ for (Iterator iter= definedCommandIds.iterator(); iter.hasNext();) {
+ String id= (String) iter.next();
+ if (id.startsWith(COMMAND_PREFIX)) {
+ boolean isAssist= id.endsWith(ASSIST_SUFFIX);
+ CorrectionCommandHandler handler= new CorrectionCommandHandler(editor, id, isAssist);
+ IHandlerActivation activation= handlerService.activateHandler(id, handler, new LegacyHandlerSubmissionExpression(null, null, editor.getSite()));
+ fCorrectionHandlerActivations.add(activation);
+ }
+ }
+ }
+
+ public void deregisterCommands() {
+ IHandlerService handlerService= (IHandlerService) PlatformUI.getWorkbench().getAdapter(IHandlerService.class);
+ if (handlerService != null && fCorrectionHandlerActivations != null) {
+ handlerService.deactivateHandlers(fCorrectionHandlerActivations);
+ fCorrectionHandlerActivations= null;
+ }
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionContext.java
new file mode 100644
index 00000000000..7a93b2bcb96
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionContext.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.jface.text.source.TextInvocationContext;
+
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.ui.text.IInvocationContext;
+
+public class CorrectionContext extends TextInvocationContext implements IInvocationContext {
+ private ITranslationUnit fTranslationUnit;
+
+ /*
+ * Constructor for CorrectionContext.
+ */
+ public CorrectionContext(ITranslationUnit tu, ISourceViewer sourceViewer, int offset, int length) {
+ super(sourceViewer, offset, length);
+ fTranslationUnit= tu;
+ }
+
+ /*
+ * Constructor for CorrectionContext.
+ */
+ public CorrectionContext(ITranslationUnit tu, int offset, int length) {
+ this(tu, null, offset, length);
+ }
+
+ /**
+ * Returns the translation unit.
+ * @return an ITranslationUnit
+ */
+ public ITranslationUnit getTranslationUnit() {
+ return fTranslationUnit;
+ }
+
+ /**
+ * Returns the length.
+ * @return int
+ */
+ public int getSelectionLength() {
+ return Math.max(getLength(), 0);
+ }
+
+ /**
+ * Returns the offset.
+ * @return int
+ */
+ public int getSelectionOffset() {
+ return getOffset();
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionMessages.java
new file mode 100644
index 00000000000..1649393d4d0
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionMessages.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import org.eclipse.osgi.util.NLS;
+
+/**
+ * Helper class to get NLSed messages.
+ */
+public final class CorrectionMessages extends NLS {
+ private static final String BUNDLE_NAME= CorrectionMessages.class.getName();
+
+ static {
+ NLS.initializeMessages(BUNDLE_NAME, CorrectionMessages.class);
+ }
+
+ private CorrectionMessages() {
+ // Do not instantiate
+ }
+
+ public static String CCorrectionProcessor_error_quickassist_message;
+ public static String CCorrectionProcessor_error_quickfix_message;
+ public static String CCorrectionProcessor_error_status;
+ public static String MarkerResolutionProposal_additionaldesc;
+ public static String NoCorrectionProposal_description;
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionMessages.properties
new file mode 100644
index 00000000000..2fbf63639cf
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/CorrectionMessages.properties
@@ -0,0 +1,18 @@
+###############################################################################
+# Copyright (c) 2000, 2007 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
+# Sergey Prigogin (Google)
+###############################################################################
+
+CCorrectionProcessor_error_quickassist_message=An error occurred while computing quick assists. Check log for details.
+CCorrectionProcessor_error_quickfix_message=An error occurred while computing quick fixes. Check log for details.
+CCorrectionProcessor_error_status=Exception while processing quick fixes or quick assists
+
+MarkerResolutionProposal_additionaldesc=Problem description: {0}
+NoCorrectionProposal_description=No suggestions available
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/ICommandAccess.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/ICommandAccess.java
new file mode 100644
index 00000000000..0f445784a9a
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/ICommandAccess.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.correction;
+
+/**
+ * Correction proposals implement this interface to by invokable by a command.
+ * (e.g. keyboard shortcut)
+ */
+public interface ICommandAccess {
+
+ /**
+ * Returns the id of the command that should invoke this correction proposal
+ * @return the id of the command. This id must start with {@link CorrectionCommandInstaller#COMMAND_PREFIX}
+ * to be recognixes as correction command.
+ */
+ String getCommandId();
+
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/IHtmlTagConstants.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/IHtmlTagConstants.java
new file mode 100644
index 00000000000..d21b15fedac
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/IHtmlTagConstants.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.correction;
+
+/**
+ * Html tag constants.
+ */
+public interface IHtmlTagConstants {
+
+ /** Html tag close prefix */
+ public static final String HTML_CLOSE_PREFIX= ""; //$NON-NLS-1$
+
+ /** Html entity characters */
+ public static final char[] HTML_ENTITY_CHARACTERS= new char[] { '<', '>', ' ', '&', '^', '~', '\"' };
+
+ /**
+ * Html entity start.
+ */
+ public static final char HTML_ENTITY_START= '&';
+ /**
+ * Html entity end.
+ */
+ public static final char HTML_ENTITY_END= ';';
+
+ /** Html entity codes */
+ public static final String[] HTML_ENTITY_CODES= new String[] { "<", ">", " ", "&", "ˆ", "˜", """ }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+
+ /** Html general tags */
+ public static final String[] HTML_GENERAL_TAGS= new String[] { "a", "b", "blockquote", "br", "code", "dd", "dl", "dt", "em", "hr", "h1", "h2", "h3", "h4", "h5", "h6", "i", "li", "nl", "ol", "p", "pre", "q", "strong", "tbody", "td", "th", "tr", "tt", "ul" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$ //$NON-NLS-13$ //$NON-NLS-14$ //$NON-NLS-15$ //$NON-NLS-16$ //$NON-NLS-17$ //$NON-NLS-18$ //$NON-NLS-19$ //$NON-NLS-20$ //$NON-NLS-21$ //$NON-NLS-22$ //$NON-NLS-23$ //$NON-NLS-24$ //$NON-NLS-25$ //$NON-NLS-26$ //$NON-NLS-27$ //$NON-NLS-28$ //$NON-NLS-29$ //$NON-NLS-30$
+
+ /** Html tag postfix */
+ public static final char HTML_TAG_POSTFIX= '>';
+
+ /** Html tag prefix */
+ public static final char HTML_TAG_PREFIX= '<';
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/IStatusLineProposal.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/IStatusLineProposal.java
new file mode 100644
index 00000000000..aef22356746
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/IStatusLineProposal.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.correction;
+
+/**
+ * A proposal which is able to show a message
+ * on the status line of the content assistant
+ * in which this proposal is shown.
+ *
+ * @see org.eclipse.jface.text.contentassist.IContentAssistantExtension2
+ */
+public interface IStatusLineProposal {
+ /**
+ * The message to show when this proposal is
+ * selected by the user in the content assistant.
+ *
+ * @return The message to show, or null for no message.
+ */
+ public String getStatusMessage();
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/MarkerResolutionProposal.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/MarkerResolutionProposal.java
new file mode 100644
index 00000000000..41f04a4f0ff
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/MarkerResolutionProposal.java
@@ -0,0 +1,120 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+
+import org.eclipse.ui.IMarkerResolution;
+import org.eclipse.ui.IMarkerResolution2;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.text.ICCompletionProposal;
+
+import org.eclipse.cdt.internal.ui.CPluginImages;
+
+public class MarkerResolutionProposal implements ICCompletionProposal {
+ private IMarkerResolution fResolution;
+ private IMarker fMarker;
+
+ /**
+ * Constructor for MarkerResolutionProposal.
+ */
+ public MarkerResolutionProposal(IMarkerResolution resolution, IMarker marker) {
+ fResolution= resolution;
+ fMarker= marker;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse.jface.text.IDocument)
+ */
+ public void apply(IDocument document) {
+ fResolution.run(fMarker);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ if (fResolution instanceof IMarkerResolution2) {
+ return ((IMarkerResolution2) fResolution).getDescription();
+ }
+ if (fResolution instanceof ICCompletionProposal) {
+ return ((ICCompletionProposal) fResolution).getAdditionalProposalInfo();
+ }
+ try {
+ String problemDesc= (String) fMarker.getAttribute(IMarker.MESSAGE);
+ return CorrectionMessages.bind(CorrectionMessages.MarkerResolutionProposal_additionaldesc,
+ problemDesc);
+ } catch (CoreException e) {
+ CUIPlugin.getDefault().log(e);
+ }
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
+ */
+ public IContextInformation getContextInformation() {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ return fResolution.getLabel();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ if (fResolution instanceof IMarkerResolution2) {
+ return ((IMarkerResolution2) fResolution).getImage();
+ }
+ if (fResolution instanceof ICCompletionProposal) {
+ return ((ICCompletionProposal) fResolution).getImage();
+ }
+ return CPluginImages.get(CPluginImages.IMG_CORRECTION_CHANGE);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.ui.text.java.ICCompletionProposal#getRelevance()
+ */
+ public int getRelevance() {
+ if (fResolution instanceof ICCompletionProposal) {
+ return ((ICCompletionProposal) fResolution).getRelevance();
+ }
+ return 10;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(org.eclipse.jface.text.IDocument)
+ */
+ public Point getSelection(IDocument document) {
+ if (fResolution instanceof ICCompletionProposal) {
+ return ((ICCompletionProposal) fResolution).getSelection(document);
+ }
+ return null;
+ }
+
+ public String getIdString() {
+ return getDisplayString();
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/ProblemLocation.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/ProblemLocation.java
new file mode 100644
index 00000000000..be7da40c887
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/ProblemLocation.java
@@ -0,0 +1,138 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import org.eclipse.cdt.core.model.ICModelMarker;
+import org.eclipse.cdt.core.parser.IPersistableProblem;
+import org.eclipse.cdt.core.parser.IProblem;
+import org.eclipse.cdt.ui.text.IProblemLocation;
+
+import org.eclipse.cdt.internal.ui.editor.CMarkerAnnotation;
+import org.eclipse.cdt.internal.ui.editor.ICAnnotation;
+
+public class ProblemLocation implements IProblemLocation {
+ private final int fId;
+ private final String[] fArguments;
+ private final int fOffset;
+ private final int fLength;
+ private final boolean fIsError;
+ private final String fMarkerType;
+
+ public ProblemLocation(int offset, int length, ICAnnotation annotation) {
+ fId= annotation.getId();
+ fArguments= annotation.getArguments();
+ fOffset= offset;
+ fLength= length;
+ fIsError= CMarkerAnnotation.ERROR_ANNOTATION_TYPE.equals(annotation.getType());
+
+ String markerType= annotation.getMarkerType();
+ fMarkerType= markerType != null ? markerType : ICModelMarker.C_MODEL_PROBLEM_MARKER;
+ }
+
+ public ProblemLocation(int offset, int length, int id, String[] arguments, boolean isError, String markerType) {
+ fId= id;
+ fArguments= arguments;
+ fOffset= offset;
+ fLength= length;
+ fIsError= isError;
+ fMarkerType= markerType;
+ }
+
+ public ProblemLocation(IProblem problem) {
+ fId= problem.getID();
+ fArguments= problem.getArguments();
+ fOffset= problem.getSourceStart();
+ fLength= problem.getSourceEnd() - fOffset + 1;
+ fIsError= problem.isError();
+ fMarkerType= problem instanceof IPersistableProblem ?
+ ((IPersistableProblem) problem).getMarkerType() : ICModelMarker.C_MODEL_PROBLEM_MARKER;
+ }
+
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getProblemId()
+ */
+ public int getProblemId() {
+ return fId;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getProblemArguments()
+ */
+ public String[] getProblemArguments() {
+ return fArguments;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getLength()
+ */
+ public int getLength() {
+ return fLength;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.internal.ui.text.correction.IProblemLocation#getOffset()
+ */
+ public int getOffset() {
+ return fOffset;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.ui.text.java.IProblemLocation#isError()
+ */
+ public boolean isError() {
+ return fIsError;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jdt.ui.text.java.IProblemLocation#getMarkerType()
+ */
+ public String getMarkerType() {
+ return fMarkerType;
+ }
+
+ public String toString() {
+ StringBuffer buf= new StringBuffer();
+ buf.append("Id: ").append(getErrorCode(fId)).append('\n'); //$NON-NLS-1$
+ buf.append('[').append(fOffset).append(", ").append(fLength).append(']').append('\n'); //$NON-NLS-1$
+ String[] arg= fArguments;
+ if (arg != null) {
+ for (int i= 0; i < arg.length; i++) {
+ buf.append(arg[i]);
+ buf.append('\n');
+ }
+ }
+ return buf.toString();
+ }
+
+ private String getErrorCode(int code) {
+ StringBuffer buf= new StringBuffer();
+ if ((code & IProblem.SCANNER_RELATED) != 0) {
+ buf.append("ScannerRelated + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.PREPROCESSOR_RELATED) != 0) {
+ buf.append("PreprocessorRelated + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.SEMANTICS_RELATED) != 0) {
+ buf.append("SemanticsRelated + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.INTERNAL_RELATED) != 0) {
+ buf.append("Internal + "); //$NON-NLS-1$
+ }
+ if ((code & IProblem.SYNTAX_RELATED) != 0) {
+ buf.append("Syntax + "); //$NON-NLS-1$
+ }
+ buf.append(code & IProblem.IGNORE_CATEGORIES_MASK);
+
+ return buf.toString();
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/QuickAssistLightBulbUpdater.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/QuickAssistLightBulbUpdater.java
new file mode 100644
index 00000000000..1fd9ed6d67a
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/correction/QuickAssistLightBulbUpdater.java
@@ -0,0 +1,287 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.correction;
+
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.Position;
+import org.eclipse.jface.text.source.Annotation;
+import org.eclipse.jface.text.source.IAnnotationAccessExtension;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.IAnnotationPresentation;
+import org.eclipse.jface.text.source.ImageUtilities;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.widgets.Canvas;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.editors.text.EditorsUI;
+import org.eclipse.ui.texteditor.AnnotationPreference;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+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.ITranslationUnit;
+import org.eclipse.cdt.core.model.IWorkingCopy;
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.PreferenceConstants;
+
+import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable;
+
+import org.eclipse.cdt.internal.ui.CPluginImages;
+import org.eclipse.cdt.internal.ui.editor.ASTProvider;
+import org.eclipse.cdt.internal.ui.util.EditorUtility;
+import org.eclipse.cdt.internal.ui.viewsupport.ISelectionListenerWithAST;
+import org.eclipse.cdt.internal.ui.viewsupport.SelectionListenerWithASTManager;
+
+/**
+ *
+ */
+public class QuickAssistLightBulbUpdater {
+
+ public static class AssistAnnotation extends Annotation implements IAnnotationPresentation {
+ //XXX: To be fully correct this should be a non-static fields in QuickAssistLightBulbUpdater
+ private static final int LAYER;
+
+ static {
+ Annotation annotation= new Annotation("org.eclipse.jdt.ui.warning", false, null); //$NON-NLS-1$
+ AnnotationPreference preference= EditorsUI.getAnnotationPreferenceLookup().getAnnotationPreference(annotation);
+ if (preference != null)
+ LAYER= preference.getPresentationLayer() - 1;
+ else
+ LAYER= IAnnotationAccessExtension.DEFAULT_LAYER;
+ }
+
+ private Image fImage;
+
+ public AssistAnnotation() {
+ }
+
+ /*
+ * @see org.eclipse.jface.text.source.IAnnotationPresentation#getLayer()
+ */
+ public int getLayer() {
+ return LAYER;
+ }
+
+ private Image getImage() {
+ if (fImage == null) {
+ fImage= CPluginImages.get(CPluginImages.IMG_OBJS_QUICK_ASSIST);
+ }
+ return fImage;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.text.source.Annotation#paint(org.eclipse.swt.graphics.GC, org.eclipse.swt.widgets.Canvas, org.eclipse.swt.graphics.Rectangle)
+ */
+ public void paint(GC gc, Canvas canvas, Rectangle r) {
+ ImageUtilities.drawImage(getImage(), gc, canvas, r, SWT.CENTER, SWT.TOP);
+ }
+ }
+
+ private final Annotation fAnnotation;
+ private boolean fIsAnnotationShown;
+ private ITextEditor fEditor;
+ private ITextViewer fViewer;
+
+ private ISelectionListenerWithAST fListener;
+ private IPropertyChangeListener fPropertyChangeListener;
+
+ public QuickAssistLightBulbUpdater(ITextEditor part, ITextViewer viewer) {
+ fEditor= part;
+ fViewer= viewer;
+ fAnnotation= new AssistAnnotation();
+ fIsAnnotationShown= false;
+ fPropertyChangeListener= null;
+ }
+
+ public boolean isSetInPreferences() {
+ return PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_QUICKASSIST_LIGHTBULB);
+ }
+
+ private void installSelectionListener() {
+ fListener= new ISelectionListenerWithAST() {
+ public void selectionChanged(IEditorPart part,
+ ITextSelection selection, IASTTranslationUnit astRoot) {
+ doSelectionChanged(selection.getOffset(), selection.getLength(), astRoot);
+ }
+ };
+ SelectionListenerWithASTManager.getDefault().addListener(fEditor, fListener);
+ }
+
+ private void uninstallSelectionListener() {
+ if (fListener != null) {
+ SelectionListenerWithASTManager.getDefault().removeListener(fEditor, fListener);
+ fListener= null;
+ }
+ IAnnotationModel model= getAnnotationModel();
+ if (model != null) {
+ removeLightBulb(model);
+ }
+ }
+
+ public void install() {
+ if (isSetInPreferences()) {
+ installSelectionListener();
+ }
+ if (fPropertyChangeListener == null) {
+ fPropertyChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ doPropertyChanged(event.getProperty());
+ }
+ };
+ PreferenceConstants.getPreferenceStore().addPropertyChangeListener(fPropertyChangeListener);
+ }
+ }
+
+ public void uninstall() {
+ uninstallSelectionListener();
+ if (fPropertyChangeListener != null) {
+ PreferenceConstants.getPreferenceStore().removePropertyChangeListener(fPropertyChangeListener);
+ fPropertyChangeListener= null;
+ }
+ }
+
+ protected void doPropertyChanged(String property) {
+ if (property.equals(PreferenceConstants.EDITOR_QUICKASSIST_LIGHTBULB)) {
+ if (isSetInPreferences()) {
+ IWorkingCopy workingCopy = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(fEditor.getEditorInput());
+ if (workingCopy != null) {
+ installSelectionListener();
+ final Point point= fViewer.getSelectedRange();
+ ASTProvider.getASTProvider().runOnAST(workingCopy, ASTProvider.WAIT_YES, null, new ASTRunnable() {
+ public IStatus runOnAST(ILanguage lang, IASTTranslationUnit astRoot) {
+ if (astRoot != null) {
+ doSelectionChanged(point.x, point.y, astRoot);
+ }
+ return Status.OK_STATUS;
+ }
+ });
+ }
+ } else {
+ uninstallSelectionListener();
+ }
+ }
+ }
+
+ private ITranslationUnit getTranslationUnit() {
+ ICElement elem= EditorUtility.getEditorInputCElement(fEditor);
+ if (elem instanceof ITranslationUnit) {
+ return (ITranslationUnit) elem;
+ }
+ return null;
+ }
+
+ private IAnnotationModel getAnnotationModel() {
+ return CUIPlugin.getDefault().getDocumentProvider().getAnnotationModel(fEditor.getEditorInput());
+ }
+
+ private IDocument getDocument() {
+ return CUIPlugin.getDefault().getDocumentProvider().getDocument(fEditor.getEditorInput());
+ }
+
+ private void doSelectionChanged(int offset, int length, IASTTranslationUnit astRoot) {
+ final IAnnotationModel model= getAnnotationModel();
+ final ITranslationUnit cu= getTranslationUnit();
+ if (model == null || cu == null) {
+ return;
+ }
+
+ final CorrectionContext context= new CorrectionContext(cu, offset, length);
+
+ boolean hasQuickFix= hasQuickFixLightBulb(model, context.getSelectionOffset());
+ if (hasQuickFix) {
+ removeLightBulb(model);
+ return; // there is already a quick fix light bulb at the new location
+ }
+
+ calculateLightBulb(model, context);
+ }
+
+ /*
+ * Needs to be called synchronized
+ */
+ private void calculateLightBulb(IAnnotationModel model, CorrectionContext context) {
+ boolean needsAnnotation= CCorrectionProcessor.hasAssists(context);
+ if (fIsAnnotationShown) {
+ model.removeAnnotation(fAnnotation);
+ }
+ if (needsAnnotation) {
+ model.addAnnotation(fAnnotation, new Position(context.getSelectionOffset(), context.getSelectionLength()));
+ }
+ fIsAnnotationShown= needsAnnotation;
+ }
+
+ private void removeLightBulb(IAnnotationModel model) {
+ synchronized (this) {
+ if (fIsAnnotationShown) {
+ model.removeAnnotation(fAnnotation);
+ fIsAnnotationShown= false;
+ }
+ }
+ }
+
+ /*
+ * Tests if there is already a quick fix light bulb on the current line
+ */
+ private boolean hasQuickFixLightBulb(IAnnotationModel model, int offset) {
+ try {
+ IDocument document= getDocument();
+ if (document == null) {
+ return false;
+ }
+
+ // we access a document and annotation model from within a job
+ // since these are only read accesses, we won't hurt anyone else if
+ // this goes boink
+
+ // may throw an IndexOutOfBoundsException upon concurrent document modification
+ int currLine= document.getLineOfOffset(offset);
+
+ // this iterator is not protected, it may throw ConcurrentModificationExceptions
+ Iterator iter= model.getAnnotationIterator();
+ while (iter.hasNext()) {
+ Annotation annot= (Annotation) iter.next();
+ if (CCorrectionProcessor.isQuickFixableType(annot)) {
+ // may throw an IndexOutOfBoundsException upon concurrent annotation model changes
+ Position pos= model.getPosition(annot);
+ if (pos != null) {
+ // may throw an IndexOutOfBoundsException upon concurrent document modification
+ int startLine= document.getLineOfOffset(pos.getOffset());
+ if (startLine == currLine && CCorrectionProcessor.hasCorrections(annot)) {
+ return true;
+ }
+ }
+ }
+ }
+ } catch (BadLocationException e) {
+ // ignore
+ } catch (IndexOutOfBoundsException e) {
+ // concurrent modification - too bad, ignore
+ } catch (ConcurrentModificationException e) {
+ // concurrent modification - too bad, ignore
+ }
+ return false;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/AddWordProposal.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/AddWordProposal.java
new file mode 100644
index 00000000000..25ec1f88794
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/AddWordProposal.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.dialogs.MessageDialogWithToggle;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.dialogs.PreferencesUtil;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.spelling.SpellingProblem;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.PreferenceConstants;
+import org.eclipse.cdt.ui.text.ICCompletionProposal;
+import org.eclipse.cdt.ui.text.IInvocationContext;
+
+import org.eclipse.cdt.internal.ui.CPluginImages;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker;
+
+/**
+ * Proposal to add the unknown word to the dictionaries.
+ */
+public class AddWordProposal implements ICCompletionProposal {
+ private static final String PREF_KEY_DO_NOT_ASK= "do_not_ask_to_install_user_dictionary"; //$NON-NLS-1$
+
+ /** The invocation context */
+ private final IInvocationContext fContext;
+
+ /** The word to add */
+ private final String fWord;
+
+ /**
+ * Creates a new add word proposal
+ *
+ * @param word
+ * The word to add
+ * @param context
+ * The invocation context
+ */
+ public AddWordProposal(final String word, final IInvocationContext context) {
+ fContext= context;
+ fWord= word;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse.jface.text.IDocument)
+ */
+ public final void apply(final IDocument document) {
+ final ISpellCheckEngine engine= SpellCheckEngine.getInstance();
+ final ISpellChecker checker= engine.getSpellChecker();
+
+ if (checker == null)
+ return;
+
+ IQuickAssistInvocationContext quickAssistContext= null;
+ if (fContext instanceof IQuickAssistInvocationContext)
+ quickAssistContext= (IQuickAssistInvocationContext)fContext;
+
+ if (!checker.acceptsWords()) {
+ final Shell shell;
+ if (quickAssistContext != null && quickAssistContext.getSourceViewer() != null)
+ shell= quickAssistContext.getSourceViewer().getTextWidget().getShell();
+ else
+ shell= CUIPlugin.getActiveWorkbenchShell();
+
+ if (!canAskToConfigure() || !askUserToConfigureUserDictionary(shell))
+ return;
+
+ String[] preferencePageIds= new String[] { "org.eclipse.ui.editors.preferencePages.Spelling" }; //$NON-NLS-1$
+ PreferencesUtil.createPreferenceDialogOn(shell, preferencePageIds[0], preferencePageIds, null).open();
+ }
+
+ if (checker.acceptsWords()) {
+ checker.addWord(fWord);
+ if (quickAssistContext != null && quickAssistContext.getSourceViewer() != null) {
+ SpellingProblem.removeAll(quickAssistContext.getSourceViewer(), fWord);
+ }
+ }
+ }
+
+ /**
+ * Asks the user whether he wants to configure
+ * a user dictionary.
+ *
+ * @param shell
+ * @return true
if the user wants to configure the user dictionary
+ */
+ private boolean askUserToConfigureUserDictionary(Shell shell) {
+ MessageDialogWithToggle toggleDialog= MessageDialogWithToggle.openYesNoQuestion(
+ shell,
+ Messages.Spelling_add_askToConfigure_title,
+ Messages.Spelling_add_askToConfigure_question,
+ Messages.Spelling_add_askToConfigure_ignoreMessage,
+ false,
+ null,
+ null);
+
+ PreferenceConstants.getPreferenceStore().setValue(PREF_KEY_DO_NOT_ASK, toggleDialog.getToggleState());
+
+ return toggleDialog.getReturnCode() == IDialogConstants.YES_ID;
+ }
+
+ /**
+ * Tells whether this proposal can ask to
+ * configure a user dictionary.
+ *
+ * @return true
if it can ask the user
+ */
+ static boolean canAskToConfigure() {
+ return !PreferenceConstants.getPreferenceStore().getBoolean(PREF_KEY_DO_NOT_ASK);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ return Messages.bind(Messages.Spelling_add_info, WordCorrectionProposal.getHtmlRepresentation(fWord));
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
+ */
+ public final IContextInformation getContextInformation() {
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ return Messages.bind(Messages.Spelling_add_label, fWord);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return CPluginImages.get(CPluginImages.IMG_CORRECTION_ADD);
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.java.IJavaCompletionProposal#getRelevance()
+ */
+ public int getRelevance() {
+ return Integer.MIN_VALUE;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(org.eclipse.jface.text.IDocument)
+ */
+ public final Point getSelection(final IDocument document) {
+ return new Point(fContext.getSelectionOffset(), fContext.getSelectionLength());
+ }
+
+ public String getIdString() {
+ return fWord;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingEngine.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingEngine.java
new file mode 100644
index 00000000000..4e65a831f69
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingEngine.java
@@ -0,0 +1,136 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.ITypedRegion;
+import org.eclipse.jface.text.TextUtilities;
+import org.eclipse.jface.text.TypedRegion;
+import org.eclipse.jface.text.rules.IToken;
+import org.eclipse.jface.text.rules.RuleBasedScanner;
+import org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.text.ICPartitions;
+
+import org.eclipse.cdt.internal.ui.text.CTextTools;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEventListener;
+
+/**
+ * C/C++ spelling engine
+ */
+public class CSpellingEngine extends SpellingEngine {
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.SpellingEngine#check(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IRegion[], org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker, org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ protected void check(IDocument document, IRegion[] regions, ISpellChecker checker, ISpellingProblemCollector collector, IProgressMonitor monitor) {
+ ISpellEventListener listener= new SpellEventListener(collector, document);
+ boolean isIgnoringStringLiterals= SpellingPreferences.isIgnoreStringLiterals();
+ try {
+ checker.addListener(listener);
+ try {
+ for (int i= 0; i < regions.length; i++) {
+ IRegion region= regions[i];
+ ITypedRegion[] partitions= TextUtilities.computePartitioning(document,
+ ICPartitions.C_PARTITIONING, region.getOffset(), region.getLength(), false);
+ for (int index= 0; index < partitions.length; index++) {
+ if (monitor != null && monitor.isCanceled())
+ return;
+
+ ITypedRegion partition= partitions[index];
+ final String type= partition.getType();
+
+ if (isIgnoringStringLiterals && type.equals(ICPartitions.C_STRING))
+ continue;
+
+ if (type.equals(ICPartitions.C_PREPROCESSOR)) {
+ CTextTools textTools = CUIPlugin.getDefault().getTextTools();
+ RuleBasedScanner scanner = textTools.getCppPreprocessorScanner();
+ scanner.setRange(document, partition.getOffset(), partition.getLength());
+ int firstTokenOffset = -1;
+ int firstTokenLength = -1;
+ while (true) {
+ IToken token = scanner.nextToken();
+ if (token.isEOF()) {
+ break;
+ }
+ if (token.isOther()) {
+ int offset = scanner.getTokenOffset();
+ int length = scanner.getTokenLength();
+ if (firstTokenOffset < 0) {
+ firstTokenOffset = offset;
+ firstTokenLength = length;
+ }
+ String subregionType = null;
+ char c = document.getChar(offset);
+ if (c == '"') {
+ if (!isIgnoringStringLiterals &&
+ !isIncludeDirective(document, firstTokenOffset, firstTokenLength)) {
+ subregionType = ICPartitions.C_STRING;
+ }
+ } else if (c == '/' && length >= 2) {
+ c = document.getChar(offset + 1);
+ if (c == '/') {
+ subregionType = ICPartitions.C_SINGLE_LINE_COMMENT;
+ } else if (c == '*') {
+ subregionType = ICPartitions.C_MULTI_LINE_COMMENT;
+ }
+ }
+ if (subregionType != null) {
+ TypedRegion subregion = new TypedRegion(offset, length, subregionType);
+ checker.execute(new SpellCheckIterator(document, subregion,
+ checker.getLocale()));
+ }
+ }
+ }
+ } else if (!type.equals(IDocument.DEFAULT_CONTENT_TYPE) &&
+ !type.equals(ICPartitions.C_CHARACTER)) {
+ checker.execute(new SpellCheckIterator(document, partition, checker.getLocale()));
+ }
+ }
+ }
+ } catch (BadLocationException x) {
+ CUIPlugin.getDefault().log(x);
+ }
+ } finally {
+ checker.removeListener(listener);
+ }
+ }
+
+ /**
+ * Returns true
if the token at the given offset and length is an include directive.
+ * @param document
+ * @param offset
+ * @param length
+ * @return
+ * @throws BadLocationException
+ */
+ private boolean isIncludeDirective(IDocument document, int offset, int length) throws BadLocationException {
+ while (length > 0) {
+ char c = document.getChar(offset);
+ if (c == '#' || Character.isWhitespace(c)) {
+ offset++;
+ length--;
+ } else if (c == 'i') {
+ return document.get(offset, length).startsWith("include"); //$NON-NLS-1$
+ } else {
+ break;
+ }
+ }
+ return false;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingProblem.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingProblem.java
new file mode 100644
index 00000000000..2fc57143629
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingProblem.java
@@ -0,0 +1,202 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.ui.texteditor.spelling.SpellingProblem;
+
+import org.eclipse.cdt.ui.text.ICCompletionProposal;
+
+import org.eclipse.cdt.internal.ui.text.correction.CorrectionContext;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEvent;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.RankedWordProposal;
+
+/**
+ * A {@link SpellingProblem} that adapts a {@link ISpellEvent}.
+ *
+ * TODO: remove {@link ISpellEvent} notification mechanism
+ *
+ */
+public class CSpellingProblem extends SpellingProblem {
+ /** Spell event */
+ private ISpellEvent fSpellEvent;
+
+ /**
+ * The associated document.
+ */
+ private IDocument fDocument;
+
+ /**
+ * Initialize with the given spell event.
+ *
+ * @param spellEvent the spell event
+ * @param document the document
+ */
+ public CSpellingProblem(ISpellEvent spellEvent, IDocument document) {
+ Assert.isLegal(document != null);
+ Assert.isLegal(spellEvent != null);
+ fSpellEvent= spellEvent;
+ fDocument= document;
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.SpellingProblem#getOffset()
+ */
+ public int getOffset() {
+ return fSpellEvent.getBegin();
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.SpellingProblem#getLength()
+ */
+ public int getLength() {
+ return fSpellEvent.getEnd() - fSpellEvent.getBegin() + 1;
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.SpellingProblem#getMessage()
+ */
+ public String getMessage() {
+ if (isSentenceStart() && isDictionaryMatch())
+ return Messages.bind(Messages.Spelling_error_case_label, fSpellEvent.getWord());
+
+ return Messages.bind(Messages.Spelling_error_label, fSpellEvent.getWord());
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.SpellingProblem#getProposals()
+ */
+ public ICompletionProposal[] getProposals() {
+ String[] arguments= getArguments();
+ if (arguments == null)
+ return new ICompletionProposal[0];
+
+ final int threshold= SpellingPreferences.spellingProposalThreshold();
+ int size= 0;
+ List proposals= null;
+
+ RankedWordProposal proposal= null;
+ ICCompletionProposal[] result= null;
+ int index= 0;
+
+ boolean fixed= false;
+ boolean match= false;
+ boolean sentence= false;
+
+ final ISpellCheckEngine engine= SpellCheckEngine.getInstance();
+ final ISpellChecker checker= engine.getSpellChecker();
+
+ if (checker != null) {
+ CorrectionContext context= new CorrectionContext(null, getOffset(), getLength());
+
+ if ((sentence && match) && !fixed) {
+ result= new ICCompletionProposal[] { new ChangeCaseProposal(
+ arguments, getOffset(), getLength(), context,
+ engine.getLocale()) };
+ } else {
+ proposals= new ArrayList(checker.getProposals(arguments[0],
+ sentence));
+ size= proposals.size();
+
+ if (threshold > 0 && size > threshold) {
+ Collections.sort(proposals);
+ proposals= proposals.subList(size - threshold - 1, size - 1);
+ size= proposals.size();
+ }
+
+ boolean extendable= !fixed ? (checker.acceptsWords() || AddWordProposal.canAskToConfigure()) : false;
+ result= new ICCompletionProposal[size + (extendable ? 3 : 2)];
+
+ for (index= 0; index < size; index++) {
+ proposal= (RankedWordProposal) proposals.get(index);
+ result[index]= new WordCorrectionProposal(proposal
+ .getText(), arguments, getOffset(), getLength(),
+ context, proposal.getRank());
+ }
+
+ if (extendable)
+ result[index++]= new AddWordProposal(arguments[0], context);
+
+ result[index++]= new WordIgnoreProposal(arguments[0], context);
+ result[index++]= new DisableSpellCheckingProposal(context);
+ }
+ }
+
+ return result;
+ }
+
+ public String[] getArguments() {
+ String prefix= ""; //$NON-NLS-1$
+ String postfix= ""; //$NON-NLS-1$
+ String word;
+ try {
+ word= fDocument.get(getOffset(), getLength());
+ } catch (BadLocationException e) {
+ return null;
+ }
+
+ try {
+ IRegion line= fDocument.getLineInformationOfOffset(getOffset());
+ int end= getOffset() + getLength();
+ prefix= fDocument.get(line.getOffset(), getOffset()
+ - line.getOffset());
+ postfix= fDocument.get(end + 1, line.getOffset() + line.getLength()
+ - end);
+ } catch (BadLocationException exception) {
+ // Do nothing
+ }
+
+ return new String[] {
+ word,
+ prefix,
+ postfix,
+ isSentenceStart() ? Boolean.toString(true) : Boolean
+ .toString(false),
+ isDictionaryMatch() ? Boolean.toString(true) : Boolean
+ .toString(false) };
+ }
+
+ /**
+ * Returns true
iff the corresponding word was found in the dictionary.
+ *
+ * NOTE: to be removed, see {@link #getProposals()}
+ *
+ *
+ * @return true
iff the corresponding word was found in the dictionary
+ */
+ public boolean isDictionaryMatch() {
+ return fSpellEvent.isMatch();
+ }
+
+ /**
+ * Returns true
iff the corresponding word starts a sentence.
+ *
+ * NOTE: to be removed, see {@link #getProposals()}
+ *
+ *
+ * @return true
iff the corresponding word starts a sentence
+ */
+ public boolean isSentenceStart() {
+ return fSpellEvent.isStart();
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingReconcileStrategy.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingReconcileStrategy.java
new file mode 100644
index 00000000000..616d4dd1fe6
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingReconcileStrategy.java
@@ -0,0 +1,163 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.source.IAnnotationModel;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.editors.text.EditorsUI;
+import org.eclipse.ui.texteditor.IDocumentProvider;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector;
+import org.eclipse.ui.texteditor.spelling.SpellingProblem;
+import org.eclipse.ui.texteditor.spelling.SpellingReconcileStrategy;
+import org.eclipse.ui.texteditor.spelling.SpellingService;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.model.IProblemRequestor;
+import org.eclipse.cdt.core.parser.IProblem;
+
+/**
+ * Reconcile strategy for spell checking comments.
+ */
+public class CSpellingReconcileStrategy extends SpellingReconcileStrategy {
+ /**
+ * Spelling problem collector that forwards {@link SpellingProblem}s as
+ * {@link IProblem}s to the {@link IProblemRequestor}.
+ */
+ private class SpellingProblemCollector implements ISpellingProblemCollector {
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector#accept(org.eclipse.ui.texteditor.spelling.SpellingProblem)
+ */
+ public void accept(SpellingProblem problem) {
+ IProblemRequestor requestor= fRequestor;
+ if (requestor != null) {
+ try {
+ int line= getDocument().getLineOfOffset(problem.getOffset()) + 1;
+ String word= getDocument().get(problem.getOffset(), problem.getLength());
+ boolean dictionaryMatch= false;
+ boolean sentenceStart= false;
+ if (problem instanceof CSpellingProblem) {
+ dictionaryMatch= ((CSpellingProblem)problem).isDictionaryMatch();
+ sentenceStart= ((CSpellingProblem) problem).isSentenceStart();
+ }
+ // see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=81514
+ IEditorInput editorInput= fEditor.getEditorInput();
+ if (editorInput != null) {
+ CoreSpellingProblem iProblem= new CoreSpellingProblem(problem.getOffset(), problem.getOffset() + problem.getLength() - 1, line, problem.getMessage(), word, dictionaryMatch, sentenceStart, getDocument(), editorInput.getName());
+ requestor.acceptProblem(iProblem);
+ }
+ } catch (BadLocationException x) {
+ // drop this SpellingProblem
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector#beginCollecting()
+ */
+ public void beginCollecting() {
+ if (fRequestor != null)
+ fRequestor.beginReporting();
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector#endCollecting()
+ */
+ public void endCollecting() {
+ if (fRequestor != null)
+ fRequestor.endReporting();
+ }
+ }
+
+
+ /** The id of the problem */
+ public static final int SPELLING_PROBLEM_ID= 0x80000000;
+
+ /** Properties file content type */
+ private static final IContentType CXX_CONTENT_TYPE= Platform.getContentTypeManager().getContentType(CCorePlugin.CONTENT_TYPE_CXXSOURCE);
+
+ /** The text editor to operate on. */
+ private ITextEditor fEditor;
+
+ /** The problem requester. */
+ private IProblemRequestor fRequestor;
+
+ /**
+ * Creates a new comment reconcile strategy.
+ *
+ * @param viewer the source viewer
+ * @param editor the text editor to operate on
+ */
+ public CSpellingReconcileStrategy(ISourceViewer viewer, ITextEditor editor) {
+ super(viewer, CSpellingService.getInstance());
+ fEditor= editor;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#reconcile(org.eclipse.jface.text.IRegion)
+ */
+ public void reconcile(IRegion region) {
+ if (fRequestor != null && isSpellingEnabled())
+ super.reconcile(region);
+ }
+
+ private boolean isSpellingEnabled() {
+ return EditorsUI.getPreferenceStore().getBoolean(SpellingService.PREFERENCE_SPELLING_ENABLED);
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.SpellingReconcileStrategy#createSpellingProblemCollector()
+ */
+ protected ISpellingProblemCollector createSpellingProblemCollector() {
+ return new SpellingProblemCollector();
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.SpellingReconcileStrategy#getContentType()
+ */
+ protected IContentType getContentType() {
+ return CXX_CONTENT_TYPE;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.reconciler.IReconcilingStrategy#setDocument(org.eclipse.jface.text.IDocument)
+ */
+ public void setDocument(IDocument document) {
+ super.setDocument(document);
+ updateProblemRequester();
+ }
+
+ /**
+ * Update the problem requester based on the current editor
+ */
+ private void updateProblemRequester() {
+ IAnnotationModel model= getAnnotationModel();
+ fRequestor= (model instanceof IProblemRequestor) ? (IProblemRequestor) model : null;
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.SpellingReconcileStrategy#getAnnotationModel()
+ */
+ protected IAnnotationModel getAnnotationModel() {
+ final IDocumentProvider documentProvider= fEditor.getDocumentProvider();
+ if (documentProvider == null)
+ return null;
+ return documentProvider.getAnnotationModel(fEditor.getEditorInput());
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingService.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingService.java
new file mode 100644
index 00000000000..2a0c0661ffa
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CSpellingService.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Google, 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:
+ * Sergey Prigogin (Google) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.ISafeRunnable;
+import org.eclipse.core.runtime.SafeRunner;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.ui.editors.text.EditorsUI;
+import org.eclipse.ui.texteditor.spelling.ISpellingEngine;
+import org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector;
+import org.eclipse.ui.texteditor.spelling.SpellingContext;
+import org.eclipse.ui.texteditor.spelling.SpellingService;
+
+/**
+ * Platform's SpellingService uses a spelling engine that is independent
+ * of the content type (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=185695).
+ * We are providing our own SpellingService to be able to use the C/C++ specific
+ * spelling engine even when it is not selected in
+ * Preferences/General/Editors/Text Editors/Spelling.
+ */
+public class CSpellingService extends SpellingService {
+ private static CSpellingService fInstance;
+
+ private IPreferenceStore fPreferences;
+ private ISpellingEngine fEngine;
+
+
+ public static CSpellingService getInstance() {
+ if (fInstance == null) {
+ fInstance = new CSpellingService(EditorsUI.getPreferenceStore());
+ }
+ return fInstance;
+ }
+
+ public void check(final IDocument document, final IRegion[] regions, final SpellingContext context,
+ final ISpellingProblemCollector collector, final IProgressMonitor monitor) {
+ try {
+ collector.beginCollecting();
+ if (fPreferences.getBoolean(PREFERENCE_SPELLING_ENABLED))
+ if (fEngine == null) {
+ fEngine = new CSpellingEngine();
+ }
+ ISafeRunnable runnable= new ISafeRunnable() {
+ public void run() throws Exception {
+ fEngine.check(document, regions, context, collector, monitor);
+ }
+ public void handleException(Throwable x) {
+ }
+ };
+ SafeRunner.run(runnable);
+ } finally {
+ collector.endCollecting();
+ }
+ }
+
+ private CSpellingService(IPreferenceStore preferences) {
+ super(preferences);
+ fPreferences = preferences;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/ChangeCaseProposal.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/ChangeCaseProposal.java
new file mode 100644
index 00000000000..d1263b0a9a5
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/ChangeCaseProposal.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import java.util.Locale;
+
+import org.eclipse.cdt.ui.text.IInvocationContext;
+
+/**
+ * Proposal to change the letter case of a word.
+ */
+public class ChangeCaseProposal extends WordCorrectionProposal {
+
+ /**
+ * Creates a new change case proposal.
+ *
+ * @param arguments The problem arguments associated with the spelling problem
+ * @param offset The offset in the document where to apply the proposal
+ * @param length The length in the document to apply the proposal
+ * @param context The invocation context for this proposal
+ * @param locale The locale to use for the case change
+ */
+ public ChangeCaseProposal(final String[] arguments, final int offset, final int length,
+ final IInvocationContext context, final Locale locale) {
+ super(Character.isLowerCase(arguments[0].charAt(0)) ? Character.toUpperCase(arguments[0].charAt(0)) + arguments[0].substring(1) : arguments[0],
+ arguments, offset, length, context, Integer.MAX_VALUE);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ return Messages.Spelling_case_label;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CoreSpellingProblem.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CoreSpellingProblem.java
new file mode 100644
index 00000000000..633fd0f9380
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/CoreSpellingProblem.java
@@ -0,0 +1,185 @@
+/*******************************************************************************
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+import org.eclipse.cdt.core.parser.IPersistableProblem;
+
+/**
+ * Spelling problem to be accepted by problem requesters.
+ */
+public class CoreSpellingProblem implements IPersistableProblem {
+ // spelling 'marker type' name. Only virtual as spelling problems are never persisted in markers.
+ // marker type is used in the quickFixProcessor extension point
+ public static final String MARKER_TYPE= "org.eclipse.cdt.internal.spelling"; //$NON-NLS-1$
+
+ /** The end offset of the problem */
+ private int fSourceEnd= 0;
+
+ /** The line number of the problem */
+ private int fLineNumber= 1;
+
+ /** The start offset of the problem */
+ private int fSourceStart= 0;
+
+ /** The description of the problem */
+ private String fMessage;
+
+ /** The misspelled word */
+ private String fWord;
+
+ /** Was the word found in the dictionary? */
+ private boolean fMatch;
+
+ /** Does the word start a new sentence? */
+ private boolean fSentence;
+
+ /** The associated document */
+ private IDocument fDocument;
+
+ /** The originating file name */
+ private String fOrigin;
+
+ /**
+ * Initialize with the given parameters.
+ *
+ * @param start the start offset
+ * @param end the end offset
+ * @param line the line
+ * @param message the message
+ * @param word the word
+ * @param match true
iff the word was found in the dictionary
+ * @param sentence true
iff the word starts a sentence
+ * @param document the document
+ * @param origin the originating file name
+ */
+ public CoreSpellingProblem(int start, int end, int line, String message, String word, boolean match, boolean sentence, IDocument document, String origin) {
+ super();
+ fSourceStart= start;
+ fSourceEnd= end;
+ fLineNumber= line;
+ fMessage= message;
+ fWord= word;
+ fMatch= match;
+ fSentence= sentence;
+ fDocument= document;
+ fOrigin= origin;
+ }
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#getArguments()
+ */
+ public String[] getArguments() {
+ String prefix= ""; //$NON-NLS-1$
+ String postfix= ""; //$NON-NLS-1$
+
+ try {
+ IRegion line= fDocument.getLineInformationOfOffset(fSourceStart);
+
+ prefix= fDocument.get(line.getOffset(), fSourceStart - line.getOffset());
+ postfix= fDocument.get(fSourceEnd + 1, line.getOffset() + line.getLength() - fSourceEnd);
+ } catch (BadLocationException exception) {
+ // Do nothing
+ }
+ return new String[] { fWord, prefix, postfix, fSentence ? Boolean.toString(true) : Boolean.toString(false), fMatch ? Boolean.toString(true) : Boolean.toString(false) };
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#getID()
+ */
+ public int getID() {
+ return CSpellingReconcileStrategy.SPELLING_PROBLEM_ID;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#getMessage()
+ */
+ public String getMessage() {
+ return fMessage;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#getOriginatingFileName()
+ */
+ public char[] getOriginatingFileName() {
+ return fOrigin.toCharArray();
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#getSourceEnd()
+ */
+ public int getSourceEnd() {
+ return fSourceEnd;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#getSourceLineNumber()
+ */
+ public int getSourceLineNumber() {
+ return fLineNumber;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#getSourceStart()
+ */
+ public int getSourceStart() {
+ return fSourceStart;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#isError()
+ */
+ public boolean isError() {
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#isWarning()
+ */
+ public boolean isWarning() {
+ return true;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#setSourceStart(int)
+ */
+ public void setSourceStart(int sourceStart) {
+ fSourceStart= sourceStart;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#setSourceEnd(int)
+ */
+ public void setSourceEnd(int sourceEnd) {
+ fSourceEnd= sourceEnd;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.IProblem#setSourceLineNumber(int)
+ */
+ public void setSourceLineNumber(int lineNumber) {
+ fLineNumber= lineNumber;
+ }
+
+ /*
+ * @see org.eclipse.cdt.core.parser.CategorizedProblem#getMarkerType()
+ */
+ public String getMarkerType() {
+ return MARKER_TYPE;
+ }
+
+ public boolean checkCategory(int bitmask) {
+ return (getID() & bitmask) != 0;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/DisableSpellCheckingProposal.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/DisableSpellCheckingProposal.java
new file mode 100644
index 00000000000..8c20447ef99
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/DisableSpellCheckingProposal.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.editors.text.EditorsUI;
+import org.eclipse.ui.texteditor.spelling.SpellingService;
+
+import org.eclipse.cdt.ui.text.ICCompletionProposal;
+import org.eclipse.cdt.ui.text.IInvocationContext;
+
+import org.eclipse.cdt.internal.ui.CPluginImages;
+
+
+/**
+ * Proposal to disable spell checking.
+ */
+public class DisableSpellCheckingProposal implements ICCompletionProposal {
+ private final String ID_DISABLE = "DISABLE"; //$NON-NLS-1$
+
+ /** The invocation context */
+ private IInvocationContext fContext;
+
+ /**
+ * Creates a new proposal.
+ *
+ * @param context the invocation context
+ */
+ public DisableSpellCheckingProposal(IInvocationContext context) {
+ fContext= context;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse.jface.text.IDocument)
+ */
+ public final void apply(final IDocument document) {
+ IPreferenceStore store= EditorsUI.getPreferenceStore();
+ store.setValue(SpellingService.PREFERENCE_SPELLING_ENABLED, false);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ return Messages.Spelling_disable_info;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
+ */
+ public final IContextInformation getContextInformation() {
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ return Messages.Spelling_disable_label;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return CPluginImages.get(CPluginImages.IMG_OBJS_NLS_NEVER_TRANSLATE);
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.java.IJavaCompletionProposal#getRelevance()
+ */
+ public final int getRelevance() {
+ return Integer.MIN_VALUE + 1;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(org.eclipse.jface.text.IDocument)
+ */
+ public final Point getSelection(final IDocument document) {
+ return new Point(fContext.getSelectionOffset(), fContext.getSelectionLength());
+ }
+
+ public String getIdString() {
+ return ID_DISABLE;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/Messages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/Messages.java
new file mode 100644
index 00000000000..2efd0f575af
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/Messages.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2007 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 - Initial API and implementation
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.osgi.util.NLS;
+
+public class Messages extends NLS {
+ private static final String BUNDLE_NAME = Messages.class.getName();
+
+ public static String AbstractSpellingDictionary_encodingError;
+ public static String Spelling_add_askToConfigure_ignoreMessage;
+ public static String Spelling_add_askToConfigure_question;
+ public static String Spelling_add_askToConfigure_title;
+ public static String Spelling_add_info;
+ public static String Spelling_add_label;
+ public static String Spelling_case_label;
+ public static String Spelling_correct_label;
+ public static String Spelling_disable_info;
+ public static String Spelling_disable_label;
+ public static String Spelling_error_case_label;
+ public static String Spelling_error_label;
+ public static String Spelling_ignore_info;
+ public static String Spelling_ignore_label;
+
+ static {
+ // Initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, Messages.class);
+ }
+
+ private Messages() {
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/Messages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/Messages.properties
new file mode 100644
index 00000000000..032894175fa
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/Messages.properties
@@ -0,0 +1,26 @@
+###############################################################################
+# Copyright (c) 2000, 2007 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
+# Sergey Prigogin (Google)
+###############################################################################
+
+AbstractSpellingDictionary_encodingError=Could not read: ''{0}'', where the bad characters are replaced by ''{1}''. Check the encoding of the spelling dictionary ({2}).
+Spelling_add_askToConfigure_ignoreMessage=&Do not show 'Add word' proposals if user dictionary is missing
+Spelling_add_askToConfigure_question=A user dictionary is needed to add words.\nDo you want to configure it now?\n
+Spelling_add_askToConfigure_title=Missing User Dictionary
+Spelling_add_info=Adds the word ''{0}'' to the dictionary
+Spelling_add_label=Add ''{0}'' to dictionary
+Spelling_case_label=Change to upper case
+Spelling_correct_label=Change to ''{0}''
+Spelling_disable_info=Disables spell checking.
+Spelling_disable_label=Disable spell checking
+Spelling_error_case_label= The word ''{0}'' should have an initial upper case letter
+Spelling_error_label=The word ''{0}'' is not correctly spelled
+Spelling_ignore_info=Ignores ''{0}'' during the current session
+Spelling_ignore_label=Ignore ''{0}'' during the current session
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellCheckEngine.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellCheckEngine.java
new file mode 100644
index 00000000000..4a8252fbe98
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellCheckEngine.java
@@ -0,0 +1,429 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.eclipse.core.runtime.FileLocator;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+
+import org.eclipse.cdt.internal.ui.text.spelling.engine.DefaultSpellChecker;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellDictionary;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.LocaleSensitiveSpellDictionary;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.PersistentSpellDictionary;
+
+/**
+ * Spell check engine for C/C++ source spell checking.
+ */
+public class SpellCheckEngine implements ISpellCheckEngine, IPropertyChangeListener {
+ /** The dictionary location */
+ public static final String DICTIONARY_LOCATION= "dictionaries/"; //$NON-NLS-1$
+
+ /** The singleton engine instance */
+ private static ISpellCheckEngine fgEngine= null;
+
+ /**
+ * Caches the locales of installed dictionaries.
+ */
+ private static Set fgLocalesWithInstalledDictionaries;
+
+
+ /**
+ * Returns the locales for which this
+ * spell check engine has dictionaries.
+ *
+ * @return The available locales for this engine
+ */
+ public static Set getLocalesWithInstalledDictionaries() {
+ if (fgLocalesWithInstalledDictionaries != null)
+ return fgLocalesWithInstalledDictionaries;
+
+ URL location;
+ try {
+ location= getDictionaryLocation();
+ if (location == null)
+ return fgLocalesWithInstalledDictionaries= Collections.EMPTY_SET;
+ } catch (MalformedURLException ex) {
+ CUIPlugin.getDefault().log(ex);
+ return fgLocalesWithInstalledDictionaries= Collections.EMPTY_SET;
+ }
+
+ String[] fileNames;
+ try {
+ URL url= FileLocator.toFileURL(location);
+ File file= new File(url.getFile());
+ if (!file.isDirectory())
+ return fgLocalesWithInstalledDictionaries= Collections.EMPTY_SET;
+ fileNames= file.list();
+ if (fileNames == null)
+ return fgLocalesWithInstalledDictionaries= Collections.EMPTY_SET;
+ } catch (IOException ex) {
+ CUIPlugin.getDefault().log(ex);
+ return fgLocalesWithInstalledDictionaries= Collections.EMPTY_SET;
+ }
+
+ fgLocalesWithInstalledDictionaries= new HashSet();
+ int fileNameCount= fileNames.length;
+ for (int i= 0; i < fileNameCount; i++) {
+ String fileName= fileNames[i];
+ int localeEnd= fileName.indexOf(".dictionary"); //$NON-NLS-1$
+ if (localeEnd > 1) {
+ String localeName= fileName.substring(0, localeEnd);
+ int languageEnd=localeName.indexOf('_');
+ if (languageEnd == -1) {
+ fgLocalesWithInstalledDictionaries.add(new Locale(localeName));
+ } else if (languageEnd == 2 && localeName.length() == 5) {
+ fgLocalesWithInstalledDictionaries.add(new Locale(localeName.substring(0, 2), localeName.substring(3)));
+ } else if (localeName.length() > 6 && localeName.charAt(5) == '_') {
+ fgLocalesWithInstalledDictionaries.add(new Locale(localeName.substring(0, 2), localeName.substring(3, 5), localeName.substring(6)));
+ }
+ }
+ }
+
+ return fgLocalesWithInstalledDictionaries;
+ }
+
+ /**
+ * Returns the default locale for this engine.
+ *
+ * @return The default locale
+ */
+ public static Locale getDefaultLocale() {
+ return Locale.getDefault();
+ }
+
+ /**
+ * Returns the dictionary closest to the given locale.
+ *
+ * @param locale the locale
+ * @return the dictionary or null
if none is suitable
+ */
+ public ISpellDictionary findDictionary(Locale locale) {
+ ISpellDictionary dictionary= (ISpellDictionary)fLocaleDictionaries.get(locale);
+ if (dictionary != null)
+ return dictionary;
+
+ // Try same language
+ String language= locale.getLanguage();
+ Iterator iter= fLocaleDictionaries.entrySet().iterator();
+ while (iter.hasNext()) {
+ Entry entry= (Entry)iter.next();
+ Locale dictLocale= (Locale)entry.getKey();
+ if (dictLocale.getLanguage().equals(language))
+ return (ISpellDictionary)entry.getValue();
+ }
+
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine#findDictionary(java.util.Locale)
+ */
+ public static Locale findClosestLocale(Locale locale) {
+ if (locale == null || locale.toString().length() == 0)
+ return locale;
+
+ if (getLocalesWithInstalledDictionaries().contains(locale))
+ return locale;
+
+ // Try same language
+ String language= locale.getLanguage();
+ Iterator iter= getLocalesWithInstalledDictionaries().iterator();
+ while (iter.hasNext()) {
+ Locale dictLocale= (Locale)iter.next();
+ if (dictLocale.getLanguage().equals(language))
+ return dictLocale;
+ }
+
+ // Try whether American English is present
+ Locale defaultLocale= Locale.US;
+ if (getLocalesWithInstalledDictionaries().contains(defaultLocale))
+ return defaultLocale;
+
+ return null;
+ }
+
+ /**
+ * Returns the URL for the dictionary location where
+ * the Platform dictionaries are located.
+ *
+ * This is in org.eclipse.cdt.ui/dictionaries/
+ * which can also be populated via fragments.
+ *
+ *
+ * @throws MalformedURLException if the URL could not be created
+ * @return The dictionary location, or null
iff the location is not known
+ */
+ public static URL getDictionaryLocation() throws MalformedURLException {
+ // Unfortunately, dictionaries used by JDT are not accessible,
+ // so we have to provide our own copies of the same files.
+ final CUIPlugin plugin= CUIPlugin.getDefault();
+ if (plugin != null)
+ return plugin.getBundle().getEntry("/" + DICTIONARY_LOCATION); //$NON-NLS-1$
+
+ return null;
+ }
+
+ /**
+ * Returns the singleton instance of the spell check engine.
+ *
+ * @return The singleton instance of the spell check engine
+ */
+ public static final synchronized ISpellCheckEngine getInstance() {
+ if (fgEngine == null)
+ fgEngine= new SpellCheckEngine();
+
+ return fgEngine;
+ }
+
+ /**
+ * Shuts down the singleton instance of the spell check engine.
+ */
+ public static final synchronized void shutdownInstance() {
+ if (fgEngine != null) {
+ fgEngine.shutdown();
+ fgEngine= null;
+ }
+ }
+
+ /** The registered locale insensitive dictionaries */
+ private Set fGlobalDictionaries= new HashSet();
+
+ /** The spell checker for fLocale */
+ private ISpellChecker fChecker= null;
+
+ /** The registered locale sensitive dictionaries */
+ private Map fLocaleDictionaries= new HashMap();
+
+ /** The user dictionary */
+ private ISpellDictionary fUserDictionary= null;
+
+ /**
+ * Creates a new spell check manager.
+ */
+ private SpellCheckEngine() {
+ fGlobalDictionaries.add(new TaskTagDictionary());
+
+ try {
+ Locale locale= null;
+ final URL location= getDictionaryLocation();
+
+ for (final Iterator iterator= getLocalesWithInstalledDictionaries().iterator(); iterator.hasNext();) {
+ locale= (Locale)iterator.next();
+ fLocaleDictionaries.put(locale, new LocaleSensitiveSpellDictionary(locale, location));
+ }
+ } catch (MalformedURLException exception) {
+ // Do nothing
+ }
+
+ SpellingPreferences.addPropertyChangeListener(this);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine#getSpellChecker()
+ */
+ public final synchronized ISpellChecker getSpellChecker() throws IllegalStateException {
+ if (fGlobalDictionaries == null)
+ throw new IllegalStateException("spell checker has been shut down"); //$NON-NLS-1$
+
+ IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore();
+ Locale locale= getCurrentLocale(store);
+ if (fUserDictionary == null && "".equals(locale.toString())) //$NON-NLS-1$
+ return null;
+
+ if (fChecker != null && fChecker.getLocale().equals(locale))
+ return fChecker;
+
+ resetSpellChecker();
+
+ fChecker= new DefaultSpellChecker(store, locale);
+ resetUserDictionary();
+
+ for (Iterator iterator= fGlobalDictionaries.iterator(); iterator.hasNext();) {
+ ISpellDictionary dictionary= (ISpellDictionary)iterator.next();
+ fChecker.addDictionary(dictionary);
+ }
+
+ ISpellDictionary dictionary= findDictionary(fChecker.getLocale());
+ if (dictionary != null)
+ fChecker.addDictionary(dictionary);
+
+ return fChecker;
+ }
+
+ /**
+ * Returns the current locale of the spelling preferences.
+ *
+ * @param store the preference store
+ * @return The current locale of the spelling preferences
+ */
+ private Locale getCurrentLocale(IPreferenceStore store) {
+ return convertToLocale(SpellingPreferences.getSpellingLocale());
+ }
+
+ public static Locale convertToLocale(String locale) {
+ Locale defaultLocale= SpellCheckEngine.getDefaultLocale();
+ if (locale.equals(defaultLocale.toString()))
+ return defaultLocale;
+
+ if (locale.length() >= 5)
+ return new Locale(locale.substring(0, 2), locale.substring(3, 5));
+
+ return new Locale(""); //$NON-NLS-1$
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.ISpellCheckEngine#getLocale()
+ */
+ public synchronized final Locale getLocale() {
+ if (fChecker == null)
+ return null;
+
+ return fChecker.getLocale();
+ }
+
+ /*
+ * @see org.eclipse.jface.util.IPropertyChangeListener#propertyChange(org.eclipse.jface.util.PropertyChangeEvent)
+ */
+ public final void propertyChange(final PropertyChangeEvent event) {
+ if (event.getProperty().equals(SpellingPreferences.SPELLING_LOCALE)) {
+ resetSpellChecker();
+ return;
+ }
+
+ if (event.getProperty().equals(SpellingPreferences.SPELLING_USER_DICTIONARY)) {
+ resetUserDictionary();
+ return;
+ }
+
+ if (event.getProperty().equals(SpellingPreferences.SPELLING_USER_DICTIONARY_ENCODING)) {
+ resetUserDictionary();
+ return;
+ }
+ }
+
+ /**
+ * Resets the current checker's user dictionary.
+ */
+ private synchronized void resetUserDictionary() {
+ if (fChecker == null)
+ return;
+
+ // Update user dictionary
+ if (fUserDictionary != null) {
+ fChecker.removeDictionary(fUserDictionary);
+ fUserDictionary.unload();
+ fUserDictionary= null;
+ }
+
+ final String filePath= SpellingPreferences.getSpellingUserDictionary();
+ if (filePath.length() > 0) {
+ try {
+ File file= new File(filePath);
+ if (!file.exists() && !file.createNewFile())
+ return;
+
+ final URL url= new URL("file", null, filePath); //$NON-NLS-1$
+ InputStream stream= url.openStream();
+ if (stream != null) {
+ try {
+ fUserDictionary= new PersistentSpellDictionary(url);
+ fChecker.addDictionary(fUserDictionary);
+ } finally {
+ stream.close();
+ }
+ }
+ } catch (MalformedURLException exception) {
+ // Do nothing
+ } catch (IOException exception) {
+ // Do nothing
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine#registerDictionary(org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellDictionary)
+ */
+ public synchronized final void registerGlobalDictionary(final ISpellDictionary dictionary) {
+ fGlobalDictionaries.add(dictionary);
+ resetSpellChecker();
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine#registerDictionary(java.util.Locale, org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellDictionary)
+ */
+ public synchronized final void registerDictionary(final Locale locale, final ISpellDictionary dictionary) {
+ fLocaleDictionaries.put(locale, dictionary);
+ resetSpellChecker();
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine#unload()
+ */
+ public synchronized final void shutdown() {
+ SpellingPreferences.removePropertyChangeListener(this);
+
+ ISpellDictionary dictionary= null;
+ for (final Iterator iterator= fGlobalDictionaries.iterator(); iterator.hasNext();) {
+ dictionary= (ISpellDictionary)iterator.next();
+ dictionary.unload();
+ }
+ fGlobalDictionaries= null;
+
+ for (final Iterator iterator= fLocaleDictionaries.values().iterator(); iterator.hasNext();) {
+ dictionary= (ISpellDictionary)iterator.next();
+ dictionary.unload();
+ }
+ fLocaleDictionaries= null;
+
+ fUserDictionary= null;
+ fChecker= null;
+ }
+
+ private synchronized void resetSpellChecker() {
+ if (fChecker != null) {
+ ISpellDictionary dictionary= (ISpellDictionary)fLocaleDictionaries.get(fChecker.getLocale());
+ if (dictionary != null)
+ dictionary.unload();
+ }
+ fChecker= null;
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.ISpellCheckEngine#unregisterDictionary(org.eclipse.cdt.ui.text.spelling.engine.ISpellDictionary)
+ */
+ public synchronized final void unregisterDictionary(final ISpellDictionary dictionary) {
+ fGlobalDictionaries.remove(dictionary);
+ fLocaleDictionaries.values().remove(dictionary);
+ dictionary.unload();
+ resetSpellChecker();
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellCheckIterator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellCheckIterator.java
new file mode 100644
index 00000000000..5ec10a2fdd2
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellCheckIterator.java
@@ -0,0 +1,336 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ ******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import java.util.LinkedList;
+import java.util.Locale;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+import com.ibm.icu.text.BreakIterator;
+
+import org.eclipse.cdt.internal.ui.text.spelling.engine.DefaultSpellChecker;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckIterator;
+
+
+/**
+ * Iterator to spell check multiline comment regions.
+ */
+public class SpellCheckIterator implements ISpellCheckIterator {
+ /** The content of the region */
+ protected final String fContent;
+
+ /** The last token */
+ protected String fLastToken= null;
+
+ /** The next break */
+ protected int fNext= 1;
+
+ /** The offset of the region */
+ protected final int fOffset;
+
+ /** The predecessor break */
+ private int fPredecessor;
+
+ /** The previous break */
+ protected int fPrevious= 0;
+
+ /** The sentence breaks */
+ private final LinkedList fSentenceBreaks= new LinkedList();
+
+ /** Does the current word start a sentence? */
+ private boolean fStartsSentence= false;
+
+ /** The successor break */
+ protected int fSuccessor;
+
+ /** The word iterator */
+ private final BreakIterator fWordIterator;
+
+ private boolean fIsIgnoringSingleLetters;
+
+ /**
+ * Creates a new spell check iterator.
+ *
+ * @param document the document containing the specified partition
+ * @param region the region to spell check
+ * @param locale the locale to use for spell checking
+ */
+ public SpellCheckIterator(IDocument document, IRegion region, Locale locale) {
+ this(document, region, locale, BreakIterator.getWordInstance(locale));
+ }
+
+ /**
+ * Creates a new spell check iterator.
+ *
+ * @param document the document containing the specified partition
+ * @param region the region to spell check
+ * @param locale the locale to use for spell checking
+ * @param breakIterator the break-iterator
+ */
+ public SpellCheckIterator(IDocument document, IRegion region, Locale locale, BreakIterator breakIterator) {
+ fOffset= region.getOffset();
+ fWordIterator= breakIterator;
+
+ String content;
+ try {
+ content= document.get(region.getOffset(), region.getLength());
+ } catch (Exception exception) {
+ content= ""; //$NON-NLS-1$
+ }
+ fContent= content;
+
+ fWordIterator.setText(content);
+ fPredecessor= fWordIterator.first();
+ fSuccessor= fWordIterator.next();
+
+ final BreakIterator iterator= BreakIterator.getSentenceInstance(locale);
+ iterator.setText(content);
+
+ int offset= iterator.current();
+ while (offset != BreakIterator.DONE) {
+ fSentenceBreaks.add(new Integer(offset));
+ offset= iterator.next();
+ }
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckIterator#setIgnoreSingleLetters(boolean)
+ */
+ public void setIgnoreSingleLetters(boolean state) {
+ fIsIgnoringSingleLetters= state;
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.ISpellCheckIterator#getBegin()
+ */
+ public final int getBegin() {
+ return fPrevious + fOffset;
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.ISpellCheckIterator#getEnd()
+ */
+ public final int getEnd() {
+ return fNext + fOffset - 1;
+ }
+
+ /*
+ * @see java.util.Iterator#hasNext()
+ */
+ public final boolean hasNext() {
+ return fSuccessor != BreakIterator.DONE;
+ }
+
+ /**
+ * Does the specified token consist of at least one letter and digits
+ * only?
+ *
+ * @param begin the begin index
+ * @param end the end index
+ * @return true
iff the token consists of digits and at
+ * least one letter only, false
otherwise
+ */
+ protected final boolean isAlphaNumeric(final int begin, final int end) {
+ char character= 0;
+ boolean letter= false;
+ for (int index= begin; index < end; index++) {
+ character= fContent.charAt(index);
+ if (Character.isLetter(character))
+ letter= true;
+
+ if (!Character.isLetterOrDigit(character))
+ return false;
+ }
+ return letter;
+ }
+
+ /**
+ * Checks the last token against the given tags?
+ *
+ * @param tags the tags to check
+ * @return true
iff the last token is in the given array
+ */
+ protected final boolean isToken(final String[] tags) {
+ return isToken(fLastToken, tags);
+ }
+
+ /**
+ * Checks the given token against the given tags?
+ *
+ * @param token the token to check
+ * @param tags the tags to check
+ * @return true
iff the last token is in the given array
+ */
+ protected final boolean isToken(final String token, final String[] tags) {
+ if (token != null) {
+ for (int index= 0; index < tags.length; index++) {
+ if (token.equals(tags[index]))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Is the current token a single letter token surrounded by
+ * non-whitespace characters?
+ *
+ * @param begin the begin index
+ * @return true
iff the token is a single letter token,
+ * false
otherwise
+ */
+ protected final boolean isSingleLetter(final int begin) {
+ if (!Character.isLetter(fContent.charAt(begin)))
+ return false;
+
+ if (begin > 0 && !Character.isWhitespace(fContent.charAt(begin - 1)))
+ return false;
+
+ if (begin < fContent.length() - 1 && !Character.isWhitespace(fContent.charAt(begin + 1)))
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Does the specified token look like an URL?
+ *
+ * @param begin the begin index
+ * @return true
iff this token look like an URL,
+ * false
otherwise
+ */
+ protected final boolean isUrlToken(final int begin) {
+ for (int index= 0; index < DefaultSpellChecker.URL_PREFIXES.length; index++) {
+ if (fContent.startsWith(DefaultSpellChecker.URL_PREFIXES[index], begin))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Does the specified token consist of whitespace only?
+ *
+ * @param begin the begin index
+ * @param end the end index
+ * @return true
iff the token consists of whitespace
+ * only, false
otherwise
+ */
+ protected final boolean isWhitespace(final int begin, final int end) {
+ for (int index= begin; index < end; index++) {
+ if (!Character.isWhitespace(fContent.charAt(index)))
+ return false;
+ }
+ return true;
+ }
+
+ /*
+ * @see java.util.Iterator#next()
+ */
+ public Object next() {
+ String token= nextToken();
+ while (token == null && fSuccessor != BreakIterator.DONE)
+ token= nextToken();
+
+ fLastToken= token;
+ return token;
+ }
+
+ /**
+ * Advances the end index to the next word break.
+ */
+ protected final void nextBreak() {
+ fNext= fSuccessor;
+ fPredecessor= fSuccessor;
+ fSuccessor= fWordIterator.next();
+ }
+
+ /**
+ * Returns the next sentence break.
+ *
+ * @return the next sentence break
+ */
+ protected final int nextSentence() {
+ return ((Integer) fSentenceBreaks.getFirst()).intValue();
+ }
+
+ /**
+ * Determines the next token to be spell checked.
+ *
+ * @return the next token to be spell checked, or null
+ * iff the next token is not a candidate for spell checking.
+ */
+ protected String nextToken() {
+ String token= null;
+ fPrevious= fPredecessor;
+ fStartsSentence= false;
+ nextBreak();
+
+ boolean update= false;
+ if (fNext - fPrevious > 0) {
+ if (!isWhitespace(fPrevious, fNext) && isAlphaNumeric(fPrevious, fNext)) {
+ if (isUrlToken(fPrevious)) {
+ skipTokens(fPrevious, ' ');
+ } else if (fNext - fPrevious > 1 || isSingleLetter(fPrevious) && !fIsIgnoringSingleLetters) {
+ token= fContent.substring(fPrevious, fNext);
+ }
+ update= true;
+ }
+ }
+
+ if (update && fSentenceBreaks.size() > 0) {
+ if (fPrevious >= nextSentence()) {
+ while (fSentenceBreaks.size() > 0 && fPrevious >= nextSentence())
+ fSentenceBreaks.removeFirst();
+
+ fStartsSentence= (fLastToken == null) || (token != null);
+ }
+ }
+ return token;
+ }
+
+ /*
+ * @see java.util.Iterator#remove()
+ */
+ public final void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Skip the tokens until the stop character is reached.
+ *
+ * @param begin the begin index
+ * @param stop the stop character
+ */
+ protected final void skipTokens(final int begin, final char stop) {
+ int end= begin;
+
+ while (end < fContent.length() && fContent.charAt(end) != stop)
+ end++;
+
+ if (end < fContent.length()) {
+ fNext= end;
+ fPredecessor= fNext;
+ fSuccessor= fWordIterator.following(fNext);
+ } else {
+ fSuccessor= BreakIterator.DONE;
+ }
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.ISpellCheckIterator#startsSentence()
+ */
+ public final boolean startsSentence() {
+ return fStartsSentence;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellingEngine.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellingEngine.java
new file mode 100644
index 00000000000..ede9fa8355a
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellingEngine.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.ui.texteditor.spelling.ISpellingEngine;
+import org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector;
+import org.eclipse.ui.texteditor.spelling.SpellingContext;
+
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEvent;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEventListener;
+
+/**
+ * Internal abstract spelling engine, subclasses provide a content-type specific implementation.
+ */
+public abstract class SpellingEngine implements ISpellingEngine {
+ /**
+ * {@link ISpellEvent}listener that forwards events as
+ * {@link org.eclipse.ui.texteditor.spelling.SpellingProblem}.
+ */
+ protected static class SpellEventListener implements ISpellEventListener {
+ /** Spelling problem collector */
+ private ISpellingProblemCollector fCollector;
+
+ /**
+ * The document.
+ */
+ private IDocument fDocument;
+
+ /**
+ * Initialize with the given spelling problem collector.
+ *
+ * @param collector the spelling problem collector
+ * @param document the document
+ */
+ public SpellEventListener(ISpellingProblemCollector collector, IDocument document) {
+ fCollector= collector;
+ fDocument= document;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEventListener#handle(org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEvent)
+ */
+ public void handle(ISpellEvent event) {
+ fCollector.accept(new CSpellingProblem(event, fDocument));
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingEngine#check(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IRegion[], org.eclipse.ui.texteditor.spelling.SpellingContext, org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void check(IDocument document, IRegion[] regions, SpellingContext context, ISpellingProblemCollector collector, IProgressMonitor monitor) {
+ if (collector != null) {
+ final ISpellCheckEngine spellingEngine= SpellCheckEngine.getInstance();
+ ISpellChecker checker= spellingEngine.getSpellChecker();
+ if (checker != null)
+ check(document, regions, checker, collector, monitor);
+ }
+ }
+
+ /**
+ * Spell checks the given document regions with the given arguments.
+ *
+ * @param document the document
+ * @param regions the regions
+ * @param checker the spell checker
+ * @param collector the spelling problem collector
+ * @param monitor the progress monitor, can be null
+ */
+ protected abstract void check(IDocument document, IRegion[] regions, ISpellChecker checker, ISpellingProblemCollector collector, IProgressMonitor monitor);
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellingEngineDispatcher.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellingEngineDispatcher.java
new file mode 100644
index 00000000000..aa45eb7c1b9
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellingEngineDispatcher.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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.content.IContentType;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.ui.editors.text.EditorsUI;
+import org.eclipse.ui.texteditor.spelling.ISpellingEngine;
+import org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector;
+import org.eclipse.ui.texteditor.spelling.SpellingContext;
+import org.eclipse.ui.texteditor.spelling.SpellingEngineDescriptor;
+
+import org.eclipse.cdt.core.CCorePlugin;
+
+/**
+ * A dispatcher that decides what spelling engine to use depending on content type.
+ * When C/C++ spelling engine is selected in Preferences/General/Editors/Text Editors/Spelling
+ * this class is called to perform spelling check for all text-based content types.
+ * If the content type does not match one of C/C++ content types, the spelling check
+ * is delegated to the default spelling engine, most likely the one provided by JDT.
+ */
+public class SpellingEngineDispatcher implements ISpellingEngine {
+ private static final String C_SPELLING_ENGINE_ID = "org.eclipse.cdt.internal.ui.text.spelling.CSpellingEngine"; //$NON-NLS-1$
+
+ /** C/C++ source content type */
+ private static final IContentType CHEADER_CONTENT_TYPE= Platform.getContentTypeManager().getContentType(CCorePlugin.CONTENT_TYPE_CHEADER);
+ private static final IContentType CSOURCE_CONTENT_TYPE= Platform.getContentTypeManager().getContentType(CCorePlugin.CONTENT_TYPE_CSOURCE);
+ private static final IContentType CXXHEADER_CONTENT_TYPE= Platform.getContentTypeManager().getContentType(CCorePlugin.CONTENT_TYPE_CXXHEADER);
+ private static final IContentType CXXSOURCE_CONTENT_TYPE= Platform.getContentTypeManager().getContentType(CCorePlugin.CONTENT_TYPE_CXXSOURCE);
+
+ /** Available spelling engines by content type */
+ private Map fEngines= new HashMap();
+ private ISpellingEngine defaultEngine;
+
+ /**
+ * Initialize concrete engines.
+ */
+ public SpellingEngineDispatcher() {
+ SpellingEngine engine = new CSpellingEngine();
+ if (CHEADER_CONTENT_TYPE != null)
+ fEngines.put(CHEADER_CONTENT_TYPE, engine);
+ if (CSOURCE_CONTENT_TYPE != null)
+ fEngines.put(CSOURCE_CONTENT_TYPE, engine);
+ if (CXXHEADER_CONTENT_TYPE != null)
+ fEngines.put(CXXHEADER_CONTENT_TYPE, engine);
+ if (CXXSOURCE_CONTENT_TYPE != null)
+ fEngines.put(CXXSOURCE_CONTENT_TYPE, engine);
+ try {
+ SpellingEngineDescriptor descriptor =
+ EditorsUI.getSpellingService().getDefaultSpellingEngineDescriptor();
+ if (!C_SPELLING_ENGINE_ID.equals(descriptor.getId())) { // Do not delegate to itself.
+ defaultEngine = descriptor.createEngine();
+ }
+ } catch (CoreException e) {
+ }
+ }
+
+ /*
+ * @see org.eclipse.ui.texteditor.spelling.ISpellingEngine#check(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IRegion[], org.eclipse.ui.texteditor.spelling.SpellingContext, org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void check(IDocument document, IRegion[] regions, SpellingContext context, ISpellingProblemCollector collector, IProgressMonitor monitor) {
+ ISpellingEngine engine= getEngine(context.getContentType());
+ if (engine == null)
+ engine= defaultEngine;
+ if (engine != null)
+ engine.check(document, regions, context, collector, monitor);
+ }
+
+ /**
+ * Returns a spelling engine for the given content type or
+ * null
if none could be found.
+ *
+ * @param contentType the content type
+ * @return a spelling engine for the given content type or
+ * null
if none could be found
+ */
+ private ISpellingEngine getEngine(IContentType contentType) {
+ if (contentType == null)
+ return null;
+
+ if (fEngines.containsKey(contentType))
+ return (ISpellingEngine) fEngines.get(contentType);
+
+ return getEngine(contentType.getBaseType());
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellingPreferences.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellingPreferences.java
new file mode 100644
index 00000000000..d2388488b82
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/SpellingPreferences.java
@@ -0,0 +1,150 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Google, 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:
+ * Sergey Prigogin (Google) - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.cdt.ui.PreferenceConstants;
+import org.eclipse.jface.preference.IPreferenceStore;
+import org.eclipse.jface.util.IPropertyChangeListener;
+
+/**
+ * This class encapsulates spelling preferences.
+ * If the source of spelling preferences were to move from CDT to the platform,
+ * this class would make refactoring easier.
+ */
+public class SpellingPreferences {
+ private static IPreferenceStore preferenceStore = PreferenceConstants.getPreferenceStore();
+ static final String SPELLING_LOCALE = PreferenceConstants.SPELLING_LOCALE;
+ static final String SPELLING_USER_DICTIONARY = PreferenceConstants.SPELLING_USER_DICTIONARY;
+ static final String SPELLING_USER_DICTIONARY_ENCODING = PreferenceConstants.SPELLING_USER_DICTIONARY_ENCODING;
+ private static final String SPELLING_PROPOSAL_THRESHOLD = PreferenceConstants.SPELLING_PROPOSAL_THRESHOLD;
+ private static final String SPELLING_ENABLE_CONTENTASSIST = PreferenceConstants.SPELLING_ENABLE_CONTENTASSIST;
+ private static final String SPELLING_IGNORE_DIGITS = PreferenceConstants.SPELLING_IGNORE_DIGITS;
+ private static final String SPELLING_IGNORE_MIXED = PreferenceConstants.SPELLING_IGNORE_MIXED;
+ private static final String SPELLING_IGNORE_NON_LETTERS = PreferenceConstants.SPELLING_IGNORE_NON_LETTERS;
+ private static final String SPELLING_IGNORE_SENTENCE = PreferenceConstants.SPELLING_IGNORE_SENTENCE;
+ private static final String SPELLING_IGNORE_SINGLE_LETTERS = PreferenceConstants.SPELLING_IGNORE_SINGLE_LETTERS;
+ private static final String SPELLING_IGNORE_STRING_LITERALS = PreferenceConstants.SPELLING_IGNORE_STRING_LITERALS;
+ private static final String SPELLING_IGNORE_UPPER = PreferenceConstants.SPELLING_IGNORE_UPPER;
+ private static final String SPELLING_IGNORE_URLS = PreferenceConstants.SPELLING_IGNORE_URLS;
+
+ /**
+ * @see IPreferenceStore#addPropertyChangeListener(IPropertyChangeListener)
+ */
+ public static void addPropertyChangeListener(IPropertyChangeListener listener) {
+ preferenceStore.addPropertyChangeListener(listener);
+ }
+
+ /**
+ * @see IPreferenceStore#removePropertyChangeListener(IPropertyChangeListener)
+ */
+ public static void removePropertyChangeListener(IPropertyChangeListener listener) {
+ preferenceStore.removePropertyChangeListener(listener);
+ }
+
+ /**
+ * The locale used for spell checking.
+ */
+ public static String getSpellingLocale() {
+ return preferenceStore.getString(SPELLING_LOCALE);
+ }
+
+ /**
+ * The workspace user dictionary.
+ */
+ public static String getSpellingUserDictionary() {
+ return preferenceStore.getString(SPELLING_USER_DICTIONARY);
+ }
+
+ /**
+ * The encoding of the workspace user dictionary.
+ */
+ public static String getSpellingUserDictionaryEncoding() {
+ return preferenceStore.getString(SPELLING_USER_DICTIONARY_ENCODING);
+ }
+
+ /**
+ * Returns the number of proposals offered during spell checking.
+ */
+ public static int spellingProposalThreshold() {
+ return preferenceStore.getInt(SPELLING_PROPOSAL_THRESHOLD);
+ }
+
+ /**
+ * Returns true
if spelling content assist is enabled.
+ */
+ public static boolean isEnabledSpellingContentAssist() {
+ return preferenceStore.getBoolean(SPELLING_ENABLE_CONTENTASSIST);
+ }
+
+ /**
+ * Returns true
if words containing digits should
+ * be skipped during spell checking.
+ */
+ public static boolean isIgnoreDigits() {
+ return preferenceStore.getBoolean(SPELLING_IGNORE_DIGITS);
+ }
+
+ /**
+ * Returns true
if mixed case words should be
+ * skipped during spell checking.
+ */
+ public static boolean isIgnoreMixed() {
+ return preferenceStore.getBoolean(SPELLING_IGNORE_MIXED);
+ }
+
+ /**
+ * Returns true
if non-letters at word boundaries
+ * should be ignored during spell checking.
+ */
+ public static boolean isIgnoreNonLetters() {
+ return preferenceStore.getBoolean(SPELLING_IGNORE_NON_LETTERS);
+ }
+
+ /**
+ * Returns true
if sentence capitalization should
+ * be ignored during spell checking.
+ */
+ public static boolean isIgnoreSentence() {
+ return preferenceStore.getBoolean(SPELLING_IGNORE_SENTENCE);
+ }
+
+ /**
+ * Returns true
if single letters
+ * should be ignored during spell checking.
+ */
+ public static boolean isIgnoreSingleLetters() {
+ return preferenceStore.getBoolean(SPELLING_IGNORE_SINGLE_LETTERS);
+ }
+
+ /**
+ * Returns true
if string literals
+ * should be ignored during spell checking.
+ */
+ public static boolean isIgnoreStringLiterals() {
+ return preferenceStore.getBoolean(SPELLING_IGNORE_STRING_LITERALS);
+ }
+
+ /**
+ * Returns true
if upper case words should be
+ * skipped during spell checking.
+ */
+ public static boolean isIgnoreUpper() {
+ return preferenceStore.getBoolean(SPELLING_IGNORE_UPPER);
+ }
+
+ /**
+ * Returns true
if URLs should be ignored during
+ * spell checking.
+ */
+ public static boolean isIgnoreUrls() {
+ return preferenceStore.getBoolean(SPELLING_IGNORE_URLS);
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/TaskTagDictionary.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/TaskTagDictionary.java
new file mode 100644
index 00000000000..baf4acbbbcd
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/TaskTagDictionary.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import java.net.URL;
+import java.util.StringTokenizer;
+
+import org.eclipse.core.runtime.Plugin;
+import org.eclipse.core.runtime.Preferences.IPropertyChangeListener;
+import org.eclipse.core.runtime.Preferences.PropertyChangeEvent;
+
+import org.eclipse.cdt.core.CCorePlugin;
+import org.eclipse.cdt.core.CCorePreferenceConstants;
+
+import org.eclipse.cdt.internal.ui.text.spelling.engine.AbstractSpellDictionary;
+
+/**
+ * Dictionary for task tags.
+ */
+public class TaskTagDictionary extends AbstractSpellDictionary implements IPropertyChangeListener {
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.AbstractSpellDictionary#getName()
+ */
+ protected final URL getURL() {
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.AbstractSpellDictionary#load(java.net.URL)
+ */
+ protected synchronized boolean load(final URL url) {
+ final Plugin plugin= CCorePlugin.getDefault();
+ if (plugin != null) {
+ plugin.getPluginPreferences().addPropertyChangeListener(this);
+ return updateTaskTags();
+ }
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent)
+ */
+ public void propertyChange(final PropertyChangeEvent event) {
+ if (CCorePreferenceConstants.TODO_TASK_TAGS.equals(event.getProperty()))
+ updateTaskTags();
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.ISpellDictionary#unload()
+ */
+ public synchronized void unload() {
+ final Plugin plugin= CCorePlugin.getDefault();
+ if (plugin != null)
+ plugin.getPluginPreferences().removePropertyChangeListener(this);
+
+ super.unload();
+ }
+
+ /**
+ * Handles the compiler task tags property change event.
+ *
+ * @return true
if the task tags got updated
+ */
+ protected boolean updateTaskTags() {
+ final String tags= CCorePlugin.getOption(CCorePreferenceConstants.TODO_TASK_TAGS);
+ if (tags != null) {
+ unload();
+
+ final StringTokenizer tokenizer= new StringTokenizer(tags, ","); //$NON-NLS-1$
+ while (tokenizer.hasMoreTokens())
+ hashWord(tokenizer.nextToken());
+
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.AbstractSpellDictionary#stripNonLetters(java.lang.String)
+ */
+ protected String stripNonLetters(String word) {
+ return word;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/TextSpellingEngine.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/TextSpellingEngine.java
new file mode 100644
index 00000000000..b50ad739f92
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/TextSpellingEngine.java
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+import org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector;
+
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEventListener;
+
+/**
+ * Text spelling engine
+ */
+public class TextSpellingEngine extends SpellingEngine {
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.SpellingEngine#check(org.eclipse.jface.text.IDocument, org.eclipse.jface.text.IRegion[], org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker, org.eclipse.ui.texteditor.spelling.ISpellingProblemCollector, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ protected void check(IDocument document, IRegion[] regions, ISpellChecker checker, ISpellingProblemCollector collector, IProgressMonitor monitor) {
+ ISpellEventListener listener= new SpellEventListener(collector, document);
+ try {
+ checker.addListener(listener);
+ for (int i= 0; i < regions.length; i++) {
+ if (monitor != null && monitor.isCanceled())
+ return;
+ checker.execute(new SpellCheckIterator(document, regions[i], checker.getLocale()));
+ }
+ } finally {
+ checker.removeListener(listener);
+ }
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordCompletionProposalComputer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordCompletionProposalComputer.java
new file mode 100644
index 00000000000..aaf10588424
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordCompletionProposalComputer.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext;
+import org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer;
+
+import org.eclipse.cdt.internal.ui.CPluginImages;
+import org.eclipse.cdt.internal.ui.text.contentassist.CCompletionProposal;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.RankedWordProposal;
+
+/**
+ * Content assist processor to complete words.
+ * Note: This is currently not supported because the spelling engine
+ * cannot return word proposals but only correction proposals.
+ */
+public final class WordCompletionProposalComputer implements ICompletionProposalComputer {
+ /** The prefix rank shift */
+ private static final int PREFIX_RANK_SHIFT= 500;
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#computeCompletionProposals(org.eclipse.jface.text.contentassist.TextContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public List computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) {
+ if (contributes()) {
+ try {
+ IDocument document= context.getDocument();
+ final int offset= context.getInvocationOffset();
+
+ final IRegion region= document.getLineInformationOfOffset(offset);
+ final String content= document.get(region.getOffset(), region.getLength());
+
+ int index= offset - region.getOffset() - 1;
+ while (index >= 0 && Character.isLetter(content.charAt(index)))
+ index--;
+
+ final int start= region.getOffset() + index + 1;
+ final String candidate= content.substring(index + 1, offset - region.getOffset());
+
+ if (candidate.length() > 0) {
+ final ISpellCheckEngine engine= SpellCheckEngine.getInstance();
+ final ISpellChecker checker= engine.getSpellChecker();
+
+ if (checker != null) {
+ final List proposals= new ArrayList(checker.getProposals(candidate, Character.isUpperCase(candidate.charAt(0))));
+ final List result= new ArrayList(proposals.size());
+
+ for (Iterator it= proposals.iterator(); it.hasNext();) {
+ RankedWordProposal word= (RankedWordProposal) it.next();
+ String text= word.getText();
+ if (text.startsWith(candidate))
+ word.setRank(word.getRank() + PREFIX_RANK_SHIFT);
+
+ result.add(new CCompletionProposal(text, start, candidate.length(),
+ CPluginImages.get(CPluginImages.IMG_CORRECTION_RENAME), text, word.getRank()) {
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.java.JavaCompletionProposal#validate(org.eclipse.jface.text.IDocument, int, org.eclipse.jface.text.DocumentEvent)
+ */
+ public boolean validate(IDocument doc, int validate_offset, DocumentEvent event) {
+ return offset == validate_offset;
+ }
+ });
+ }
+
+ return result;
+ }
+ }
+ } catch (BadLocationException exception) {
+ // log & ignore
+ CUIPlugin.getDefault().log(exception);
+ }
+ }
+ return Collections.EMPTY_LIST;
+ }
+
+ private boolean contributes() {
+ return SpellingPreferences.isEnabledSpellingContentAssist();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#computeContextInformation(org.eclipse.jface.text.contentassist.TextContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public List computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) {
+ return Collections.EMPTY_LIST;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#getErrorMessage()
+ */
+ public String getErrorMessage() {
+ return null; // no error message available
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.java.IJavaCompletionProposalComputer#sessionStarted()
+ */
+ public void sessionStarted() {
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.java.IJavaCompletionProposalComputer#sessionEnded()
+ */
+ public void sessionEnded() {
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordCorrectionProposal.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordCorrectionProposal.java
new file mode 100644
index 00000000000..99ea295e101
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordCorrectionProposal.java
@@ -0,0 +1,172 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+
+import org.eclipse.cdt.ui.text.ICCompletionProposal;
+import org.eclipse.cdt.ui.text.IInvocationContext;
+
+import org.eclipse.cdt.internal.ui.CPluginImages;
+import org.eclipse.cdt.internal.ui.text.IHtmlTagConstants;
+
+/**
+ * Proposal to correct the incorrectly spelled word.
+ */
+public class WordCorrectionProposal implements ICCompletionProposal {
+ /** The invocation context */
+ private final IInvocationContext fContext;
+
+ /** The length in the document */
+ private final int fLength;
+
+ /** The line where to apply the correction */
+ private final String fLine;
+
+ /** The offset in the document */
+ private final int fOffset;
+
+ /** The relevance of this proposal */
+ private final int fRelevance;
+
+ /** The word to complete */
+ private final String fWord;
+
+ /**
+ * Creates a new word correction proposal.
+ *
+ * @param word the corrected word
+ * @param arguments the problem arguments associated with the spelling problem
+ * @param offset the offset in the document where to apply the proposal
+ * @param length the length in the document to apply the proposal
+ * @param context the invocation context for this proposal
+ * @param relevance the relevance of this proposal
+ */
+ public WordCorrectionProposal(final String word, final String[] arguments, final int offset,
+ final int length, final IInvocationContext context, final int relevance) {
+ fWord= Character.isUpperCase(arguments[0].charAt(0)) ?
+ Character.toUpperCase(word.charAt(0)) + word.substring(1) : word;
+
+ fOffset= offset;
+ fLength= length;
+ fContext= context;
+ fRelevance= relevance;
+
+ final StringBuffer buffer= new StringBuffer(80);
+
+ buffer.append("...
"); //$NON-NLS-1$
+ buffer.append(getHtmlRepresentation(arguments[1]));
+ buffer.append(""); //$NON-NLS-1$
+ buffer.append(getHtmlRepresentation(fWord));
+ buffer.append(""); //$NON-NLS-1$
+ buffer.append(getHtmlRepresentation(arguments[2]));
+ buffer.append("
..."); //$NON-NLS-1$
+
+ fLine= buffer.toString();
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse.jface.text.IDocument)
+ */
+ public final void apply(final IDocument document) {
+ try {
+ document.replace(fOffset, fLength, fWord);
+ } catch (BadLocationException exception) {
+ // Do nothing
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ return fLine;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
+ */
+ public final IContextInformation getContextInformation() {
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ return Messages.bind(Messages.Spelling_correct_label, fWord);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return CPluginImages.get(CPluginImages.IMG_CORRECTION_RENAME);
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.java.IJavaCompletionProposal#getRelevance()
+ */
+ public final int getRelevance() {
+ return fRelevance;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(org.eclipse.jface.text.IDocument)
+ */
+ public final Point getSelection(final IDocument document) {
+ int offset= fContext.getSelectionOffset();
+ int length= fContext.getSelectionLength();
+
+ final int delta= fWord.length() - fLength;
+ if (offset <= fOffset && offset + length >= fOffset) {
+ length += delta;
+ } else if (offset > fOffset && offset + length > fOffset + fLength) {
+ offset += delta;
+ length -= delta;
+ } else {
+ length += delta;
+ }
+
+ return new Point(offset, length);
+ }
+
+ /**
+ * Returns the html representation of the specified string.
+ *
+ * @param string The string to return the html representation for
+ * @return The html representation for the string
+ */
+ public static String getHtmlRepresentation(final String string) {
+ final int length= string.length();
+ final StringBuffer buffer= new StringBuffer(string);
+
+ for (int offset= length - 1; offset >= 0; offset--) {
+ for (int index= 0; index < IHtmlTagConstants.HTML_ENTITY_CHARACTERS.length; index++) {
+ if (string.charAt(offset) == IHtmlTagConstants.HTML_ENTITY_CHARACTERS[index]) {
+ buffer.replace(offset, offset + 1, String.valueOf(IHtmlTagConstants.HTML_ENTITY_CODES[index]));
+ break;
+ }
+ }
+ }
+ return buffer.toString();
+ }
+
+ public String getIdString() {
+ return fWord;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordIgnoreProposal.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordIgnoreProposal.java
new file mode 100644
index 00000000000..ddc1a5cb624
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordIgnoreProposal.java
@@ -0,0 +1,116 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.quickassist.IQuickAssistInvocationContext;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.texteditor.ITextEditor;
+import org.eclipse.ui.texteditor.spelling.SpellingProblem;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+import org.eclipse.cdt.ui.text.ICCompletionProposal;
+import org.eclipse.cdt.ui.text.IInvocationContext;
+
+import org.eclipse.cdt.internal.ui.CPluginImages;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker;
+
+/**
+ * Proposal to ignore the word during the current editing session.
+ */
+public class WordIgnoreProposal implements ICCompletionProposal {
+ /** The invocation context */
+ private IInvocationContext fContext;
+
+ /** The word to ignore */
+ private String fWord;
+
+ /**
+ * Creates a new spell ignore proposal.
+ *
+ * @param word The word to ignore
+ * @param context The invocation context
+ */
+ public WordIgnoreProposal(final String word, final IInvocationContext context) {
+ fWord= word;
+ fContext= context;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#apply(org.eclipse.jface.text.IDocument)
+ */
+ public final void apply(final IDocument document) {
+ final ISpellCheckEngine engine= SpellCheckEngine.getInstance();
+ final ISpellChecker checker= engine.getSpellChecker();
+ if (checker != null) {
+ checker.ignoreWord(fWord);
+ if (fContext instanceof IQuickAssistInvocationContext) {
+ ISourceViewer sourceViewer= ((IQuickAssistInvocationContext) fContext).getSourceViewer();
+ if (sourceViewer != null) {
+ SpellingProblem.removeAll(sourceViewer, fWord);
+ }
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getAdditionalProposalInfo()
+ */
+ public String getAdditionalProposalInfo() {
+ return Messages.bind(Messages.Spelling_ignore_info, WordCorrectionProposal.getHtmlRepresentation(fWord));
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getContextInformation()
+ */
+ public final IContextInformation getContextInformation() {
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getDisplayString()
+ */
+ public String getDisplayString() {
+ return Messages.bind(Messages.Spelling_ignore_label, fWord);
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getImage()
+ */
+ public Image getImage() {
+ return CPluginImages.get(CPluginImages.IMG_OBJS_NLS_NEVER_TRANSLATE);
+ }
+ /*
+ * @see org.eclipse.cdt.ui.text.java.IJavaCompletionProposal#getRelevance()
+ */
+ public final int getRelevance() {
+ return Integer.MIN_VALUE + 1;
+ }
+
+ /*
+ * @see org.eclipse.jface.text.contentassist.ICompletionProposal#getSelection(org.eclipse.jface.text.IDocument)
+ */
+ public final Point getSelection(final IDocument document) {
+ return new Point(fContext.getSelectionOffset(), fContext.getSelectionLength());
+ }
+
+ public String getIdString() {
+ return fWord;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordQuickFixProcessor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordQuickFixProcessor.java
new file mode 100644
index 00000000000..e9cf76746f7
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/WordQuickFixProcessor.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.cdt.core.model.ITranslationUnit;
+import org.eclipse.cdt.ui.text.ICCompletionProposal;
+import org.eclipse.cdt.ui.text.IInvocationContext;
+import org.eclipse.cdt.ui.text.IProblemLocation;
+import org.eclipse.cdt.ui.text.IQuickFixProcessor;
+
+import org.eclipse.cdt.internal.ui.text.IHtmlTagConstants;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellCheckEngine;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker;
+import org.eclipse.cdt.internal.ui.text.spelling.engine.RankedWordProposal;
+
+/**
+ * Quick fix processor for incorrectly spelled words.
+ */
+public class WordQuickFixProcessor implements IQuickFixProcessor {
+ /**
+ * Creates a new word quick fix processor.
+ */
+ public WordQuickFixProcessor() {
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.java.IQuickFixProcessor#getCorrections(org.eclipse.cdt.ui.text.java.ContentAssistInvocationContext,org.eclipse.cdt.ui.text.java.IProblemLocation[])
+ */
+ public ICCompletionProposal[] getCorrections(IInvocationContext context, IProblemLocation[] locations) throws CoreException {
+ final int threshold= SpellingPreferences.spellingProposalThreshold();
+
+ int size= 0;
+ List proposals= null;
+ String[] arguments= null;
+
+ IProblemLocation location= null;
+ RankedWordProposal proposal= null;
+ ICCompletionProposal[] result= null;
+
+ boolean fixed= false;
+ boolean match= false;
+ boolean sentence= false;
+
+ final ISpellCheckEngine engine= SpellCheckEngine.getInstance();
+ final ISpellChecker checker= engine.getSpellChecker();
+
+ if (checker != null) {
+ for (int index= 0; index < locations.length; index++) {
+ location= locations[index];
+ if (location.getProblemId() == CSpellingReconcileStrategy.SPELLING_PROBLEM_ID) {
+ arguments= location.getProblemArguments();
+ if (arguments != null && arguments.length > 4) {
+ sentence= Boolean.valueOf(arguments[3]).booleanValue();
+ match= Boolean.valueOf(arguments[4]).booleanValue();
+ fixed= arguments[0].charAt(0) == IHtmlTagConstants.HTML_TAG_PREFIX;
+
+ if ((sentence && match) && !fixed) {
+ result= new ICCompletionProposal[] { new ChangeCaseProposal(arguments, location.getOffset(), location.getLength(), context, engine.getLocale())};
+ } else {
+ proposals= new ArrayList(checker.getProposals(arguments[0], sentence));
+ size= proposals.size();
+
+ if (threshold > 0 && size > threshold) {
+ Collections.sort(proposals);
+ proposals= proposals.subList(size - threshold - 1, size - 1);
+ size= proposals.size();
+ }
+
+ boolean extendable= !fixed ? (checker.acceptsWords() || AddWordProposal.canAskToConfigure()) : false;
+ result= new ICCompletionProposal[size + (extendable ? 3 : 2)];
+
+ for (index= 0; index < size; index++) {
+ proposal= (RankedWordProposal)proposals.get(index);
+ result[index]= new WordCorrectionProposal(proposal.getText(), arguments, location.getOffset(), location.getLength(), context, proposal.getRank());
+ }
+
+ if (extendable)
+ result[index++]= new AddWordProposal(arguments[0], context);
+
+ result[index++]= new WordIgnoreProposal(arguments[0], context);
+ result[index++]= new DisableSpellCheckingProposal(context);
+ }
+ break;
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.java.IQuickFixProcessor#hasCorrections(org.eclipse.cdt.core.ICompilationUnit,int)
+ */
+ public final boolean hasCorrections(ITranslationUnit unit, int id) {
+ return id == CSpellingReconcileStrategy.SPELLING_PROBLEM_ID;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/AbstractSpellDictionary.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/AbstractSpellDictionary.java
new file mode 100644
index 00000000000..fb179db494a
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/AbstractSpellDictionary.java
@@ -0,0 +1,608 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+import java.nio.charset.CodingErrorAction;
+import java.nio.charset.MalformedInputException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+
+import org.eclipse.cdt.internal.ui.text.spelling.Messages;
+import org.eclipse.cdt.internal.ui.text.spelling.SpellingPreferences;
+
+/**
+ * Partial implementation of a spell dictionary.
+ */
+public abstract class AbstractSpellDictionary implements ISpellDictionary {
+ /** The bucket capacity */
+ protected static final int BUCKET_CAPACITY= 4;
+
+ /** The word buffer capacity */
+ protected static final int BUFFER_CAPACITY= 32;
+
+ /** The distance threshold */
+ protected static final int DISTANCE_THRESHOLD= 160;
+
+ /** The hash capacity */
+ protected static final int HASH_CAPACITY= 22 * 1024;
+
+ /** The phonetic distance algorithm */
+ private IPhoneticDistanceAlgorithm fDistanceAlgorithm= new DefaultPhoneticDistanceAlgorithm();
+
+ /** The mapping from phonetic hashes to word lists */
+ private final Map fHashBuckets= new HashMap(HASH_CAPACITY);
+
+ /** The phonetic hash provider */
+ private IPhoneticHashProvider fHashProvider= new DefaultPhoneticHashProvider();
+
+ /** Is the dictionary already loaded? */
+ private boolean fLoaded= false;
+ /**
+ * Must the dictionary be loaded?
+ */
+ private boolean fMustLoad= true;
+
+ /**
+ * Tells whether to strip non-letters at word boundaries.
+ */
+ boolean fIsStrippingNonLetters= true;
+
+ /**
+ * Returns all candidates with the same phonetic hash.
+ *
+ * @param hash
+ * The hash to retrieve the candidates of
+ * @return Array of candidates for the phonetic hash
+ */
+ protected final Object getCandidates(final String hash) {
+ return fHashBuckets.get(hash);
+ }
+
+ /**
+ * Returns all candidates that have a phonetic hash within a bounded
+ * distance to the specified word.
+ *
+ * @param word
+ * The word to find the nearest matches for
+ * @param sentence
+ * true
iff the proposals start a new sentence,
+ * false
otherwise
+ * @param hashs
+ * Array of close hashes to find the matches
+ * @return Set of ranked words with bounded distance to the specified word
+ */
+ protected final Set getCandidates(final String word, final boolean sentence, final ArrayList hashs) {
+
+ int distance= 0;
+ String hash= null;
+
+ final StringBuffer buffer= new StringBuffer(BUFFER_CAPACITY);
+ final HashSet result= new HashSet(BUCKET_CAPACITY * hashs.size());
+
+ for (int index= 0; index < hashs.size(); index++) {
+
+ hash= (String)hashs.get(index);
+
+ final Object candidates= getCandidates(hash);
+ if (candidates == null)
+ continue;
+ else if (candidates instanceof String) {
+ String candidate= (String)candidates;
+ distance= fDistanceAlgorithm.getDistance(word, candidate);
+ if (distance < DISTANCE_THRESHOLD) {
+ buffer.setLength(0);
+ buffer.append(candidate);
+ if (sentence)
+ buffer.setCharAt(0, Character.toUpperCase(buffer.charAt(0)));
+ result.add(new RankedWordProposal(buffer.toString(), -distance));
+ }
+ continue;
+ }
+
+ final ArrayList candidateList= (ArrayList)candidates;
+ for (int offset= 0; offset < candidateList.size(); offset++) {
+
+ String candidate= (String)candidateList.get(offset);
+ distance= fDistanceAlgorithm.getDistance(word, candidate);
+
+ if (distance < DISTANCE_THRESHOLD) {
+
+ buffer.setLength(0);
+ buffer.append(candidate);
+
+ if (sentence)
+ buffer.setCharAt(0, Character.toUpperCase(buffer.charAt(0)));
+
+ result.add(new RankedWordProposal(buffer.toString(), -distance));
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns all approximations that have a phonetic hash with smallest
+ * possible distance to the specified word.
+ *
+ * @param word
+ * The word to find the nearest matches for
+ * @param sentence
+ * true
iff the proposals start a new sentence,
+ * false
otherwise
+ * @param result
+ * Set of ranked words with smallest possible distance to the
+ * specified word
+ */
+ protected final void getCandidates(final String word, final boolean sentence, final Set result) {
+
+ int distance= 0;
+ int minimum= Integer.MAX_VALUE;
+
+ StringBuffer buffer= new StringBuffer(BUFFER_CAPACITY);
+
+ final Object candidates= getCandidates(fHashProvider.getHash(word));
+ if (candidates == null)
+ return;
+ else if (candidates instanceof String) {
+ String candidate= (String)candidates;
+ distance= fDistanceAlgorithm.getDistance(word, candidate);
+ buffer.append(candidate);
+ if (sentence)
+ buffer.setCharAt(0, Character.toUpperCase(buffer.charAt(0)));
+ result.add(new RankedWordProposal(buffer.toString(), -distance));
+ return;
+ }
+
+ final ArrayList candidateList= (ArrayList)candidates;
+ final ArrayList matches= new ArrayList(candidateList.size());
+
+ for (int index= 0; index < candidateList.size(); index++) {
+ String candidate= (String)candidateList.get(index);
+ distance= fDistanceAlgorithm.getDistance(word, candidate);
+
+ if (distance <= minimum) {
+
+ if (distance < minimum)
+ matches.clear();
+
+ buffer.setLength(0);
+ buffer.append(candidate);
+
+ if (sentence)
+ buffer.setCharAt(0, Character.toUpperCase(buffer.charAt(0)));
+
+ matches.add(new RankedWordProposal(buffer.toString(), -distance));
+ minimum= distance;
+ }
+ }
+
+ result.addAll(matches);
+ }
+
+ /**
+ * Tells whether this dictionary is empty.
+ *
+ * @return true
if this dictionary is empty
+ */
+ protected boolean isEmpty() {
+ return fHashBuckets.size() == 0;
+ }
+
+ /**
+ * Returns the used phonetic distance algorithm.
+ *
+ * @return The phonetic distance algorithm
+ */
+ protected final IPhoneticDistanceAlgorithm getDistanceAlgorithm() {
+ return fDistanceAlgorithm;
+ }
+
+ /**
+ * Returns the used phonetic hash provider.
+ *
+ * @return The phonetic hash provider
+ */
+ protected final IPhoneticHashProvider getHashProvider() {
+ return fHashProvider;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellDictionary#getProposals(java.lang.String,boolean)
+ */
+ public Set getProposals(final String word, final boolean sentence) {
+
+ try {
+
+ if (!fLoaded) {
+ synchronized (this) {
+ fLoaded= load(getURL());
+ if (fLoaded)
+ compact();
+ }
+ }
+
+ } catch (MalformedURLException exception) {
+ // Do nothing
+ }
+
+ final String hash= fHashProvider.getHash(word);
+ final char[] mutators= fHashProvider.getMutators();
+
+ final ArrayList neighborhood= new ArrayList((word.length() + 1) * (mutators.length + 2));
+ neighborhood.add(hash);
+
+ final Set candidates= getCandidates(word, sentence, neighborhood);
+ neighborhood.clear();
+
+ char previous= 0;
+ char next= 0;
+
+ char[] characters= word.toCharArray();
+ for (int index= 0; index < word.length() - 1; index++) {
+
+ next= characters[index];
+ previous= characters[index + 1];
+
+ characters[index]= previous;
+ characters[index + 1]= next;
+
+ neighborhood.add(fHashProvider.getHash(new String(characters)));
+
+ characters[index]= next;
+ characters[index + 1]= previous;
+ }
+
+ final String sentinel= word + " "; //$NON-NLS-1$
+
+ characters= sentinel.toCharArray();
+ int offset= characters.length - 1;
+
+ while (true) {
+
+ for (int index= 0; index < mutators.length; index++) {
+
+ characters[offset]= mutators[index];
+ neighborhood.add(fHashProvider.getHash(new String(characters)));
+ }
+
+ if (offset == 0)
+ break;
+
+ characters[offset]= characters[offset - 1];
+ --offset;
+ }
+
+ char mutated= 0;
+ characters= word.toCharArray();
+
+ for (int index= 0; index < word.length(); index++) {
+
+ mutated= characters[index];
+ for (int mutator= 0; mutator < mutators.length; mutator++) {
+
+ characters[index]= mutators[mutator];
+ neighborhood.add(fHashProvider.getHash(new String(characters)));
+ }
+ characters[index]= mutated;
+ }
+
+ characters= word.toCharArray();
+ final char[] deleted= new char[characters.length - 1];
+
+ for (int index= 0; index < deleted.length; index++)
+ deleted[index]= characters[index];
+
+ next= characters[characters.length - 1];
+ offset= deleted.length;
+
+ while (true) {
+
+ neighborhood.add(fHashProvider.getHash(new String(characters)));
+ if (offset == 0)
+ break;
+
+ previous= next;
+ next= deleted[offset - 1];
+
+ deleted[offset - 1]= previous;
+ --offset;
+ }
+
+ neighborhood.remove(hash);
+ final Set matches= getCandidates(word, sentence, neighborhood);
+
+ if (matches.size() == 0 && candidates.size() == 0)
+ getCandidates(word, sentence, candidates);
+
+ candidates.addAll(matches);
+
+ return candidates;
+ }
+
+ /**
+ * Returns the URL of the dictionary word list.
+ *
+ * @throws MalformedURLException
+ * if the URL could not be retrieved
+ * @return The URL of the dictionary word list
+ */
+ protected abstract URL getURL() throws MalformedURLException;
+
+ /**
+ * Hashes the word into the dictionary.
+ *
+ * @param word
+ * The word to hash in the dictionary
+ */
+ protected final void hashWord(final String word) {
+
+ final String hash= fHashProvider.getHash(word);
+ Object bucket= fHashBuckets.get(hash);
+
+ if (bucket == null) {
+ fHashBuckets.put(hash, word);
+ } else if (bucket instanceof ArrayList) {
+ ((ArrayList)bucket).add(word);
+ } else {
+ ArrayList list= new ArrayList(BUCKET_CAPACITY);
+ list.add(bucket);
+ list.add(word);
+ fHashBuckets.put(hash, list);
+ }
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellDictionary#isCorrect(java.lang.String)
+ */
+ public boolean isCorrect(String word) {
+ word= stripNonLetters(word);
+ try {
+
+ if (!fLoaded) {
+ synchronized (this) {
+ fLoaded= load(getURL());
+ if (fLoaded)
+ compact();
+ }
+ }
+
+ } catch (MalformedURLException exception) {
+ // Do nothing
+ }
+
+ final Object candidates= getCandidates(fHashProvider.getHash(word));
+ if (candidates == null)
+ return false;
+ else if (candidates instanceof String) {
+ String candidate= (String)candidates;
+ if (candidate.equals(word) || candidate.equals(word.toLowerCase()))
+ return true;
+ return false;
+ }
+ final ArrayList candidateList= (ArrayList)candidates;
+ if (candidateList.contains(word) || candidateList.contains(word.toLowerCase()))
+ return true;
+
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellDictionary#setStripNonLetters(boolean)
+ */
+ public void setStripNonLetters(boolean state) {
+ fIsStrippingNonLetters= state;
+ }
+
+ /**
+ * Strips non-letter characters from the given word.
+ *
+ * This will only happen if the corresponding preference is enabled.
+ *
+ *
+ * @param word the word to strip
+ * @return the stripped word
+ */
+ protected String stripNonLetters(String word) {
+ if (!fIsStrippingNonLetters)
+ return word;
+
+ int i= 0;
+ int j= word.length() - 1;
+ while (i <= j && !Character.isLetter(word.charAt(i)))
+ i++;
+ if (i > j)
+ return ""; //$NON-NLS-1$
+
+ while (j > i && !Character.isLetter(word.charAt(j)))
+ j--;
+
+ return word.substring(i, j+1);
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.ISpellDictionary#isLoaded()
+ */
+ public final synchronized boolean isLoaded() {
+ return fLoaded || fHashBuckets.size() > 0;
+ }
+
+ /**
+ * Loads a dictionary word list from disk.
+ *
+ * @param url
+ * The URL of the word list to load
+ * @return true
iff the word list could be loaded, false
+ * otherwise
+ */
+ protected synchronized boolean load(final URL url) {
+ if (!fMustLoad)
+ return fLoaded;
+
+ if (url != null) {
+ InputStream stream= null;
+ int line= 0;
+ try {
+ stream= url.openStream();
+ if (stream != null) {
+ String word= null;
+
+ // Setup a reader with a decoder in order to read over malformed input if needed.
+ CharsetDecoder decoder= Charset.forName(getEncoding()).newDecoder();
+ decoder.onMalformedInput(CodingErrorAction.REPORT);
+ decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
+ final BufferedReader reader= new BufferedReader(new InputStreamReader(stream, decoder));
+
+ boolean doRead= true;
+ while (doRead) {
+ try {
+ word= reader.readLine();
+ } catch (MalformedInputException ex) {
+ // Tell the decoder to replace malformed input in order to read the line.
+ decoder.onMalformedInput(CodingErrorAction.REPLACE);
+ decoder.reset();
+ word= reader.readLine();
+ decoder.onMalformedInput(CodingErrorAction.REPORT);
+
+ String message= Messages.bind(Messages.AbstractSpellingDictionary_encodingError,
+ new String[] { word, decoder.replacement(), url.toString() });
+ IStatus status= new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, IStatus.OK, message, ex);
+ CUIPlugin.getDefault().log(status);
+
+ doRead= word != null;
+ continue;
+ }
+ doRead= word != null;
+ if (doRead)
+ hashWord(word);
+ }
+ return true;
+ }
+ } catch (FileNotFoundException e) {
+ String urlString= url.toString();
+ String lowercaseUrlString= urlString.toLowerCase();
+ if (urlString.equals(lowercaseUrlString)) {
+ CUIPlugin.getDefault().log(e);
+ } else {
+ try {
+ return load(new URL(lowercaseUrlString));
+ } catch (MalformedURLException ex) {
+ CUIPlugin.getDefault().log(ex);
+ }
+ }
+ } catch (IOException exception) {
+ if (line > 0) {
+ String message= Messages.bind(Messages.AbstractSpellingDictionary_encodingError,
+ String.valueOf(line), url.toString());
+ IStatus status= new Status(IStatus.ERROR, CUIPlugin.PLUGIN_ID, IStatus.OK, message, exception);
+ CUIPlugin.getDefault().log(status);
+ } else {
+ CUIPlugin.getDefault().log(exception);
+ }
+ } finally {
+ fMustLoad= false;
+ try {
+ if (stream != null)
+ stream.close();
+ } catch (IOException x) {
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Compacts the dictionary.
+ */
+ private void compact() {
+ Iterator iter= fHashBuckets.values().iterator();
+ while (iter.hasNext()) {
+ Object element= iter.next();
+ if (element instanceof ArrayList)
+ ((ArrayList)element).trimToSize();
+ }
+ }
+
+ /**
+ * Sets the phonetic distance algorithm to use.
+ *
+ * @param algorithm
+ * The phonetic distance algorithm
+ */
+ protected final void setDistanceAlgorithm(final IPhoneticDistanceAlgorithm algorithm) {
+ fDistanceAlgorithm= algorithm;
+ }
+
+ /**
+ * Sets the phonetic hash provider to use.
+ *
+ * @param provider
+ * The phonetic hash provider
+ */
+ protected final void setHashProvider(final IPhoneticHashProvider provider) {
+ fHashProvider= provider;
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.ISpellDictionary#unload()
+ */
+ public synchronized void unload() {
+ fLoaded= false;
+ fMustLoad= true;
+ fHashBuckets.clear();
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.ISpellDictionary#acceptsWords()
+ */
+ public boolean acceptsWords() {
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellDictionary#addWord(java.lang.String)
+ */
+ public void addWord(final String word) {
+ // Do nothing
+ }
+
+ /**
+ * Returns the encoding of this dictionary.
+ *
+ * @return the encoding of this dictionary
+ */
+ protected String getEncoding() {
+ String encoding= SpellingPreferences.getSpellingUserDictionaryEncoding();
+ if (encoding == null || encoding.length() == 0)
+ encoding= ResourcesPlugin.getEncoding();
+ return encoding;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/DefaultPhoneticDistanceAlgorithm.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/DefaultPhoneticDistanceAlgorithm.java
new file mode 100644
index 00000000000..bbb570bda18
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/DefaultPhoneticDistanceAlgorithm.java
@@ -0,0 +1,102 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+/**
+ * Default phonetic distance algorithm for English words.
+ *
+ * This algorithm implements the Levenshtein text edit distance.
+ *
+ */
+public final class DefaultPhoneticDistanceAlgorithm implements IPhoneticDistanceAlgorithm {
+
+ /** The change case cost */
+ public static final int COST_CASE= 10;
+
+ /** The insert character cost */
+ public static final int COST_INSERT= 95;
+
+ /** The remove character cost */
+ public static final int COST_REMOVE= 95;
+
+ /** The substitute characters cost */
+ public static final int COST_SUBSTITUTE= 100;
+
+ /** The swap characters cost */
+ public static final int COST_SWAP= 90;
+
+ /*
+ * @see org.eclipse.spelling.done.IPhoneticDistanceAlgorithm#getDistance(java.lang.String,java.lang.String)
+ */
+ public final int getDistance(final String from, final String to) {
+
+ final char[] first= (" " + from).toCharArray(); //$NON-NLS-1$
+ final char[] second= (" " + to).toCharArray(); //$NON-NLS-1$
+
+ final int rows= first.length;
+ final int columns= second.length;
+
+ final int[][] metric= new int[rows][columns];
+ for (int column= 1; column < columns; column++)
+ metric[0][column]= metric[0][column - 1] + COST_REMOVE;
+
+ for (int row= 1; row < rows; row++)
+ metric[row][0]= metric[row - 1][0] + COST_INSERT;
+
+ char source, target;
+
+ int swap= Integer.MAX_VALUE;
+ int change= Integer.MAX_VALUE;
+
+ int minimum, diagonal, insert, remove;
+ for (int row= 1; row < rows; row++) {
+
+ source= first[row];
+ for (int column= 1; column < columns; column++) {
+
+ target= second[column];
+ diagonal= metric[row - 1][column - 1];
+
+ if (source == target) {
+ metric[row][column]= diagonal;
+ continue;
+ }
+
+ change= Integer.MAX_VALUE;
+ if (Character.toLowerCase(source) == Character.toLowerCase(target))
+ change= COST_CASE + diagonal;
+
+ swap= Integer.MAX_VALUE;
+ if (row != 1 && column != 1 && source == second[column - 1] && first[row - 1] == target)
+ swap= COST_SWAP + metric[row - 2][column - 2];
+
+ minimum= COST_SUBSTITUTE + diagonal;
+ if (swap < minimum)
+ minimum= swap;
+
+ remove= metric[row][column - 1];
+ if (COST_REMOVE + remove < minimum)
+ minimum= COST_REMOVE + remove;
+
+ insert= metric[row - 1][column];
+ if (COST_INSERT + insert < minimum)
+ minimum= COST_INSERT + insert;
+ if (change < minimum)
+ minimum= change;
+
+ metric[row][column]= minimum;
+ }
+ }
+ return metric[rows - 1][columns - 1];
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/DefaultPhoneticHashProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/DefaultPhoneticHashProvider.java
new file mode 100644
index 00000000000..b51d3f3153d
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/DefaultPhoneticHashProvider.java
@@ -0,0 +1,674 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+/**
+ * Default phonetic hash provider for english languages.
+ *
+ * This algorithm uses an adapted version double metaphone algorithm by
+ * Lawrence Philips.
+ *
+ */
+public final class DefaultPhoneticHashProvider implements IPhoneticHashProvider {
+ private static final String[] meta01= { "ACH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta02= { "BACHER", "MACHER", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta03= { "CAESAR", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta04= { "CHIA", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta05= { "CH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta06= { "CHAE", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta07= { "HARAC", "HARIS", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta08= { "HOR", "HYM", "HIA", "HEM", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ private static final String[] meta09= { "CHORE", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta10= { "VAN ", "VON ", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta11= { "SCH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta12= { "ORCHES", "ARCHIT", "ORCHID", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta13= { "T", "S", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta14= { "A", "O", "U", "E", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ private static final String[] meta15= { "L", "R", "N", "M", "B", "H", "F", "V", "W", " ", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$
+ private static final String[] meta16= { "MC", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta17= { "CZ", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta18= { "WICZ", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta19= { "CIA", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta20= { "CC", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta21= { "I", "E", "H", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta22= { "HU", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta23= { "UCCEE", "UCCES", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta24= { "CK", "CG", "CQ", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta25= { "CI", "CE", "CY", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta26= { "GN", "KN", "PN", "WR", "PS", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+ private static final String[] meta27= { " C", " Q", " G", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta28= { "C", "K", "Q", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta29= { "CE", "CI", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta30= { "DG", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta31= { "I", "E", "Y", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta32= { "DT", "DD", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta33= { "B", "H", "D", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta34= { "B", "H", "D", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta35= { "B", "H", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta36= { "C", "G", "L", "R", "T", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
+ private static final String[] meta37= { "EY", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta38= { "LI", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta39= { "ES", "EP", "EB", "EL", "EY", "IB", "IL", "IN", "IE", "EI", "ER", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$ //$NON-NLS-10$ //$NON-NLS-11$ //$NON-NLS-12$
+ private static final String[] meta40= { "ER", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta41= { "DANGER", "RANGER", "MANGER", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta42= { "E", "I", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta43= { "RGY", "OGY", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta44= { "E", "I", "Y", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta45= { "AGGI", "OGGI", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta46= { "VAN ", "VON ", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta47= { "SCH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta48= { "ET", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta49= { "C", "X", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta50= { "JOSE", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta51= { "SAN ", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta52= { "SAN ", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta53= { "JOSE", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta54= { "L", "T", "K", "S", "N", "M", "B", "Z", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$ //$NON-NLS-9$
+ private static final String[] meta55= { "S", "K", "L", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta56= { "ILLO", "ILLA", "ALLE", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta57= { "AS", "OS", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta58= { "A", "O", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta59= { "ALLE", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta60= { "UMB", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta61= { "ER", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta62= { "P", "B", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta63= { "IE", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta64= { "ME", "MA", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta65= { "ISL", "YSL", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta66= { "SUGAR", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta67= { "SH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta68= { "HEIM", "HOEK", "HOLM", "HOLZ", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ private static final String[] meta69= { "SIO", "SIA", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta70= { "SIAN", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta71= { "M", "N", "L", "W", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ private static final String[] meta72= { "Z", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta73= { "Z", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta74= { "SC", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta75= { "OO", "ER", "EN", "UY", "ED", "EM", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+ private static final String[] meta76= { "ER", "EN", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta77= { "I", "E", "Y", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ private static final String[] meta78= { "AI", "OI", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta79= { "S", "Z", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta80= { "TION", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta81= { "TIA", "TCH", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta82= { "TH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta83= { "TTH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta84= { "OM", "AM", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta85= { "VAN ", "VON ", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta86= { "SCH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta87= { "T", "D", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta88= { "WR", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta89= { "WH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta90= { "EWSKI", "EWSKY", "OWSKI", "OWSKY", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
+ private static final String[] meta91= { "SCH", "" }; //$NON-NLS-1$ //$NON-NLS-2$
+ private static final String[] meta92= { "WICZ", "WITZ", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta93= { "IAU", "EAU", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta94= { "AU", "OU", "" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ private static final String[] meta95= { "W", "K", "CZ", "WITZ" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+
+ /** The mutator characters */
+ private static final char[] MUTATOR_CHARACTERS= { 'A', 'B', 'X', 'S', 'K', 'J', 'T', 'F', 'H', 'L', 'M', 'N', 'P', 'R', '0' };
+
+ /** The vowel characters */
+ private static final char[] VOWEL_CHARACTERS= new char[] { 'A', 'E', 'I', 'O', 'U', 'Y' };
+
+ /**
+ * Test whether the specified string contains one of the candidates in the
+ * list.
+ *
+ * @param candidates
+ * Array of candidates to check
+ * @param token
+ * The token to check for occurrences of the candidates
+ * @param offset
+ * The offset where to begin checking in the string
+ * @param length
+ * The length of the range in the string to check
+ * @return true
iff the string contains one of the
+ * candidates, false
otherwise.
+ */
+ protected static final boolean hasOneOf(final String[] candidates, final char[] token, final int offset, final int length) {
+ if (offset < 0 || offset >= token.length || candidates.length == 0)
+ return false;
+
+ final String checkable= new String(token, offset, length);
+ for (int index= 0; index < candidates.length; index++) {
+
+ if (candidates[index].equals(checkable))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Test whether the specified token contains one of the candidates in the
+ * list.
+ *
+ * @param candidates
+ * Array of candidates to check
+ * @param token
+ * The token to check for occurrences of the candidates
+ * @return true
iff the string contains one of the
+ * candidates, false
otherwise.
+ */
+ protected static final boolean hasOneOf(final String[] candidates, final String token) {
+ for (int index= 0; index < candidates.length; index++) {
+
+ if (token.indexOf(candidates[index]) >= 0)
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether the specified token contains a vowel at the specified
+ * offset.
+ *
+ * @param token
+ * The token to check for a vowel
+ * @param offset
+ * The offset where to begin checking in the token
+ * @param length
+ * The length of the range in the token to check
+ * @return true
iff the token contains a vowel, false
+ * otherwise.
+ */
+ protected static final boolean hasVowel(final char[] token, final int offset, final int length) {
+ if (offset >= 0 && offset < length) {
+ final char character= token[offset];
+ for (int index= 0; index < VOWEL_CHARACTERS.length; index++) {
+ if (VOWEL_CHARACTERS[index] == character)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.IPhoneticHasher#getHash(java.lang.String)
+ */
+ public final String getHash(final String word) {
+ final String input= word.toUpperCase() + " "; //$NON-NLS-1$
+ final char[] hashable= input.toCharArray();
+
+ final boolean has95= hasOneOf(meta95, input);
+ final StringBuffer buffer= new StringBuffer(hashable.length);
+
+ int offset= 0;
+ if (hasOneOf(meta26, hashable, 0, 2))
+ offset += 1;
+
+ if (hashable[0] == 'X') {
+ buffer.append('S');
+ offset += 1;
+ }
+
+ while (offset < hashable.length) {
+ switch (hashable[offset]) {
+ case 'A' :
+ case 'E' :
+ case 'I' :
+ case 'O' :
+ case 'U' :
+ case 'Y' :
+ if (offset == 0)
+ buffer.append('A');
+ offset += 1;
+ break;
+ case 'B' :
+ buffer.append('P');
+ if (hashable[offset + 1] == 'B')
+ offset += 2;
+ else
+ offset += 1;
+ break;
+ case 'C' :
+ if ((offset > 1) && !hasVowel(hashable, offset - 2, hashable.length) && hasOneOf(meta01, hashable, (offset - 1), 3) && (hashable[offset + 2] != 'I') && (hashable[offset + 2] != 'E') || hasOneOf(meta02, hashable, (offset - 2), 6)) {
+ buffer.append('K');
+ offset += 2;
+ break;
+ }
+ if ((offset == 0) && hasOneOf(meta03, hashable, offset, 6)) {
+ buffer.append('S');
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta04, hashable, offset, 4)) {
+ buffer.append('K');
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta05, hashable, offset, 2)) {
+ if ((offset > 0) && hasOneOf(meta06, hashable, offset, 4)) {
+ buffer.append('K');
+ offset += 2;
+ break;
+ }
+ if ((offset == 0) && hasOneOf(meta07, hashable, (offset + 1), 5) || hasOneOf(meta08, hashable, offset + 1, 3) && !hasOneOf(meta09, hashable, 0, 5)) {
+ buffer.append('K');
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta10, hashable, 0, 4) || hasOneOf(meta11, hashable, 0, 3) || hasOneOf(meta12, hashable, offset - 2, 6) || hasOneOf(meta13, hashable, offset + 2, 1) || (hasOneOf(meta14, hashable, offset - 1, 1) || (offset == 0)) && hasOneOf(meta15, hashable, offset + 2, 1)) {
+ buffer.append('K');
+ } else {
+ if (offset > 0) {
+ if (hasOneOf(meta16, hashable, 0, 2))
+ buffer.append('K');
+ else
+ buffer.append('X');
+ } else {
+ buffer.append('X');
+ }
+ }
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta17, hashable, offset, 2) && !hasOneOf(meta18, hashable, offset, 4)) {
+ buffer.append('S');
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta19, hashable, offset, 2)) {
+ buffer.append('X');
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta20, hashable, offset, 2) && !((offset == 1) && hashable[0] == 'M')) {
+ if (hasOneOf(meta21, hashable, offset + 2, 1) && !hasOneOf(meta22, hashable, offset + 2, 2)) {
+ if (((offset == 1) && (hashable[offset - 1] == 'A')) || hasOneOf(meta23, hashable, (offset - 1), 5))
+ buffer.append("KS"); //$NON-NLS-1$
+ else
+ buffer.append('X');
+ offset += 3;
+ break;
+ } else {
+ buffer.append('K');
+ offset += 2;
+ break;
+ }
+ }
+ if (hasOneOf(meta24, hashable, offset, 2)) {
+ buffer.append('K');
+ offset += 2;
+ break;
+ } else if (hasOneOf(meta25, hashable, offset, 2)) {
+ buffer.append('S');
+ offset += 2;
+ break;
+ }
+ buffer.append('K');
+ if (hasOneOf(meta27, hashable, offset + 1, 2))
+ offset += 3;
+ else if (hasOneOf(meta28, hashable, offset + 1, 1) && !hasOneOf(meta29, hashable, offset + 1, 2))
+ offset += 2;
+ else
+ offset += 1;
+ break;
+ case '\u00C7' :
+ buffer.append('S');
+ offset += 1;
+ break;
+ case 'D' :
+ if (hasOneOf(meta30, hashable, offset, 2)) {
+ if (hasOneOf(meta31, hashable, offset + 2, 1)) {
+ buffer.append('J');
+ offset += 3;
+ break;
+ } else {
+ buffer.append("TK"); //$NON-NLS-1$
+ offset += 2;
+ break;
+ }
+ }
+ buffer.append('T');
+ if (hasOneOf(meta32, hashable, offset, 2)) {
+ offset += 2;
+ } else {
+ offset += 1;
+ }
+ break;
+ case 'F' :
+ if (hashable[offset + 1] == 'F')
+ offset += 2;
+ else
+ offset += 1;
+ buffer.append('F');
+ break;
+ case 'G' :
+ if (hashable[offset + 1] == 'H') {
+ if ((offset > 0) && !hasVowel(hashable, offset - 1, hashable.length)) {
+ buffer.append('K');
+ offset += 2;
+ break;
+ }
+ if (offset < 3) {
+ if (offset == 0) {
+ if (hashable[offset + 2] == 'I')
+ buffer.append('J');
+ else
+ buffer.append('K');
+ offset += 2;
+ break;
+ }
+ }
+ if ((offset > 1) && hasOneOf(meta33, hashable, offset - 2, 1) || ((offset > 2) && hasOneOf(meta34, hashable, offset - 3, 1)) || ((offset > 3) && hasOneOf(meta35, hashable, offset - 4, 1))) {
+ offset += 2;
+ break;
+ } else {
+ if ((offset > 2) && (hashable[offset - 1] == 'U') && hasOneOf(meta36, hashable, offset - 3, 1)) {
+ buffer.append('F');
+ } else {
+ if ((offset > 0) && (hashable[offset - 1] != 'I'))
+ buffer.append('K');
+ }
+ offset += 2;
+ break;
+ }
+ }
+ if (hashable[offset + 1] == 'N') {
+ if ((offset == 1) && hasVowel(hashable, 0, hashable.length) && !has95) {
+ buffer.append("KN"); //$NON-NLS-1$
+ } else {
+ if (!hasOneOf(meta37, hashable, offset + 2, 2) && (hashable[offset + 1] != 'Y') && !has95) {
+ buffer.append("N"); //$NON-NLS-1$
+ } else {
+ buffer.append("KN"); //$NON-NLS-1$
+ }
+ }
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta38, hashable, offset + 1, 2) && !has95) {
+ buffer.append("KL"); //$NON-NLS-1$
+ offset += 2;
+ break;
+ }
+ if ((offset == 0) && ((hashable[offset + 1] == 'Y') || hasOneOf(meta39, hashable, offset + 1, 2))) {
+ buffer.append('K');
+ offset += 2;
+ break;
+ }
+ if ((hasOneOf(meta40, hashable, offset + 1, 2) || (hashable[offset + 1] == 'Y')) && !hasOneOf(meta41, hashable, 0, 6) && !hasOneOf(meta42, hashable, offset - 1, 1) && !hasOneOf(meta43, hashable, offset - 1, 3)) {
+ buffer.append('K');
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta44, hashable, offset + 1, 1) || hasOneOf(meta45, hashable, offset - 1, 4)) {
+ if (hasOneOf(meta46, hashable, 0, 4) || hasOneOf(meta47, hashable, 0, 3) || hasOneOf(meta48, hashable, offset + 1, 2)) {
+ buffer.append('K');
+ } else {
+ buffer.append('J');
+ }
+ offset += 2;
+ break;
+ }
+ if (hashable[offset + 1] == 'G')
+ offset += 2;
+ else
+ offset += 1;
+ buffer.append('K');
+ break;
+ case 'H' :
+ if (((offset == 0) || hasVowel(hashable, offset - 1, hashable.length)) && hasVowel(hashable, offset + 1, hashable.length)) {
+ buffer.append('H');
+ offset += 2;
+ } else {
+ offset += 1;
+ }
+ break;
+ case 'J' :
+ if (hasOneOf(meta50, hashable, offset, 4) || hasOneOf(meta51, hashable, 0, 4)) {
+ if ((offset == 0) && (hashable[offset + 4] == ' ') || hasOneOf(meta52, hashable, 0, 4)) {
+ buffer.append('H');
+ } else {
+ buffer.append('J');
+ }
+ offset += 1;
+ break;
+ }
+ if ((offset == 0) && !hasOneOf(meta53, hashable, offset, 4)) {
+ buffer.append('J');
+ } else {
+ if (hasVowel(hashable, offset - 1, hashable.length) && !has95 && ((hashable[offset + 1] == 'A') || hashable[offset + 1] == 'O')) {
+ buffer.append('J');
+ } else {
+ if (offset == (hashable.length - 1)) {
+ buffer.append('J');
+ } else {
+ if (!hasOneOf(meta54, hashable, offset + 1, 1) && !hasOneOf(meta55, hashable, offset - 1, 1)) {
+ buffer.append('J');
+ }
+ }
+ }
+ }
+ if (hashable[offset + 1] == 'J')
+ offset += 2;
+ else
+ offset += 1;
+ break;
+ case 'K' :
+ if (hashable[offset + 1] == 'K')
+ offset += 2;
+ else
+ offset += 1;
+ buffer.append('K');
+ break;
+ case 'L' :
+ if (hashable[offset + 1] == 'L') {
+ if (((offset == (hashable.length - 3)) && hasOneOf(meta56, hashable, offset - 1, 4)) || ((hasOneOf(meta57, hashable, (hashable.length - 1) - 1, 2) || hasOneOf(meta58, hashable, hashable.length - 1, 1)) && hasOneOf(meta59, hashable, offset - 1, 4))) {
+ buffer.append('L');
+ offset += 2;
+ break;
+ }
+ offset += 2;
+ } else
+ offset += 1;
+ buffer.append('L');
+ break;
+ case 'M' :
+ if ((hasOneOf(meta60, hashable, offset - 1, 3) && (((offset + 1) == (hashable.length - 1)) || hasOneOf(meta61, hashable, offset + 2, 2))) || (hashable[offset + 1] == 'M'))
+ offset += 2;
+ else
+ offset += 1;
+ buffer.append('M');
+ break;
+ case 'N' :
+ if (hashable[offset + 1] == 'N')
+ offset += 2;
+ else
+ offset += 1;
+ buffer.append('N');
+ break;
+ case '\u00D1' :
+ offset += 1;
+ buffer.append('N');
+ break;
+ case 'P' :
+ if (hashable[offset + 1] == 'N') {
+ buffer.append('F');
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta62, hashable, offset + 1, 1))
+ offset += 2;
+ else
+ offset += 1;
+ buffer.append('P');
+ break;
+ case 'Q' :
+ if (hashable[offset + 1] == 'Q')
+ offset += 2;
+ else
+ offset += 1;
+ buffer.append('K');
+ break;
+ case 'R' :
+ if (!((offset == (hashable.length - 1)) && !has95 && hasOneOf(meta63, hashable, offset - 2, 2) && !hasOneOf(meta64, hashable, offset - 4, 2)))
+ buffer.append('R');
+ if (hashable[offset + 1] == 'R')
+ offset += 2;
+ else
+ offset += 1;
+ break;
+ case 'S' :
+ if (hasOneOf(meta65, hashable, offset - 1, 3)) {
+ offset += 1;
+ break;
+ }
+ if ((offset == 0) && hasOneOf(meta66, hashable, offset, 5)) {
+ buffer.append('X');
+ offset += 1;
+ break;
+ }
+ if (hasOneOf(meta67, hashable, offset, 2)) {
+ if (hasOneOf(meta68, hashable, offset + 1, 4))
+ buffer.append('S');
+ else
+ buffer.append('X');
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta69, hashable, offset, 3) || hasOneOf(meta70, hashable, offset, 4)) {
+ buffer.append('S');
+ offset += 3;
+ break;
+ }
+ if (((offset == 0) && hasOneOf(meta71, hashable, offset + 1, 1)) || hasOneOf(meta72, hashable, offset + 1, 1)) {
+ buffer.append('S');
+ if (hasOneOf(meta73, hashable, offset + 1, 1))
+ offset += 2;
+ else
+ offset += 1;
+ break;
+ }
+ if (hasOneOf(meta74, hashable, offset, 2)) {
+ if (hashable[offset + 2] == 'H')
+ if (hasOneOf(meta75, hashable, offset + 3, 2)) {
+ if (hasOneOf(meta76, hashable, offset + 3, 2)) {
+ buffer.append("X"); //$NON-NLS-1$
+ } else {
+ buffer.append("SK"); //$NON-NLS-1$
+ }
+ offset += 3;
+ break;
+ } else {
+ buffer.append('X');
+ offset += 3;
+ break;
+ }
+ if (hasOneOf(meta77, hashable, offset + 2, 1)) {
+ buffer.append('S');
+ offset += 3;
+ break;
+ }
+ buffer.append("SK"); //$NON-NLS-1$
+ offset += 3;
+ break;
+ }
+ if (!((offset == (hashable.length - 1)) && hasOneOf(meta78, hashable, offset - 2, 2)))
+ buffer.append('S');
+ if (hasOneOf(meta79, hashable, offset + 1, 1))
+ offset += 2;
+ else
+ offset += 1;
+ break;
+ case 'T' :
+ if (hasOneOf(meta80, hashable, offset, 4)) {
+ buffer.append('X');
+ offset += 3;
+ break;
+ }
+ if (hasOneOf(meta81, hashable, offset, 3)) {
+ buffer.append('X');
+ offset += 3;
+ break;
+ }
+ if (hasOneOf(meta82, hashable, offset, 2) || hasOneOf(meta83, hashable, offset, 3)) {
+ if (hasOneOf(meta84, hashable, (offset + 2), 2) || hasOneOf(meta85, hashable, 0, 4) || hasOneOf(meta86, hashable, 0, 3)) {
+ buffer.append('T');
+ } else {
+ buffer.append('0');
+ }
+ offset += 2;
+ break;
+ }
+ if (hasOneOf(meta87, hashable, offset + 1, 1)) {
+ offset += 2;
+ } else
+ offset += 1;
+ buffer.append('T');
+ break;
+ case 'V' :
+ if (hashable[offset + 1] == 'V')
+ offset += 2;
+ else
+ offset += 1;
+ buffer.append('F');
+ break;
+ case 'W' :
+ if (hasOneOf(meta88, hashable, offset, 2)) {
+ buffer.append('R');
+ offset += 2;
+ break;
+ }
+ if ((offset == 0) && (hasVowel(hashable, offset + 1, hashable.length) || hasOneOf(meta89, hashable, offset, 2))) {
+ buffer.append('A');
+ }
+ if (((offset == (hashable.length - 1)) && hasVowel(hashable, offset - 1, hashable.length)) || hasOneOf(meta90, hashable, offset - 1, 5) || hasOneOf(meta91, hashable, 0, 3)) {
+ buffer.append('F');
+ offset += 1;
+ break;
+ }
+ if (hasOneOf(meta92, hashable, offset, 4)) {
+ buffer.append("TS"); //$NON-NLS-1$
+ offset += 4;
+ break;
+ }
+ offset += 1;
+ break;
+ case 'X' :
+ if (!((offset == (hashable.length - 1)) && (hasOneOf(meta93, hashable, offset - 3, 3) || hasOneOf(meta94, hashable, offset - 2, 2))))
+ buffer.append("KS"); //$NON-NLS-1$
+ if (hasOneOf(meta49, hashable, offset + 1, 1))
+ offset += 2;
+ else
+ offset += 1;
+ break;
+ case 'Z' :
+ if (hashable[offset + 1] == 'H') {
+ buffer.append('J');
+ offset += 2;
+ break;
+ } else {
+ buffer.append('S');
+ }
+ if (hashable[offset + 1] == 'Z')
+ offset += 2;
+ else
+ offset += 1;
+ break;
+ default :
+ offset += 1;
+ }
+ }
+ return buffer.toString();
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.IPhoneticHasher#getMutators()
+ */
+ public final char[] getMutators() {
+ return MUTATOR_CHARACTERS;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/DefaultSpellChecker.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/DefaultSpellChecker.java
new file mode 100644
index 00000000000..af279cd5805
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/DefaultSpellChecker.java
@@ -0,0 +1,344 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.preference.IPreferenceStore;
+
+import org.eclipse.cdt.internal.ui.text.spelling.SpellingPreferences;
+
+/**
+ * Default spell checker for standard text.
+ */
+public class DefaultSpellChecker implements ISpellChecker {
+ /** Array of URL prefixes */
+ public static final String[] URL_PREFIXES= new String[] { "http://", "https://", "www.", "ftp://", "ftps://", "news://", "mailto://" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+
+ /**
+ * Does this word contain digits?
+ *
+ * @param word the word to check
+ * @return true
iff this word contains digits, false
otherwise
+ */
+ protected static boolean isDigits(final String word) {
+ for (int index= 0; index < word.length(); index++) {
+ if (Character.isDigit(word.charAt(index)))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Does this word contain mixed-case letters?
+ *
+ * @param word
+ * The word to check
+ * @param sentence
+ * true
iff the specified word starts a new
+ * sentence, false
otherwise
+ * @return true
iff the contains mixed-case letters, false
+ * otherwise
+ */
+ protected static boolean isMixedCase(final String word, final boolean sentence) {
+ final int length= word.length();
+ boolean upper= Character.isUpperCase(word.charAt(0));
+
+ if (sentence && upper && (length > 1))
+ upper= Character.isUpperCase(word.charAt(1));
+
+ if (upper) {
+ for (int index= length - 1; index > 0; index--) {
+ if (Character.isLowerCase(word.charAt(index)))
+ return true;
+ }
+ } else {
+ for (int index= length - 1; index > 0; index--) {
+ if (Character.isUpperCase(word.charAt(index)))
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Does this word contain upper-case letters only?
+ *
+ * @param word
+ * The word to check
+ * @return true
iff this word only contains upper-case
+ * letters, false
otherwise
+ */
+ protected static boolean isUpperCase(final String word) {
+ for (int index= word.length() - 1; index >= 0; index--) {
+ if (Character.isLowerCase(word.charAt(index)))
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Does this word look like an URL?
+ *
+ * @param word
+ * The word to check
+ * @return true
iff this word looks like an URL, false
+ * otherwise
+ */
+ protected static boolean isUrl(final String word) {
+ for (int index= 0; index < URL_PREFIXES.length; index++) {
+ if (word.startsWith(URL_PREFIXES[index]))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * The dictionaries to use for spell checking. Synchronized to avoid
+ * concurrent modifications.
+ */
+ private final Set fDictionaries= Collections.synchronizedSet(new HashSet());
+
+ /**
+ * The words to be ignored. Synchronized to avoid concurrent modifications.
+ */
+ private final Set fIgnored= Collections.synchronizedSet(new HashSet());
+
+ /**
+ * The spell event listeners. Synchronized to avoid concurrent
+ * modifications.
+ */
+ private final Set fListeners= Collections.synchronizedSet(new HashSet());
+
+ /**
+ * The locale of this checker.
+ */
+ private Locale fLocale;
+
+ /**
+ * Creates a new default spell checker.
+ *
+ * @param store the preference store for this spell checker
+ * @param locale the locale
+ */
+ public DefaultSpellChecker(IPreferenceStore store, Locale locale) {
+ Assert.isLegal(store != null);
+ Assert.isLegal(locale != null);
+
+ fLocale= locale;
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.ISpellChecker#addDictionary(org.eclipse.spelling.done.ISpellDictionary)
+ */
+ public final void addDictionary(final ISpellDictionary dictionary) {
+ // synchronizing is necessary as this is a write access
+ fDictionaries.add(dictionary);
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.ISpellChecker#addListener(org.eclipse.spelling.done.ISpellEventListener)
+ */
+ public final void addListener(final ISpellEventListener listener) {
+ // synchronizing is necessary as this is a write access
+ fListeners.add(listener);
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.ISpellChecker#acceptsWords()
+ */
+ public boolean acceptsWords() {
+ // synchronizing might not be needed here since acceptWords is
+ // a read-only access and only called in the same thread as
+ // the modifying methods add/checkWord (?)
+ Set copy;
+ synchronized (fDictionaries) {
+ copy= new HashSet(fDictionaries);
+ }
+
+ ISpellDictionary dictionary= null;
+ for (final Iterator iterator= copy.iterator(); iterator.hasNext();) {
+ dictionary= (ISpellDictionary) iterator.next();
+ if (dictionary.acceptsWords())
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker#addWord(java.lang.String)
+ */
+ public void addWord(final String word) {
+ // synchronizing is necessary as this is a write access
+ Set copy;
+ synchronized (fDictionaries) {
+ copy= new HashSet(fDictionaries);
+ }
+
+ final String addable= word.toLowerCase();
+ for (final Iterator iterator= copy.iterator(); iterator.hasNext();) {
+ ISpellDictionary dictionary= (ISpellDictionary) iterator.next();
+ if (dictionary.acceptsWords())
+ dictionary.addWord(addable);
+ }
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.ISpellChecker#checkWord(java.lang.String)
+ */
+ public final void checkWord(final String word) {
+ // synchronizing is necessary as this is a write access
+ fIgnored.remove(word.toLowerCase());
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.ISpellChecker#execute(org.eclipse.spelling.ISpellCheckTokenizer)
+ */
+ public void execute(final ISpellCheckIterator iterator) {
+ final boolean ignoreDigits= SpellingPreferences.isIgnoreDigits();
+ final boolean ignoreMixed= SpellingPreferences.isIgnoreMixed();
+ final boolean ignoreSentence= SpellingPreferences.isIgnoreSentence();
+ final boolean ignoreUpper= SpellingPreferences.isIgnoreUpper();
+ final boolean ignoreUrls= SpellingPreferences.isIgnoreUrls();
+ final boolean ignoreNonLetters= SpellingPreferences.isIgnoreNonLetters();
+ final boolean ignoreSingleLetters= SpellingPreferences.isIgnoreSingleLetters();
+
+ iterator.setIgnoreSingleLetters(ignoreSingleLetters);
+
+ Iterator iter= fDictionaries.iterator();
+ while (iter.hasNext())
+ ((ISpellDictionary) iter.next()).setStripNonLetters(ignoreNonLetters);
+
+ String word= null;
+ boolean starts= false;
+
+ while (iterator.hasNext()) {
+ word= (String) iterator.next();
+ if (word != null) {
+ // synchronizing is necessary as this is called inside the reconciler
+ if (!fIgnored.contains(word)) {
+ starts= iterator.startsSentence();
+ if (!isCorrect(word)) {
+ boolean isMixed= isMixedCase(word, true);
+ boolean isUpper= isUpperCase(word);
+ boolean isDigits= isDigits(word);
+ boolean isUrl= isUrl(word);
+
+ if ( !ignoreMixed && isMixed || !ignoreUpper && isUpper || !ignoreDigits && isDigits || !ignoreUrls && isUrl || !(isMixed || isUpper || isDigits || isUrl))
+ fireEvent(new SpellEvent(this, word, iterator.getBegin(), iterator.getEnd(), starts, false));
+ } else {
+ if (!ignoreSentence && starts && Character.isLowerCase(word.charAt(0)))
+ fireEvent(new SpellEvent(this, word, iterator.getBegin(), iterator.getEnd(), true, true));
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Fires the specified event.
+ *
+ * @param event
+ * Event to fire
+ */
+ protected final void fireEvent(final ISpellEvent event) {
+ // synchronizing is necessary as this is called from execute
+ Set copy;
+ synchronized (fListeners) {
+ copy= new HashSet(fListeners);
+ }
+ for (final Iterator iterator= copy.iterator(); iterator.hasNext();) {
+ ((ISpellEventListener)iterator.next()).handle(event);
+ }
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.ISpellChecker#getProposals(java.lang.String,boolean)
+ */
+ public Set getProposals(final String word, final boolean sentence) {
+ // synchronizing might not be needed here since getProposals is
+ // a read-only access and only called in the same thread as
+ // the modifying methods add/removeDictionary (?)
+ Set copy;
+ synchronized (fDictionaries) {
+ copy= new HashSet(fDictionaries);
+ }
+
+ ISpellDictionary dictionary= null;
+ final HashSet proposals= new HashSet();
+
+ for (final Iterator iterator= copy.iterator(); iterator.hasNext();) {
+ dictionary= (ISpellDictionary)iterator.next();
+ proposals.addAll(dictionary.getProposals(word, sentence));
+ }
+ return proposals;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker#ignoreWord(java.lang.String)
+ */
+ public final void ignoreWord(final String word) {
+ // synchronizing is necessary as this is a write access
+ fIgnored.add(word.toLowerCase());
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker#isCorrect(java.lang.String)
+ */
+ public final boolean isCorrect(final String word) {
+ // synchronizing is necessary as this is called from execute
+ Set copy;
+ synchronized (fDictionaries) {
+ copy= new HashSet(fDictionaries);
+ }
+
+ if (fIgnored.contains(word.toLowerCase()))
+ return true;
+
+ ISpellDictionary dictionary= null;
+ for (final Iterator iterator= copy.iterator(); iterator.hasNext();) {
+ dictionary= (ISpellDictionary) iterator.next();
+ if (dictionary.isCorrect(word))
+ return true;
+ }
+ return false;
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.ISpellChecker#removeDictionary(org.eclipse.spelling.done.ISpellDictionary)
+ */
+ public final void removeDictionary(final ISpellDictionary dictionary) {
+ // synchronizing is necessary as this is a write access
+ fDictionaries.remove(dictionary);
+ }
+
+ /*
+ * @see org.eclipse.spelling.done.ISpellChecker#removeListener(org.eclipse.spelling.done.ISpellEventListener)
+ */
+ public final void removeListener(final ISpellEventListener listener) {
+ // synchronizing is necessary as this is a write access
+ fListeners.remove(listener);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellChecker#getLocale()
+ */
+ public Locale getLocale() {
+ return fLocale;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/IPhoneticDistanceAlgorithm.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/IPhoneticDistanceAlgorithm.java
new file mode 100644
index 00000000000..f7d7be929d0
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/IPhoneticDistanceAlgorithm.java
@@ -0,0 +1,30 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+/**
+ * Interface of algorithms to compute the phonetic distance between two words.
+ */
+public interface IPhoneticDistanceAlgorithm {
+
+ /**
+ * Returns the non-negative phonetic distance between two words
+ *
+ * @param from
+ * The first word
+ * @param to
+ * The second word
+ * @return The non-negative phonetic distance between the words.
+ */
+ public int getDistance(String from, String to);
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/IPhoneticHashProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/IPhoneticHashProvider.java
new file mode 100644
index 00000000000..8cb1aed5296
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/IPhoneticHashProvider.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+/**
+ * Interface of hashers to compute the phonetic hash for a word.
+ */
+public interface IPhoneticHashProvider {
+
+ /**
+ * Returns the phonetic hash for the word.
+ *
+ * @param word
+ * The word to get the phonetic hash for
+ * @return The phonetic hash for the word
+ */
+ public String getHash(String word);
+
+ /**
+ * Returns an array of characters to compute possible mutations.
+ *
+ * @return Array of possible mutator characters
+ */
+ public char[] getMutators();
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellCheckEngine.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellCheckEngine.java
new file mode 100644
index 00000000000..60929a9cabb
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellCheckEngine.java
@@ -0,0 +1,83 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.util.Locale;
+
+import org.eclipse.cdt.internal.ui.text.spelling.SpellingPreferences;
+
+/**
+ * Interface for a spell check engine.
+ *
+ * This engine can be configured with multiple
+ * dictionaries.
+ *
+ */
+public interface ISpellCheckEngine {
+
+ /**
+ * Returns a spell checker configured with the global
+ * dictionaries and the locale dictionary that correspond to the current
+ * {@linkplain SpellingPreferences#getSpellingLocale() locale preference}.
+ *
+ * Note: Changes to the spelling engine dictionaries
+ * are not propagated to this spell checker.
+ *
+ * @return a configured instance of the spell checker or null
if none
+ * @throws IllegalStateException if called after being shut down
+ */
+ ISpellChecker getSpellChecker() throws IllegalStateException;
+
+ /**
+ * Returns the locale of the current spell check engine.
+ *
+ * @return the locale of the current spell check engine
+ */
+ Locale getLocale();
+
+ /**
+ * Registers a global dictionary.
+ *
+ * @param dictionary the global dictionary to register
+ */
+ void registerGlobalDictionary(ISpellDictionary dictionary);
+
+ /**
+ * Registers a dictionary tuned for the specified locale with this engine.
+ *
+ * @param locale
+ * The locale to register the dictionary with
+ * @param dictionary
+ * The dictionary to register
+ */
+ void registerDictionary(Locale locale, ISpellDictionary dictionary);
+
+ /**
+ * Shuts down this spell check engine and its associated components.
+ *
+ * Further calls to this engine result in exceptions.
+ *
+ */
+ void shutdown();
+
+ /**
+ * Unregisters a dictionary previously registered either by a call to
+ * registerDictionary(Locale,ISpellDictionary)
or registerDictionary(ISpellDictionary)
.
+ *
+ * If the dictionary was not registered before, nothing happens.
+ *
+ * @param dictionary the dictionary to unregister
+ */
+ void unregisterDictionary(ISpellDictionary dictionary);
+
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellCheckIterator.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellCheckIterator.java
new file mode 100644
index 00000000000..71e814eee85
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellCheckIterator.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.util.Iterator;
+
+/**
+ * Interface for iterators used for spell checking.
+ */
+public interface ISpellCheckIterator extends Iterator {
+ /**
+ * Returns the begin index (inclusive) of the current word.
+ *
+ * @return The begin index of the current word
+ */
+ public int getBegin();
+
+ /**
+ * Returns the end index (exclusive) of the current word.
+ *
+ * @return The end index of the current word
+ */
+ public int getEnd();
+
+ /**
+ * Does the current word start a new sentence?
+ *
+ * @return true iff the current word starts a new sentence, false
otherwise
+ */
+ public boolean startsSentence();
+
+ /**
+ * Tells whether to ignore single letters
+ * from being checked.
+ *
+ * @param state true
if single letters should be ignored
+ */
+ public void setIgnoreSingleLetters(boolean state);
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellChecker.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellChecker.java
new file mode 100644
index 00000000000..7e8134bbbb8
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellChecker.java
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Interface for spell checkers.
+ */
+public interface ISpellChecker {
+
+ /**
+ * Adds a dictionary to the list of active dictionaries.
+ *
+ * @param dictionary The dictionary to add
+ */
+ void addDictionary(ISpellDictionary dictionary);
+
+ /**
+ * Adds a spell event listener to the active listeners.
+ *
+ * @param listener The listener to add
+ */
+ void addListener(ISpellEventListener listener);
+
+ /**
+ * Returns whether this spell checker accepts word additions.
+ *
+ * @return true
if word additions are accepted, false
otherwise
+ */
+ boolean acceptsWords();
+
+ /**
+ * Adds the specified word to the set of correct words.
+ *
+ * @param word The word to add to the set of correct words
+ */
+ void addWord(String word);
+
+ /**
+ * Checks the specified word until calling ignoreWord(String)
.
+ *
+ * @param word The word to check
+ */
+ void checkWord(String word);
+
+ /**
+ * Checks the spelling with the spell check iterator. Implementations must
+ * be thread safe as this may be called inside a reconciler thread.
+ *
+ * @param iterator The iterator to use for spell checking
+ */
+ void execute(ISpellCheckIterator iterator);
+
+ /**
+ * Returns the ranked proposals for a word.
+ *
+ * @param word The word to retrieve the proposals for
+ * @param sentence true
iff the proposals should start a
+ * sentence, false
otherwise
+ * @return Set of ranked proposals for the word
+ */
+ Set getProposals(String word, boolean sentence);
+
+ /**
+ * Ignores the specified word until calling checkWord(String)
.
+ *
+ * @param word The word to ignore
+ */
+ void ignoreWord(String word);
+
+ /**
+ * Is the specified word correctly spelled? Implementations must be thread
+ * safe as this may be called from within a reconciler thread.
+ *
+ * @param word The word to check its spelling
+ * @return true
iff the word is correctly spelled, false
+ * otherwise
+ */
+ boolean isCorrect(String word);
+
+ /**
+ * Remove a dictionary from the list of active dictionaries.
+ *
+ * @param dictionary The dictionary to remove
+ */
+ void removeDictionary(ISpellDictionary dictionary);
+
+ /**
+ * Removes a spell event listener from the active listeners.
+ *
+ * @param listener The listener to remove
+ */
+ void removeListener(ISpellEventListener listener);
+
+ /**
+ * Returns the current locale of the spell check engine.
+ *
+ * @return The current locale of the engine
+ */
+ Locale getLocale();
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellDictionary.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellDictionary.java
new file mode 100644
index 00000000000..df4e4ebe076
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellDictionary.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.util.Set;
+
+/**
+ * Interface of dictionaries to use for spell checking.
+ */
+public interface ISpellDictionary {
+ /**
+ * Returns whether this dictionary accepts new words.
+ *
+ * @return true
if this dictionary accepts new words, false
otherwise
+ */
+ public boolean acceptsWords();
+
+ /**
+ * Externalizes the specified word.
+ *
+ * @param word The word to externalize in the dictionary
+ */
+ public void addWord(String word);
+
+ /**
+ * Returns the ranked word proposals for an incorrectly spelled word.
+ *
+ * @param word The word to retrieve the proposals for
+ * @param sentence true
iff the proposals start a new sentence,
+ * false
otherwise
+ * @return Array of ranked word proposals
+ */
+ public Set getProposals(String word, boolean sentence);
+
+ /**
+ * Is the specified word correctly spelled?
+ *
+ * @param word the word to spell check
+ * @return true
iff this word is correctly spelled, false
otherwise
+ */
+ public boolean isCorrect(String word);
+
+ /**
+ * Is the dictionary loaded?
+ *
+ * @return true
iff it is loaded, false
otherwise
+ */
+ public boolean isLoaded();
+
+ /**
+ * Empties the dictionary.
+ */
+ public void unload();
+
+ /**
+ * Tells whether to strip non-letters from word boundaries.
+ *
+ * @param state true
if non-letters should be stripped
+ */
+ public void setStripNonLetters(boolean state);
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellEvent.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellEvent.java
new file mode 100644
index 00000000000..e1703d9cc25
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellEvent.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.util.Set;
+
+/**
+ * Event fired by spell checkers.
+ */
+public interface ISpellEvent {
+
+ /**
+ * Returns the begin index of the incorrectly spelled word.
+ *
+ * @return The begin index of the word
+ */
+ public int getBegin();
+
+ /**
+ * Returns the end index of the incorrectly spelled word.
+ *
+ * @return The end index of the word
+ */
+ public int getEnd();
+
+ /**
+ * Returns the proposals for the incorrectly spelled word.
+ *
+ * @return Array of proposals for the word
+ */
+ public Set getProposals();
+
+ /**
+ * Returns the incorrectly spelled word.
+ *
+ * @return The incorrect word
+ */
+ public String getWord();
+
+ /**
+ * Was the incorrectly spelled word found in the dictionary?
+ *
+ * @return true
iff the word was found, false
otherwise
+ */
+ public boolean isMatch();
+
+ /**
+ * Does the incorrectly spelled word start a new sentence?
+ *
+ * @return true iff the word starts a new sentence, false
otherwise
+ */
+ public boolean isStart();
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellEventListener.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellEventListener.java
new file mode 100644
index 00000000000..eb9ecc18e90
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/ISpellEventListener.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+/**
+ * Interface for spell event listeners.
+ */
+public interface ISpellEventListener {
+ /**
+ * Handles a spell event.
+ *
+ * @param event Event to handle
+ */
+ public void handle(ISpellEvent event);
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/LocaleSensitiveSpellDictionary.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/LocaleSensitiveSpellDictionary.java
new file mode 100644
index 00000000000..d7aaf50b2aa
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/LocaleSensitiveSpellDictionary.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Locale;
+
+/**
+ * Platform wide read-only locale sensitive dictionary for spell checking.
+ */
+public class LocaleSensitiveSpellDictionary extends AbstractSpellDictionary {
+ /** The locale of this dictionary */
+ private final Locale fLocale;
+
+ /** The location of the dictionaries */
+ private final URL fLocation;
+
+ /**
+ * Creates a new locale sensitive spell dictionary.
+ *
+ * @param locale The locale for this dictionary
+ * @param location The location of the locale sensitive dictionaries
+ */
+ public LocaleSensitiveSpellDictionary(final Locale locale, final URL location) {
+ fLocation= location;
+ fLocale= locale;
+ }
+
+ /**
+ * Returns the locale of this dictionary.
+ *
+ * @return The locale of this dictionary
+ */
+ public final Locale getLocale() {
+ return fLocale;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.AbstractSpellDictionary#getURL()
+ */
+ protected final URL getURL() throws MalformedURLException {
+ return new URL(fLocation, fLocale.toString() + ".dictionary"); //$NON-NLS-1$
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/PersistentSpellDictionary.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/PersistentSpellDictionary.java
new file mode 100644
index 00000000000..142ac9e8f7f
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/PersistentSpellDictionary.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+import org.eclipse.cdt.ui.CUIPlugin;
+
+/**
+ * Persistent modifiable word-list based dictionary.
+ */
+public class PersistentSpellDictionary extends AbstractSpellDictionary {
+ /** The word list location */
+ private final URL fLocation;
+
+ /**
+ * Creates a new persistent spell dictionary.
+ *
+ * @param url The URL of the word list for this dictionary
+ */
+ public PersistentSpellDictionary(final URL url) {
+ fLocation= url;
+ }
+
+ /*
+ * @see org.eclipse.cdt.ui.text.spelling.engine.AbstractSpellDictionary#acceptsWords()
+ */
+ public boolean acceptsWords() {
+ return true;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellDictionary#addWord(java.lang.String)
+ */
+ public void addWord(final String word) {
+ if (isCorrect(word))
+ return;
+
+ OutputStreamWriter writer= null;
+ try {
+ Charset charset= Charset.forName(getEncoding());
+ ByteBuffer byteBuffer= charset.encode(word + "\n"); //$NON-NLS-1$
+ int size= byteBuffer.limit();
+ final byte[] byteArray;
+ if (byteBuffer.hasArray())
+ byteArray= byteBuffer.array();
+ else {
+ byteArray= new byte[size];
+ byteBuffer.get(byteArray);
+ }
+
+ FileOutputStream fileStream= new FileOutputStream(fLocation.getPath(), true);
+
+ // Encoding UTF-16 charset writes a BOM. In which case we need to cut it away if the file isn't empty
+ int bomCutSize= 0;
+ if (!isEmpty() && "UTF-16".equals(charset.name())) //$NON-NLS-1$
+ bomCutSize= 2;
+
+ fileStream.write(byteArray, bomCutSize, size - bomCutSize);
+ } catch (IOException exception) {
+ CUIPlugin.getDefault().log(exception);
+ return;
+ } finally {
+ try {
+ if (writer != null)
+ writer.close();
+ } catch (IOException e) {
+ }
+ }
+
+ hashWord(word);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.AbstractSpellDictionary#getURL()
+ */
+ protected final URL getURL() {
+ return fLocation;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/RankedWordProposal.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/RankedWordProposal.java
new file mode 100644
index 00000000000..ad34766cddd
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/RankedWordProposal.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+/**
+ * Ranked word proposal for quick fix and content assist.
+ */
+public class RankedWordProposal implements Comparable {
+ /** The word rank */
+ private int fRank;
+
+ /** The word text */
+ private final String fText;
+
+ /**
+ * Creates a new ranked word proposal.
+ *
+ * @param text The text of this proposal
+ * @param rank The rank of this proposal
+ */
+ public RankedWordProposal(final String text, final int rank) {
+ fText= text;
+ fRank= rank;
+ }
+
+ /*
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+ public final int compareTo(Object object) {
+
+ final RankedWordProposal word= (RankedWordProposal)object;
+ final int rank= word.getRank();
+
+ if (fRank < rank)
+ return -1;
+
+ if (fRank > rank)
+ return 1;
+
+ return 0;
+ }
+
+ /*
+ * @see java.lang.Object#equals(java.lang.Object)
+ */
+ public final boolean equals(Object object) {
+ if (object instanceof RankedWordProposal)
+ return object.hashCode() == hashCode();
+
+ return false;
+ }
+
+ /**
+ * Returns the rank of the word
+ *
+ * @return The rank of the word
+ */
+ public final int getRank() {
+ return fRank;
+ }
+
+ /**
+ * Returns the text of this word.
+ *
+ * @return The text of this word
+ */
+ public final String getText() {
+ return fText;
+ }
+
+ /*
+ * @see java.lang.Object#hashCode()
+ */
+ public final int hashCode() {
+ return fText.hashCode();
+ }
+
+ /**
+ * Sets the rank of the word.
+ *
+ * @param rank The rank to set
+ */
+ public final void setRank(final int rank) {
+ fRank= rank;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/SpellEvent.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/SpellEvent.java
new file mode 100644
index 00000000000..08d55121b4b
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/spelling/engine/SpellEvent.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * 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
+ * Sergey Prigogin (Google)
+ *******************************************************************************/
+
+package org.eclipse.cdt.internal.ui.text.spelling.engine;
+
+import java.util.Set;
+
+/**
+ * Spell event fired for words detected by a spell check iterator.
+ */
+public class SpellEvent implements ISpellEvent {
+ /** The begin index of the word in the spell checkable medium */
+ private final int fBegin;
+
+ /** The spell checker that causes the event */
+ private final ISpellChecker fChecker;
+
+ /** The end index of the word in the spell checkable medium */
+ private final int fEnd;
+
+ /** Was the word found in the dictionary? */
+ private final boolean fMatch;
+
+ /** Does the word start a new sentence? */
+ private final boolean fSentence;
+
+ /** The word that causes the spell event */
+ private final String fWord;
+
+ /**
+ * Creates a new spell event.
+ *
+ * @param checker The spell checker that causes the event
+ * @param word The word that causes the event
+ * @param begin The begin index of the word in the spell checkable medium
+ * @param end The end index of the word in the spell checkable medium
+ * @param sentence true
iff the word starts a new sentence,
+ * false
otherwise
+ * @param match true
iff the word was found in the dictionary,
+ * false
otherwise
+ */
+ protected SpellEvent(final ISpellChecker checker, final String word, final int begin, final int end, final boolean sentence, final boolean match) {
+ fChecker= checker;
+ fEnd= end;
+ fBegin= begin;
+ fWord= word;
+ fSentence= sentence;
+ fMatch= match;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEvent#getBegin()
+ */
+ public final int getBegin() {
+ return fBegin;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEvent#getEnd()
+ */
+ public final int getEnd() {
+ return fEnd;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEvent#getProposals()
+ */
+ public final Set getProposals() {
+ return fChecker.getProposals(fWord, fSentence);
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEvent#getWord()
+ */
+ public final String getWord() {
+ return fWord;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEvent#isMatch()
+ */
+ public final boolean isMatch() {
+ return fMatch;
+ }
+
+ /*
+ * @see org.eclipse.cdt.internal.ui.text.spelling.engine.ISpellEvent#isStart()
+ */
+ public final boolean isStart() {
+ return fSentence;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/EditorUtility.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/EditorUtility.java
index cda8d9a31f0..2e4c9dadfbd 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/EditorUtility.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/EditorUtility.java
@@ -400,14 +400,19 @@ public class EditorUtility {
if (page != null) {
IEditorPart part= page.getActiveEditor();
if (part != null) {
- IEditorInput editorInput= part.getEditorInput();
- if (editorInput != null) {
- return (ICElement)editorInput.getAdapter(ICElement.class);
- }
+ return getEditorInputCElement(part);
}
}
return null;
}
+
+ public static ICElement getEditorInputCElement(IEditorPart part) {
+ IEditorInput editorInput= part.getEditorInput();
+ if (editorInput == null) {
+ return null;
+ }
+ return (ICElement) editorInput.getAdapter(ICElement.class);
+ }
/**
* Gets the working copy of an compilation unit opened in an editor
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/ISelectionListenerWithAST.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/ISelectionListenerWithAST.java
new file mode 100644
index 00000000000..41fe031f8d4
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/ISelectionListenerWithAST.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * 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.viewsupport;
+
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.ui.IEditorPart;
+
+import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
+
+/**
+ * Listener to be informed on text selection changes in an editor (post selection), including the corresponding AST.
+ * The AST is shared and must not be modified.
+ * Listeners can be registered in a SelectionListenerWithASTManager
.
+ */
+public interface ISelectionListenerWithAST {
+
+ /**
+ * Called when a selection has changed. The method is called in a post selection event in an background
+ * thread.
+ *
+ * @param part The editor part in which the selection change has occurred.
+ * @param selection The new text selection
+ * @param astRoot The AST tree corresponding to the editor's input. This AST is shared and must
+ * not be modified.
+ */
+ void selectionChanged(IEditorPart part, ITextSelection selection, IASTTranslationUnit astRoot);
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/SelectionListenerWithASTManager.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/SelectionListenerWithASTManager.java
new file mode 100644
index 00000000000..abbc9414241
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/viewsupport/SelectionListenerWithASTManager.java
@@ -0,0 +1,179 @@
+/*******************************************************************************
+ * 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.internal.ui.viewsupport;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.jface.text.ITextSelection;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit;
+import org.eclipse.cdt.core.model.ILanguage;
+import org.eclipse.cdt.core.model.IWorkingCopy;
+import org.eclipse.cdt.ui.CUIPlugin;
+
+import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable;
+
+import org.eclipse.cdt.internal.ui.editor.ASTProvider;
+
+/**
+ * Infrastructure to share an AST for editor post selection listeners.
+ */
+public class SelectionListenerWithASTManager {
+ private static SelectionListenerWithASTManager fgDefault;
+
+ /**
+ * @return Returns the default manager instance.
+ */
+ public static SelectionListenerWithASTManager getDefault() {
+ if (fgDefault == null) {
+ fgDefault= new SelectionListenerWithASTManager();
+ }
+ return fgDefault;
+ }
+
+
+ private final static class PartListenerGroup {
+ private ITextEditor fPart;
+ private ISelectionListener fPostSelectionListener;
+ private ISelectionChangedListener fSelectionListener;
+ private Job fCurrentJob;
+ private ListenerList fAstListeners;
+
+ public PartListenerGroup(ITextEditor editorPart) {
+ fPart= editorPart;
+ fCurrentJob= null;
+ fAstListeners= new ListenerList(ListenerList.IDENTITY);
+
+ fSelectionListener= new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ ISelection selection= event.getSelection();
+ if (selection instanceof ITextSelection) {
+ fireSelectionChanged((ITextSelection) selection);
+ }
+ }
+ };
+
+ fPostSelectionListener= new ISelectionListener() {
+ public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+ if (part == fPart && selection instanceof ITextSelection)
+ firePostSelectionChanged((ITextSelection) selection);
+ }
+ };
+ }
+
+ public boolean isEmpty() {
+ return fAstListeners.isEmpty();
+ }
+
+ public void install(ISelectionListenerWithAST listener) {
+ if (isEmpty()) {
+ fPart.getEditorSite().getPage().addPostSelectionListener(fPostSelectionListener);
+ ISelectionProvider selectionProvider= fPart.getSelectionProvider();
+ if (selectionProvider != null)
+ selectionProvider.addSelectionChangedListener(fSelectionListener);
+ }
+ fAstListeners.add(listener);
+ }
+
+ public void uninstall(ISelectionListenerWithAST listener) {
+ fAstListeners.remove(listener);
+ if (isEmpty()) {
+ fPart.getEditorSite().getPage().removePostSelectionListener(fPostSelectionListener);
+ ISelectionProvider selectionProvider= fPart.getSelectionProvider();
+ if (selectionProvider != null)
+ selectionProvider.removeSelectionChangedListener(fSelectionListener);
+ }
+ }
+
+ public void fireSelectionChanged(final ITextSelection selection) {
+ if (fCurrentJob != null) {
+ fCurrentJob.cancel();
+ }
+ }
+
+ public void firePostSelectionChanged(final ITextSelection selection) {
+ if (fCurrentJob != null) {
+ fCurrentJob.cancel();
+ }
+
+ IWorkingCopy workingCopy = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(fPart.getEditorInput());
+ if (workingCopy == null)
+ return;
+
+ ASTProvider.getASTProvider().runOnAST(workingCopy, ASTProvider.WAIT_YES, null, new ASTRunnable() {
+ public IStatus runOnAST(ILanguage lang, IASTTranslationUnit astRoot) {
+ if (astRoot != null) {
+ Object[] listeners;
+ synchronized (PartListenerGroup.this) {
+ listeners= fAstListeners.getListeners();
+ }
+ for (int i= 0; i < listeners.length; i++) {
+ ((ISelectionListenerWithAST) listeners[i]).selectionChanged(fPart, selection, astRoot);
+ }
+ }
+ return Status.OK_STATUS;
+ }
+ });
+ }
+ }
+
+
+ private Map fListenerGroups;
+
+ private SelectionListenerWithASTManager() {
+ fListenerGroups= new HashMap();
+ }
+
+ /**
+ * Registers a selection listener for the given editor part.
+ * @param part The editor part to listen to.
+ * @param listener The listener to register.
+ */
+ public void addListener(ITextEditor part, ISelectionListenerWithAST listener) {
+ synchronized (this) {
+ PartListenerGroup partListener= (PartListenerGroup) fListenerGroups.get(part);
+ if (partListener == null) {
+ partListener= new PartListenerGroup(part);
+ fListenerGroups.put(part, partListener);
+ }
+ partListener.install(listener);
+ }
+ }
+
+ /**
+ * Unregisters a selection listener.
+ * @param part The editor part the listener was registered.
+ * @param listener The listener to unregister.
+ */
+ public void removeListener(ITextEditor part, ISelectionListenerWithAST listener) {
+ synchronized (this) {
+ PartListenerGroup partListener= (PartListenerGroup) fListenerGroups.get(part);
+ if (partListener != null) {
+ partListener.uninstall(listener);
+ if (partListener.isEmpty()) {
+ fListenerGroups.remove(part);
+ }
+ }
+ }
+ }
+}
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 35529728b61..d6d85e5c883 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
@@ -9,17 +9,24 @@
* IBM Corporation - initial API and implementation
* QNX Software System
* Anton Leherbauer (Wind River Systems)
+ * Sergey Prigogin (Google)
*******************************************************************************/
package org.eclipse.cdt.ui;
+import java.util.Locale;
+
import org.eclipse.jface.action.Action;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.preference.PreferenceConverter;
+import org.eclipse.jface.resource.ColorRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.RGB;
+import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants;
+import org.eclipse.cdt.internal.ui.ICThemeConstants;
import org.eclipse.cdt.internal.ui.text.ICColorConstants;
+import org.eclipse.cdt.internal.ui.text.spelling.SpellCheckEngine;
/**
* Preference constants used in the CDT-UI preference store. Clients should only read the
@@ -972,6 +979,198 @@ public class PreferenceConstants {
*/
public static final String EDITOR_SEMANTIC_HIGHLIGHTING_ENABLED= "semanticHighlighting.enabled"; //$NON-NLS-1$
+ /**
+ * A named preference that controls if quick assist light bulbs are shown.
+ *
+ * Value is of type Boolean
: if true
light bulbs are shown
+ * for quick assists.
+ *
+ *
+ * @since 5.0
+ */
+ public static final String EDITOR_QUICKASSIST_LIGHTBULB="org.eclipse.cdt.quickassist.lightbulb"; //$NON-NLS-1$
+
+ /**
+ * A named preference that holds the background color used in the code assist selection dialog.
+ *
+ * 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 5.0
+ */
+ public final static String CODEASSIST_PROPOSALS_BACKGROUND= "content_assist_proposals_background"; //$NON-NLS-1$
+
+ /**
+ * A named preference that holds the foreground color used in the code assist selection dialog.
+ *
+ * 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 5.0
+ */
+ public final static String CODEASSIST_PROPOSALS_FOREGROUND= "content_assist_proposals_foreground"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls whether words containing digits should
+ * be skipped during spell checking.
+ *
+ * Value is of type Boolean
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_IGNORE_DIGITS= "spelling_ignore_digits"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls whether mixed case words should be
+ * skipped during spell checking.
+ *
+ * Value is of type Boolean
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_IGNORE_MIXED= "spelling_ignore_mixed"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls whether sentence capitalization should
+ * be ignored during spell checking.
+ *
+ * Value is of type Boolean
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_IGNORE_SENTENCE= "spelling_ignore_sentence"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls whether upper case words should be
+ * skipped during spell checking.
+ *
+ * Value is of type Boolean
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_IGNORE_UPPER= "spelling_ignore_upper"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls whether URLs should be ignored during
+ * spell checking.
+ *
+ * Value is of type Boolean
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_IGNORE_URLS= "spelling_ignore_urls"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls whether single letters
+ * should be ignored during spell checking.
+ *
+ * Value is of type Boolean
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_IGNORE_SINGLE_LETTERS= "spelling_ignore_single_letters"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls whether string literals
+ * should be ignored during spell checking.
+ *
+ * Value is of type Boolean
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_IGNORE_STRING_LITERALS= "spelling_ignore_string_literals"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls whether non-letters at word boundaries
+ * should be ignored during spell checking.
+ *
+ * Value is of type Boolean
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_IGNORE_NON_LETTERS= "spelling_ignore_non_letters"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls the locale used for spell checking.
+ *
+ * Value is of type String
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_LOCALE= "spelling_locale"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls the number of proposals offered during
+ * spell checking.
+ *
+ * Value is of type Integer
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_PROPOSAL_THRESHOLD= "spelling_proposal_threshold"; //$NON-NLS-1$
+
+ /**
+ * A named preference that controls the maximum number of problems reported
+ * during spell checking.
+ *
+ * Value is of type Integer
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_PROBLEMS_THRESHOLD= "spelling_problems_threshold"; //$NON-NLS-1$
+
+ /**
+ * A named preference that specifies the workspace user dictionary.
+ *
+ * Value is of type Integer
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_USER_DICTIONARY= "spelling_user_dictionary"; //$NON-NLS-1$
+
+ /**
+ * A named preference that specifies encoding of the workspace user dictionary.
+ *
+ * Value is of type String
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_USER_DICTIONARY_ENCODING= "spelling_user_dictionary_encoding"; //$NON-NLS-1$
+
+ /**
+ * A named preference that specifies whether spelling dictionaries are available to content assist.
+ *
+ * Note: This is currently not supported because the spelling engine
+ * cannot return word proposals but only correction proposals.
+ *
+ * Value is of type Boolean
.
+ *
+ *
+ * @since 5.0
+ */
+ public final static String SPELLING_ENABLE_CONTENTASSIST= "spelling_enable_contentassist"; //$NON-NLS-1$
+
/**
* Returns the CDT-UI preference store.
*
@@ -987,13 +1186,15 @@ public class PreferenceConstants {
* @param store the preference store to be initialized
*/
public static void initializeDefaultValues(IPreferenceStore store) {
+ ColorRegistry registry= PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getColorRegistry();
store.setDefault(PreferenceConstants.EDITOR_CORRECTION_INDICATION, false);
store.setDefault(PreferenceConstants.EDITOR_SHOW_SEGMENTS, false);
store.setDefault(PreferenceConstants.PREF_SHOW_CU_CHILDREN, true);
- // Turned off by default since there are too many false reports right now
- store.setDefault(PreferenceConstants.EDITOR_EVALUATE_TEMPORARY_PROBLEMS, false);
+ // This option has to be turned on for the spelling checker too work.
+ // As of 4.0, it doesn't produce false positives any more.
+ store.setDefault(PreferenceConstants.EDITOR_EVALUATE_TEMPORARY_PROBLEMS, true);
int sourceHoverModifier= SWT.MOD2;
String sourceHoverModifierName= Action.findModifierString(sourceHoverModifier); // Shift
@@ -1088,5 +1289,81 @@ public class PreferenceConstants {
// content assist
store.setDefault(PreferenceConstants.CODEASSIST_EXCLUDED_CATEGORIES, "org.eclipse.cdt.ui.textProposalCategory\0"); //$NON-NLS-1$
store.setDefault(PreferenceConstants.CODEASSIST_CATEGORY_ORDER, "org.eclipse.cdt.ui.parserProposalCategory:65539\0org.eclipse.cdt.ui.textProposalCategory:65541\0org.eclipse.cdt.ui.templateProposalCategory:2\0"); //$NON-NLS-1$
+
+ setDefaultAndFireEvent(
+ store,
+ PreferenceConstants.CODEASSIST_PROPOSALS_BACKGROUND,
+ findRGB(registry, ICThemeConstants.CODEASSIST_PROPOSALS_BACKGROUND, new RGB(255, 255, 255)));
+ setDefaultAndFireEvent(
+ store,
+ PreferenceConstants.CODEASSIST_PROPOSALS_FOREGROUND,
+ findRGB(registry, ICThemeConstants.CODEASSIST_PROPOSALS_FOREGROUND, new RGB(0, 0, 0)));
+
+ // spell checking
+ store.setDefault(PreferenceConstants.SPELLING_LOCALE, "en_US"); //$NON-NLS-1$
+ String isInitializedKey= "spelling_locale_initialized"; //$NON-NLS-1$
+ if (!store.getBoolean(isInitializedKey)) {
+ store.setValue(isInitializedKey, true);
+ Locale locale= SpellCheckEngine.getDefaultLocale();
+ locale= SpellCheckEngine.findClosestLocale(locale);
+ if (locale != null)
+ store.setValue(PreferenceConstants.SPELLING_LOCALE, locale.toString());
+ }
+ store.setDefault(PreferenceConstants.SPELLING_IGNORE_DIGITS, true);
+ store.setDefault(PreferenceConstants.SPELLING_IGNORE_MIXED, true);
+ store.setDefault(PreferenceConstants.SPELLING_IGNORE_SENTENCE, true);
+ store.setDefault(PreferenceConstants.SPELLING_IGNORE_UPPER, true);
+ store.setDefault(PreferenceConstants.SPELLING_IGNORE_URLS, true);
+ store.setDefault(PreferenceConstants.SPELLING_IGNORE_SINGLE_LETTERS, true);
+ store.setDefault(PreferenceConstants.SPELLING_IGNORE_STRING_LITERALS, true);
+ store.setDefault(PreferenceConstants.SPELLING_IGNORE_NON_LETTERS, true);
+ store.setDefault(PreferenceConstants.SPELLING_USER_DICTIONARY, ""); //$NON-NLS-1$
+
+ // Note: For backwards compatibility we must use the property and not the workspace default
+ store.setDefault(PreferenceConstants.SPELLING_USER_DICTIONARY_ENCODING, System.getProperty("file.encoding")); //$NON-NLS-1$
+
+ store.setDefault(PreferenceConstants.SPELLING_PROPOSAL_THRESHOLD, 20);
+ store.setDefault(PreferenceConstants.SPELLING_PROBLEMS_THRESHOLD, 100);
+ /*
+ * XXX: This is currently disabled because the spelling engine
+ * cannot return word proposals but only correction proposals.
+ */
+ store.setToDefault(PreferenceConstants.SPELLING_ENABLE_CONTENTASSIST);
}
+
+ /**
+ * Sets the default value and fires a property
+ * change event if necessary.
+ *
+ * @param store the preference store
+ * @param key the preference key
+ * @param newValue the new value
+ * @since 5.0
+ */
+ private static void setDefaultAndFireEvent(IPreferenceStore store, String key, RGB newValue) {
+ RGB oldValue= null;
+ if (store.isDefault(key))
+ oldValue= PreferenceConverter.getDefaultColor(store, key);
+
+ PreferenceConverter.setDefault(store, key, newValue);
+
+ if (oldValue != null && !oldValue.equals(newValue))
+ store.firePropertyChangeEvent(key, oldValue, newValue);
+ }
+
+ /**
+ * Returns the RGB for the given key in the given color registry.
+ *
+ * @param registry the color registry
+ * @param key the key for the constant in the registry
+ * @param defaultRGB the default RGB if no entry is found
+ * @return RGB the RGB
+ * @since 5.0
+ */
+ private static RGB findRGB(ColorRegistry registry, String key, RGB defaultRGB) {
+ RGB rgb= registry.getRGB(key);
+ if (rgb != null)
+ return rgb;
+ return defaultRGB;
+ }
}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/IInvocationContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/IInvocationContext.java
new file mode 100644
index 00000000000..d4f762c6187
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/IInvocationContext.java
@@ -0,0 +1,36 @@
+/*******************************************************************************
+ * 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.text;
+
+import org.eclipse.cdt.core.model.ITranslationUnit;
+
+/**
+ * Context information for quick fix and quick assist processors.
+ *
+ * Note: this interface is not intended to be implemented.
+ *
+ */
+public interface IInvocationContext {
+ /**
+ * @return Returns the offset of the current selection
+ */
+ int getSelectionOffset();
+
+ /**
+ * @return Returns the length of the current selection
+ */
+ int getSelectionLength();
+
+ /**
+ * @return ITranslationUnit or null
+ */
+ ITranslationUnit getTranslationUnit();
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/IProblemLocation.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/IProblemLocation.java
new file mode 100644
index 00000000000..87a01a77784
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/IProblemLocation.java
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * 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.text;
+
+/**
+ * Problem information for quick fix and quick assist processors.
+ */
+public interface IProblemLocation {
+ /**
+ * Returns the start offset of the problem.
+ *
+ * @return the start offset of the problem
+ */
+ int getOffset();
+
+ /**
+ * Returns the length of the problem.
+ *
+ * @return the length of the problem
+ */
+ int getLength();
+
+ /**
+ * Returns the marker type of this problem.
+ *
+ * @return The marker type of the problem.
+ */
+ String getMarkerType();
+
+ /**
+ * Returns the id of problem. Note that problem ids are defined per problem marker type.
+ *
+ * @return The id of the problem.
+ */
+ int getProblemId();
+
+ /**
+ * Returns the original arguments recorded into the problem.
+ *
+ * @return String[] Returns the problem arguments.
+ */
+ String[] getProblemArguments();
+
+ /**
+ * Returns if the problem has error severity.
+ *
+ * @return true
if the problem has error severity
+ */
+ boolean isError();
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/IQuickFixProcessor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/IQuickFixProcessor.java
new file mode 100644
index 00000000000..ecf71e03e16
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/IQuickFixProcessor.java
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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.text;
+
+import org.eclipse.core.runtime.CoreException;
+
+import org.eclipse.cdt.core.model.ITranslationUnit;
+
+/**
+ * Interface to be implemented by contributors to the extension point
+ * org.eclipse.cdt.ui.quickFixProcessors
.
+ *
+ * @since 5.0
+ */
+public interface IQuickFixProcessor {
+ /**
+ * Returns true
if the processor has proposals for the given problem. This test should be an
+ * optimistic guess and be very cheap.
+ *
+ * @param unit the compilation unit
+ * @param problemId the problem Id. The id is of a problem of the problem type(s) this processor specified in
+ * the extension point.
+ * @return true
if the processor has proposals for the given problem
+ */
+ boolean hasCorrections(ITranslationUnit unit, int problemId);
+
+ /**
+ * Collects corrections or code manipulations for the given context.
+ *
+ * @param context Defines current compilation unit, position and a shared AST
+ * @param locations Problems are the current location.
+ * @return the corrections applicable at the location or null
if no proposals
+ * can be offered
+ * @throws CoreException CoreException can be thrown if the operation fails
+ */
+ ICCompletionProposal[] getCorrections(IInvocationContext context,
+ IProblemLocation[] locations) throws CoreException;
+}