diff --git a/core/org.eclipse.cdt.ui/icons/elcl16/codeassist_co.gif b/core/org.eclipse.cdt.ui/icons/elcl16/codeassist_co.gif new file mode 100644 index 00000000000..be2962b9c37 Binary files /dev/null and b/core/org.eclipse.cdt.ui/icons/elcl16/codeassist_co.gif differ diff --git a/core/org.eclipse.cdt.ui/icons/elcl16/templateprop_co.gif b/core/org.eclipse.cdt.ui/icons/elcl16/templateprop_co.gif new file mode 100644 index 00000000000..fdde5fbb95e Binary files /dev/null and b/core/org.eclipse.cdt.ui/icons/elcl16/templateprop_co.gif differ diff --git a/core/org.eclipse.cdt.ui/icons/elcl16/wordassist_co.gif b/core/org.eclipse.cdt.ui/icons/elcl16/wordassist_co.gif new file mode 100644 index 00000000000..c9b97fe7f26 Binary files /dev/null and b/core/org.eclipse.cdt.ui/icons/elcl16/wordassist_co.gif differ diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties index c984588b68f..2f468d06c39 100644 --- a/core/org.eclipse.cdt.ui/plugin.properties +++ b/core/org.eclipse.cdt.ui/plugin.properties @@ -137,14 +137,12 @@ CPluginBuildConsolePreferencePage.name=Build Console CPluginFileTypesPreferencePage.name=File Types CodeFormatterPreferencePage.name=Code Style CodeAssistPreferencePage.name=Content Assist +CodeAssistAdvancedPreferencePage.name=Advanced SmartTypingPreferencePage.name=Typing ColoringPreferencePage.name=Syntax Coloring FoldingPreferencePage.name=Folding HoverPreferencePage.name=Hovers -todoPageName=C/C++ Task Tags -todoTaskPrefName=Task Tags - Editors.DefaultTextEditor = Default Text Editor AsmEditor.name = Assembly Editor @@ -334,8 +332,8 @@ c.contextType.name = C/C++ comment.contextType.name = Comment # completion - completionContributors=Content Assist Completion Contributor +completionProposalComputer=Completion Proposal Computer # Indexer Preference Name indexerPrefName=Indexer @@ -363,3 +361,12 @@ navigatorContent.name = CDT Elements OpenCallHierarchy.label = Open Call Hierarchy OpenCallHierarchy.tooltip = Open Call Hierarchy + +ParserProposalCategory=Parsing-based Proposals +DefaultProposalCategory= &Basic Proposals +TemplateProposalCategory= Te&mplate Proposals +TextProposalCategory= &Word Proposals + +SpecificContentAssist.name= C/C++ Content Assist +SpecificContentAssist.desc= A parameterizable command that invokes content assist with a single completion proposal category +SpecificContentAssist.param= type diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index 2688d158cfe..8ec24ee3628 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -17,6 +17,7 @@ + @@ -609,6 +610,12 @@ class="org.eclipse.cdt.internal.ui.preferences.CodeAssistPreferencePage" id="org.eclipse.cdt.ui.preferences.CodeAssistPreferencePage"> + + - - - + priority="3"/--> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/org.eclipse.cdt.ui/schema/completionProposalComputer.exsd b/core/org.eclipse.cdt.ui/schema/completionProposalComputer.exsd new file mode 100644 index 00000000000..68cfad9734c --- /dev/null +++ b/core/org.eclipse.cdt.ui/schema/completionProposalComputer.exsd @@ -0,0 +1,209 @@ + + + + + + + + + This extension point allows to contribute completion proposal computers to participate in the content assist process of the C/C++ editor. + + + + + + + + + + + + + + + + + + The fully qualified identifier of the target extension point + + + + + + + The identifier of the extension instance, unique within the declaring plug-in (the plug-in's identifier will be prepended to form a platform-wide unique id) + + + + + + + The optional name of the extension instance + + + + + + + + + + + + + A proposal computer contribution. If no partition types are specified, the computer is added to all partition types. + + + + + + + + + + The name of the class that implements the contributed computer. The +class must be public and implement +<samp>org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer</samp> +and must have a public 0-argument constructor. + + + + + + + + + + If the attribute is set to "true" it will force this plug-in to be loaded on content assist invocation. + + + + + + + The id of a <tt>proposalCategory</tt> + + + + + + + + + + + + + + + + + A C/C++ partition type for which the specified computer can provide completion proposals. See <code>IDocument.DEFAULT_CONTENT_TYPE</code> and <code>ICPartitions</code> for valid values. + + + + + + + + + + + + + + + + + + + + + + + + + + A proposal category contribution defines categories of proposals used to group them in the UI. + + + + + + + The optional icon of the category, which can be displayed in the user preferences. + + + + + + + + + + + + + + + 4.0 + + + + + + + + + The following is an example of a completion proposal computer contribution: + +<p> +<pre> + <extension point="org.eclipse.cdt.ui.completionProposalComputer" + id="textual_proposals" + name="Text Proposals"> + <proposalCategory icon="icons/wordcompletions.png"/> + </extension> + <extension point="org.eclipse.cdt.ui.completionProposalComputer" + id="WordCompletionProposalComputer" + name="Word Completion Proposal Computer"> + <completionProposalComputer + class="org.eclipse.cdt.internal.ui.text.contentassist.HippieProposalComputer" + categoryId="org.eclipse.ui.texteditor.textual_proposals"> + <partition type="__c_multiline_comment"/> + </completionProposalComputer> + </extension> +</pre> +</p> + + + + + + + + + The contributed class must extend <code>org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer</code> + + + + + + + + + see <code>org.eclipse.cdt.internal.ui.text.contentassist.HippieProposalComputer</code> for an example. + + + + + + + + + Copyright (c) 2006, 2007 IBM Corporation and others.<br> +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 <a href="http://www.eclipse.org/legal/epl-v10.html">http://www.eclipse.org/legal/epl-v10.html</a> + + + + diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/CUIMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/CUIMessages.properties index 53aac83e8d3..0a80d9eb39b 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/CUIMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/CUIMessages.properties @@ -95,3 +95,5 @@ TextEditorDropAdapter.error.title=Drag and Drop TextEditorDropAdapter.error.message=A problem occurred during drag and drop. TextEditorDropAdapter.unreadableFile=Unreadable file: ''{0}'' TextEditorDropAdapter.noFile=Not a file: ''{0}'' + +OptionalMessageDialog_dontShowAgain= Do not show this &message again diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/OptionalMessageDialog.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/OptionalMessageDialog.java new file mode 100644 index 00000000000..b8bb439045b --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/dialogs/OptionalMessageDialog.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.dialogs; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.MessageDialog; + +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.ui.CUIMessages; + +/** + * This is a MessageDialog which allows the user + * to choose that the dialog isn't shown again the next time. + */ +public class OptionalMessageDialog extends MessageDialog { + + // String constants for widgets + private static final String CHECKBOX_TEXT= CUIMessages.getString("OptionalMessageDialog_dontShowAgain"); //$NON-NLS-1$ + + // Dialog store id constants + private static final String STORE_ID= "OptionalMessageDialog.hide."; //$NON-NLS-1$ + + public static final int NOT_SHOWN= IDialogConstants.CLIENT_ID + 1; + + private Button fHideDialogCheckBox; + private String fId; + + /** + * Opens the dialog but only if the user hasn't choosen to hide it. + * Returns NOT_SHOWN if the dialog was not shown. + */ + public static int open(String id, Shell parent, String title, Image titleImage, String message, int dialogType, String[] buttonLabels, int defaultButtonIndex) { + if (!isDialogEnabled(id)) + return OptionalMessageDialog.NOT_SHOWN; + + MessageDialog dialog= new OptionalMessageDialog(id, parent, title, titleImage, message, dialogType, buttonLabels, defaultButtonIndex); + return dialog.open(); + } + + protected OptionalMessageDialog(String id, Shell parent, String title, Image titleImage, String message, int dialogType, String[] buttonLabels, int defaultButtonIndex) { + super(parent, title, titleImage, message, dialogType, buttonLabels, defaultButtonIndex); + fId= id; + } + + protected Control createCustomArea(Composite parent) { + Composite composite= new Composite(parent, SWT.NONE); + GridLayout layout= new GridLayout(); + layout.marginHeight= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); + layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); + layout.horizontalSpacing= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + composite.setLayout(layout); + composite.setLayoutData(new GridData(GridData.FILL_BOTH)); + + fHideDialogCheckBox= new Button(composite, SWT.CHECK | SWT.LEFT); + fHideDialogCheckBox.setText(CHECKBOX_TEXT); + fHideDialogCheckBox.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + setDialogEnabled(fId, !((Button)e.widget).getSelection()); + } + }); + applyDialogFont(fHideDialogCheckBox); + return fHideDialogCheckBox; + } + + //--------------- Configuration handling -------------- + + /** + * Returns this dialog + * + * @return the settings to be used + */ + private static IDialogSettings getDialogSettings() { + IDialogSettings settings= CUIPlugin.getDefault().getDialogSettings(); + settings= settings.getSection(STORE_ID); + if (settings == null) + settings= CUIPlugin.getDefault().getDialogSettings().addNewSection(STORE_ID); + return settings; + } + + /** + * Answers whether the optional dialog is enabled and should be shown. + */ + public static boolean isDialogEnabled(String key) { + IDialogSettings settings= getDialogSettings(); + return !settings.getBoolean(key); + } + + /** + * Sets whether the optional dialog is enabled and should be shown. + */ + public static void setDialogEnabled(String key, boolean isEnabled) { + IDialogSettings settings= getDialogSettings(); + settings.put(key, !isEnabled); + } + + /** + * Clears all remembered information about hidden dialogs + */ + public static void clearAllRememberedStates() { + IDialogSettings settings= CUIPlugin.getDefault().getDialogSettings(); + settings.addNewSection(STORE_ID); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java index 4a962bd29bd..a939ea0085b 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/CEditor.java @@ -2271,9 +2271,10 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IR setAction("ContentAssistProposal", action); //$NON-NLS-1$ markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$ - action = new TextOperationAction(CEditorMessages.getResourceBundle(), "ContentAssistTip.", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$ + action= new TextOperationAction(CEditorMessages.getResourceBundle(), "ContentAssistContextInformation.", this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); //$NON-NLS-1$ action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION); - setAction("ContentAssistTip", action); //$NON-NLS-1$ + setAction("ContentAssistContextInformation", action); //$NON-NLS-1$ + markAsStateDependentAction("ContentAssistContextInformation", true); //$NON-NLS-1$ action = new OpenDeclarationsAction(this); action.setActionDefinitionId(ICEditorActionDefinitionIds.OPEN_DECL); @@ -2983,10 +2984,11 @@ public class CEditor extends TextEditor implements ISelectionChangedListener, IR protected String[] collectContextMenuPreferencePages() { // Add C/C++ Editor relevant pages String[] parentPrefPageIds = super.collectContextMenuPreferencePages(); - String[] prefPageIds = new String[parentPrefPageIds.length + 8]; + String[] prefPageIds = new String[parentPrefPageIds.length + 9]; int nIds = 0; prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CEditorPreferencePage"; //$NON-NLS-1$ prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CodeAssistPreferencePage"; //$NON-NLS-1$ + prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CodeAssistPreferenceAdvanced"; //$NON-NLS-1$ prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.HoverPreferencePage"; //$NON-NLS-1$ prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.FoldingPreferencePage"; //$NON-NLS-1$ prefPageIds[nIds++] = "org.eclipse.cdt.ui.preferences.CodeColoringPreferencePage"; //$NON-NLS-1$ diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SpecificContentAssistAction.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SpecificContentAssistAction.java new file mode 100644 index 00000000000..3384e607d41 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SpecificContentAssistAction.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.editor; + + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.ITextSelection; +import org.eclipse.jface.text.TextUtilities; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.texteditor.IDocumentProvider; +import org.eclipse.ui.texteditor.ITextEditor; +import org.eclipse.ui.texteditor.IUpdate; + +import org.eclipse.cdt.ui.text.ICPartitions; + +import org.eclipse.cdt.internal.ui.text.contentassist.CompletionProposalCategory; +import org.eclipse.cdt.internal.ui.text.contentassist.CompletionProposalComputerRegistry; + +/** + * Action to run content assist on a specific proposal category. + * + * @since 4.0 + */ +final class SpecificContentAssistAction extends Action implements IUpdate { + /** + * The category represented by this action. + */ + private final CompletionProposalCategory fCategory; + /** + * The content assist executor. + */ + private final SpecificContentAssistExecutor fExecutor= new SpecificContentAssistExecutor(CompletionProposalComputerRegistry.getDefault()); + /** + * The editor. + */ + private CEditor fEditor; + + /** + * Creates a new action for a certain proposal category. + * + * @param category + */ + public SpecificContentAssistAction(CompletionProposalCategory category) { + fCategory= category; + setText(category.getName()); + setImageDescriptor(category.getImageDescriptor()); + setActionDefinitionId("org.eclipse.cdt.ui.specific_content_assist.command"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.jface.action.Action#run() + */ + public void run() { + ITextEditor editor= getActiveEditor(); + if (editor == null) + return; + + fExecutor.invokeContentAssist(editor, fCategory.getId()); + + return; + } + + private ITextEditor getActiveEditor() { + return fEditor; + } + + /** + * Sets the active editor part. + * + * @param part the editor, possibly null + */ + public void setActiveEditor(IEditorPart part) { + CEditor editor; + if (part instanceof CEditor) + editor= (CEditor) part; + else + editor= null; + fEditor= editor; + setEnabled(computeEnablement(fEditor)); + } + + private boolean computeEnablement(ITextEditor editor) { + if (editor == null) + return false; + ITextOperationTarget target= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class); + boolean hasContentAssist= target != null && target.canDoOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); + if (!hasContentAssist) + return false; + + ISelection selection= editor.getSelectionProvider().getSelection(); + return isValidSelection(selection); + } + + /** + * Computes the partition type at the selection start and checks whether the proposal category + * has any computers for this partition. + * + * @param selection the selection + * @return true if there are any computers for the selection + */ + private boolean isValidSelection(ISelection selection) { + if (!(selection instanceof ITextSelection)) + return false; + int offset= ((ITextSelection) selection).getOffset(); + + IDocument document= getDocument(); + if (document == null) + return false; + + String contentType; + try { + contentType= TextUtilities.getContentType(document, ICPartitions.C_PARTITIONING, offset, true); + } catch (BadLocationException x) { + return false; + } + + return fCategory.hasComputers(contentType); + } + + private IDocument getDocument() { + Assert.isTrue(fEditor != null); + IDocumentProvider provider= fEditor.getDocumentProvider(); + if (provider == null) + return null; + + IDocument document= provider.getDocument(fEditor.getEditorInput()); + return document; + } + + /* + * @see org.eclipse.ui.texteditor.IUpdate#update() + */ + public void update() { + setEnabled(computeEnablement(fEditor)); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SpecificContentAssistExecutor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SpecificContentAssistExecutor.java new file mode 100644 index 00000000000..54fe92814e1 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/SpecificContentAssistExecutor.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.editor; + +import java.util.Collection; +import java.util.Iterator; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.text.ITextOperationTarget; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.ui.texteditor.ITextEditor; + +import org.eclipse.cdt.internal.ui.text.contentassist.CompletionProposalCategory; +import org.eclipse.cdt.internal.ui.text.contentassist.CompletionProposalComputerRegistry; + + +/** + * A content assist executor can invoke content assist for a specific proposal category on an editor. + * + * @since 4.0 + */ +public final class SpecificContentAssistExecutor { + + private final CompletionProposalComputerRegistry fRegistry; + + /** + * Creates a new executor. + * + * @param registry the computer registry to use for the enablement of proposal categories + */ + public SpecificContentAssistExecutor(CompletionProposalComputerRegistry registry) { + Assert.isNotNull(registry); + fRegistry= registry; + } + + /** + * Invokes content assist on editor, showing only proposals computed by the + * CompletionProposalCategory with the given categoryId. + * + * @param editor the editor to invoke code assist on + * @param categoryId the id of the proposal category to show proposals for + */ + public void invokeContentAssist(final ITextEditor editor, String categoryId) { + Collection categories= fRegistry.getProposalCategories(); + boolean[] inclusionState= new boolean[categories.size()]; + boolean[] separateState= new boolean[categories.size()]; + int i= 0; + for (Iterator it= categories.iterator(); it.hasNext(); i++) { + CompletionProposalCategory cat= (CompletionProposalCategory) it.next(); + inclusionState[i]= cat.isIncluded(); + cat.setIncluded(cat.getId().equals(categoryId)); + separateState[i]= cat.isSeparateCommand(); + cat.setSeparateCommand(false); + } + + try { + ITextOperationTarget target= (ITextOperationTarget) editor.getAdapter(ITextOperationTarget.class); + if (target != null && target.canDoOperation(ISourceViewer.CONTENTASSIST_PROPOSALS)) + target.doOperation(ISourceViewer.CONTENTASSIST_PROPOSALS); + } finally { + i= 0; + for (Iterator it= categories.iterator(); it.hasNext(); i++) { + CompletionProposalCategory cat= (CompletionProposalCategory) it.next(); + cat.setIncluded(inclusionState[i]); + cat.setSeparateCommand(separateState[i]); + } + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistAdvancedConfigurationBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistAdvancedConfigurationBlock.java new file mode 100644 index 00000000000..78549c01a6e --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistAdvancedConfigurationBlock.java @@ -0,0 +1,698 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.preferences; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.commands.Command; +import org.eclipse.core.commands.IParameter; +import org.eclipse.core.commands.Parameterization; +import org.eclipse.core.commands.ParameterizedCommand; +import org.eclipse.core.commands.common.NotDefinedException; + +import org.eclipse.core.runtime.Assert; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +import org.eclipse.jface.bindings.Binding; +import org.eclipse.jface.bindings.TriggerSequence; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.ArrayContentProvider; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.ViewerComparator; + +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.commands.ICommandService; +import org.eclipse.ui.dialogs.PreferencesUtil; +import org.eclipse.ui.keys.IBindingService; +import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; +import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; + +import org.eclipse.cdt.ui.PreferenceConstants; + +import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; +import org.eclipse.cdt.internal.ui.text.contentassist.CompletionProposalCategory; +import org.eclipse.cdt.internal.ui.text.contentassist.CompletionProposalComputerRegistry; +import org.eclipse.cdt.internal.ui.util.Messages; +import org.eclipse.cdt.internal.ui.util.PixelConverter; +import org.eclipse.cdt.internal.ui.util.SWTUtil; + + +/** + * + * @since 3.2 + */ +final class CodeAssistAdvancedConfigurationBlock extends OptionsConfigurationBlock { + + private static final Key PREF_EXCLUDED_CATEGORIES= getCDTUIKey(PreferenceConstants.CODEASSIST_EXCLUDED_CATEGORIES); + private static final Key PREF_CATEGORY_ORDER= getCDTUIKey(PreferenceConstants.CODEASSIST_CATEGORY_ORDER); + + private static Key[] getAllKeys() { + return new Key[] { + PREF_EXCLUDED_CATEGORIES, + PREF_CATEGORY_ORDER, + }; + } + + private final class DefaultTableLabelProvider extends LabelProvider implements ITableLabelProvider { + + /* + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int) + */ + public Image getColumnImage(Object element, int columnIndex) { + if (columnIndex == 0) + return ((ModelElement) element).getImage(); + return null; + } + + /* + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int) + */ + public String getColumnText(Object element, int columnIndex) { + switch (columnIndex) { + case 0: + return ((ModelElement) element).getName(); + case 1: + return ((ModelElement) element).getKeybindingAsString(); + default: + Assert.isTrue(false); + return null; + } + } + + /* + * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object) + */ + public String getText(Object element) { + return getColumnText(element, 0); // needed to make the sorter work + } + } + + private final class SeparateTableLabelProvider extends LabelProvider implements ITableLabelProvider { + + /* + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int) + */ + public Image getColumnImage(Object element, int columnIndex) { + if (columnIndex == 0) + return ((ModelElement) element).getImage(); + return null; + } + + /* + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int) + */ + public String getColumnText(Object element, int columnIndex) { + switch (columnIndex) { + case 0: + return ((ModelElement) element).getName(); + default: + Assert.isTrue(false); + return null; + } + } + } + + private final Comparator fCategoryComparator= new Comparator() { + private int getRank(Object o) { + return ((ModelElement) o).getRank(); + } + + public int compare(Object o1, Object o2) { + return getRank(o1) - getRank(o2); + } + }; + + private final class PreferenceModel { + private static final int LIMIT= 0xffff; + private static final String COLON= ":"; //$NON-NLS-1$ + private static final String SEPARATOR= "\0"; //$NON-NLS-1$ + + private final List fElements; + /** + * The read-only list of elements. + */ + final List elements; + + public PreferenceModel(CompletionProposalComputerRegistry registry) { + List categories= registry.getProposalCategories(); + fElements= new ArrayList(); + for (Iterator it= categories.iterator(); it.hasNext();) { + CompletionProposalCategory category= (CompletionProposalCategory) it.next(); + if (category.hasComputers()) { + fElements.add(new ModelElement(category, this)); + } + } + Collections.sort(fElements, fCategoryComparator); + elements= Collections.unmodifiableList(fElements); + } + + public void moveUp(ModelElement category) { + int index= fElements.indexOf(category); + if (index > 0) { + Object item= fElements.remove(index); + fElements.add(index - 1, item); + writeOrderPreference(null, false); + } + } + + public void moveDown(ModelElement category) { + int index= fElements.indexOf(category); + if (index < fElements.size() - 1) { + Object item= fElements.remove(index); + fElements.add(index + 1, item); + writeOrderPreference(null, false); + } + } + + private void writeInclusionPreference(ModelElement changed, boolean isInDefaultCategory) { + StringBuffer buf= new StringBuffer(); + for (Iterator it= fElements.iterator(); it.hasNext();) { + ModelElement item= (ModelElement) it.next(); + boolean included= changed == item ? isInDefaultCategory : item.isInDefaultCategory(); + if (!included) + buf.append(item.getId() + SEPARATOR); + } + + String newValue= buf.toString(); + String oldValue= setValue(PREF_EXCLUDED_CATEGORIES, newValue); + validateSettings(PREF_EXCLUDED_CATEGORIES, oldValue, newValue); + } + + private void writeOrderPreference(ModelElement changed, boolean isSeparate) { + StringBuffer buf= new StringBuffer(); + int i= 0; + for (Iterator it= fElements.iterator(); it.hasNext(); i++) { + ModelElement item= (ModelElement) it.next(); + boolean separate= changed == item ? isSeparate : item.isSeparateCommand(); + int rank= separate ? i : i + LIMIT; + buf.append(item.getId() + COLON + rank + SEPARATOR); + } + + String newValue= buf.toString(); + String oldValue= setValue(PREF_CATEGORY_ORDER, newValue); + validateSettings(PREF_CATEGORY_ORDER, oldValue, newValue); + } + + + private boolean readInclusionPreference(CompletionProposalCategory cat) { + String[] ids= getTokens(getValue(PREF_EXCLUDED_CATEGORIES), SEPARATOR); + for (int i= 0; i < ids.length; i++) { + if (ids[i].equals(cat.getId())) + return false; + } + return true; + } + + private int readOrderPreference(CompletionProposalCategory cat) { + String[] sortOrderIds= getTokens(getValue(PREF_CATEGORY_ORDER), SEPARATOR); + for (int i= 0; i < sortOrderIds.length; i++) { + String[] idAndRank= getTokens(sortOrderIds[i], COLON); + if (idAndRank[0].equals(cat.getId())) + return Integer.parseInt(idAndRank[1]); + } + return LIMIT + 1; + } + + public void update() { + Collections.sort(fElements, fCategoryComparator); + } + } + + private final class ModelElement { + private final CompletionProposalCategory fCategory; + private final Command fCommand; + private final IParameter fParam; + private final PreferenceModel fPreferenceModel; + + ModelElement(CompletionProposalCategory category, PreferenceModel model) { + fCategory= category; + ICommandService commandSvc= (ICommandService) PlatformUI.getWorkbench().getAdapter(ICommandService.class); + fCommand= commandSvc.getCommand("org.eclipse.cdt.ui.specific_content_assist.command"); //$NON-NLS-1$ + IParameter type; + try { + type= fCommand.getParameters()[0]; + } catch (NotDefinedException x) { + Assert.isTrue(false); + type= null; + } + fParam= type; + fPreferenceModel= model; + } + Image getImage() { + return CodeAssistAdvancedConfigurationBlock.this.getImage(fCategory.getImageDescriptor()); + } + String getName() { + return fCategory.getDisplayName(); + } + String getKeybindingAsString() { + final Parameterization[] params= { new Parameterization(fParam, fCategory.getId()) }; + final ParameterizedCommand pCmd= new ParameterizedCommand(fCommand, params); + String key= getKeyboardShortcut(pCmd); + return key; + } + boolean isInDefaultCategory() { + return fPreferenceModel.readInclusionPreference(fCategory); + } + void setInDefaultCategory(boolean included) { + if (included != isInDefaultCategory()) + fPreferenceModel.writeInclusionPreference(this, included); + } + String getId() { + return fCategory.getId(); + } + int getRank() { + int rank= getInternalRank(); + if (rank > PreferenceModel.LIMIT) + return rank - PreferenceModel.LIMIT; + return rank; + } + void moveUp() { + fPreferenceModel.moveUp(this); + } + void moveDown() { + fPreferenceModel.moveDown(this); + } + private int getInternalRank() { + return fPreferenceModel.readOrderPreference(fCategory); + } + boolean isSeparateCommand() { + return getInternalRank() < PreferenceModel.LIMIT; + } + + void setSeparateCommand(boolean separate) { + if (separate != isSeparateCommand()) + fPreferenceModel.writeOrderPreference(this, separate); + } + + void update() { + fCategory.setIncluded(isInDefaultCategory()); + int rank= getInternalRank(); + fCategory.setSortOrder(rank); + fCategory.setSeparateCommand(rank < PreferenceModel.LIMIT); + } + } + + /** element type: {@link ModelElement}. */ + private final PreferenceModel fModel; + private final Map fImages= new HashMap(); + + private CheckboxTableViewer fDefaultViewer; + private CheckboxTableViewer fSeparateViewer; + private Button fUpButton; + private Button fDownButton; + + CodeAssistAdvancedConfigurationBlock(IStatusChangeListener statusListener, IWorkbenchPreferenceContainer container) { + super(statusListener, null, getAllKeys(), container); + fModel= new PreferenceModel(CompletionProposalComputerRegistry.getDefault()); + } + + /* + * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#createContents(org.eclipse.swt.widgets.Composite) + */ + protected Control createContents(Composite parent) { + + ScrolledPageContent scrolled= new ScrolledPageContent(parent, SWT.H_SCROLL | SWT.V_SCROLL); + + scrolled.setExpandHorizontal(true); + scrolled.setExpandVertical(true); + + Composite composite= new Composite(scrolled, SWT.NONE); + int columns= 2; + GridLayout layout= new GridLayout(columns, false); + composite.setLayout(layout); + + + createDefaultLabel(composite, columns); + createDefaultViewer(composite, columns); + createKeysLink(composite, columns); + + createFiller(composite, columns); + + createSeparateLabel(composite, columns); + createSeparateSection(composite); + + createFiller(composite, columns); + + updateControls(); + if (fModel.elements.size() > 0) { + fDefaultViewer.getTable().select(0); + fSeparateViewer.getTable().select(0); + handleTableSelection(); + } + + scrolled.setContent(composite); + scrolled.setMinSize(composite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); + return scrolled; + } + + private void createDefaultLabel(Composite composite, int h_span) { + final ICommandService commandSvc= (ICommandService) PlatformUI.getWorkbench().getAdapter(ICommandService.class); + final Command command= commandSvc.getCommand(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); + ParameterizedCommand pCmd= new ParameterizedCommand(command, null); + String key= getKeyboardShortcut(pCmd); + if (key == null) + key= PreferencesMessages.CodeAssistAdvancedConfigurationBlock_no_shortcut; + + PixelConverter pixelConverter= new PixelConverter(composite); + int width= pixelConverter.convertWidthInCharsToPixels(40); + + Label label= new Label(composite, SWT.NONE | SWT.WRAP); + label.setText(Messages.format(PreferencesMessages.CodeAssistAdvancedConfigurationBlock_page_description, new Object[] { key })); + GridData gd= new GridData(GridData.FILL, GridData.FILL, true, false, h_span, 1); + gd.widthHint= width; + label.setLayoutData(gd); + + createFiller(composite, h_span); + + label= new Label(composite, SWT.NONE | SWT.WRAP); + label.setText(PreferencesMessages.CodeAssistAdvancedConfigurationBlock_default_table_description); + gd= new GridData(GridData.FILL, GridData.FILL, true, false, h_span, 1); + gd.widthHint= width; + label.setLayoutData(gd); + } + + private void createDefaultViewer(Composite composite, int h_span) { + fDefaultViewer= CheckboxTableViewer.newCheckList(composite, SWT.SINGLE | SWT.BORDER); + Table table= fDefaultViewer.getTable(); + table.setHeaderVisible(true); + table.setLinesVisible(false); + table.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, false, false, h_span, 1)); + + TableColumn nameColumn= new TableColumn(table, SWT.NONE); + nameColumn.setText(PreferencesMessages.CodeAssistAdvancedConfigurationBlock_default_table_category_column_title); + nameColumn.setResizable(false); + TableColumn keyColumn= new TableColumn(table, SWT.NONE); + keyColumn.setText(PreferencesMessages.CodeAssistAdvancedConfigurationBlock_default_table_keybinding_column_title); + keyColumn.setResizable(false); + + fDefaultViewer.addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + boolean checked= event.getChecked(); + ModelElement element= (ModelElement) event.getElement(); + element.setInDefaultCategory(checked); + } + }); + + fDefaultViewer.setContentProvider(new ArrayContentProvider()); + + DefaultTableLabelProvider labelProvider= new DefaultTableLabelProvider(); + fDefaultViewer.setLabelProvider(labelProvider); + fDefaultViewer.setInput(fModel.elements); + fDefaultViewer.setComparator(new ViewerComparator()); // sort alphabetically + + final int ICON_AND_CHECKBOX_WITH= 50; + final int HEADER_MARGIN= 20; + int minNameWidth= computeWidth(table, nameColumn.getText()) + HEADER_MARGIN; + int minKeyWidth= computeWidth(table, keyColumn.getText()) + HEADER_MARGIN; + for (int i= 0; i < fModel.elements.size(); i++) { + minNameWidth= Math.max(minNameWidth, computeWidth(table, labelProvider.getColumnText(fModel.elements.get(i), 0)) + ICON_AND_CHECKBOX_WITH); + minKeyWidth= Math.max(minKeyWidth, computeWidth(table, labelProvider.getColumnText(fModel.elements.get(i), 1))); + } + + nameColumn.setWidth(minNameWidth); + keyColumn.setWidth(minKeyWidth); + } + + private void createKeysLink(Composite composite, int h_span) { + Link link= new Link(composite, SWT.NONE | SWT.WRAP); + link.setText(PreferencesMessages.CodeAssistAdvancedConfigurationBlock_key_binding_hint); + link.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + PreferencesUtil.createPreferenceDialogOn(getShell(), e.text, null, null); + } + }); + + PixelConverter pixelConverter= new PixelConverter(composite); + int width= pixelConverter.convertWidthInCharsToPixels(40); + + // limit the size of the Link as it would take all it can get + GridData gd= new GridData(GridData.FILL, GridData.FILL, false, false, h_span, 1); + gd.widthHint= width; + link.setLayoutData(gd); + } + + private void createFiller(Composite composite, int h_span) { + Label filler= new Label(composite, SWT.NONE); + filler.setVisible(false); + filler.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, h_span, 1)); + } + + private void createSeparateLabel(Composite composite, int h_span) { + PixelConverter pixelConverter= new PixelConverter(composite); + int width= pixelConverter.convertWidthInCharsToPixels(40); + + Label label= new Label(composite, SWT.NONE | SWT.WRAP); + label.setText(PreferencesMessages.CodeAssistAdvancedConfigurationBlock_separate_table_description); + GridData gd= new GridData(GridData.FILL, GridData.FILL, false, false, h_span, 1); + gd.widthHint= width; + label.setLayoutData(gd); + } + + private void createSeparateSection(Composite composite) { + createSeparateViewer(composite); + createButtonList(composite); + } + + private void createSeparateViewer(Composite composite) { + fSeparateViewer= CheckboxTableViewer.newCheckList(composite, SWT.SINGLE | SWT.BORDER); + Table table= fSeparateViewer.getTable(); + table.setHeaderVisible(false); + table.setLinesVisible(false); + table.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, true, false, 1, 1)); + + TableColumn nameColumn= new TableColumn(table, SWT.NONE); + nameColumn.setText(PreferencesMessages.CodeAssistAdvancedConfigurationBlock_separate_table_category_column_title); + nameColumn.setResizable(false); + + fSeparateViewer.setContentProvider(new ArrayContentProvider()); + + ITableLabelProvider labelProvider= new SeparateTableLabelProvider(); + fSeparateViewer.setLabelProvider(labelProvider); + fSeparateViewer.setInput(fModel.elements); + + final int ICON_AND_CHECKBOX_WITH= 50; + final int HEADER_MARGIN= 20; + int minNameWidth= computeWidth(table, nameColumn.getText()) + HEADER_MARGIN; + for (int i= 0; i < fModel.elements.size(); i++) { + minNameWidth= Math.max(minNameWidth, computeWidth(table, labelProvider.getColumnText(fModel.elements.get(i), 0)) + ICON_AND_CHECKBOX_WITH); + } + + nameColumn.setWidth(minNameWidth); + + fSeparateViewer.addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + boolean checked= event.getChecked(); + ModelElement element= (ModelElement) event.getElement(); + element.setSeparateCommand(checked); + } + }); + + table.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + handleTableSelection(); + } + }); + + } + + private void createButtonList(Composite parent) { + Composite composite= new Composite(parent, SWT.NONE); + composite.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false)); + + GridLayout layout= new GridLayout(); + layout.marginWidth= 0; + layout.marginHeight= 0; + composite.setLayout(layout); + + fUpButton= new Button(composite, SWT.PUSH | SWT.CENTER); + fUpButton.setText(PreferencesMessages.CodeAssistAdvancedConfigurationBlock_Up); + fUpButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + int index= getSelectionIndex(); + if (index != -1) { + ((ModelElement) fModel.elements.get(index)).moveUp(); + fSeparateViewer.refresh(); + handleTableSelection(); + } + } + }); + fUpButton.setLayoutData(new GridData()); + SWTUtil.setButtonDimensionHint(fUpButton); + + fDownButton= new Button(composite, SWT.PUSH | SWT.CENTER); + fDownButton.setText(PreferencesMessages.CodeAssistAdvancedConfigurationBlock_Down); + fDownButton.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + int index= getSelectionIndex(); + if (index != -1) { + ((ModelElement) fModel.elements.get(index)).moveDown(); + fSeparateViewer.refresh(); + handleTableSelection(); + } + } + }); + fDownButton.setLayoutData(new GridData()); + SWTUtil.setButtonDimensionHint(fDownButton); + } + + private void handleTableSelection() { + ModelElement item= getSelectedItem(); + if (item != null) { + int index= getSelectionIndex(); + fUpButton.setEnabled(index > 0); + fDownButton.setEnabled(index < fModel.elements.size() - 1); + } else { + fUpButton.setEnabled(false); + fDownButton.setEnabled(false); + } + } + + private ModelElement getSelectedItem() { + return (ModelElement) ((IStructuredSelection) fSeparateViewer.getSelection()).getFirstElement(); + } + + private int getSelectionIndex() { + return fSeparateViewer.getTable().getSelectionIndex(); + } + + /* + * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#updateControls() + */ + protected void updateControls() { + super.updateControls(); + + fModel.update(); + updateCheckedState(); + fDefaultViewer.refresh(); + fSeparateViewer.refresh(); + handleTableSelection(); + } + + private void updateCheckedState() { + final int size= fModel.elements.size(); + List defaultChecked= new ArrayList(size); + List separateChecked= new ArrayList(size); + + for (Iterator it= fModel.elements.iterator(); it.hasNext();) { + ModelElement element= (ModelElement) it.next(); + if (element.isInDefaultCategory()) + defaultChecked.add(element); + if (element.isSeparateCommand()) + separateChecked.add(element); + } + + fDefaultViewer.setCheckedElements(defaultChecked.toArray(new Object[defaultChecked.size()])); + fSeparateViewer.setCheckedElements(separateChecked.toArray(new Object[separateChecked.size()])); + } + + /* + * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#processChanges(org.eclipse.ui.preferences.IWorkbenchPreferenceContainer) + */ + protected boolean processChanges(IWorkbenchPreferenceContainer container) { + for (Iterator it= fModel.elements.iterator(); it.hasNext();) { + ModelElement item= (ModelElement) it.next(); + item.update(); + } + + return super.processChanges(container); + } + + /* + * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#validateSettings(org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock.Key, java.lang.String, java.lang.String) + */ + protected void validateSettings(Key changedKey, String oldValue, String newValue) { + } + + /* + * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#getFullBuildDialogStrings(boolean) + */ + protected String[] getFullBuildDialogStrings(boolean workspaceSettings) { + // no builds triggered by our settings + return null; + } + + /* + * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#dispose() + */ + public void dispose() { + for (Iterator it= fImages.values().iterator(); it.hasNext();) { + Image image= (Image) it.next(); + image.dispose(); + } + + super.dispose(); + } + + private int computeWidth(Control control, String name) { + if (name == null) + return 0; + GC gc= new GC(control); + try { + gc.setFont(JFaceResources.getDialogFont()); + return gc.stringExtent(name).x + 10; + } finally { + gc.dispose(); + } + } + + private static String getKeyboardShortcut(ParameterizedCommand command) { + final IBindingService bindingSvc= (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class); + final Binding[] bindings= bindingSvc.getBindings(); + for (int i= 0; i < bindings.length; i++) { + Binding binding= bindings[i]; + if (command.equals(binding.getParameterizedCommand())) { + TriggerSequence triggers= binding.getTriggerSequence(); + return triggers.format(); + } + } + return null; + } + + private Image getImage(ImageDescriptor imgDesc) { + if (imgDesc == null) + return null; + + Image img= (Image) fImages.get(imgDesc); + if (img == null) { + img= imgDesc.createImage(false); + fImages.put(imgDesc, img); + } + return img; + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistAdvancedPreferencePage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistAdvancedPreferencePage.java new file mode 100644 index 00000000000..ac1e7aadfc3 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistAdvancedPreferencePage.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.preferences; + +import org.eclipse.core.resources.IProject; + +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; + +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; + +import org.eclipse.cdt.internal.ui.ICHelpContextIds; + +public final class CodeAssistAdvancedPreferencePage extends PropertyAndPreferencePage { + + private CodeAssistAdvancedConfigurationBlock fConfigurationBlock; + + public void createControl(Composite parent) { + IWorkbenchPreferenceContainer container= (IWorkbenchPreferenceContainer) getContainer(); + fConfigurationBlock= new CodeAssistAdvancedConfigurationBlock(getNewStatusChangedListener(), container); + + super.createControl(parent); + PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), ICHelpContextIds.C_EDITOR_PREF_PAGE); + } + + protected Control createPreferenceContent(Composite composite) { + return fConfigurationBlock.createContents(composite); + } + + protected boolean hasProjectSpecificOptions(IProject project) { + return false; + } + + protected String getPreferencePageID() { + return "org.eclipse.cdt.ui.preferences.CodeAssistPreferenceAdvanced"; //$NON-NLS-1$ + } + + protected String getPropertyPageID() { + return null; + } + + /* + * @see org.eclipse.jface.dialogs.DialogPage#dispose() + */ + public void dispose() { + if (fConfigurationBlock != null) { + fConfigurationBlock.dispose(); + } + super.dispose(); + } + + /* + * @see org.eclipse.jface.preference.IPreferencePage#performDefaults() + */ + protected void performDefaults() { + super.performDefaults(); + if (fConfigurationBlock != null) { + fConfigurationBlock.performDefaults(); + } + } + + /* + * @see org.eclipse.jface.preference.IPreferencePage#performOk() + */ + public boolean performOk() { + if (fConfigurationBlock != null && !fConfigurationBlock.performOk()) { + return false; + } + return super.performOk(); + } + + /* + * @see org.eclipse.jface.preference.IPreferencePage#performApply() + */ + public void performApply() { + if (fConfigurationBlock != null) { + fConfigurationBlock.performApply(); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistPreferencePage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistPreferencePage.java index 88629172e34..e25208bae54 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistPreferencePage.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/CodeAssistPreferencePage.java @@ -45,13 +45,13 @@ public class CodeAssistPreferencePage extends AbstractPreferencePage { overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.INT, ContentAssistPreference.AUTOACTIVATION_DELAY)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.AUTOINSERT)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.PREFIX_COMPLETION)); - overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.INT, ContentAssistPreference.TIMEOUT_DELAY)); +// overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.INT, ContentAssistPreference.TIMEOUT_DELAY)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, ContentAssistPreference.AUTOACTIVATION_TRIGGERS_DOT)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, ContentAssistPreference.AUTOACTIVATION_TRIGGERS_ARROW)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, ContentAssistPreference.AUTOACTIVATION_TRIGGERS_DOUBLECOLON)); - overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.SHOW_DOCUMENTED_PROPOSALS)); - overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.ORDER_PROPOSALS)); - overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.ADD_INCLUDE)); +// overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.SHOW_DOCUMENTED_PROPOSALS)); +// overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.ORDER_PROPOSALS)); +// overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.ADD_INCLUDE)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.CURRENT_FILE_SEARCH_SCOPE)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.BOOLEAN, ContentAssistPreference.PROJECT_SEARCH_SCOPE)); overlayKeys.add(new OverlayPreferenceStore.OverlayKey(OverlayPreferenceStore.STRING, ContentAssistPreference.PROPOSALS_FILTER)); @@ -106,8 +106,8 @@ public class CodeAssistPreferencePage extends AbstractPreferencePage { label = PreferencesMessages.CEditorPreferencePage_ContentAssistPage_sortingSection_title; Group sortingGroup = addGroupBox(contentAssistComposite, label, 2); - label= PreferencesMessages.CEditorPreferencePage_ContentAssistPage_showProposalsInAlphabeticalOrder; - addCheckBox(sortingGroup, label, ContentAssistPreference.ORDER_PROPOSALS, 0); +// label= PreferencesMessages.CEditorPreferencePage_ContentAssistPage_showProposalsInAlphabeticalOrder; +// addCheckBox(sortingGroup, label, ContentAssistPreference.ORDER_PROPOSALS, 0); label = PreferencesMessages.CEditorPreferencePage_ContentAssistPage_proposalFilterSelect ; addComboBox(sortingGroup, label, ContentAssistPreference.PROPOSALS_FILTER, 20, 0); @@ -147,7 +147,7 @@ public class CodeAssistPreferencePage extends AbstractPreferencePage { store.setDefault(ContentAssistPreference.CURRENT_FILE_SEARCH_SCOPE, true); store.setDefault(ContentAssistPreference.PROJECT_SEARCH_SCOPE, false); - store.setDefault(ContentAssistPreference.TIMEOUT_DELAY, 3000); +// store.setDefault(ContentAssistPreference.TIMEOUT_DELAY, 3000); store.setDefault(ContentAssistPreference.AUTOACTIVATION_TRIGGERS_DOT, true); store.setDefault(ContentAssistPreference.AUTOACTIVATION_TRIGGERS_ARROW, true); @@ -156,8 +156,8 @@ public class CodeAssistPreferencePage extends AbstractPreferencePage { store.setDefault(ContentAssistPreference.AUTOINSERT, true); store.setDefault(ContentAssistPreference.PREFIX_COMPLETION, true); - store.setDefault(ContentAssistPreference.ORDER_PROPOSALS, false); - store.setDefault(ContentAssistPreference.ADD_INCLUDE, true); +// store.setDefault(ContentAssistPreference.ORDER_PROPOSALS, false); +// store.setDefault(ContentAssistPreference.ADD_INCLUDE, true); store.setDefault(ContentAssistPreference.PROPOSALS_FILTER, ProposalFilterPreferencesUtil.getProposalFilternamesAsString()); // $NON_NLS 1$ } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/OptionsConfigurationBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/OptionsConfigurationBlock.java index 94ad696cec3..3d319f09738 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/OptionsConfigurationBlock.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/OptionsConfigurationBlock.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2006 IBM Corporation and others. + * 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 @@ -7,72 +7,130 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.ui.preferences; -import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; import java.util.Map; import java.util.StringTokenizer; -import org.eclipse.core.resources.IncrementalProjectBuilder; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IResourceProxy; -import org.eclipse.core.resources.IResourceProxyVisitor; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.SubProgressMonitor; - -import org.eclipse.osgi.util.NLS; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.ProjectScope; +import org.eclipse.core.runtime.preferences.DefaultScope; +import org.eclipse.core.runtime.preferences.IEclipsePreferences; +import org.eclipse.core.runtime.preferences.IScopeContext; +import org.eclipse.core.runtime.preferences.InstanceScope; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.resource.JFaceResources; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; 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.Label; +import org.eclipse.swt.widgets.Link; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Widget; - -import org.eclipse.jface.dialogs.IDialogConstants; -import org.eclipse.jface.dialogs.MessageDialog; -import org.eclipse.jface.dialogs.ProgressMonitorDialog; -import org.eclipse.jface.operation.IRunnableWithProgress; - -import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.forms.events.ExpansionAdapter; +import org.eclipse.ui.forms.events.ExpansionEvent; +import org.eclipse.ui.forms.widgets.ExpandableComposite; +import org.eclipse.ui.preferences.IWorkbenchPreferenceContainer; +import org.eclipse.ui.preferences.IWorkingCopyManager; +import org.eclipse.ui.preferences.WorkingCopyManager; +import org.osgi.service.prefs.BackingStoreException; import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.model.CoreModel; -import org.eclipse.cdt.core.model.CModelException; -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.util.ExceptionHandler; -import org.eclipse.cdt.internal.ui.util.EditorUtility; + import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; /** - */ + * Abstract options configuration block providing a general implementation for setting up + * an options configuration page. + */ public abstract class OptionsConfigurationBlock { + + public static final class Key { + + private String fQualifier; + private String fKey; + + public Key(String qualifier, String key) { + fQualifier= qualifier; + fKey= key; + } + + public String getName() { + return fKey; + } + + private IEclipsePreferences getNode(IScopeContext context, IWorkingCopyManager manager) { + IEclipsePreferences node= context.getNode(fQualifier); + if (manager != null) { + return manager.getWorkingCopy(node); + } + return node; + } + + public String getStoredValue(IScopeContext context, IWorkingCopyManager manager) { + return getNode(context, manager).get(fKey, null); + } + + public String getStoredValue(IScopeContext[] lookupOrder, boolean ignoreTopScope, IWorkingCopyManager manager) { + for (int i= ignoreTopScope ? 1 : 0; i < lookupOrder.length; i++) { + String value= getStoredValue(lookupOrder[i], manager); + if (value != null) { + return value; + } + } + return null; + } + + public void setStoredValue(IScopeContext context, String value, IWorkingCopyManager manager) { + if (value != null) { + getNode(context, manager).put(fKey, value); + } else { + getNode(context, manager).remove(fKey); + } + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return fQualifier + '/' + fKey; + } + + public String getQualifier() { + return fQualifier; + } + + } + protected static class ControlData { - private String fKey; + private Key fKey; private String[] fValues; - public ControlData(String key, String[] values) { + public ControlData(Key key, String[] values) { fKey= key; fValues= values; } - public String getKey() { + public Key getKey() { return fKey; } @@ -86,75 +144,170 @@ public abstract class OptionsConfigurationBlock { } public int getSelection(String value) { - for (int i= 0; i < fValues.length; i++) { - if (value.equals(fValues[i])) { - return i; + if (value != null) { + for (int i= 0; i < fValues.length; i++) { + if (value.equals(fValues[i])) { + return i; + } } } - throw new IllegalArgumentException(); + return fValues.length -1; // assume the last option is the least severe } } + private static final String REBUILD_COUNT_KEY= "preferences_build_requested"; //$NON-NLS-1$ - protected Map fWorkingValues; + private static final String SETTINGS_EXPANDED= "expanded"; //$NON-NLS-1$ - protected ArrayList fCheckBoxes; - protected ArrayList fComboBoxes; - protected ArrayList fTextBoxes; + protected final ArrayList fCheckBoxes; + protected final ArrayList fComboBoxes; + protected final ArrayList fTextBoxes; + protected final HashMap fLabels; + protected final ArrayList fExpandedComposites; private SelectionListener fSelectionListener; private ModifyListener fTextModifyListener; protected IStatusChangeListener fContext; - protected ICProject fProject; // project or null + protected final IProject fProject; // project or null + protected final Key[] fAllKeys; + + private IScopeContext[] fLookupOrder; private Shell fShell; - public OptionsConfigurationBlock(IStatusChangeListener context, ICProject project) { + private final IWorkingCopyManager fManager; + private IWorkbenchPreferenceContainer fContainer; + + private Map fDisabledProjectSettings; // null when project specific settings are turned off + + private int fRebuildCount; /// used to prevent multiple dialogs that ask for a rebuild + + public OptionsConfigurationBlock(IStatusChangeListener context, IProject project, Key[] allKeys, IWorkbenchPreferenceContainer container) { fContext= context; fProject= project; + fAllKeys= allKeys; + fContainer= container; + if (container == null) { + fManager= new WorkingCopyManager(); + } else { + fManager= container.getWorkingCopyManager(); + } - fWorkingValues= getOptions(true); + if (fProject != null) { + fLookupOrder= new IScopeContext[] { + new ProjectScope(fProject), + new InstanceScope(), + new DefaultScope() + }; + } else { + fLookupOrder= new IScopeContext[] { + new InstanceScope(), + new DefaultScope() + }; + } + + testIfOptionsComplete(allKeys); + if (fProject == null || hasProjectSpecificOptions(fProject)) { + fDisabledProjectSettings= null; + } else { + fDisabledProjectSettings= new IdentityHashMap(); + for (int i= 0; i < allKeys.length; i++) { + Key curr= allKeys[i]; + fDisabledProjectSettings.put(curr, curr.getStoredValue(fLookupOrder, false, fManager)); + } + } + + settingsUpdated(); fCheckBoxes= new ArrayList(); fComboBoxes= new ArrayList(); - fTextBoxes= new ArrayList(2); - } - - protected abstract String[] getAllKeys(); - - protected Map getOptions(boolean inheritCCoreOptions) { - if (fProject != null) { - return fProject.getOptions(inheritCCoreOptions); - } - return CCorePlugin.getOptions(); - } - - protected Map getDefaultOptions() { - return CCorePlugin.getDefaultOptions(); + fTextBoxes= new ArrayList(2); + fLabels= new HashMap(); + fExpandedComposites= new ArrayList(); + + fRebuildCount= getRebuildCount(); } - public final boolean hasProjectSpecificOptions() { - if (fProject != null) { - Map settings= fProject.getOptions(false); - String[] allKeys= getAllKeys(); + protected final IWorkbenchPreferenceContainer getPreferenceContainer() { + return fContainer; + } + + protected static Key getKey(String plugin, String key) { + return new Key(plugin, key); + } + + protected final static Key getCDTCoreKey(String key) { + return getKey(CCorePlugin.PLUGIN_ID, key); + } + + protected final static Key getCDTUIKey(String key) { + return getKey(CUIPlugin.PLUGIN_ID, key); + } + + + private void testIfOptionsComplete(Key[] allKeys) { + for (int i= 0; i < allKeys.length; i++) { + if (allKeys[i].getStoredValue(fLookupOrder, false, fManager) == null) { + CUIPlugin.getDefault().logErrorMessage("preference option missing: " + allKeys[i] + " (" + this.getClass().getName() +')'); //$NON-NLS-1$//$NON-NLS-2$ + } + } + } + + private int getRebuildCount() { + return fManager.getWorkingCopy(new DefaultScope().getNode(CUIPlugin.PLUGIN_ID)).getInt(REBUILD_COUNT_KEY, 0); + } + + private void incrementRebuildCount() { + fRebuildCount++; + fManager.getWorkingCopy(new DefaultScope().getNode(CUIPlugin.PLUGIN_ID)).putInt(REBUILD_COUNT_KEY, fRebuildCount); + } + + + protected void settingsUpdated() { + } + + + public void selectOption(String key, String qualifier) { + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + if (curr.getName().equals(key) && curr.getQualifier().equals(qualifier)) { + selectOption(curr); + } + } + } + + public void selectOption(Key key) { + Control control= findControl(key); + if (control != null) { + if (!fExpandedComposites.isEmpty()) { + ExpandableComposite expandable= getParentExpandableComposite(control); + if (expandable != null) { + for (int i= 0; i < fExpandedComposites.size(); i++) { + ExpandableComposite curr= (ExpandableComposite) fExpandedComposites.get(i); + curr.setExpanded(curr == expandable); + } + expandedStateChanged(expandable); + } + } + control.setFocus(); + } + } + + + public final boolean hasProjectSpecificOptions(IProject project) { + if (project != null) { + IScopeContext projectContext= new ProjectScope(project); + Key[] allKeys= fAllKeys; for (int i= 0; i < allKeys.length; i++) { - if (settings.get(allKeys[i]) != null) { + if (allKeys[i].getStoredValue(projectContext, fManager) != null) { return true; } } } return false; } - - protected final void setOptions(Map map) { - if (fProject != null) { - fProject.setOptions(map); - } else { - CCorePlugin.setOptions((HashMap) map); - } - } - + protected Shell getShell() { return fShell; } @@ -165,58 +318,150 @@ public abstract class OptionsConfigurationBlock { protected abstract Control createContents(Composite parent); - protected void addCheckBox(Composite parent, String label, String key, String[] values, int indent) { + protected Button addCheckBox(Composite parent, String label, Key key, String[] values, int indent) { ControlData data= new ControlData(key, values); GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_FILL); - gd.horizontalSpan= 2; + gd.horizontalSpan= 3; gd.horizontalIndent= indent; Button checkBox= new Button(parent, SWT.CHECK); + checkBox.setFont(JFaceResources.getDialogFont()); checkBox.setText(label); checkBox.setData(data); checkBox.setLayoutData(gd); checkBox.addSelectionListener(getSelectionListener()); - String currValue= (String)fWorkingValues.get(key); + makeScrollableCompositeAware(checkBox); + + String currValue= getValue(key); checkBox.setSelection(data.getSelection(currValue) == 0); fCheckBoxes.add(checkBox); + + return checkBox; } - protected void addComboBox(Composite parent, String label, String key, String[] values, String[] valueLabels, int indent) { + protected Button addCheckBoxWithLink(Composite parent, String label, Key key, String[] values, int indent, int widthHint, SelectionListener listener) { ControlData data= new ControlData(key, values); - GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + GridData gd= new GridData(GridData.FILL, GridData.FILL, true, false); + gd.horizontalSpan= 3; + gd.horizontalIndent= indent; + + Composite composite= new Composite(parent, SWT.NONE); + GridLayout layout= new GridLayout(); + layout.marginHeight= 0; + layout.marginWidth= 0; + layout.numColumns= 2; + composite.setLayout(layout); + composite.setLayoutData(gd); + + Button checkBox= new Button(composite, SWT.CHECK); + checkBox.setFont(JFaceResources.getDialogFont()); + checkBox.setData(data); + checkBox.setLayoutData(new GridData(GridData.FILL, GridData.BEGINNING, false, false)); + checkBox.addSelectionListener(getSelectionListener()); + + gd= new GridData(GridData.FILL, GridData.CENTER, true, false); + gd.widthHint= widthHint; + + Link link= new Link(composite, SWT.NONE); + link.setText(label); + link.setLayoutData(gd); + if (listener != null) { + link.addSelectionListener(listener); + } + + makeScrollableCompositeAware(link); + makeScrollableCompositeAware(checkBox); + + String currValue= getValue(key); + checkBox.setSelection(data.getSelection(currValue) == 0); + + fCheckBoxes.add(checkBox); + + return checkBox; + } + + protected Combo addComboBox(Composite parent, String label, Key key, String[] values, String[] valueLabels, int indent) { + GridData gd= new GridData(GridData.FILL, GridData.CENTER, true, false, 2, 1); gd.horizontalIndent= indent; - Label labelControl= new Label(parent, SWT.LEFT | SWT.WRAP); + Label labelControl= new Label(parent, SWT.LEFT); + labelControl.setFont(JFaceResources.getDialogFont()); labelControl.setText(label); labelControl.setLayoutData(gd); + + Combo comboBox= newComboControl(parent, key, values, valueLabels); + comboBox.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); + + fLabels.put(comboBox, labelControl); - Combo comboBox= new Combo(parent, SWT.READ_ONLY); + return comboBox; + } + + protected Combo addInversedComboBox(Composite parent, String label, Key key, String[] values, String[] valueLabels, int indent) { + GridData gd= new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); + gd.horizontalIndent= indent; + gd.horizontalSpan= 3; + + Composite composite= new Composite(parent, SWT.NONE); + GridLayout layout= new GridLayout(); + layout.marginHeight= 0; + layout.marginWidth= 0; + layout.numColumns= 2; + composite.setLayout(layout); + composite.setLayoutData(gd); + + Combo comboBox= newComboControl(composite, key, values, valueLabels); + comboBox.setFont(JFaceResources.getDialogFont()); + comboBox.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); + + Label labelControl= new Label(composite, SWT.LEFT | SWT.WRAP); + labelControl.setText(label); + labelControl.setLayoutData(new GridData()); + + fLabels.put(comboBox, labelControl); + return comboBox; + } + + protected Combo newComboControl(Composite composite, Key key, String[] values, String[] valueLabels) { + ControlData data= new ControlData(key, values); + + Combo comboBox= new Combo(composite, SWT.READ_ONLY); comboBox.setItems(valueLabels); comboBox.setData(data); - comboBox.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL)); comboBox.addSelectionListener(getSelectionListener()); + comboBox.setFont(JFaceResources.getDialogFont()); + + makeScrollableCompositeAware(comboBox); - String currValue= (String)fWorkingValues.get(key); + String currValue= getValue(key); comboBox.select(data.getSelection(currValue)); fComboBoxes.add(comboBox); + return comboBox; } - - protected Text addTextField(Composite parent, String label, String key, int indent, int widthHint) { - Label labelControl= new Label(parent, SWT.NONE); + + protected Text addTextField(Composite parent, String label, Key key, int indent, int widthHint) { + Label labelControl= new Label(parent, SWT.WRAP); labelControl.setText(label); + labelControl.setFont(JFaceResources.getDialogFont()); labelControl.setLayoutData(new GridData()); Text textBox= new Text(parent, SWT.BORDER | SWT.SINGLE); textBox.setData(key); textBox.setLayoutData(new GridData()); - String currValue= (String) fWorkingValues.get(key); - textBox.setText(currValue); + makeScrollableCompositeAware(textBox); + + fLabels.put(textBox, labelControl); + + String currValue= getValue(key); + if (currValue != null) { + textBox.setText(currValue); + } textBox.addModifyListener(getTextModifyListener()); GridData data= new GridData(GridData.HORIZONTAL_ALIGN_FILL); @@ -224,12 +469,83 @@ public abstract class OptionsConfigurationBlock { data.widthHint= widthHint; } data.horizontalIndent= indent; + data.horizontalSpan= 2; textBox.setLayoutData(data); fTextBoxes.add(textBox); return textBox; - } - + } + + protected ScrolledPageContent getParentScrolledComposite(Control control) { + Control parent= control.getParent(); + while (!(parent instanceof ScrolledPageContent) && parent != null) { + parent= parent.getParent(); + } + if (parent instanceof ScrolledPageContent) { + return (ScrolledPageContent) parent; + } + return null; + } + + protected ExpandableComposite getParentExpandableComposite(Control control) { + Control parent= control.getParent(); + while (!(parent instanceof ExpandableComposite) && parent != null) { + parent= parent.getParent(); + } + if (parent instanceof ExpandableComposite) { + return (ExpandableComposite) parent; + } + return null; + } + + private void makeScrollableCompositeAware(Control control) { + ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(control); + if (parentScrolledComposite != null) { + parentScrolledComposite.adaptChild(control); + } + } + + protected ExpandableComposite createStyleSection(Composite parent, String label, int nColumns) { + ExpandableComposite excomposite= new ExpandableComposite(parent, SWT.NONE, ExpandableComposite.TWISTIE | ExpandableComposite.CLIENT_INDENT); + excomposite.setText(label); + excomposite.setExpanded(false); + excomposite.setFont(JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT)); + excomposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false, nColumns, 1)); + excomposite.addExpansionListener(new ExpansionAdapter() { + public void expansionStateChanged(ExpansionEvent e) { + expandedStateChanged((ExpandableComposite) e.getSource()); + } + }); + fExpandedComposites.add(excomposite); + makeScrollableCompositeAware(excomposite); + return excomposite; + } + + protected final void expandedStateChanged(ExpandableComposite expandable) { + ScrolledPageContent parentScrolledComposite= getParentScrolledComposite(expandable); + if (parentScrolledComposite != null) { + parentScrolledComposite.reflow(true); + } + } + + protected void restoreSectionExpansionStates(IDialogSettings settings) { + for (int i= 0; i < fExpandedComposites.size(); i++) { + ExpandableComposite excomposite= (ExpandableComposite) fExpandedComposites.get(i); + if (settings == null) { + excomposite.setExpanded(i == 0); // only expand the first node by default + } else { + excomposite.setExpanded(settings.getBoolean(SETTINGS_EXPANDED + String.valueOf(i))); + } + } + } + + protected void storeSectionExpansionStates(IDialogSettings settings) { + for (int i= 0; i < fExpandedComposites.size(); i++) { + ExpandableComposite curr= (ExpandableComposite) fExpandedComposites.get(i); + settings.put(SETTINGS_EXPANDED + String.valueOf(i), curr.isExpanded()); + } + } + protected SelectionListener getSelectionListener() { if (fSelectionListener == null) { fSelectionListener= new SelectionListener() { @@ -264,27 +580,60 @@ public abstract class OptionsConfigurationBlock { } else { return; } - fWorkingValues.put(data.getKey(), newValue); - - validateSettings(data.getKey(), newValue); + String oldValue= setValue(data.getKey(), newValue); + validateSettings(data.getKey(), oldValue, newValue); } protected void textChanged(Text textControl) { - String key= (String) textControl.getData(); + Key key= (Key) textControl.getData(); String number= textControl.getText(); - fWorkingValues.put(key, number); - validateSettings(key, number); + String oldValue= setValue(key, number); + validateSettings(key, oldValue, number); } - protected boolean checkValue(String key, String value) { - return value.equals(fWorkingValues.get(key)); + protected boolean checkValue(Key key, String value) { + return value.equals(getValue(key)); } - /* (non-javadoc) + protected String getValue(Key key) { + if (fDisabledProjectSettings != null) { + return (String) fDisabledProjectSettings.get(key); + } + return key.getStoredValue(fLookupOrder, false, fManager); + } + + + protected boolean getBooleanValue(Key key) { + return Boolean.valueOf(getValue(key)).booleanValue(); + } + + protected String setValue(Key key, String value) { + if (fDisabledProjectSettings != null) { + return (String) fDisabledProjectSettings.put(key, value); + } + String oldValue= getValue(key); + key.setStoredValue(fLookupOrder[0], value, fManager); + return oldValue; + } + + protected String setValue(Key key, boolean value) { + return setValue(key, String.valueOf(value)); + } + + /** + * Returns the value as actually stored in the preference store. + * @param key + * @return the value as actually stored in the preference store. + */ + protected String getStoredValue(Key key) { + return key.getStoredValue(fLookupOrder, false, fManager); + } + + /** * Update fields and validate. * @param changedKey Key that changed, or null, if all changed. */ - protected abstract void validateSettings(String changedKey, String newValue); + protected abstract void validateSettings(Key changedKey, String oldValue, String newValue); protected String[] getTokens(String text, String separator) { @@ -295,183 +644,242 @@ public abstract class OptionsConfigurationBlock { res[i]= tok.nextToken().trim(); } return res; - } + } - - public boolean performOk(boolean enabled) { - String[] allKeys= getAllKeys(); - Map actualOptions= getOptions(false); - - // preserve other options - boolean hasChanges= false; - for (int i= 0; i < allKeys.length; i++) { - String key= allKeys[i]; - String oldVal= (String) actualOptions.get(key); - String val= null; - if (enabled) { - val= (String) fWorkingValues.get(key); - if (!val.equals(oldVal)) { - hasChanges= true; - actualOptions.put(key, val); - } - } else { + private boolean getChanges(IScopeContext currContext, List changedSettings) { + boolean needsBuild= false; + for (int i= 0; i < fAllKeys.length; i++) { + Key key= fAllKeys[i]; + String oldVal= key.getStoredValue(currContext, null); + String val= key.getStoredValue(currContext, fManager); + if (val == null) { if (oldVal != null) { - actualOptions.remove(key); - hasChanges= true; + changedSettings.add(key); + needsBuild |= !oldVal.equals(key.getStoredValue(fLookupOrder, true, fManager)); + } + } else if (!val.equals(oldVal)) { + changedSettings.add(key); + needsBuild |= oldVal != null || !val.equals(key.getStoredValue(fLookupOrder, true, fManager)); + } + } + return needsBuild; + } + + public void useProjectSpecificSettings(boolean enable) { + boolean hasProjectSpecificOption= fDisabledProjectSettings == null; + if (enable != hasProjectSpecificOption && fProject != null) { + if (enable) { + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + String val= (String) fDisabledProjectSettings.get(curr); + curr.setStoredValue(fLookupOrder[0], val, fManager); + } + fDisabledProjectSettings= null; + updateControls(); + validateSettings(null, null, null); + } else { + fDisabledProjectSettings= new IdentityHashMap(); + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + String oldSetting= curr.getStoredValue(fLookupOrder, false, fManager); + fDisabledProjectSettings.put(curr, oldSetting); + curr.setStoredValue(fLookupOrder[0], null, fManager); // clear project settings } } } + } + + public boolean areSettingsEnabled() { + return fDisabledProjectSettings == null || fProject == null; + } + + + public boolean performOk() { + return processChanges(fContainer); + } + + public boolean performApply() { + return processChanges(null); // apply directly + } + + protected boolean processChanges(IWorkbenchPreferenceContainer container) { + IScopeContext currContext= fLookupOrder[0]; - - if (hasChanges) { - boolean doReParse = false; - String[] strings = getFullReParseDialogStrings(fProject == null); + List /* */ changedOptions= new ArrayList(); + boolean needsBuild= getChanges(currContext, changedOptions); + if (changedOptions.isEmpty()) { + return true; + } + if (needsBuild) { + int count= getRebuildCount(); + if (count > fRebuildCount) { + needsBuild= false; // build already requested + fRebuildCount= count; + } + } + + boolean doBuild= false; + if (needsBuild) { + String[] strings= getFullBuildDialogStrings(fProject == null); if (strings != null) { - MessageDialog dialog = new MessageDialog(getShell(), strings[0], null, strings[1], MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL }, 2); + MessageDialog dialog= new MessageDialog(getShell(), strings[0], null, strings[1], MessageDialog.QUESTION, new String[] { IDialogConstants.YES_LABEL, IDialogConstants.NO_LABEL, IDialogConstants.CANCEL_LABEL }, 2); int res= dialog.open(); if (res == 0) { - doReParse = true; + doBuild= true; } else if (res != 1) { return false; // cancel pressed } } - setOptions(actualOptions); - if (doReParse) { - doFullReParse(); + } + if (container != null) { + // no need to apply the changes to the original store: will be done by the page container + if (doBuild) { // post build + incrementRebuildCount(); + // do a re-index? +// container.registerUpdateJob(CoreUtility.getBuildJob(fProject)); } + } else { + // apply changes right away + try { + fManager.applyChanges(); + } catch (BackingStoreException e) { + CUIPlugin.getDefault().log(e); + return false; + } + if (doBuild) { + // do a re-index? +// CoreUtility.getBuildJob(fProject).schedule(); + } + } return true; } - protected abstract String[] getFullBuildDialogStrings(boolean workspaceSettings); - protected abstract String[] getFullReParseDialogStrings(boolean workspaceSettings); - - protected void doFullBuild() { - ProgressMonitorDialog dialog= new ProgressMonitorDialog(getShell()); - try { - dialog.run(true, true, new IRunnableWithProgress() { - public void run(IProgressMonitor monitor) throws InvocationTargetException { - monitor.beginTask("", 1); //$NON-NLS-1$ - try { - if (fProject != null) { - monitor.setTaskName(NLS.bind(PreferencesMessages.OptionsConfigurationBlock_buildproject_taskname, fProject.getElementName())); - fProject.getProject().build(IncrementalProjectBuilder.FULL_BUILD, new SubProgressMonitor(monitor,1)); - } else { - monitor.setTaskName(PreferencesMessages.OptionsConfigurationBlock_buildall_taskname); - CUIPlugin.getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, new SubProgressMonitor(monitor,1)); - } - } catch (CoreException e) { - throw new InvocationTargetException(e); - } finally { - monitor.done(); - } - } - }); - } catch (InterruptedException e) { - // cancelled by user - } catch (InvocationTargetException e) { - String title= PreferencesMessages.OptionsConfigurationBlock_builderror_title; - String message= PreferencesMessages.OptionsConfigurationBlock_builderror_message; - ExceptionHandler.handle(e, getShell(), title, message); - } + private final String[] getFullBuildDialogStrings(boolean workspaceSettings) { + // rebuild (re-index) is not implemented + // no builds triggered by our settings + return null; } - - protected void doFullReParse() { - ProgressMonitorDialog dialog= new ProgressMonitorDialog(getShell()); - try { - dialog.run(true, true, new IRunnableWithProgress() { - public void run(IProgressMonitor monitor) throws InvocationTargetException { - monitor.beginTask("", 1); //$NON-NLS-1$ - - if (fProject != null) { - monitor.setTaskName(NLS.bind(PreferencesMessages.OptionsConfigurationBlock_parseproject_taskname, fProject.getElementName())); - reParseHierarchy(fProject.getResource(), monitor); - } else { - monitor.setTaskName(PreferencesMessages.OptionsConfigurationBlock_parseall_taskname); - reParseHierarchy(CUIPlugin.getWorkspace().getRoot(), monitor); - } - - monitor.done(); - } - }); - } catch (InterruptedException e) { - // cancelled by user - } catch (InvocationTargetException e) { - String title= PreferencesMessages.OptionsConfigurationBlock_parseerror_title; - String message= PreferencesMessages.OptionsConfigurationBlock_parseerror_message; - ExceptionHandler.handle(e, getShell(), title, message); - } - } - - protected void reParseHierarchy(IResource root, final IProgressMonitor monitor) { - try { - root.accept( - new IResourceProxyVisitor() { - public boolean visit(IResourceProxy proxy) { - switch(proxy.getType()) { - case IResource.FILE : - IFile file = (IFile)proxy.requestResource(); - - if (CoreModel.isTranslationUnit(file)) { - CoreModel cModel = CoreModel.getDefault(); - ITranslationUnit translationUnit = (ITranslationUnit)cModel.create(file); - - try { - IEditorInput input = EditorUtility.getEditorInput(file); - if (input != null) { - ITranslationUnit workingCopy = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(input); - if (workingCopy != null) { - // We have a copy in an editor - use it - translationUnit = workingCopy; - } - } - - translationUnit.makeConsistent(monitor, true /*forced*/); - } catch (CModelException e) { - } - } - - return false; // Do not look into file's structure - } - - return true; - } - } - , IResource.NONE); - } catch (CoreException e) { - } - } - + public void performDefaults() { - fWorkingValues= getDefaultOptions(); + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + String defValue= curr.getStoredValue(fLookupOrder, true, fManager); + setValue(curr, defValue); + } + + settingsUpdated(); updateControls(); - validateSettings(null, null); + validateSettings(null, null, null); + } + + /** + * @since 3.1 + */ + public void performRevert() { + for (int i= 0; i < fAllKeys.length; i++) { + Key curr= fAllKeys[i]; + String origValue= curr.getStoredValue(fLookupOrder, false, null); + setValue(curr, origValue); + } + + settingsUpdated(); + updateControls(); + validateSettings(null, null, null); + } + + public void dispose() { } protected void updateControls() { // update the UI for (int i= fCheckBoxes.size() - 1; i >= 0; i--) { - Button curr= (Button) fCheckBoxes.get(i); - ControlData data= (ControlData) curr.getData(); - - String currValue= (String) fWorkingValues.get(data.getKey()); - curr.setSelection(data.getSelection(currValue) == 0); + updateCheckBox((Button) fCheckBoxes.get(i)); } for (int i= fComboBoxes.size() - 1; i >= 0; i--) { - Combo curr= (Combo) fComboBoxes.get(i); - ControlData data= (ControlData) curr.getData(); - - String currValue= (String) fWorkingValues.get(data.getKey()); - curr.select(data.getSelection(currValue)); + updateCombo((Combo) fComboBoxes.get(i)); } for (int i= fTextBoxes.size() - 1; i >= 0; i--) { - Text curr= (Text) fTextBoxes.get(i); - String key= (String) curr.getData(); - - String currValue= (String) fWorkingValues.get(key); + updateText((Text) fTextBoxes.get(i)); + } + } + + protected void updateCombo(Combo curr) { + ControlData data= (ControlData) curr.getData(); + + String currValue= getValue(data.getKey()); + curr.select(data.getSelection(currValue)); + } + + protected void updateCheckBox(Button curr) { + ControlData data= (ControlData) curr.getData(); + + String currValue= getValue(data.getKey()); + curr.setSelection(data.getSelection(currValue) == 0); + } + + protected void updateText(Text curr) { + Key key= (Key) curr.getData(); + + String currValue= getValue(key); + if (currValue != null) { curr.setText(currValue); } } + protected Button getCheckBox(Key key) { + for (int i= fCheckBoxes.size() - 1; i >= 0; i--) { + Button curr= (Button) fCheckBoxes.get(i); + ControlData data= (ControlData) curr.getData(); + if (key.equals(data.getKey())) { + return curr; + } + } + return null; + } + protected Combo getComboBox(Key key) { + for (int i= fComboBoxes.size() - 1; i >= 0; i--) { + Combo curr= (Combo) fComboBoxes.get(i); + ControlData data= (ControlData) curr.getData(); + if (key.equals(data.getKey())) { + return curr; + } + } + return null; + } + + protected Text getTextControl(Key key) { + for (int i= fTextBoxes.size() - 1; i >= 0; i--) { + Text curr= (Text) fTextBoxes.get(i); + ControlData data= (ControlData) curr.getData(); + if (key.equals(data.getKey())) { + return curr; + } + } + return null; + } + + protected Control findControl(Key key) { + Combo comboBox= getComboBox(key); + if (comboBox != null) { + return comboBox; + } + Button checkBox= getCheckBox(key); + if (checkBox != null) { + return checkBox; + } + Text text= getTextControl(key); + if (text != null) { + return text; + } + return null; + } + + protected void setComboEnabled(Key key, boolean enabled) { + Combo combo= getComboBox(key); + Label label= (Label) fLabels.get(combo); + combo.setEnabled(enabled); + label.setEnabled(enabled); + } } 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 8d8b89cfb4b..217f174e122 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 @@ -21,44 +21,16 @@ public final class PreferencesMessages extends NLS { // Do not instantiate } - public static String OptionsConfigurationBlock_builderror_title; - public static String OptionsConfigurationBlock_builderror_message; - public static String OptionsConfigurationBlock_buildall_taskname; - public static String OptionsConfigurationBlock_buildproject_taskname; - public static String OptionsConfigurationBlock_parseerror_title; - public static String OptionsConfigurationBlock_parseerror_message; - public static String OptionsConfigurationBlock_parseall_taskname; - public static String OptionsConfigurationBlock_parseproject_taskname; - public static String TodoTaskPreferencePage_title; - public static String TodoTaskPropertyPage_useworkspacesettings_label; - public static String TodoTaskPropertyPage_useworkspacesettings_change; - public static String TodoTaskPropertyPage_useprojectsettings_label; - public static String TodoTaskConfigurationBlock_markers_tasks_high_priority; - public static String TodoTaskConfigurationBlock_markers_tasks_normal_priority; - public static String TodoTaskConfigurationBlock_markers_tasks_low_priority; - public static String TodoTaskConfigurationBlock_markers_tasks_label; - public static String TodoTaskConfigurationBlock_markers_tasks_add_button; - public static String TodoTaskConfigurationBlock_markers_tasks_remove_button; - public static String TodoTaskConfigurationBlock_markers_tasks_edit_button; - public static String TodoTaskConfigurationBlock_markers_tasks_name_column; - public static String TodoTaskConfigurationBlock_markers_tasks_priority_column; - public static String TodoTaskConfigurationBlock_needsbuild_title; - public static String TodoTaskConfigurationBlock_needsfullbuild_message; - public static String TodoTaskConfigurationBlock_needsprojectbuild_message; - public static String TodoTaskConfigurationBlock_needsparse_title; - public static String TodoTaskConfigurationBlock_needsfullparse_message; - public static String TodoTaskConfigurationBlock_needsprojectparse_message; - public static String TodoTaskInputDialog_new_title; - public static String TodoTaskInputDialog_edit_title; - public static String TodoTaskInputDialog_name_label; - public static String TodoTaskInputDialog_priority_label; - public static String TodoTaskInputDialog_priority_high; - public static String TodoTaskInputDialog_priority_normal; - public static String TodoTaskInputDialog_priority_low; - public static String TodoTaskInputDialog_error_enterName; - public static String TodoTaskInputDialog_error_comma; - public static String TodoTaskInputDialog_error_entryExists; - public static String TodoTaskInputDialog_error_noSpace; + public static String CodeAssistAdvancedConfigurationBlock_default_table_category_column_title; + public static String CodeAssistAdvancedConfigurationBlock_default_table_description; + public static String CodeAssistAdvancedConfigurationBlock_default_table_keybinding_column_title; + public static String CodeAssistAdvancedConfigurationBlock_key_binding_hint; + public static String CodeAssistAdvancedConfigurationBlock_page_description; + public static String CodeAssistAdvancedConfigurationBlock_separate_table_category_column_title; + public static String CodeAssistAdvancedConfigurationBlock_separate_table_description; + public static String CodeAssistAdvancedConfigurationBlock_no_shortcut; + public static String CodeAssistAdvancedConfigurationBlock_Up; + public static String CodeAssistAdvancedConfigurationBlock_Down; public static String CEditorPreferencePage_link; public static String CEditorPreferencePage_link_tooltip; public static String CEditorPreferencePage_colors; 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 c0b720ad808..c6a45af57be 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 @@ -10,56 +10,6 @@ # Anton Leherbauer (Wind River Systems) ############################################################################### -OptionsConfigurationBlock_builderror_title=Preference Changes -OptionsConfigurationBlock_builderror_message=Problem while building. Check log for details. - -OptionsConfigurationBlock_buildall_taskname=Build all... -OptionsConfigurationBlock_buildproject_taskname=Build project ''{0}''... - - -OptionsConfigurationBlock_parseerror_title=Preference Changes -OptionsConfigurationBlock_parseerror_message=Problem while parsing. Check log for details. - -OptionsConfigurationBlock_parseall_taskname=Parse all... -OptionsConfigurationBlock_parseproject_taskname=Parse project ''{0}''... - - -TodoTaskPreferencePage_title=Task Tags - -TodoTaskPropertyPage_useworkspacesettings_label=Use &workspace settings -TodoTaskPropertyPage_useworkspacesettings_change=&Configure Workspace Settings... -TodoTaskPropertyPage_useprojectsettings_label=Use pr&oject settings - -TodoTaskConfigurationBlock_markers_tasks_high_priority=High -TodoTaskConfigurationBlock_markers_tasks_normal_priority=Normal -TodoTaskConfigurationBlock_markers_tasks_low_priority=Low -TodoTaskConfigurationBlock_markers_tasks_label=&Strings indicating tasks in Java comments: -TodoTaskConfigurationBlock_markers_tasks_add_button=Ne&w... -TodoTaskConfigurationBlock_markers_tasks_remove_button=Remo&ve -TodoTaskConfigurationBlock_markers_tasks_edit_button=Edi&t... -TodoTaskConfigurationBlock_markers_tasks_name_column=Tag -TodoTaskConfigurationBlock_markers_tasks_priority_column=Priority - -TodoTaskConfigurationBlock_needsbuild_title=Task Tags Settings Changed -TodoTaskConfigurationBlock_needsfullbuild_message=The task tags settings have changed. A full rebuild is required to make changes effective. Do the full build now? -TodoTaskConfigurationBlock_needsprojectbuild_message=The task tags settings have changed. A rebuild of the project is required to make changes effective. Do the project build now? - -TodoTaskConfigurationBlock_needsparse_title=Task Tags Settings Changed -TodoTaskConfigurationBlock_needsfullparse_message=The task tags settings have changed. A full re-parse is required to make changes effective. Do the full re-parse now? -TodoTaskConfigurationBlock_needsprojectparse_message=The task tags settings have changed. Re-parsing of the project is required to make changes effective. Do the project re-parse now? - -TodoTaskInputDialog_new_title=New Task Tag -TodoTaskInputDialog_edit_title=Edit Task Tag -TodoTaskInputDialog_name_label=T&ag: -TodoTaskInputDialog_priority_label=&Priority: -TodoTaskInputDialog_priority_high=High -TodoTaskInputDialog_priority_normal=Normal -TodoTaskInputDialog_priority_low=Low -TodoTaskInputDialog_error_enterName=Enter task tag name. -TodoTaskInputDialog_error_comma=Name cannot contain a comma. -TodoTaskInputDialog_error_entryExists=Entry with the same name already exists. -TodoTaskInputDialog_error_noSpace=Name can not start or end with a whitespace. - CEditorPreferencePage_link=C/C++ Editor Preferences. Note that some preferences may be set on the Text Editors preference page. CEditorPreferencePage_link_tooltip=Show the shared text editor preferences @@ -87,6 +37,21 @@ CEditorPreferencePage_ContentAssistPage_parameterBackgroundColor=Parameter hint CEditorPreferencePage_ContentAssistPage_parameterForegroundColor=Parameter hint foreground CEditorPreferencePage_ContentAssistPage_autoActivationEnableDoubleColon=Enable "::" as trigger CEditorPreferencePage_ContentAssistPage_sortingSection_title=Sorting and Filtering + +# {0} will be replaced by the keyboard shortcut for the command (e.g. "Ctrl+Space", or "no shortcut") +CodeAssistAdvancedConfigurationBlock_page_description=Configure the behavior of the content assist ({0}) command. +# stand-in text if there is no keyboard shortcut in above statement +CodeAssistAdvancedConfigurationBlock_no_shortcut=no shortcut +CodeAssistAdvancedConfigurationBlock_default_table_description=Select the proposal kinds contained in the 'default' content assist list: +CodeAssistAdvancedConfigurationBlock_default_table_category_column_title=Default Proposal Kinds +CodeAssistAdvancedConfigurationBlock_default_table_keybinding_column_title=Key Binding +# do not translate the href argument (org.eclipse.ui.preferencePages.Keys) +CodeAssistAdvancedConfigurationBlock_key_binding_hint=Individual key bindings can be assigned to each proposal kind on the Keys preference page. +CodeAssistAdvancedConfigurationBlock_separate_table_description=Content assist cycling: Select the proposal kinds that are cycled through when repeatedly invoking content assist: +CodeAssistAdvancedConfigurationBlock_separate_table_category_column_title=Separate Proposal Kinds +CodeAssistAdvancedConfigurationBlock_Up=&Up +CodeAssistAdvancedConfigurationBlock_Down=D&own + CEditorColoringConfigurationBlock_MultiLine=Multi-line comment CEditorColoringConfigurationBlock_singleLine=Single-line comment CEditorColoringConfigurationBlock_keywords=Keywords diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskConfigurationBlock.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskConfigurationBlock.java deleted file mode 100644 index 731f781e985..00000000000 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskConfigurationBlock.java +++ /dev/null @@ -1,300 +0,0 @@ -/******************************************************************************* - * 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.preferences; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.core.runtime.IStatus; -import org.eclipse.jface.viewers.ITableLabelProvider; -import org.eclipse.jface.viewers.LabelProvider; -import org.eclipse.jface.window.Window; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Table; - -import org.eclipse.cdt.core.CCorePlugin; -import org.eclipse.cdt.core.CCorePreferenceConstants; -import org.eclipse.cdt.core.model.ICProject; - -import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; -import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; -import org.eclipse.cdt.internal.ui.util.SWTUtil; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.IListAdapter; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.ListDialogField; - -/** - */ -public class TodoTaskConfigurationBlock extends OptionsConfigurationBlock { - - private static final String PREF_TRANSLATION_TASK_TAGS= CCorePreferenceConstants.TRANSLATION_TASK_TAGS; - private static final String PREF_TRANSLATION_TASK_PRIORITIES= CCorePreferenceConstants.TRANSLATION_TASK_PRIORITIES; - - private static final String PRIORITY_HIGH= CCorePlugin.TRANSLATION_TASK_PRIORITY_HIGH; - private static final String PRIORITY_NORMAL= CCorePlugin.TRANSLATION_TASK_PRIORITY_NORMAL; - private static final String PRIORITY_LOW= CCorePlugin.TRANSLATION_TASK_PRIORITY_LOW; - - public static class TodoTask { - public String name; - public String priority; - } - - private static class TodoTaskLabelProvider extends LabelProvider implements ITableLabelProvider { - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.ILabelProvider#getImage(java.lang.Object) - */ - public Image getImage(Object element) { - return null; - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) - */ - public String getText(Object element) { - return getColumnText(element, 0); - } - - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int) - */ - public Image getColumnImage(Object element, int columnIndex) { - return null; - } - /* (non-Javadoc) - * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int) - */ - public String getColumnText(Object element, int columnIndex) { - TodoTask task= (TodoTask) element; - if (columnIndex == 0) { - return task.name; - } - if (PRIORITY_HIGH.equals(task.priority)) { - return PreferencesMessages.TodoTaskConfigurationBlock_markers_tasks_high_priority; - } else if (PRIORITY_NORMAL.equals(task.priority)) { - return PreferencesMessages.TodoTaskConfigurationBlock_markers_tasks_normal_priority; - } else if (PRIORITY_LOW.equals(task.priority)) { - return PreferencesMessages.TodoTaskConfigurationBlock_markers_tasks_low_priority; - } - return ""; //$NON-NLS-1$ - } - - } - - private IStatus fTaskTagsStatus; - private ListDialogField fTodoTasksList; - - public TodoTaskConfigurationBlock(IStatusChangeListener context, ICProject project) { - super(context, project); - - TaskTagAdapter adapter= new TaskTagAdapter(); - String[] buttons= new String[] { - PreferencesMessages.TodoTaskConfigurationBlock_markers_tasks_add_button, - PreferencesMessages.TodoTaskConfigurationBlock_markers_tasks_remove_button, - null, - PreferencesMessages.TodoTaskConfigurationBlock_markers_tasks_edit_button, - }; - fTodoTasksList= new ListDialogField(adapter, buttons, new TodoTaskLabelProvider()); - fTodoTasksList.setDialogFieldListener(adapter); - fTodoTasksList.setLabelText(PreferencesMessages.TodoTaskConfigurationBlock_markers_tasks_label); - fTodoTasksList.setRemoveButtonIndex(1); - - String[] columnsHeaders= new String[] { - PreferencesMessages.TodoTaskConfigurationBlock_markers_tasks_name_column, - PreferencesMessages.TodoTaskConfigurationBlock_markers_tasks_priority_column, - }; - - fTodoTasksList.setTableColumns(new ListDialogField.ColumnsDescription(columnsHeaders, true)); - unpackTodoTasks(); - if (fTodoTasksList.getSize() > 0) { - fTodoTasksList.selectFirstElement(); - } else { - fTodoTasksList.enableButton(3, false); - } - - fTaskTagsStatus= new StatusInfo(); - } - - protected final String[] getAllKeys() { - return new String[] { - PREF_TRANSLATION_TASK_TAGS, PREF_TRANSLATION_TASK_PRIORITIES - }; - } - - public class TaskTagAdapter implements IListAdapter, IDialogFieldListener { - - private boolean canEdit(ListDialogField field) { - return field.getSelectedElements().size() == 1; - } - - public void customButtonPressed(ListDialogField field, int index) { - doTodoButtonPressed(index); - } - - public void selectionChanged(ListDialogField field) { - field.enableButton(3, canEdit(field)); - } - - public void doubleClicked(ListDialogField field) { - if (canEdit(field)) { - doTodoButtonPressed(3); - } - } - - public void dialogFieldChanged(DialogField field) { - validateSettings(PREF_TRANSLATION_TASK_TAGS, null); - } - - } - - protected Control createContents(Composite parent) { - setShell(parent.getShell()); - - Composite markersComposite= createMarkersTabContent(parent); - - validateSettings(null, null); - - return markersComposite; - } - - private Composite createMarkersTabContent(Composite folder) { - - GridLayout layout= new GridLayout(); - layout.marginHeight= 0; - layout.marginWidth= 0; - layout.numColumns= 2; - - Composite markersComposite= new Composite(folder, SWT.NULL); - markersComposite.setLayout(layout); - - fTodoTasksList.doFillIntoGrid(markersComposite, 3); - LayoutUtil.setHorizontalSpan(fTodoTasksList.getLabelControl(null), 2); - - Table table= fTodoTasksList.getTableViewer().getTable(); - GridData data= (GridData)fTodoTasksList.getListControl(null).getLayoutData(); - data.grabExcessHorizontalSpace= true; - data.verticalAlignment= 0; - data.heightHint= SWTUtil.getTableHeightHint(table, 6); - - return markersComposite; - } - - protected void validateSettings(String changedKey, String newValue) { - if (changedKey != null) { - if (PREF_TRANSLATION_TASK_TAGS.equals(changedKey)) { - fTaskTagsStatus= validateTaskTags(); - } else { - return; - } - } else { - fTaskTagsStatus= validateTaskTags(); - } - IStatus status= fTaskTagsStatus; //StatusUtil.getMostSevere(new IStatus[] { fTaskTagsStatus }); - fContext.statusChanged(status); - } - - private IStatus validateTaskTags() { - return new StatusInfo(); - } - - /* (non-Javadoc) - * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#performOk(boolean) - */ - public boolean performOk(boolean enabled) { - packTodoTasks(); - return super.performOk(enabled); - } - - - protected String[] getFullBuildDialogStrings(boolean workspaceSettings) { - String title= PreferencesMessages.TodoTaskConfigurationBlock_needsbuild_title; - String message; - if (fProject == null) { - message= PreferencesMessages.TodoTaskConfigurationBlock_needsfullbuild_message; - } else { - message= PreferencesMessages.TodoTaskConfigurationBlock_needsprojectbuild_message; - } - return new String[] { title, message }; - } - - protected String[] getFullReParseDialogStrings(boolean workspaceSettings) { - String title= PreferencesMessages.TodoTaskConfigurationBlock_needsparse_title; - String message; - if (fProject == null) { - message= PreferencesMessages.TodoTaskConfigurationBlock_needsfullparse_message; - } else { - message= PreferencesMessages.TodoTaskConfigurationBlock_needsprojectparse_message; - } - return new String[] { title, message }; - } - - /* (non-Javadoc) - * @see org.eclipse.cdt.internal.ui.preferences.OptionsConfigurationBlock#updateControls() - */ - protected void updateControls() { - unpackTodoTasks(); - } - - private void unpackTodoTasks() { - String currTags= (String) fWorkingValues.get(PREF_TRANSLATION_TASK_TAGS); - String currPrios= (String) fWorkingValues.get(PREF_TRANSLATION_TASK_PRIORITIES); - String[] tags= getTokens(currTags, ","); //$NON-NLS-1$ - String[] prios= getTokens(currPrios, ","); //$NON-NLS-1$ - ArrayList elements= new ArrayList(tags.length); - for (int i= 0; i < tags.length; i++) { - TodoTask task= new TodoTask(); - task.name= tags[i].trim(); - task.priority= (i < prios.length) ? prios[i] : PRIORITY_NORMAL; - elements.add(task); - } - fTodoTasksList.setElements(elements); - } - - private void packTodoTasks() { - StringBuffer tags= new StringBuffer(); - StringBuffer prios= new StringBuffer(); - List list= fTodoTasksList.getElements(); - for (int i= 0; i < list.size(); i++) { - if (i > 0) { - tags.append(','); - prios.append(','); - } - TodoTask elem= (TodoTask) list.get(i); - tags.append(elem.name); - prios.append(elem.priority); - } - fWorkingValues.put(PREF_TRANSLATION_TASK_TAGS, tags.toString()); - fWorkingValues.put(PREF_TRANSLATION_TASK_PRIORITIES, prios.toString()); - } - - void doTodoButtonPressed(int index) { - TodoTask edited= null; - if (index != 0) { - edited= (TodoTask) fTodoTasksList.getSelectedElements().get(0); - } - - TodoTaskInputDialog dialog= new TodoTaskInputDialog(getShell(), edited, fTodoTasksList.getElements()); - if (dialog.open() == Window.OK) { - if (edited != null) { - fTodoTasksList.replaceElement(edited, dialog.getResult()); - } else { - fTodoTasksList.addElement(dialog.getResult()); - } - } - } -} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskInputDialog.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskInputDialog.java deleted file mode 100644 index 04cd5771e31..00000000000 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskInputDialog.java +++ /dev/null @@ -1,162 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.cdt.internal.ui.preferences; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Shell; - -import org.eclipse.ui.PlatformUI; - -import org.eclipse.cdt.core.CCorePlugin; - -import org.eclipse.cdt.internal.ui.ICHelpContextIds; -import org.eclipse.cdt.internal.ui.dialogs.StatusDialog; -import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; -import org.eclipse.cdt.internal.ui.preferences.TodoTaskConfigurationBlock.TodoTask; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.ComboDialogField; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.StringDialogField; - -/** - * Dialog to enter a na new task tag - */ -public class TodoTaskInputDialog extends StatusDialog { - - private class CompilerTodoTaskInputAdapter implements IDialogFieldListener { - public void dialogFieldChanged(DialogField field) { - doValidation(); - } - } - - private StringDialogField fNameDialogField; - private ComboDialogField fPriorityDialogField; - - private List fExistingNames; - - public TodoTaskInputDialog(Shell parent, TodoTask task, List existingEntries) { - super(parent); - - fExistingNames= new ArrayList(existingEntries.size()); - for (int i= 0; i < existingEntries.size(); i++) { - TodoTask curr= (TodoTask) existingEntries.get(i); - if (!curr.equals(task)) { - fExistingNames.add(curr.name); - } - } - - if (task == null) { - setTitle(PreferencesMessages.TodoTaskInputDialog_new_title); - } else { - setTitle(PreferencesMessages.TodoTaskInputDialog_edit_title); - } - - CompilerTodoTaskInputAdapter adapter= new CompilerTodoTaskInputAdapter(); - - fNameDialogField= new StringDialogField(); - fNameDialogField.setLabelText(PreferencesMessages.TodoTaskInputDialog_name_label); - fNameDialogField.setDialogFieldListener(adapter); - - fNameDialogField.setText((task != null) ? task.name : ""); //$NON-NLS-1$ - - String[] items= new String[] { - PreferencesMessages.TodoTaskInputDialog_priority_high, - PreferencesMessages.TodoTaskInputDialog_priority_normal, - PreferencesMessages.TodoTaskInputDialog_priority_low - }; - - fPriorityDialogField= new ComboDialogField(SWT.READ_ONLY); - fPriorityDialogField.setLabelText(PreferencesMessages.TodoTaskInputDialog_priority_label); - fPriorityDialogField.setItems(items); - if (task != null) { - if (CCorePlugin.TRANSLATION_TASK_PRIORITY_HIGH.equals(task.priority)) { - fPriorityDialogField.selectItem(0); - } else if (CCorePlugin.TRANSLATION_TASK_PRIORITY_NORMAL.equals(task.priority)) { - fPriorityDialogField.selectItem(1); - } else { - fPriorityDialogField.selectItem(2); - } - } else { - fPriorityDialogField.selectItem(1); - } - } - - public TodoTask getResult() { - TodoTask task= new TodoTask(); - task.name= fNameDialogField.getText().trim(); - switch (fPriorityDialogField.getSelectionIndex()) { - case 0 : - task.priority= CCorePlugin.TRANSLATION_TASK_PRIORITY_HIGH; - break; - case 1 : - task.priority= CCorePlugin.TRANSLATION_TASK_PRIORITY_NORMAL; - break; - default : - task.priority= CCorePlugin.TRANSLATION_TASK_PRIORITY_LOW; - break; - } - return task; - } - - protected Control createDialogArea(Composite parent) { - Composite composite= (Composite) super.createDialogArea(parent); - - Composite inner= new Composite(composite, SWT.NONE); - GridLayout layout= new GridLayout(); - layout.marginHeight= 0; - layout.marginWidth= 0; - layout.numColumns= 2; - inner.setLayout(layout); - - fNameDialogField.doFillIntoGrid(inner, 2); - fPriorityDialogField.doFillIntoGrid(inner, 2); - - LayoutUtil.setHorizontalGrabbing(fNameDialogField.getTextControl(null)); - LayoutUtil.setWidthHint(fNameDialogField.getTextControl(null), convertWidthInCharsToPixels(45)); - - fNameDialogField.postSetFocusOnDialogField(parent.getDisplay()); - - applyDialogFont(composite); - return composite; - } - - void doValidation() { - StatusInfo status= new StatusInfo(); - String newText= fNameDialogField.getText(); - if (newText.length() == 0) { - status.setError(PreferencesMessages.TodoTaskInputDialog_error_enterName); - } else { - if (newText.indexOf(',') != -1) { - status.setError(PreferencesMessages.TodoTaskInputDialog_error_comma); - } else if (fExistingNames.contains(newText)) { - status.setError(PreferencesMessages.TodoTaskInputDialog_error_entryExists); - } else if (Character.isWhitespace(newText.charAt(0)) || Character.isWhitespace(newText.charAt(newText.length() - 1))) { - status.setError(PreferencesMessages.TodoTaskInputDialog_error_noSpace); - } - } - updateStatus(status); - } - - /* - * @see org.eclipse.jface.window.Window#configureShell(Shell) - */ - protected void configureShell(Shell newShell) { - super.configureShell(newShell); - PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell, ICHelpContextIds.TODO_TASK_INPUT_DIALOG); - } -} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskPreferencePage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskPreferencePage.java deleted file mode 100644 index 722dcc91eda..00000000000 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskPreferencePage.java +++ /dev/null @@ -1,99 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.cdt.internal.ui.preferences; - -import org.eclipse.core.runtime.IStatus; - -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.preference.PreferencePage; - -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.IWorkbenchPreferencePage; -import org.eclipse.ui.PlatformUI; - -import org.eclipse.cdt.internal.ui.ICHelpContextIds; -import org.eclipse.cdt.ui.CUIPlugin; -import org.eclipse.cdt.internal.ui.dialogs.StatusUtil; -import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; - -/* - * The page to configure the compiler options. - */ -public class TodoTaskPreferencePage extends PreferencePage implements IWorkbenchPreferencePage, IStatusChangeListener { - - public static final String ID= "org.eclipse.cdt.ui.propertyPages.TodoTaskPropertyPage"; //$NON-NLS-1$ - - private TodoTaskConfigurationBlock fConfigurationBlock; - - public TodoTaskPreferencePage() { - setPreferenceStore(CUIPlugin.getDefault().getPreferenceStore()); - //setDescription(PreferencesMessages.getString("TodoTaskPreferencePage.description")); //$NON-NLS-1$ - - // only used when page is shown programatically - setTitle(PreferencesMessages.TodoTaskPreferencePage_title); - - fConfigurationBlock= new TodoTaskConfigurationBlock(this, null); - } - - /* - * @see IWorkbenchPreferencePage#init(org.eclipse.ui.IWorkbench) - */ - public void init(IWorkbench workbench) { - } - - /* - * @see PreferencePage#createControl(Composite) - */ - public void createControl(Composite parent) { - // added for 1GEUGE6: ITPJUI:WIN2000 - Help is the same on all preference pages - super.createControl(parent); - PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), ICHelpContextIds.TODO_TASK_PREFERENCE_PAGE); - } - - /* - * @see PreferencePage#createContents(Composite) - */ - protected Control createContents(Composite parent) { - Control result= fConfigurationBlock.createContents(parent); - Dialog.applyDialogFont(result); - return result; - } - - /* - * @see IPreferencePage#performOk() - */ - public boolean performOk() { - if (!fConfigurationBlock.performOk(true)) { - return false; - } - return super.performOk(); - } - - /* - * @see PreferencePage#performDefaults() - */ - protected void performDefaults() { - fConfigurationBlock.performDefaults(); - super.performDefaults(); - } - - /* (non-Javadoc) - * @see org.eclipse.cdt.internal.ui.wizards.IStatusChangeListener#statusChanged(org.eclipse.core.runtime.IStatus) - */ - public void statusChanged(IStatus status) { - setValid(!status.matches(IStatus.ERROR)); - StatusUtil.applyToStatusLine(this, status); - } - -} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskPropertyPage.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskPropertyPage.java deleted file mode 100644 index 79f008fe8c9..00000000000 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/preferences/TodoTaskPropertyPage.java +++ /dev/null @@ -1,220 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2005 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.cdt.internal.ui.preferences; - -import org.eclipse.core.runtime.IStatus; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.custom.BusyIndicator; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; - -import org.eclipse.jface.dialogs.ControlEnableState; -import org.eclipse.jface.dialogs.Dialog; -import org.eclipse.jface.preference.IPreferenceNode; -import org.eclipse.jface.preference.IPreferencePage; -import org.eclipse.jface.preference.PreferenceDialog; -import org.eclipse.jface.preference.PreferenceManager; -import org.eclipse.jface.preference.PreferenceNode; -import org.eclipse.jface.window.Window; - -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.dialogs.PropertyPage; - -import org.eclipse.cdt.core.model.ICElement; -import org.eclipse.cdt.core.model.ICProject; - -import org.eclipse.cdt.internal.ui.ICHelpContextIds; -import org.eclipse.cdt.internal.ui.dialogs.StatusInfo; -import org.eclipse.cdt.internal.ui.dialogs.StatusUtil; -import org.eclipse.cdt.internal.ui.dialogs.IStatusChangeListener; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.DialogField; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.IDialogFieldListener; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.LayoutUtil; -import org.eclipse.cdt.internal.ui.wizards.dialogfields.SelectionButtonDialogField; - -/** - * Property page used to configure project specific task tags settings - */ -public class TodoTaskPropertyPage extends PropertyPage { - - private TodoTaskConfigurationBlock fConfigurationBlock; - private Control fConfigurationBlockControl; - private ControlEnableState fBlockEnableState; - private SelectionButtonDialogField fUseWorkspaceSettings; - private SelectionButtonDialogField fChangeWorkspaceSettings; - private SelectionButtonDialogField fUseProjectSettings; - IStatus fBlockStatus; - - - public TodoTaskPropertyPage() { - fBlockStatus= new StatusInfo(); - fBlockEnableState= null; - - IDialogFieldListener listener= new IDialogFieldListener() { - public void dialogFieldChanged(DialogField field) { - doDialogFieldChanged(field); - } - }; - - fUseWorkspaceSettings= new SelectionButtonDialogField(SWT.RADIO); - fUseWorkspaceSettings.setDialogFieldListener(listener); - fUseWorkspaceSettings.setLabelText(PreferencesMessages.TodoTaskPropertyPage_useworkspacesettings_label); - - fChangeWorkspaceSettings= new SelectionButtonDialogField(SWT.PUSH); - fChangeWorkspaceSettings.setLabelText(PreferencesMessages.TodoTaskPropertyPage_useworkspacesettings_change); - fChangeWorkspaceSettings.setDialogFieldListener(listener); - - fUseWorkspaceSettings.attachDialogField(fChangeWorkspaceSettings); - - fUseProjectSettings= new SelectionButtonDialogField(SWT.RADIO); - fUseProjectSettings.setDialogFieldListener(listener); - fUseProjectSettings.setLabelText(PreferencesMessages.TodoTaskPropertyPage_useprojectsettings_label); - } - - /* - * @see org.eclipse.jface.dialogs.IDialogPage#createControl(org.eclipse.swt.widgets.Composite) - */ - public void createControl(Composite parent) { - super.createControl(parent); - PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), ICHelpContextIds.TODO_TASK_PROPERTY_PAGE); - } - - /* - * @see org.eclipse.jface.preference.IPreferencePage#createContents(Composite) - */ - protected Control createContents(Composite parent) { - IStatusChangeListener listener= new IStatusChangeListener() { - public void statusChanged(IStatus status) { - fBlockStatus= status; - doStatusChanged(); - } - }; - fConfigurationBlock= new TodoTaskConfigurationBlock(listener, getProject()); - - Composite composite= new Composite(parent, SWT.NONE); - GridLayout layout= new GridLayout(); - layout.marginHeight= 0; - layout.marginWidth= 0; - layout.numColumns= 1; - composite.setLayout(layout); - - fUseWorkspaceSettings.doFillIntoGrid(composite, 1); - LayoutUtil.setHorizontalGrabbing(fUseWorkspaceSettings.getSelectionButton(null)); - - fChangeWorkspaceSettings.doFillIntoGrid(composite, 1); - GridData data= (GridData) fChangeWorkspaceSettings.getSelectionButton(null).getLayoutData(); - data.horizontalIndent= convertWidthInCharsToPixels(3); - data.horizontalAlignment= GridData.BEGINNING; - - fUseProjectSettings.doFillIntoGrid(composite, 1); - - data= new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL ); - data.horizontalSpan= 1; - data.horizontalIndent= convertWidthInCharsToPixels(2); - - fConfigurationBlockControl= fConfigurationBlock.createContents(composite); - fConfigurationBlockControl.setLayoutData(data); - - boolean useProjectSettings= fConfigurationBlock.hasProjectSpecificOptions(); - - fUseProjectSettings.setSelection(useProjectSettings); - fUseWorkspaceSettings.setSelection(!useProjectSettings); - - updateEnableState(); - Dialog.applyDialogFont(composite); - return composite; - } - - private boolean useProjectSettings() { - return fUseProjectSettings.isSelected(); - } - - void doDialogFieldChanged(DialogField field) { - if (field == fChangeWorkspaceSettings) { - TodoTaskPreferencePage page= new TodoTaskPreferencePage(); - showPreferencePage(TodoTaskPreferencePage.ID, page); - } else { - updateEnableState(); - doStatusChanged(); - } - } - /** - * Method statusChanged. - */ - void doStatusChanged() { - updateStatus(useProjectSettings() ? fBlockStatus : new StatusInfo()); - } - - /** - * Method getProject. - */ - private ICProject getProject() { - return (ICProject) getElement().getAdapter(ICElement.class); - } - - private void updateEnableState() { - if (useProjectSettings()) { - if (fBlockEnableState != null) { - fBlockEnableState.restore(); - fBlockEnableState= null; - } - } else { - if (fBlockEnableState == null) { - fBlockEnableState= ControlEnableState.disable(fConfigurationBlockControl); - } - } - } - - /* - * @see org.eclipse.jface.preference.IPreferencePage#performDefaults() - */ - protected void performDefaults() { - if (useProjectSettings()) { - fUseProjectSettings.setSelection(false); - fUseWorkspaceSettings.setSelection(true); - fConfigurationBlock.performDefaults(); - } - super.performDefaults(); - } - - /* - * @see org.eclipse.jface.preference.IPreferencePage#performOk() - */ - public boolean performOk() { - return fConfigurationBlock.performOk(useProjectSettings()); - } - - private void updateStatus(IStatus status) { - setValid(!status.matches(IStatus.ERROR)); - StatusUtil.applyToStatusLine(this, status); - } - - private boolean showPreferencePage(String id, IPreferencePage page) { - final IPreferenceNode targetNode = new PreferenceNode(id, page); - - PreferenceManager manager = new PreferenceManager(); - manager.addToRoot(targetNode); - final PreferenceDialog dialog = new PreferenceDialog(getShell(), manager); - final boolean [] result = new boolean[] { false }; - BusyIndicator.showWhile(getShell().getDisplay(), new Runnable() { - public void run() { - dialog.create(); - dialog.setMessage(targetNode.getLabelText()); - result[0]= (dialog.open() == Window.OK); - } - }); - return result[0]; - } - -} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java index 4f57e1ab5da..a32a00188d7 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/CHeuristicScanner.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2006 IBM Corporation and others. + * 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 @@ -58,6 +58,8 @@ public final class CHeuristicScanner implements Symbols { private static final char EQUAL= '='; private static final char LANGLE= '<'; private static final char RANGLE= '>'; + private static final char DOT= '.'; + private static final char MINUS= '-'; /** * Specifies the stop condition, upon which the scanXXX methods will decide whether @@ -401,6 +403,10 @@ public final class CHeuristicScanner implements Symbols { return TokenLESSTHAN; case RANGLE: return TokenGREATERTHAN; + case DOT: + return TokenDOT; + case MINUS: + return TokenMINUS; } // else @@ -551,13 +557,31 @@ public final class CHeuristicScanner implements Symbols { * @return the matching peer character position, or NOT_FOUND */ public int findOpeningPeer(int start, char openingPeer, char closingPeer) { + return findOpeningPeer(start, CHeuristicScanner.UNBOUND, openingPeer, closingPeer); + } + + /** + * Returns the position of the opening peer character (backward search). Any scopes introduced by closing peers + * are skipped. All peers accounted for must reside in the default partition. + * + *

Note that start must not point to the closing peer, but to the first + * character being searched.

+ * + * @param start the start position + * @param bound the bound + * @param openingPeer the opening peer character (e.g. '{') + * @param closingPeer the closing peer character (e.g. '}') + * @return the matching peer character position, or NOT_FOUND + */ + public int findOpeningPeer(int start, int bound, char openingPeer, char closingPeer) { Assert.isLegal(start < fDocument.getLength()); try { + final CharacterMatch match= new CharacterMatch(new char[] {openingPeer, closingPeer}); int depth= 1; start += 1; while (true) { - start= scanBackward(start - 1, UNBOUND, new CharacterMatch(new char[] {openingPeer, closingPeer})); + start= scanBackward(start - 1, bound, match); if (start == NOT_FOUND) return NOT_FOUND; @@ -587,7 +611,7 @@ public final class CHeuristicScanner implements Symbols { if (offset < 1 || offset >= fDocument.getLength()) return null; - int begin= findOpeningPeer(offset - 1, LBRACE, RBRACE); + int begin= findOpeningPeer(offset - 1, CHeuristicScanner.UNBOUND, LBRACE, RBRACE); int end= findClosingPeer(offset, LBRACE, RBRACE); if (begin == NOT_FOUND || end == NOT_FOUND) return null; @@ -821,7 +845,7 @@ public final class CHeuristicScanner implements Symbols { case TokenELSE: return true; case TokenRPAREN: - position= findOpeningPeer(fPos, LPAREN, RPAREN); + position= findOpeningPeer(fPos, CHeuristicScanner.UNBOUND, LPAREN, RPAREN); if (position > 0) { switch (previousToken(position - 1, bound)) { case TokenIF: @@ -880,4 +904,31 @@ public final class CHeuristicScanner implements Symbols { } return false; } + + /** + * Returns true if the document, when scanned backwards from start + * appears to contain a field reference, i.e. a (optional) name preceded by a . or -> + * The start must be before the operator. + * + * @param start the position after the field reference operator. + * @param bound the first position in fDocument to not consider any more, with + * bound < start, or UNBOUND + * @return true if the current position looks like a field reference + */ + public boolean looksLikeFieldReferenceBackward(int start, int bound) { + int token= previousToken(start - 1, bound); + if (token == Symbols.TokenIDENT) { // field name + token= previousToken(getPosition(), bound); + } + if (token == Symbols.TokenDOT) { + return true; + } + if (token == Symbols.TokenGREATERTHAN) { + token= previousToken(getPosition(), bound); + if (token == Symbols.TokenMINUS) { + return true; + } + } + return false; + } } 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 7f57b91b7d2..9017b18fb24 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 @@ -81,9 +81,8 @@ import org.eclipse.cdt.internal.ui.editor.CDocumentProvider; import org.eclipse.cdt.internal.ui.editor.CElementHyperlinkDetector; 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.CCompletionProcessor2; +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.template.CTemplateCompletionProcessor; import org.eclipse.cdt.internal.ui.util.ExternalEditorInput; @@ -346,10 +345,21 @@ public class CSourceViewerConfiguration extends TextSourceViewerConfiguration { assistant.setRestoreCompletionProposalSize(getSettings("completion_proposal_size")); //$NON-NLS-1$ - IContentAssistProcessor processor = new CCompletionProcessor2(getEditor()); + IContentAssistProcessor processor = new CContentAssistProcessor(getEditor(), assistant, IDocument.DEFAULT_CONTENT_TYPE); assistant.setContentAssistProcessor(processor, IDocument.DEFAULT_CONTENT_TYPE); - assistant.setContentAssistProcessor(new CTemplateCompletionProcessor(getEditor()), ICPartitions.C_MULTI_LINE_COMMENT); + processor = new CContentAssistProcessor(getEditor(), assistant, ICPartitions.C_MULTI_LINE_COMMENT); + assistant.setContentAssistProcessor(processor, ICPartitions.C_MULTI_LINE_COMMENT); + + processor = new CContentAssistProcessor(getEditor(), assistant, ICPartitions.C_SINGLE_LINE_COMMENT); + assistant.setContentAssistProcessor(processor, ICPartitions.C_SINGLE_LINE_COMMENT); + + processor = new CContentAssistProcessor(getEditor(), assistant, ICPartitions.C_STRING); + assistant.setContentAssistProcessor(processor, ICPartitions.C_STRING); + + processor = new CContentAssistProcessor(getEditor(), assistant, ICPartitions.C_PREPROCESSOR); + assistant.setContentAssistProcessor(processor, ICPartitions.C_PREPROCESSOR); + ContentAssistPreference.configure(assistant, fPreferenceStore); assistant.setProposalPopupOrientation(IContentAssistant.PROPOSAL_OVERLAY); diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/Symbols.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/Symbols.java index 1d0f899aee0..6dc62e2a1ea 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/Symbols.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/Symbols.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2006 IBM Corporation and others. + * 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Sergey Prigogin, Google + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.ui.text; @@ -30,6 +31,8 @@ public interface Symbols { int TokenEQUAL= 12; int TokenLESSTHAN= 13; int TokenGREATERTHAN= 14; + int TokenDOT= 15; + int TokenMINUS= 16; int TokenIF= 109; int TokenDO= 1010; int TokenFOR= 1011; diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CContentAssistInvocationContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CContentAssistInvocationContext.java new file mode 100644 index 00000000000..9f5de95c1d7 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CContentAssistInvocationContext.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.ui.IEditorPart; + +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext; + + +/** + * Describes the context of a content assist invocation in a C/C++ editor. + *

+ * Clients may use but not subclass this class. + *

+ * + * @since 4.0 + */ +public class CContentAssistInvocationContext extends ContentAssistInvocationContext { + + private final IEditorPart fEditor; + private ITranslationUnit fTU= null; + private boolean fTUComputed= false; + + /** + * Creates a new context. + * + * @param viewer the viewer used by the editor + * @param offset the invocation offset + * @param editor the editor that content assist is invoked in + */ + public CContentAssistInvocationContext(ITextViewer viewer, int offset, IEditorPart editor) { + super(viewer, offset); + Assert.isNotNull(editor); + fEditor= editor; + } + + /** + * Creates a new context. + * + * @param unit the translation unit in document + */ + public CContentAssistInvocationContext(ITranslationUnit unit) { + super(); + fTU= unit; + fTUComputed= true; + fEditor= null; + } + + /** + * Returns the translation unit that content assist is invoked in, null if there + * is none. + * + * @return the translation unit that content assist is invoked in, possibly null + */ + public ITranslationUnit getTranslationUnit() { + if (!fTUComputed) { + fTUComputed= true; + fTU= CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(fEditor.getEditorInput()); + } + return fTU; + } + + /** + * Returns the project of the translation unit that content assist is invoked in, + * null if none. + * + * @return the current C project, possibly null + */ + public ICProject getProject() { + ITranslationUnit unit= getTranslationUnit(); + return unit == null ? null : unit.getCProject(); + } + + /** + * Get the editor content assist is invoked in. + * + * @return the editor, may be null + */ + public IEditorPart getEditor() { + return fEditor; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CContentAssistProcessor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CContentAssistProcessor.java new file mode 100644 index 00000000000..ed32238f6d4 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CContentAssistProcessor.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.contentassist.ContentAssistant; +import org.eclipse.jface.text.contentassist.IContextInformationValidator; +import org.eclipse.ui.IEditorPart; + +import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext; + +import org.eclipse.cdt.internal.ui.text.CParameterListValidator; + +/** + * C/C++ content assist processor. + */ +public class CContentAssistProcessor extends ContentAssistProcessor { + + private IContextInformationValidator fValidator; + private final IEditorPart fEditor; + + public CContentAssistProcessor(IEditorPart editor, ContentAssistant assistant, String partition) { + super(assistant, partition); + fEditor= editor; + } + + /* + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator() + */ + public IContextInformationValidator getContextInformationValidator() { + if (fValidator == null) { + fValidator= new CParameterListValidator(); + } + return fValidator; + } + + /* + * @see org.eclipse.cdt.internal.ui.text.contentassist.ContentAssistProcessor#filterAndSort(java.util.List, org.eclipse.core.runtime.IProgressMonitor) + */ + protected List filterAndSortProposals(List proposals, IProgressMonitor monitor, ContentAssistInvocationContext context) { + return proposals; + } + + /* + * @see org.eclipse.cdt.internal.ui.text.contentassist.ContentAssistProcessor#createContext(org.eclipse.jface.text.ITextViewer, int) + */ + protected ContentAssistInvocationContext createContext(ITextViewer viewer, int offset) { + return new CContentAssistInvocationContext(viewer, offset, fEditor); + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CompletionProposalCategory.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CompletionProposalCategory.java new file mode 100644 index 00000000000..fdf661e2389 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CompletionProposalCategory.java @@ -0,0 +1,331 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.FileLocator; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.InvalidRegistryObjectException; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.jface.action.LegacyActionTools; +import org.eclipse.jface.resource.ImageDescriptor; +import org.osgi.framework.Bundle; + +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.util.Messages; + +/** + * Describes a category extension to the "completionProposalComputer" extension point. + * + * @since 4.0 + */ +public final class CompletionProposalCategory { + /** The extension schema name of the icon attribute. */ + private static final String ICON= "icon"; //$NON-NLS-1$ + + private final String fId; + private final String fName; + private final IConfigurationElement fElement; + /** The image descriptor for this category, or null if none specified. */ + private final ImageDescriptor fImage; + + private boolean fIsSeparateCommand= true; + private boolean fIsEnabled= true; + private boolean fIsIncluded= true; + private final CompletionProposalComputerRegistry fRegistry; + + private int fSortOrder= 0x10000; + private String fLastError= null; + + CompletionProposalCategory(IConfigurationElement element, CompletionProposalComputerRegistry registry) { + fElement= element; + fRegistry= registry; + IExtension parent= (IExtension) element.getParent(); + fId= parent.getUniqueIdentifier(); + checkNotNull(fId, "id"); //$NON-NLS-1$ + String name= parent.getLabel(); + if (name == null) + fName= fId; + else + fName= name; + + String icon= element.getAttribute(ICON); + ImageDescriptor img= null; + if (icon != null) { + Bundle bundle= getBundle(); + if (bundle != null) { + Path path= new Path(icon); + URL url= FileLocator.find(bundle, path, null); + img= ImageDescriptor.createFromURL(url); + } + } + fImage= img; + + } + + CompletionProposalCategory(String id, String name, CompletionProposalComputerRegistry registry) { + fRegistry= registry; + fId= id; + fName= name; + fElement= null; + fImage= null; + } + + private Bundle getBundle() { + String namespace= fElement.getDeclaringExtension().getContributor().getName(); + Bundle bundle= Platform.getBundle(namespace); + return bundle; + } + + /** + * Checks an element that must be defined according to the extension + * point schema. Throws an + * InvalidRegistryObjectException if obj + * is null. + */ + private void checkNotNull(Object obj, String attribute) throws InvalidRegistryObjectException { + if (obj == null) { + Object[] args= { getId(), fElement.getContributor().getName(), attribute }; + String message= Messages.format(ContentAssistMessages.CompletionProposalComputerDescriptor_illegal_attribute_message, args); + IStatus status= new Status(IStatus.WARNING, CUIPlugin.getPluginId(), IStatus.OK, message, null); + CUIPlugin.getDefault().log(status); + throw new InvalidRegistryObjectException(); + } + } + + /** + * Returns the identifier of the described extension. + * + * @return Returns the id + */ + public String getId() { + return fId; + } + + /** + * Returns the name of the described extension. + * + * @return Returns the name + */ + public String getName() { + return fName; + } + + /** + * Returns the name of the described extension + * without mnemonic hint in order to be displayed + * in a message. + * + * @return Returns the name + */ + public String getDisplayName() { + return LegacyActionTools.removeMnemonics(fName); + } + + /** + * Returns the image descriptor of the described category. + * + * @return the image descriptor of the described category + */ + public ImageDescriptor getImageDescriptor() { + return fImage; + } + + /** + * Sets the separate command state of the category. + * + * @param enabled the new enabled state. + */ + public void setSeparateCommand(boolean enabled) { + fIsSeparateCommand= enabled; + } + + /** + * Returns the enablement state of the category. + * + * @return the enablement state of the category + */ + public boolean isSeparateCommand() { + return fIsSeparateCommand; + } + + /** + * @param included the included + */ + public void setIncluded(boolean included) { + fIsIncluded= included; + } + + /** + * @return included + */ + public boolean isIncluded() { + return fIsIncluded; + } + + public boolean isEnabled() { + return fIsEnabled; + } + + public void setEnabled(boolean isEnabled) { + fIsEnabled= isEnabled; + } + + /** + * Returns true if the category contains any computers, false + * otherwise. + * + * @return true if the category contains any computers, false + * otherwise + */ + public boolean hasComputers() { + List descriptors= fRegistry.getProposalComputerDescriptors(); + for (Iterator it= descriptors.iterator(); it.hasNext();) { + CompletionProposalComputerDescriptor desc= (CompletionProposalComputerDescriptor) it.next(); + if (desc.getCategory() == this) + return true; + } + return false; + } + + /** + * Returns true if the category contains any computers in the given partition, false + * otherwise. + * + * @param partition the partition + * @return true if the category contains any computers, false + * otherwise + */ + public boolean hasComputers(String partition) { + List descriptors= fRegistry.getProposalComputerDescriptors(partition); + for (Iterator it= descriptors.iterator(); it.hasNext();) { + CompletionProposalComputerDescriptor desc= (CompletionProposalComputerDescriptor) it.next(); + if (desc.getCategory() == this) + return true; + } + return false; + } + + /** + * @return sortOrder + */ + public int getSortOrder() { + return fSortOrder; + } + + /** + * @param sortOrder the sortOrder + */ + public void setSortOrder(int sortOrder) { + fSortOrder= sortOrder; + } + + /** + * Safely computes completion proposals of all computers of this category through their + * extension. If an extension is disabled, throws an exception or otherwise does not adhere to + * the contract described in {@link ICompletionProposalComputer}, it is disabled. + * + * @param context the invocation context passed on to the extension + * @param partition the partition type where to invocation occurred + * @param monitor the progress monitor passed on to the extension + * @return the list of computed completion proposals (element type: + * {@link org.eclipse.jface.text.contentassist.ICompletionProposal}) + */ + public List computeCompletionProposals(ContentAssistInvocationContext context, String partition, SubProgressMonitor monitor) { + fLastError= null; + List result= new ArrayList(); + List descriptors= new ArrayList(fRegistry.getProposalComputerDescriptors(partition)); + for (Iterator it= descriptors.iterator(); it.hasNext();) { + CompletionProposalComputerDescriptor desc= (CompletionProposalComputerDescriptor) it.next(); + if (desc.getCategory() == this) + result.addAll(desc.computeCompletionProposals(context, monitor)); + if (fLastError == null) + fLastError= desc.getErrorMessage(); + } + return result; + } + + /** + * Safely computes context information objects of all computers of this category through their + * extension. If an extension is disabled, throws an exception or otherwise does not adhere to + * the contract described in {@link ICompletionProposalComputer}, it is disabled. + * + * @param context the invocation context passed on to the extension + * @param partition the partition type where to invocation occurred + * @param monitor the progress monitor passed on to the extension + * @return the list of computed context information objects (element type: + * {@link org.eclipse.jface.text.contentassist.IContextInformation}) + */ + public List computeContextInformation(ContentAssistInvocationContext context, String partition, SubProgressMonitor monitor) { + fLastError= null; + List result= new ArrayList(); + List descriptors= new ArrayList(fRegistry.getProposalComputerDescriptors(partition)); + for (Iterator it= descriptors.iterator(); it.hasNext();) { + CompletionProposalComputerDescriptor desc= (CompletionProposalComputerDescriptor) it.next(); + if (desc.getCategory() == this) + result.addAll(desc.computeContextInformation(context, monitor)); + if (fLastError == null) + fLastError= desc.getErrorMessage(); + } + return result; + } + + /** + * Returns the error message from the computers in this category. + * + * @return the error message from the computers in this category + */ + public String getErrorMessage() { + return fLastError; + } + + /** + * Notifies the computers in this category of a proposal computation session start. + */ + public void sessionStarted() { + List descriptors= new ArrayList(fRegistry.getProposalComputerDescriptors()); + for (Iterator it= descriptors.iterator(); it.hasNext();) { + CompletionProposalComputerDescriptor desc= (CompletionProposalComputerDescriptor) it.next(); + if (desc.getCategory() == this) + desc.sessionStarted(); + if (fLastError == null) + fLastError= desc.getErrorMessage(); + } + } + + /** + * Notifies the computers in this category of a proposal computation session end. + */ + public void sessionEnded() { + List descriptors= new ArrayList(fRegistry.getProposalComputerDescriptors()); + for (Iterator it= descriptors.iterator(); it.hasNext();) { + CompletionProposalComputerDescriptor desc= (CompletionProposalComputerDescriptor) it.next(); + if (desc.getCategory() == this) + desc.sessionEnded(); + if (fLastError == null) + fLastError= desc.getErrorMessage(); + } + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CompletionProposalComputerDescriptor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CompletionProposalComputerDescriptor.java new file mode 100644 index 00000000000..d0e66d604ae --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CompletionProposalComputerDescriptor.java @@ -0,0 +1,550 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IContributor; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.InvalidRegistryObjectException; +import org.eclipse.core.runtime.PerformanceStats; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +import org.eclipse.jface.text.IDocument; + +import org.osgi.framework.Bundle; + +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.text.ICPartitions; +import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext; +import org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer; + +import org.eclipse.cdt.internal.ui.util.Messages; + +/** + * The description of an extension to the + * org.eclipse.cdt.ui.completionProposalComputer extension point. Instances are + * immutable. Instances can be obtained from a {@link CompletionProposalComputerRegistry}. + * + * @see CompletionProposalComputerRegistry + * @since 4.0 + */ +final class CompletionProposalComputerDescriptor { + /** The default category id. */ + private static final String DEFAULT_CATEGORY_ID= "org.eclipse.cdt.ui.defaultProposalCategory"; //$NON-NLS-1$ + /** The extension schema name of the category id attribute. */ + private static final String CATEGORY_ID= "categoryId"; //$NON-NLS-1$ + /** The extension schema name of the partition type attribute. */ + private static final String TYPE= "type"; //$NON-NLS-1$ + /** The extension schema name of the class attribute. */ + private static final String CLASS= "class"; //$NON-NLS-1$ + /** The extension schema name of the activate attribute. */ + private static final String ACTIVATE= "activate"; //$NON-NLS-1$ + /** The extension schema name of the partition child elements. */ + private static final String PARTITION= "partition"; //$NON-NLS-1$ + /** Set of Java partition types. */ + private static final Set PARTITION_SET; + /** The name of the performance event used to trace extensions. */ + private static final String PERFORMANCE_EVENT= CUIPlugin.getPluginId() + "/perf/content_assist/extensions"; //$NON-NLS-1$ + /** + * If true, execution time of extensions is measured and the data forwarded to + * core's {@link PerformanceStats} service. + */ + private static final boolean MEASURE_PERFORMANCE= PerformanceStats.isEnabled(PERFORMANCE_EVENT); + /** + * Independently of the {@link PerformanceStats} service, any operation that takes longer than + * {@value} milliseconds will be flagged as an violation. This timeout does not apply to the + * first invocation, as it may take longer due to plug-in initialization etc. See also + * {@link #fIsReportingDelay}. + */ + private static final long MAX_DELAY= 5000; + + /* log constants */ + private static final String COMPUTE_COMPLETION_PROPOSALS= "computeCompletionProposals()"; //$NON-NLS-1$ + private static final String COMPUTE_CONTEXT_INFORMATION= "computeContextInformation()"; //$NON-NLS-1$ + private static final String SESSION_STARTED= "sessionStarted()"; //$NON-NLS-1$ + private static final String SESSION_ENDED= "sessionEnded()"; //$NON-NLS-1$ + + static { + Set partitions= new HashSet(); + partitions.add(IDocument.DEFAULT_CONTENT_TYPE); + partitions.add(ICPartitions.C_PREPROCESSOR); + partitions.add(ICPartitions.C_MULTI_LINE_COMMENT); + partitions.add(ICPartitions.C_SINGLE_LINE_COMMENT); + partitions.add(ICPartitions.C_STRING); + partitions.add(ICPartitions.C_CHARACTER); + + PARTITION_SET= Collections.unmodifiableSet(partitions); + } + + /** The identifier of the extension. */ + private final String fId; + /** The name of the extension. */ + private final String fName; + /** The class name of the provided IJavaCompletionProposalComputer. */ + private final String fClass; + /** The activate attribute value. */ + private final boolean fActivate; + /** The partition of the extension (element type: {@link String}). */ + private final Set fPartitions; + /** The configuration element of this extension. */ + private final IConfigurationElement fElement; + /** The registry we are registered with. */ + private final CompletionProposalComputerRegistry fRegistry; + /** The computer, if instantiated, null otherwise. */ + private ICompletionProposalComputer fComputer; + /** The ui category. */ + private final CompletionProposalCategory fCategory; + /** The first error message in the most recent operation, or null. */ + private String fLastError; + /** + * Tells whether to inform the user when MAX_DELAY has been exceeded. + * We start timing execution after the first session because the first may take + * longer due to plug-in activation and initialization. + */ + private boolean fIsReportingDelay= false; + /** The start of the last operation. */ + private long fStart; + + /** + * Creates a new descriptor. + * + * @param element the configuration element to read + * @param registry the computer registry creating this descriptor + */ + CompletionProposalComputerDescriptor(IConfigurationElement element, CompletionProposalComputerRegistry registry, List categories) throws InvalidRegistryObjectException { + Assert.isLegal(registry != null); + Assert.isLegal(element != null); + + fRegistry= registry; + fElement= element; + IExtension extension= element.getDeclaringExtension(); + fId= extension.getUniqueIdentifier(); + checkNotNull(fId, "id"); //$NON-NLS-1$ + + String name= extension.getLabel(); + if (name.length() == 0) + fName= fId; + else + fName= name; + + Set partitions= new HashSet(); + IConfigurationElement[] children= element.getChildren(PARTITION); + if (children.length == 0) { + fPartitions= PARTITION_SET; // add to all partition types if no partition is configured + } else { + for (int i= 0; i < children.length; i++) { + String type= children[i].getAttribute(TYPE); + checkNotNull(type, TYPE); + partitions.add(type); + } + fPartitions= Collections.unmodifiableSet(partitions); + } + + String activateAttribute= element.getAttribute(ACTIVATE); + fActivate= Boolean.valueOf(activateAttribute).booleanValue(); + + fClass= element.getAttribute(CLASS); + checkNotNull(fClass, CLASS); + + String categoryId= element.getAttribute(CATEGORY_ID); + if (categoryId == null) + categoryId= DEFAULT_CATEGORY_ID; + CompletionProposalCategory category= null; + for (Iterator it= categories.iterator(); it.hasNext();) { + CompletionProposalCategory cat= (CompletionProposalCategory) it.next(); + if (cat.getId().equals(categoryId)) { + category= cat; + break; + } + } + if (category == null) { + // create a category if it does not exist + fCategory= new CompletionProposalCategory(categoryId, fName, registry); + categories.add(fCategory); + } else { + fCategory= category; + } + } + + /** + * Checks an element that must be defined according to the extension + * point schema. Throws an + * InvalidRegistryObjectException if obj + * is null. + */ + private void checkNotNull(Object obj, String attribute) throws InvalidRegistryObjectException { + if (obj == null) { + Object[] args= { getId(), fElement.getContributor().getName(), attribute }; + String message= Messages.format(ContentAssistMessages.CompletionProposalComputerDescriptor_illegal_attribute_message, args); + IStatus status= new Status(IStatus.WARNING, CUIPlugin.getPluginId(), IStatus.OK, message, null); + CUIPlugin.getDefault().log(status); + throw new InvalidRegistryObjectException(); + } + } + + /** + * Returns the identifier of the described extension. + * + * @return Returns the id + */ + public String getId() { + return fId; + } + + /** + * Returns the name of the described extension. + * + * @return Returns the name + */ + public String getName() { + return fName; + } + + /** + * Returns the partition types of the described extension. + * + * @return the set of partition types (element type: {@link String}) + */ + public Set getPartitions() { + return fPartitions; + } + + /** + * Returns a cached instance of the computer as described in the + * extension's xml. The computer is + * {@link #createComputer() created} the first time that this method + * is called and then cached. + * + * @return a new instance of the completion proposal computer as + * described by this descriptor + * @throws CoreException if the creation fails + * @throws InvalidRegistryObjectException if the extension is not + * valid any longer (e.g. due to plug-in unloading) + */ + private synchronized ICompletionProposalComputer getComputer() throws CoreException, InvalidRegistryObjectException { + if (fComputer == null && (fActivate || isPluginLoaded())) + fComputer= createComputer(); + return fComputer; + } + + private boolean isPluginLoaded() { + Bundle bundle= getBundle(); + return bundle != null && bundle.getState() == Bundle.ACTIVE; + } + + private Bundle getBundle() { + String namespace= fElement.getDeclaringExtension().getContributor().getName(); + Bundle bundle= Platform.getBundle(namespace); + return bundle; + } + + /** + * Returns a new instance of the computer as described in the + * extension's xml. Note that the safest way to access the computer + * is by using the {@linkplain #computeCompletionProposals} + * and {@linkplain #computeContextInformation} + * methods. These delegate the functionality to the contributed + * computer, but handle instance creation and any exceptions thrown. + * + * @return a new instance of the completion proposal computer as + * described by this descriptor + * @throws CoreException if the creation fails + * @throws InvalidRegistryObjectException if the extension is not + * valid any longer (e.g. due to plug-in unloading) + */ + public ICompletionProposalComputer createComputer() throws CoreException, InvalidRegistryObjectException { + return (ICompletionProposalComputer) fElement.createExecutableExtension(CLASS); + } + + /** + * Safely computes completion proposals through the described extension. If the extension + * is disabled, throws an exception or otherwise does not adhere to the contract described in + * {@link ICompletionProposalComputer}, an empty list is returned. + * + * @param context the invocation context passed on to the extension + * @param monitor the progress monitor passed on to the extension + * @return the list of computed completion proposals (element type: + * {@link org.eclipse.jface.text.contentassist.ICompletionProposal}) + */ + public List computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) { + if (!isEnabled()) + return Collections.EMPTY_LIST; + + IStatus status; + try { + ICompletionProposalComputer computer= getComputer(); + if (computer == null) // not active yet + return Collections.EMPTY_LIST; + + try { + PerformanceStats stats= startMeter(context, computer); + List proposals= computer.computeCompletionProposals(context, monitor); + stopMeter(stats, COMPUTE_COMPLETION_PROPOSALS); + + if (proposals != null) { + fLastError= computer.getErrorMessage(); + return proposals; + } + } finally { + fIsReportingDelay= true; + } + status= createAPIViolationStatus(COMPUTE_COMPLETION_PROPOSALS); + } catch (InvalidRegistryObjectException x) { + status= createExceptionStatus(x); + } catch (CoreException x) { + status= createExceptionStatus(x); + } catch (RuntimeException x) { + status= createExceptionStatus(x); + } finally { + monitor.done(); + } + + fRegistry.informUser(this, status); + + return Collections.EMPTY_LIST; + } + + /** + * Safely computes context information objects through the described extension. If the extension + * is disabled, throws an exception or otherwise does not adhere to the contract described in + * {@link ICompletionProposalComputer}, an empty list is returned. + * + * @param context the invocation context passed on to the extension + * @param monitor the progress monitor passed on to the extension + * @return the list of computed context information objects (element type: + * {@link org.eclipse.jface.text.contentassist.IContextInformation}) + */ + public List computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) { + if (!isEnabled()) + return Collections.EMPTY_LIST; + + IStatus status; + try { + ICompletionProposalComputer computer= getComputer(); + if (computer == null) // not active yet + return Collections.EMPTY_LIST; + + PerformanceStats stats= startMeter(context, computer); + List proposals= computer.computeContextInformation(context, monitor); + stopMeter(stats, COMPUTE_CONTEXT_INFORMATION); + + if (proposals != null) { + fLastError= computer.getErrorMessage(); + return proposals; + } + + status= createAPIViolationStatus(COMPUTE_CONTEXT_INFORMATION); + } catch (InvalidRegistryObjectException x) { + status= createExceptionStatus(x); + } catch (CoreException x) { + status= createExceptionStatus(x); + } catch (RuntimeException x) { + status= createExceptionStatus(x); + } finally { + monitor.done(); + } + + fRegistry.informUser(this, status); + + return Collections.EMPTY_LIST; + } + + + /** + * Notifies the described extension of a proposal computation session start. + *

+ * Note: This method is called every time code assist is invoked and + * is not filtered by partition type. + *

+ */ + public void sessionStarted() { + if (!isEnabled()) + return; + + IStatus status; + try { + ICompletionProposalComputer computer= getComputer(); + if (computer == null) // not active yet + return; + + PerformanceStats stats= startMeter(SESSION_STARTED, computer); + computer.sessionStarted(); + stopMeter(stats, SESSION_ENDED); + + return; + } catch (InvalidRegistryObjectException x) { + status= createExceptionStatus(x); + } catch (CoreException x) { + status= createExceptionStatus(x); + } catch (RuntimeException x) { + status= createExceptionStatus(x); + } + + fRegistry.informUser(this, status); + } + + /** + * Notifies the described extension of a proposal computation session end. + *

+ * Note: This method is called every time code assist is invoked and + * is not filtered by partition type. + *

+ */ + public void sessionEnded() { + if (!isEnabled()) + return; + + IStatus status; + try { + ICompletionProposalComputer computer= getComputer(); + if (computer == null) // not active yet + return; + + PerformanceStats stats= startMeter(SESSION_ENDED, computer); + computer.sessionEnded(); + stopMeter(stats, SESSION_ENDED); + + return; + } catch (InvalidRegistryObjectException x) { + status= createExceptionStatus(x); + } catch (CoreException x) { + status= createExceptionStatus(x); + } catch (RuntimeException x) { + status= createExceptionStatus(x); + } + + fRegistry.informUser(this, status); + } + + private PerformanceStats startMeter(Object context, ICompletionProposalComputer computer) { + final PerformanceStats stats; + if (MEASURE_PERFORMANCE) { + stats= PerformanceStats.getStats(PERFORMANCE_EVENT, computer); + stats.startRun(context.toString()); + } else { + stats= null; + } + + if (fIsReportingDelay) { + fStart= System.currentTimeMillis(); + } + + return stats; + } + + private void stopMeter(final PerformanceStats stats, String operation) { + if (MEASURE_PERFORMANCE) { + stats.endRun(); + if (stats.isFailure()) { + IStatus status= createPerformanceStatus(operation); + fRegistry.informUser(this, status); + return; + } + } + + if (fIsReportingDelay) { + long current= System.currentTimeMillis(); + if (current - fStart > MAX_DELAY) { + IStatus status= createPerformanceStatus(operation); + fRegistry.informUser(this, status); + } + } + } + + private IStatus createExceptionStatus(InvalidRegistryObjectException x) { + // extension has become invalid - log & disable + String blame= createBlameMessage(); + String reason= ContentAssistMessages.CompletionProposalComputerDescriptor_reason_invalid; + return new Status(IStatus.INFO, CUIPlugin.getPluginId(), IStatus.OK, blame + " " + reason, x); //$NON-NLS-1$ + } + + private IStatus createExceptionStatus(CoreException x) { + // unable to instantiate the extension - log & disable + String blame= createBlameMessage(); + String reason= ContentAssistMessages.CompletionProposalComputerDescriptor_reason_instantiation; + return new Status(IStatus.ERROR, CUIPlugin.getPluginId(), IStatus.OK, blame + " " + reason, x); //$NON-NLS-1$ + } + + private IStatus createExceptionStatus(RuntimeException x) { + // misbehaving extension - log & disable + String blame= createBlameMessage(); + String reason= ContentAssistMessages.CompletionProposalComputerDescriptor_reason_runtime_ex; + return new Status(IStatus.WARNING, CUIPlugin.getPluginId(), IStatus.OK, blame + " " + reason, x); //$NON-NLS-1$ + } + + private IStatus createAPIViolationStatus(String operation) { + String blame= createBlameMessage(); + Object[] args= {operation}; + String reason= Messages.format(ContentAssistMessages.CompletionProposalComputerDescriptor_reason_API, args); + return new Status(IStatus.WARNING, CUIPlugin.getPluginId(), IStatus.OK, blame + " " + reason, null); //$NON-NLS-1$ + } + + private IStatus createPerformanceStatus(String operation) { + String blame= createBlameMessage(); + Object[] args= {operation}; + String reason= Messages.format(ContentAssistMessages.CompletionProposalComputerDescriptor_reason_performance, args); + return new Status(IStatus.WARNING, CUIPlugin.getPluginId(), IStatus.OK, blame + " " + reason, null); //$NON-NLS-1$ + } + + private String createBlameMessage() { + Object[] args= { getName(), fElement.getDeclaringExtension().getContributor().getName() }; + String disable= Messages.format(ContentAssistMessages.CompletionProposalComputerDescriptor_blame_message, args); + return disable; + } + + /** + * Returns the enablement state of the described extension. + * + * @return the enablement state of the described extension + */ + private boolean isEnabled() { + return fCategory.isEnabled(); + } + + CompletionProposalCategory getCategory() { + return fCategory; + } + + /** + * Returns the error message from the described extension. + * + * @return the error message from the described extension + */ + public String getErrorMessage() { + return fLastError; + } + + /** + * Returns the contributor of the described extension. + * + * @return the contributor of the described extension + */ + IContributor getContributor() { + try { + return fElement.getContributor(); + } catch (InvalidRegistryObjectException e) { + return null; + } + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CompletionProposalComputerRegistry.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CompletionProposalComputerRegistry.java new file mode 100644 index 00000000000..69e8953048d --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/CompletionProposalComputerRegistry.java @@ -0,0 +1,394 @@ +/******************************************************************************* + * 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringTokenizer; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IContributor; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.InvalidRegistryObjectException; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Link; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.preference.IPreferenceStore; + +import org.eclipse.ui.dialogs.PreferencesUtil; + +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.PreferenceConstants; + +import org.eclipse.cdt.internal.ui.util.Messages; + +/** + * A registry for all extensions to the + * org.eclipse.cdt.ui.completionProposalComputer + * extension point. + * + * @since 4.0 + */ +public final class CompletionProposalComputerRegistry { + + private static final String EXTENSION_POINT= "completionProposalComputer"; //$NON-NLS-1$ + + /** The singleton instance. */ + private static CompletionProposalComputerRegistry fgSingleton= null; + + /** + * Returns the default computer registry. + *

+ * TODO keep this or add some other singleton, e.g. JavaPlugin? + *

+ * + * @return the singleton instance + */ + public static synchronized CompletionProposalComputerRegistry getDefault() { + if (fgSingleton == null) { + fgSingleton= new CompletionProposalComputerRegistry(); + } + + return fgSingleton; + } + + /** + * The sets of descriptors, grouped by partition type (key type: + * {@link String}, value type: + * {@linkplain List List<CompletionProposalComputerDescriptor>}). + */ + private final Map fDescriptorsByPartition= new HashMap(); + /** + * Unmodifiable versions of the sets stored in + * fDescriptorsByPartition (key type: {@link String}, + * value type: + * {@linkplain List List<CompletionProposalComputerDescriptor>}). + */ + private final Map fPublicDescriptorsByPartition= new HashMap(); + /** + * All descriptors (element type: + * {@link CompletionProposalComputerDescriptor}). + */ + private final List fDescriptors= new ArrayList(); + /** + * Unmodifiable view of fDescriptors + */ + private final List fPublicDescriptors= Collections.unmodifiableList(fDescriptors); + + private final List fCategories= new ArrayList(); + private final List fPublicCategories= Collections.unmodifiableList(fCategories); + /** + * true if this registry has been loaded. + */ + private boolean fLoaded= false; + + /** + * Creates a new instance. + */ + public CompletionProposalComputerRegistry() { + } + + /** + * Returns the list of {@link CompletionProposalComputerDescriptor}s describing all extensions + * to the javaCompletionProposalComputer extension point for the given partition + * type. + *

+ * A valid partition is either one of the constants defined in + * {@link org.eclipse.cdt.ui.text.ICPartitions} or + * {@link org.eclipse.jface.text.IDocument#DEFAULT_CONTENT_TYPE}. An empty list is returned if + * there are no extensions for the given partition. + *

+ *

+ * The returned list is read-only and is sorted in the order that the extensions were read in. + * There are no duplicate elements in the returned list. The returned list may change if plug-ins + * are loaded or unloaded while the application is running or if an extension violates the API + * contract of {@link org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer}. When + * computing proposals, it is therefore imperative to copy the returned list before iterating + * over it. + *

+ * + * @param partition + * the partition type for which to retrieve the computer descriptors + * @return the list of extensions to the javaCompletionProposalComputer extension + * point (element type: {@link CompletionProposalComputerDescriptor}) + */ + List getProposalComputerDescriptors(String partition) { + ensureExtensionPointRead(); + List result= (List) fPublicDescriptorsByPartition.get(partition); + return result != null ? result : Collections.EMPTY_LIST; + } + + /** + * Returns the list of {@link CompletionProposalComputerDescriptor}s describing all extensions + * to the javaCompletionProposalComputer extension point. + *

+ * The returned list is read-only and is sorted in the order that the extensions were read in. + * There are no duplicate elements in the returned list. The returned list may change if plug-ins + * are loaded or unloaded while the application is running or if an extension violates the API + * contract of {@link org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer}. When + * computing proposals, it is therefore imperative to copy the returned list before iterating + * over it. + *

+ * + * @return the list of extensions to the javaCompletionProposalComputer extension + * point (element type: {@link CompletionProposalComputerDescriptor}) + */ + List getProposalComputerDescriptors() { + ensureExtensionPointRead(); + return fPublicDescriptors; + } + + /** + * Returns the list of proposal categories contributed to the + * javaCompletionProposalComputer extension point. + *

+ *

+ * The returned list is read-only and is sorted in the order that the extensions were read in. + * There are no duplicate elements in the returned list. The returned list may change if + * plug-ins are loaded or unloaded while the application is running. + *

+ * + * @return list of proposal categories contributed to the + * javaCompletionProposalComputer extension point (element type: + * {@link CompletionProposalCategory}) + */ + public List getProposalCategories() { + ensureExtensionPointRead(); + return fPublicCategories; + } + + /** + * Ensures that the extensions are read and stored in + * fDescriptorsByPartition. + */ + private void ensureExtensionPointRead() { + boolean reload; + synchronized (this) { + reload= !fLoaded; + fLoaded= true; + } + if (reload) + reload(); + } + + /** + * Reloads the extensions to the extension point. + *

+ * This method can be called more than once in order to reload from + * a changed extension registry. + *

+ */ + public void reload() { + IExtensionRegistry registry= Platform.getExtensionRegistry(); + List elements= new ArrayList(Arrays.asList(registry.getConfigurationElementsFor(CUIPlugin.getPluginId(), EXTENSION_POINT))); + + Map map= new HashMap(); + List all= new ArrayList(); + + List categories= getCategories(elements); + for (Iterator iter= elements.iterator(); iter.hasNext();) { + IConfigurationElement element= (IConfigurationElement) iter.next(); + try { + CompletionProposalComputerDescriptor desc= new CompletionProposalComputerDescriptor(element, this, categories); + Set partitions= desc.getPartitions(); + for (Iterator it= partitions.iterator(); it.hasNext();) { + String partition= (String) it.next(); + List list= (List) map.get(partition); + if (list == null) { + list= new ArrayList(); + map.put(partition, list); + } + list.add(desc); + } + all.add(desc); + + } catch (InvalidRegistryObjectException x) { + /* + * Element is not valid any longer as the contributing plug-in was unloaded or for + * some other reason. Do not include the extension in the list and inform the user + * about it. + */ + Object[] args= {element.toString()}; + String message= Messages.format(ContentAssistMessages.CompletionProposalComputerRegistry_invalid_message, args); + IStatus status= new Status(IStatus.WARNING, CUIPlugin.getPluginId(), IStatus.OK, message, x); + informUser(status); + } + } + + synchronized (this) { + fCategories.clear(); + fCategories.addAll(categories); + + Set partitions= map.keySet(); + fDescriptorsByPartition.keySet().retainAll(partitions); + fPublicDescriptorsByPartition.keySet().retainAll(partitions); + for (Iterator it= partitions.iterator(); it.hasNext();) { + String partition= (String) it.next(); + List old= (List) fDescriptorsByPartition.get(partition); + List current= (List) map.get(partition); + if (old != null) { + old.clear(); + old.addAll(current); + } else { + fDescriptorsByPartition.put(partition, current); + fPublicDescriptorsByPartition.put(partition, Collections.unmodifiableList(current)); + } + } + + fDescriptors.clear(); + fDescriptors.addAll(all); + } + } + + private List getCategories(List elements) { + IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore(); + String preference= store.getString(PreferenceConstants.CODEASSIST_EXCLUDED_CATEGORIES); + Set disabled= new HashSet(); + StringTokenizer tok= new StringTokenizer(preference, "\0"); //$NON-NLS-1$ + while (tok.hasMoreTokens()) + disabled.add(tok.nextToken()); + Map ordered= new HashMap(); + preference= store.getString(PreferenceConstants.CODEASSIST_CATEGORY_ORDER); + tok= new StringTokenizer(preference, "\0"); //$NON-NLS-1$ + while (tok.hasMoreTokens()) { + StringTokenizer inner= new StringTokenizer(tok.nextToken(), ":"); //$NON-NLS-1$ + String id= inner.nextToken(); + int rank= Integer.parseInt(inner.nextToken()); + ordered.put(id, new Integer(rank)); + } + + List categories= new ArrayList(); + for (Iterator iter= elements.iterator(); iter.hasNext();) { + IConfigurationElement element= (IConfigurationElement) iter.next(); + try { + if (element.getName().equals("proposalCategory")) { //$NON-NLS-1$ + iter.remove(); // remove from list to leave only computers + + CompletionProposalCategory category= new CompletionProposalCategory(element, this); + categories.add(category); + category.setIncluded(!disabled.contains(category.getId())); + Integer rank= (Integer) ordered.get(category.getId()); + if (rank != null) { + int r= rank.intValue(); + boolean separate= r < 0xffff; + category.setSeparateCommand(separate); + category.setSortOrder(r); + } + } + } catch (InvalidRegistryObjectException x) { + /* + * Element is not valid any longer as the contributing plug-in was unloaded or for + * some other reason. Do not include the extension in the list and inform the user + * about it. + */ + Object[] args= {element.toString()}; + String message= Messages.format(ContentAssistMessages.CompletionProposalComputerRegistry_invalid_message, args); + IStatus status= new Status(IStatus.WARNING, CUIPlugin.getPluginId(), IStatus.OK, message, x); + informUser(status); + } + } + return categories; + } + + /** + * Log the status and inform the user about a misbehaving extension. + * + * @param descriptor the descriptor of the misbehaving extension + * @param status a status object that will be logged + */ + void informUser(CompletionProposalComputerDescriptor descriptor, IStatus status) { + CUIPlugin.getDefault().log(status); + String title= ContentAssistMessages.CompletionProposalComputerRegistry_error_dialog_title; + CompletionProposalCategory category= descriptor.getCategory(); + IContributor culprit= descriptor.getContributor(); + Set affectedPlugins= getAffectedContributors(category, culprit); + + final String avoidHint; + final String culpritName= culprit == null ? null : culprit.getName(); + if (affectedPlugins.isEmpty()) + avoidHint= Messages.format(ContentAssistMessages.CompletionProposalComputerRegistry_messageAvoidanceHint, new Object[] {culpritName, category.getDisplayName()}); + else + avoidHint= Messages.format(ContentAssistMessages.CompletionProposalComputerRegistry_messageAvoidanceHintWithWarning, new Object[] {culpritName, category.getDisplayName(), toString(affectedPlugins)}); + + String message= status.getMessage(); + // inlined from MessageDialog.openError + MessageDialog dialog = new MessageDialog(CUIPlugin.getActiveWorkbenchShell(), title, null /* default image */, message, MessageDialog.ERROR, new String[] { IDialogConstants.OK_LABEL }, 0) { + protected Control createCustomArea(Composite parent) { + Link link= new Link(parent, SWT.NONE); + link.setText(avoidHint); + link.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + PreferencesUtil.createPreferenceDialogOn(getShell(), "org.eclipse.cdt.ui.preferences.CodeAssistPreferenceAdvanced", null, null).open(); //$NON-NLS-1$ + } + }); + GridData gridData= new GridData(SWT.FILL, SWT.BEGINNING, true, false); + gridData.widthHint= this.getMinimumMessageWidth(); + link.setLayoutData(gridData); + return link; + } + }; + dialog.open(); + } + + /** + * Returns the names of contributors affected by disabling a category. + * + * @param category the category that would be disabled + * @param culprit the culprit plug-in, which is not included in the returned list + * @return the names of the contributors other than culprit that contribute to category (element type: {@link String}) + */ + private Set getAffectedContributors(CompletionProposalCategory category, IContributor culprit) { + Set affectedPlugins= new HashSet(); + for (Iterator it= getProposalComputerDescriptors().iterator(); it.hasNext();) { + CompletionProposalComputerDescriptor desc= (CompletionProposalComputerDescriptor) it.next(); + CompletionProposalCategory cat= desc.getCategory(); + if (cat.equals(category)) { + IContributor contributor= desc.getContributor(); + if (contributor != null && !culprit.equals(contributor)) + affectedPlugins.add(contributor.getName()); + } + } + return affectedPlugins; + } + + private Object toString(Collection collection) { + // strip brackets off AbstractCollection.toString() + String string= collection.toString(); + return string.substring(1, string.length() - 1); + } + + private void informUser(IStatus status) { + CUIPlugin.getDefault().log(status); + String title= ContentAssistMessages.CompletionProposalComputerRegistry_error_dialog_title; + String message= status.getMessage(); + MessageDialog.openError(CUIPlugin.getActiveWorkbenchShell(), title, message); + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistComputerParameter.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistComputerParameter.java new file mode 100644 index 00000000000..5a016ab9c22 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistComputerParameter.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.core.commands.IParameterValues; + +/** + * Map of parameters for the specific content assist command. + * + * @since 4.0 + */ +public final class ContentAssistComputerParameter implements IParameterValues { + /* + * @see org.eclipse.core.commands.IParameterValues#getParameterValues() + */ + public Map getParameterValues() { + Collection descriptors= CompletionProposalComputerRegistry.getDefault().getProposalCategories(); + Map map= new HashMap(descriptors.size()); + for (Iterator it= descriptors.iterator(); it.hasNext();) { + CompletionProposalCategory category= (CompletionProposalCategory) it.next(); + map.put(category.getDisplayName(), category.getId()); + } + return map; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistHandler.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistHandler.java new file mode 100644 index 00000000000..7f0811e9daa --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistHandler.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.texteditor.ITextEditor; + +import org.eclipse.cdt.internal.ui.editor.SpecificContentAssistExecutor; + +/** + * A command handler to invoke a content assist for a specific proposal category. + * + * @since 4.0 + */ +public final class ContentAssistHandler extends AbstractHandler { + private final SpecificContentAssistExecutor fExecutor= new SpecificContentAssistExecutor(CompletionProposalComputerRegistry.getDefault()); + + public ContentAssistHandler() { + } + + /* + * @see org.eclipse.core.commands.IHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + public Object execute(ExecutionEvent event) throws ExecutionException { + ITextEditor editor= getActiveEditor(); + if (editor == null) + return null; + + String categoryId= event.getParameter("org.eclipse.cdt.ui.specific_content_assist.category_id"); //$NON-NLS-1$ + if (categoryId == null) + return null; + + fExecutor.invokeContentAssist(editor, categoryId); + + return null; + } + + private ITextEditor getActiveEditor() { + IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window != null) { + IWorkbenchPage page= window.getActivePage(); + if (page != null) { + IEditorPart editor= page.getActiveEditor(); + if (editor instanceof ITextEditor) + return (ITextEditor) editor; + } + } + return null; + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistMessages.java new file mode 100644 index 00000000000..f350e41cafb --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistMessages.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import org.eclipse.osgi.util.NLS; + +/** + * Helper class to get NLSed messages. + */ +final class ContentAssistMessages extends NLS { + + private static final String BUNDLE_NAME= ContentAssistMessages.class.getName(); + + private ContentAssistMessages() { + // Do not instantiate + } + + public static String CompletionProposalComputerRegistry_messageAvoidanceHint; + public static String CompletionProposalComputerRegistry_messageAvoidanceHintWithWarning; + public static String ContentAssistProcessor_all_disabled_message; + public static String ContentAssistProcessor_all_disabled_preference_link; + public static String ContentAssistProcessor_all_disabled_title; + + static { + NLS.initializeMessages(BUNDLE_NAME, ContentAssistMessages.class); + } + + public static String ContentAssistProcessor_computing_proposals; + public static String ContentAssistProcessor_collecting_proposals; + public static String ContentAssistProcessor_sorting_proposals; + public static String ContentAssistProcessor_computing_contexts; + public static String ContentAssistProcessor_collecting_contexts; + public static String ContentAssistProcessor_sorting_contexts; + public static String CompletionProposalComputerDescriptor_illegal_attribute_message; + public static String CompletionProposalComputerDescriptor_reason_invalid; + public static String CompletionProposalComputerDescriptor_reason_instantiation; + public static String CompletionProposalComputerDescriptor_reason_runtime_ex; + public static String CompletionProposalComputerDescriptor_reason_API; + public static String CompletionProposalComputerDescriptor_reason_performance; + public static String CompletionProposalComputerDescriptor_blame_message; + public static String CompletionProposalComputerRegistry_invalid_message; + public static String CompletionProposalComputerRegistry_error_dialog_title; + public static String ContentAssistProcessor_defaultProposalCategory; + public static String ContentAssistProcessor_toggle_affordance_press_gesture; + public static String ContentAssistProcessor_toggle_affordance_click_gesture; + public static String ContentAssistProcessor_toggle_affordance_update_message; + public static String ContentAssistProcessor_empty_message; + public static String ContentAssistHistory_serialize_error; + public static String ContentAssistHistory_deserialize_error; +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistMessages.properties new file mode 100644 index 00000000000..c9b7a6c2081 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistMessages.properties @@ -0,0 +1,53 @@ +############################################################################### +# 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 +# Anton Leherbauer (Wind River Systems) +############################################################################### + +CompletionProposalComputerRegistry_error_dialog_title=Problems During Content Assist +# {0} is the plug-in id of the contributing plug-in, {1} the display name of the proposal category +CompletionProposalComputerRegistry_messageAvoidanceHint=To avoid this message, disable the ''{0}'' plug-in or disable the ''{1}'' category on the content assist preference page. +# {0} is the plug-in id of the contributing plug-in, {1} the display name of the proposal category, {2} the comma-separated list of plug-ins other than {0} the contribute to the category +CompletionProposalComputerRegistry_messageAvoidanceHintWithWarning=To avoid this message, disable the ''{0}'' plug-in or disable the ''{1}'' category on the content assist preference page. Note that disabling the category will also affect completion proposals from the following plug-ins: {2}. +# {0} is the extension id +CompletionProposalComputerRegistry_invalid_message=The extension ''{0}'' has become invalid. + +CompletionProposalComputerDescriptor_reason_invalid=The extension has become invalid. +CompletionProposalComputerDescriptor_reason_instantiation=Unable to instantiate the extension. +CompletionProposalComputerDescriptor_reason_runtime_ex=The extension has thrown a runtime exception. +# {0} specifies an operation name, e.g. "computeProposals" +CompletionProposalComputerDescriptor_reason_API=The extension violated the API contract of the ''{0}'' operation. +CompletionProposalComputerDescriptor_reason_performance=The extension took too long to return from the ''{0}'' operation. +# {0} is the name of an extension, {1} the contributing plug-in name +CompletionProposalComputerDescriptor_blame_message= The ''{0}'' proposal computer from the ''{1}'' plug-in did not complete normally. +# {0} is the identifier of an extension, {1} the contributing plug-in, {2} a required but missing xml attribute +CompletionProposalComputerDescriptor_illegal_attribute_message= The extension "{0}" from plug-in "{1}" did not specify a value for the required "{2}" attribute. Disabling the extension. + +ContentAssistProcessor_computing_proposals=Computing completion proposals +ContentAssistProcessor_collecting_proposals=Collecting proposals +ContentAssistProcessor_sorting_proposals=Sorting +ContentAssistProcessor_computing_contexts=Computing context information +ContentAssistProcessor_all_disabled_title=No Default Proposal Kinds +ContentAssistProcessor_all_disabled_message=No proposal kinds are enabled for the 'default' content assist list. +# {0} will be replaced by the label of the 'restore defaults' button +# The preference page should match the title of the 'C/C++ > Editor > Content Assist > Advanced' preference page +ContentAssistProcessor_all_disabled_preference_link=Change the settings on the Advanced Content Assist preference page or click ''{0}'' to restore the default behavior. +ContentAssistProcessor_collecting_contexts=Collecting context information +ContentAssistProcessor_sorting_contexts=Sorting +ContentAssistProcessor_defaultProposalCategory=Default Proposals +# {0} will be replaced by a keyboard shortcut (accelerator) +ContentAssistProcessor_toggle_affordance_press_gesture=Press ''{0}'' +ContentAssistProcessor_toggle_affordance_click_gesture=Click +# {0} will be replaced by a title describing the displayed proposal category, {1} by either the press_gesture or click_gesture message above, {2} by the name of the next proposal category +ContentAssistProcessor_toggle_affordance_update_message={1} to show {2} +# {0} will be replaced by a title describing the displayed proposal category +ContentAssistProcessor_empty_message= No {0} + +ContentAssistHistory_serialize_error=Problems writing content assist history to XML +ContentAssistHistory_deserialize_error=Problems reading content assist history from XML diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistPreference.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistPreference.java index 44657226ead..ed680ef92d0 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistPreference.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistPreference.java @@ -28,8 +28,8 @@ import org.eclipse.jface.util.PropertyChangeEvent; public class ContentAssistPreference { - /** Preference key for content assist auto activation */ - //public final static String AUTOACTIVATION= "content_assist_autoactivation"; +// /** Preference key for content assist auto activation (unused) */ +// public final static String AUTOACTIVATION= "content_assist_autoactivation"; /** Preference key for content assist auto activation delay */ public final static String AUTOACTIVATION_DELAY= "content_assist_autoactivation_delay"; //$NON-NLS-1$ /** Preference key for content assist timeout delay (unused) */ @@ -52,14 +52,14 @@ public class ContentAssistPreference { public final static String AUTOACTIVATION_TRIGGERS_ARROW= "content_assist_autoactivation_trigger_arrow"; //$NON-NLS-1$ public final static String AUTOACTIVATION_TRIGGERS_DOUBLECOLON= "content_assist_autoactivation_trigger_doublecolon"; //$NON-NLS-1$ - /** Preference key for visibility of proposals (unused) */ - public final static String SHOW_DOCUMENTED_PROPOSALS= "content_assist_show_visible_proposals"; //$NON-NLS-1$ - /** Preference key for alphabetic ordering of proposals */ - public final static String ORDER_PROPOSALS= "content_assist_order_proposals"; //$NON-NLS-1$ - /** Preference key for case sensitivity of propsals */ - //public final static String CASE_SENSITIVITY= "content_assist_case_sensitivity"; - /** Preference key for adding includes on code assist (unused) */ - public final static String ADD_INCLUDE= "content_assist_add_import"; //$NON-NLS-1$ +// /** Preference key for visibility of proposals (unused) */ +// public final static String SHOW_DOCUMENTED_PROPOSALS= "content_assist_show_visible_proposals"; //$NON-NLS-1$ +// /** Preference key for alphabetic ordering of proposals (unused) */ +// public final static String ORDER_PROPOSALS= "content_assist_order_proposals"; //$NON-NLS-1$ +// /** Preference key for case sensitivity of propsals */ +// public final static String CASE_SENSITIVITY= "content_assist_case_sensitivity"; +// /** Preference key for adding includes on code assist (unused) */ +// public final static String ADD_INCLUDE= "content_assist_add_import"; //$NON-NLS-1$ /** Preference key for completion search scope (unused) */ public final static String CURRENT_FILE_SEARCH_SCOPE= "content_assist_current_file_search_scope"; //$NON-NLS-1$ /** Preference key for completion search scope (unused) */ @@ -77,16 +77,16 @@ public class ContentAssistPreference { return getColor(store, key, textTools.getColorManager()); } - private static CCompletionProcessor2 getCProcessor(ContentAssistant assistant) { + private static CContentAssistProcessor getCProcessor(ContentAssistant assistant) { IContentAssistProcessor p= assistant.getContentAssistProcessor(IDocument.DEFAULT_CONTENT_TYPE); - if (p instanceof CCompletionProcessor2) - return (CCompletionProcessor2) p; + if (p instanceof CContentAssistProcessor) + return (CContentAssistProcessor) p; return null; } private static void configureCProcessor(ContentAssistant assistant, IPreferenceStore store) { - CCompletionProcessor2 jcp= getCProcessor(assistant); - if (jcp == null) + CContentAssistProcessor ccp= getCProcessor(assistant); + if (ccp == null) return; String triggers = ""; //$NON-NLS-1$ @@ -99,19 +99,20 @@ public class ContentAssistPreference { boolean useDoubleColonAsTrigger = store.getBoolean(AUTOACTIVATION_TRIGGERS_DOUBLECOLON); if(useDoubleColonAsTrigger) triggers += ":"; //$NON-NLS-1$ - jcp.setCompletionProposalAutoActivationCharacters(triggers.toCharArray()); + ccp.setCompletionProposalAutoActivationCharacters(triggers.toCharArray()); - boolean enabled= store.getBoolean(SHOW_DOCUMENTED_PROPOSALS); - //jcp.restrictProposalsToVisibility(enabled); +// boolean enabled; +// enabled= store.getBoolean(SHOW_DOCUMENTED_PROPOSALS); +// ccp.restrictProposalsToVisibility(enabled); +// +// enabled= store.getBoolean(CASE_SENSITIVITY); +// ccp.restrictProposalsToMatchingCases(enabled); - //enabled= store.getBoolean(CASE_SENSITIVITY); - //jcp.restrictProposalsToMatchingCases(enabled); +// enabled= store.getBoolean(ORDER_PROPOSALS); +// ccp.orderProposalsAlphabetically(enabled); - enabled= store.getBoolean(ORDER_PROPOSALS); - jcp.orderProposalsAlphabetically(enabled); - - enabled= store.getBoolean(ADD_INCLUDE); - jcp.allowAddingIncludes(enabled); +// enabled= store.getBoolean(ADD_INCLUDE); +// ccp.allowAddingIncludes(enabled); } @@ -157,8 +158,8 @@ public class ContentAssistPreference { private static void changeCProcessor(ContentAssistant assistant, IPreferenceStore store, String key) { - CCompletionProcessor2 jcp= getCProcessor(assistant); - if (jcp == null) + CContentAssistProcessor ccp= getCProcessor(assistant); + if (ccp == null) return; if ( (AUTOACTIVATION_TRIGGERS_DOT.equals(key)) @@ -177,22 +178,23 @@ public class ContentAssistPreference { if (useDoubleColonAsTrigger){ triggers += ":"; //$NON-NLS-1$ } - jcp.setCompletionProposalAutoActivationCharacters(triggers.toCharArray()); - } else if (SHOW_DOCUMENTED_PROPOSALS.equals(key)) { - //boolean enabled= store.getBoolean(SHOW_DOCUMENTED_PROPOSALS); - //jcp.restrictProposalsToVisibility(enabled); - } - //else if (CASE_SENSITIVITY.equals(key)) { - // boolean enabled= store.getBoolean(CASE_SENSITIVITY); - // jcp.restrictProposalsToMatchingCases(enabled); - // } - else if (ORDER_PROPOSALS.equals(key)) { - boolean enable= store.getBoolean(ORDER_PROPOSALS); - jcp.orderProposalsAlphabetically(enable); - } else if (ADD_INCLUDE.equals(key)) { - boolean enabled= store.getBoolean(ADD_INCLUDE); - jcp.allowAddingIncludes(enabled); + ccp.setCompletionProposalAutoActivationCharacters(triggers.toCharArray()); } +// else if (SHOW_DOCUMENTED_PROPOSALS.equals(key)) { +// boolean enabled= store.getBoolean(SHOW_DOCUMENTED_PROPOSALS); +// ccp.restrictProposalsToVisibility(enabled); +// } +// else if (CASE_SENSITIVITY.equals(key)) { +// boolean enabled= store.getBoolean(CASE_SENSITIVITY); +// ccp.restrictProposalsToMatchingCases(enabled); +// } +// else if (ORDER_PROPOSALS.equals(key)) { +// boolean enable= store.getBoolean(ORDER_PROPOSALS); +// ccp.orderProposalsAlphabetically(enable); +// } else if (ADD_INCLUDE.equals(key)) { +// boolean enabled= store.getBoolean(ADD_INCLUDE); +// ccp.allowAddingIncludes(enabled); +// } } /** diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistProcessor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistProcessor.java new file mode 100644 index 00000000000..c3ab20d9597 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/ContentAssistProcessor.java @@ -0,0 +1,557 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.jface.action.LegacyActionTools; +import org.eclipse.jface.bindings.TriggerSequence; +import org.eclipse.jface.bindings.keys.KeySequence; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.contentassist.ContentAssistEvent; +import org.eclipse.jface.text.contentassist.ContentAssistant; +import org.eclipse.jface.text.contentassist.ICompletionListener; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContentAssistProcessor; +import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; +import org.eclipse.jface.text.contentassist.IContentAssistantExtension3; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.contentassist.IContextInformationValidator; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.dialogs.PreferencesUtil; +import org.eclipse.ui.keys.IBindingService; +import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; + +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.PreferenceConstants; +import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext; + +import org.eclipse.cdt.internal.ui.CUIMessages; +import org.eclipse.cdt.internal.ui.dialogs.OptionalMessageDialog; +import org.eclipse.cdt.internal.ui.util.Messages; + +/** + * A content assist processor that aggregates the proposals of the + * {@link org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer}s contributed via the + * org.eclipse.cdt.ui.completionProposalComputer extension point. + *

+ * Subclasses may extend: + *

    + *
  • createContext to provide the context object passed to the computers
  • + *
  • createProgressMonitor to change the way progress is reported
  • + *
  • filterAndSort to add sorting and filtering
  • + *
  • getContextInformationValidator to add context validation (needed if any + * contexts are provided)
  • + *
  • getErrorMessage to change error reporting
  • + *
+ *

+ * + * @since 4.0 + */ +public class ContentAssistProcessor implements IContentAssistProcessor { + private static final boolean DEBUG= "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.cdt.ui/debug/ResultCollector")); //$NON-NLS-1$//$NON-NLS-2$ + + /** + * Dialog settings key for the "all categories are disabled" warning dialog. See + * {@link OptionalMessageDialog}. + */ + private static final String PREF_WARN_ABOUT_EMPTY_ASSIST_CATEGORY= "EmptyDefaultAssistCategory"; //$NON-NLS-1$ + + private static final Comparator ORDER_COMPARATOR= new Comparator() { + + public int compare(Object o1, Object o2) { + CompletionProposalCategory d1= (CompletionProposalCategory) o1; + CompletionProposalCategory d2= (CompletionProposalCategory) o2; + + return d1.getSortOrder() - d2.getSortOrder(); + } + + }; + + private final List fCategories; + private final String fPartition; + private final ContentAssistant fAssistant; + + private char[] fCompletionAutoActivationCharacters; + + /* cycling stuff */ + private int fRepetition= -1; + private List/*>*/ fCategoryIteration= null; + private String fIterationGesture= null; + private int fNumberOfComputedResults= 0; + private String fErrorMessage; + + public ContentAssistProcessor(ContentAssistant assistant, String partition) { + Assert.isNotNull(partition); + Assert.isNotNull(assistant); + fPartition= partition; + fCategories= CompletionProposalComputerRegistry.getDefault().getProposalCategories(); + fAssistant= assistant; + fAssistant.addCompletionListener(new ICompletionListener() { + + /* + * @see org.eclipse.jface.text.contentassist.ICompletionListener#assistSessionStarted(org.eclipse.jface.text.contentassist.ContentAssistEvent) + */ + public void assistSessionStarted(ContentAssistEvent event) { + if (event.processor != ContentAssistProcessor.this) + return; + + fIterationGesture= getIterationGesture(); + KeySequence binding= getIterationBinding(); + + // this may show the warning dialog if all categories are disabled + fCategoryIteration= getCategoryIteration(); + for (Iterator it= fCategories.iterator(); it.hasNext();) { + CompletionProposalCategory cat= (CompletionProposalCategory) it.next(); + cat.sessionStarted(); + } + + fRepetition= 0; + if (event.assistant instanceof IContentAssistantExtension2) { + IContentAssistantExtension2 extension= (IContentAssistantExtension2) event.assistant; + + if (fCategoryIteration.size() == 1) { + extension.setRepeatedInvocationMode(false); + extension.setShowEmptyList(false); + } else { + extension.setRepeatedInvocationMode(true); + extension.setStatusLineVisible(true); + extension.setStatusMessage(createIterationMessage()); + extension.setShowEmptyList(true); + if (extension instanceof IContentAssistantExtension3) { + IContentAssistantExtension3 ext3= (IContentAssistantExtension3) extension; + ((ContentAssistant) ext3).setRepeatedInvocationTrigger(binding); + } + } + + } + } + + /* + * @see org.eclipse.jface.text.contentassist.ICompletionListener#assistSessionEnded(org.eclipse.jface.text.contentassist.ContentAssistEvent) + */ + public void assistSessionEnded(ContentAssistEvent event) { + if (event.processor != ContentAssistProcessor.this) + return; + + for (Iterator it= fCategories.iterator(); it.hasNext();) { + CompletionProposalCategory cat= (CompletionProposalCategory) it.next(); + cat.sessionEnded(); + } + + fCategoryIteration= null; + fRepetition= -1; + fIterationGesture= null; + if (event.assistant instanceof IContentAssistantExtension2) { + IContentAssistantExtension2 extension= (IContentAssistantExtension2) event.assistant; + extension.setShowEmptyList(false); + extension.setRepeatedInvocationMode(false); + extension.setStatusLineVisible(false); + if (extension instanceof IContentAssistantExtension3) { + IContentAssistantExtension3 ext3= (IContentAssistantExtension3) extension; + ((ContentAssistant) ext3).setRepeatedInvocationTrigger(null); + } + } + } + + /* + * @see org.eclipse.jface.text.contentassist.ICompletionListener#selectionChanged(org.eclipse.jface.text.contentassist.ICompletionProposal, boolean) + */ + public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {} + + }); + } + + /* + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int) + */ + public final ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { + long start= DEBUG ? System.currentTimeMillis() : 0; + + clearState(); + + IProgressMonitor monitor= createProgressMonitor(); + monitor.beginTask(ContentAssistMessages.ContentAssistProcessor_computing_proposals, fCategories.size() + 1); + + ContentAssistInvocationContext context= createContext(viewer, offset); + long setup= DEBUG ? System.currentTimeMillis() : 0; + + monitor.subTask(ContentAssistMessages.ContentAssistProcessor_collecting_proposals); + List proposals= collectProposals(viewer, offset, monitor, context); + long collect= DEBUG ? System.currentTimeMillis() : 0; + + monitor.subTask(ContentAssistMessages.ContentAssistProcessor_sorting_proposals); + List filtered= filterAndSortProposals(proposals, monitor, context); + fNumberOfComputedResults= filtered.size(); + long filter= DEBUG ? System.currentTimeMillis() : 0; + + ICompletionProposal[] result= (ICompletionProposal[]) filtered.toArray(new ICompletionProposal[filtered.size()]); + monitor.done(); + + if (DEBUG) { + System.err.println("Code Assist Stats (" + result.length + " proposals)"); //$NON-NLS-1$ //$NON-NLS-2$ + System.err.println("Code Assist (setup):\t" + (setup - start) ); //$NON-NLS-1$ + System.err.println("Code Assist (collect):\t" + (collect - setup) ); //$NON-NLS-1$ + System.err.println("Code Assist (sort):\t" + (filter - collect) ); //$NON-NLS-1$ + } + + return result; + } + + private void clearState() { + fErrorMessage=null; + fNumberOfComputedResults= 0; + } + + private List collectProposals(ITextViewer viewer, int offset, IProgressMonitor monitor, ContentAssistInvocationContext context) { + List proposals= new ArrayList(); + List providers= getCategories(); + for (Iterator it= providers.iterator(); it.hasNext();) { + CompletionProposalCategory cat= (CompletionProposalCategory) it.next(); + List computed= cat.computeCompletionProposals(context, fPartition, new SubProgressMonitor(monitor, 1)); + proposals.addAll(computed); + if (fErrorMessage == null) + fErrorMessage= cat.getErrorMessage(); + } + + return proposals; + } + + /** + * Filters and sorts the proposals. The passed list may be modified + * and returned, or a new list may be created and returned. + * + * @param proposals the list of collected proposals (element type: + * {@link ICompletionProposal}) + * @param monitor a progress monitor + * @param context TODO + * @return the list of filtered and sorted proposals, ready for + * display (element type: {@link ICompletionProposal}) + */ + protected List filterAndSortProposals(List proposals, IProgressMonitor monitor, ContentAssistInvocationContext context) { + return proposals; + } + + /* + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int) + */ + public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { + clearState(); + + IProgressMonitor monitor= createProgressMonitor(); + monitor.beginTask(ContentAssistMessages.ContentAssistProcessor_computing_contexts, fCategories.size() + 1); + + monitor.subTask(ContentAssistMessages.ContentAssistProcessor_collecting_contexts); + List proposals= collectContextInformation(viewer, offset, monitor); + + monitor.subTask(ContentAssistMessages.ContentAssistProcessor_sorting_contexts); + List filtered= filterAndSortContextInformation(proposals, monitor); + fNumberOfComputedResults= filtered.size(); + + IContextInformation[] result= (IContextInformation[]) filtered.toArray(new IContextInformation[filtered.size()]); + monitor.done(); + return result; + } + + private List collectContextInformation(ITextViewer viewer, int offset, IProgressMonitor monitor) { + List proposals= new ArrayList(); + ContentAssistInvocationContext context= createContext(viewer, offset); + + List providers= getCategories(); + for (Iterator it= providers.iterator(); it.hasNext();) { + CompletionProposalCategory cat= (CompletionProposalCategory) it.next(); + List computed= cat.computeContextInformation(context, fPartition, new SubProgressMonitor(monitor, 1)); + proposals.addAll(computed); + if (fErrorMessage == null) + fErrorMessage= cat.getErrorMessage(); + } + + return proposals; + } + + /** + * Filters and sorts the context information objects. The passed + * list may be modified and returned, or a new list may be created + * and returned. + * + * @param contexts the list of collected proposals (element type: + * {@link IContextInformation}) + * @param monitor a progress monitor + * @return the list of filtered and sorted proposals, ready for + * display (element type: {@link IContextInformation}) + */ + protected List filterAndSortContextInformation(List contexts, IProgressMonitor monitor) { + return contexts; + } + + /** + * Sets this processor's set of characters triggering the activation of the + * completion proposal computation. + * + * @param activationSet the activation set + */ + public final void setCompletionProposalAutoActivationCharacters(char[] activationSet) { + fCompletionAutoActivationCharacters= activationSet; + } + + + /* + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters() + */ + public final char[] getCompletionProposalAutoActivationCharacters() { + return fCompletionAutoActivationCharacters; + } + + /* + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters() + */ + public char[] getContextInformationAutoActivationCharacters() { + return null; + } + + /* + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage() + */ + public String getErrorMessage() { + if (fNumberOfComputedResults > 0) + return null; + if (fErrorMessage != null) + return fErrorMessage; + return CUIMessages.getString("CEditor.contentassist.noCompletions"); //$NON-NLS-1$ + } + + /* + * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator() + */ + public IContextInformationValidator getContextInformationValidator() { + return null; + } + + /** + * Creates a progress monitor. + *

+ * The default implementation creates a + * NullProgressMonitor. + *

+ * + * @return a progress monitor + */ + protected IProgressMonitor createProgressMonitor() { + return new NullProgressMonitor(); + } + + /** + * Creates the context that is passed to the completion proposal + * computers. + * + * @param viewer the viewer that content assist is invoked on + * @param offset the content assist offset + * @return the context to be passed to the computers + */ + protected ContentAssistInvocationContext createContext(ITextViewer viewer, int offset) { + return new ContentAssistInvocationContext(viewer, offset); + } + + private List getCategories() { + if (fCategoryIteration == null) + return fCategories; + + int iteration= fRepetition % fCategoryIteration.size(); + fAssistant.setStatusMessage(createIterationMessage()); + fAssistant.setEmptyMessage(createEmptyMessage()); + fRepetition++; + +// fAssistant.setShowMessage(fRepetition % 2 != 0); +// + return (List) fCategoryIteration.get(iteration); + } + + private List getCategoryIteration() { + List sequence= new ArrayList(); + sequence.add(getDefaultCategories()); + for (Iterator it= getSeparateCategories().iterator(); it.hasNext();) { + CompletionProposalCategory cat= (CompletionProposalCategory) it.next(); + sequence.add(Collections.singletonList(cat)); + } + return sequence; + } + + private List getDefaultCategories() { + // default mix - enable all included computers + List included= getDefaultCategoriesUnchecked(); + + if (IDocument.DEFAULT_CONTENT_TYPE.equals(fPartition) && included.isEmpty() && !fCategories.isEmpty()) + if (informUserAboutEmptyDefaultCategory()) + // preferences were restored - recompute the default categories + included= getDefaultCategoriesUnchecked(); + + return included; + } + + private List getDefaultCategoriesUnchecked() { + List included= new ArrayList(); + for (Iterator it= fCategories.iterator(); it.hasNext();) { + CompletionProposalCategory category= (CompletionProposalCategory) it.next(); + if (category.isIncluded() && category.hasComputers(fPartition)) + included.add(category); + } + return included; + } + + /** + * Informs the user about the fact that there are no enabled categories in the default content + * assist set and shows a link to the preferences. + */ + private boolean informUserAboutEmptyDefaultCategory() { + if (OptionalMessageDialog.isDialogEnabled(PREF_WARN_ABOUT_EMPTY_ASSIST_CATEGORY)) { + final Shell shell= CUIPlugin.getActiveWorkbenchShell(); + String title= ContentAssistMessages.ContentAssistProcessor_all_disabled_title; + String message= ContentAssistMessages.ContentAssistProcessor_all_disabled_message; + // see PreferencePage#createControl for the 'defaults' label + final String restoreButtonLabel= JFaceResources.getString("defaults"); //$NON-NLS-1$ + final String linkMessage= Messages.format(ContentAssistMessages.ContentAssistProcessor_all_disabled_preference_link, LegacyActionTools.removeMnemonics(restoreButtonLabel)); + final int restoreId= IDialogConstants.CLIENT_ID + 10; + final int settingsId= IDialogConstants.CLIENT_ID + 11; + final OptionalMessageDialog dialog= new OptionalMessageDialog(PREF_WARN_ABOUT_EMPTY_ASSIST_CATEGORY, shell, title, null /* default image */, message, MessageDialog.WARNING, new String[] { restoreButtonLabel, IDialogConstants.CLOSE_LABEL }, 1) { + /* + * @see org.eclipse.cdt.internal.ui.dialogs.OptionalMessageDialog#createCustomArea(org.eclipse.swt.widgets.Composite) + */ + protected Control createCustomArea(Composite composite) { + // wrap link and checkbox in one composite without space + Composite parent= new Composite(composite, SWT.NONE); + GridLayout layout= new GridLayout(); + layout.marginHeight= 0; + layout.marginWidth= 0; + layout.verticalSpacing= 0; + parent.setLayout(layout); + + Composite linkComposite= new Composite(parent, SWT.NONE); + layout= new GridLayout(); + layout.marginHeight= convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN); + layout.marginWidth= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN); + layout.horizontalSpacing= convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING); + linkComposite.setLayout(layout); + + Link link= new Link(linkComposite, SWT.NONE); + link.setText(linkMessage); + link.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent e) { + setReturnCode(settingsId); + close(); + } + }); + GridData gridData= new GridData(SWT.FILL, SWT.BEGINNING, true, false); + gridData.widthHint= this.getMinimumMessageWidth(); + link.setLayoutData(gridData); + + // create checkbox and "don't show this message" prompt + super.createCustomArea(parent); + + return parent; + } + + /* + * @see org.eclipse.jface.dialogs.MessageDialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite) + */ + protected void createButtonsForButtonBar(Composite parent) { + Button[] buttons= new Button[2]; + buttons[0]= createButton(parent, restoreId, restoreButtonLabel, false); + buttons[1]= createButton(parent, IDialogConstants.CLOSE_ID, IDialogConstants.CLOSE_LABEL, true); + setButtons(buttons); + } + }; + int returnValue= dialog.open(); + if (restoreId == returnValue || settingsId == returnValue) { + if (restoreId == returnValue) { + IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore(); + store.setToDefault(PreferenceConstants.CODEASSIST_CATEGORY_ORDER); + store.setToDefault(PreferenceConstants.CODEASSIST_EXCLUDED_CATEGORIES); + } + if (settingsId == returnValue) + PreferencesUtil.createPreferenceDialogOn(shell, "org.eclipse.cdt.ui.preferences.CodeAssistPreferenceAdvanced", null, null).open(); //$NON-NLS-1$ + CompletionProposalComputerRegistry registry= CompletionProposalComputerRegistry.getDefault(); + registry.reload(); + return true; + } + } + return false; + } + + private List getSeparateCategories() { + ArrayList sorted= new ArrayList(); + for (Iterator it= fCategories.iterator(); it.hasNext();) { + CompletionProposalCategory category= (CompletionProposalCategory) it.next(); + if (category.isSeparateCommand() && category.hasComputers(fPartition)) + sorted.add(category); + } + Collections.sort(sorted, ORDER_COMPARATOR); + return sorted; + } + + private String createEmptyMessage() { + return Messages.format(ContentAssistMessages.ContentAssistProcessor_empty_message, new String[]{getCategoryLabel(fRepetition)}); + } + + private String createIterationMessage() { + return Messages.format(ContentAssistMessages.ContentAssistProcessor_toggle_affordance_update_message, new String[]{ getCategoryLabel(fRepetition), fIterationGesture, getCategoryLabel(fRepetition + 1) }); + } + + private String getCategoryLabel(int repetition) { + int iteration= repetition % fCategoryIteration.size(); + if (iteration == 0) + return ContentAssistMessages.ContentAssistProcessor_defaultProposalCategory; + return toString((CompletionProposalCategory) ((List) fCategoryIteration.get(iteration)).get(0)); + } + + private String toString(CompletionProposalCategory category) { + return category.getDisplayName(); + } + + private String getIterationGesture() { + TriggerSequence binding= getIterationBinding(); + return binding != null ? + Messages.format(ContentAssistMessages.ContentAssistProcessor_toggle_affordance_press_gesture, new Object[] { binding.format() }) + : ContentAssistMessages.ContentAssistProcessor_toggle_affordance_click_gesture; + } + + private KeySequence getIterationBinding() { + final IBindingService bindingSvc= (IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class); + TriggerSequence binding= bindingSvc.getBestActiveBindingFor(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); + if (binding instanceof KeySequence) + return (KeySequence) binding; + return null; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/HippieProposalComputer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/HippieProposalComputer.java new file mode 100644 index 00000000000..1144b0c7b4e --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/HippieProposalComputer.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.internal.ui.text.contentassist; + +import java.util.Arrays; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.ui.texteditor.HippieProposalProcessor; + +import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext; +import org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer; + + +/** + * A computer wrapper for the hippie processor. + * + * @since 4.0 + */ +public final class HippieProposalComputer implements ICompletionProposalComputer { + /** The wrapped processor. */ + private final HippieProposalProcessor fProcessor= new HippieProposalProcessor(); + + /** + * Default ctor to make it instantiatable via the extension mechanism. + */ + public HippieProposalComputer() { + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#computeCompletionProposals(org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor) + */ + public List computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) { + return Arrays.asList(fProcessor.computeCompletionProposals(context.getViewer(), context.getInvocationOffset())); + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#computeContextInformation(org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor) + */ + public List computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) { + return Arrays.asList(fProcessor.computeContextInformation(context.getViewer(), context.getInvocationOffset())); + } + + /* + * @see org.eclipse.jface.text.contentassist.ICompletionProposalComputer#getErrorMessage() + */ + public String getErrorMessage() { + return fProcessor.getErrorMessage(); + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#sessionStarted() + */ + public void sessionStarted() { + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#sessionEnded() + */ + public void sessionEnded() { + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/LegacyCompletionProposalComputer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/LegacyCompletionProposalComputer.java new file mode 100644 index 00000000000..0d199f7c7a6 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/LegacyCompletionProposalComputer.java @@ -0,0 +1,203 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.text.contentassist; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContextInformation; +import org.eclipse.jface.text.contentassist.IContextInformationExtension; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.IEditorPart; + +import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext; +import org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer; + +import org.eclipse.cdt.internal.ui.text.CHeuristicScanner; +import org.eclipse.cdt.internal.ui.text.Symbols; + +/** + * A proposal computer wrapping the legacy {@link CCompletionProcessor2}. + * + * @since 4.0 + */ +public class LegacyCompletionProposalComputer implements ICompletionProposalComputer { + + private static final class ContextInformationWrapper implements IContextInformation, IContextInformationExtension { + + private final IContextInformation fContextInformation; + private int fPosition; + + public ContextInformationWrapper(IContextInformation contextInformation) { + fContextInformation= contextInformation; + } + + /* + * @see IContextInformation#getContextDisplayString() + */ + public String getContextDisplayString() { + return fContextInformation.getContextDisplayString(); + } + + /* + * @see IContextInformation#getImage() + */ + public Image getImage() { + return fContextInformation.getImage(); + } + + /* + * @see IContextInformation#getInformationDisplayString() + */ + public String getInformationDisplayString() { + return fContextInformation.getInformationDisplayString(); + } + + /* + * @see IContextInformationExtension#getContextInformationPosition() + */ + public int getContextInformationPosition() { + return fPosition; + } + + public void setContextInformationPosition(int position) { + fPosition= position; + } + + /* + * @see org.eclipse.jface.text.contentassist.IContextInformation#equals(java.lang.Object) + */ + public boolean equals(Object object) { + if (object instanceof ContextInformationWrapper) + return fContextInformation.equals(((ContextInformationWrapper) object).fContextInformation); + else + return fContextInformation.equals(object); + } + } + + private CCompletionProcessor2 fCompletionProcessor; + + /** + * Default constructor is required (executable extension). + */ + public LegacyCompletionProposalComputer() { + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#computeCompletionProposals(org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor) + */ + public List computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) { + if (context instanceof CContentAssistInvocationContext) { + CContentAssistInvocationContext cContext= (CContentAssistInvocationContext)context; + return internalComputeCompletionProposals(context.getInvocationOffset(), cContext, monitor); + } + return Collections.EMPTY_LIST; + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#computeContextInformation(org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor) + */ + public List computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) { + if (context instanceof CContentAssistInvocationContext) { + CContentAssistInvocationContext cContext= (CContentAssistInvocationContext)context; + int contextInformationPosition= guessContextInformationPosition(cContext); + if (contextInformationPosition >= 0) { + List result= addContextInformations(cContext, contextInformationPosition, monitor); + return result; + } + } + return Collections.EMPTY_LIST; + } + + protected int guessContextInformationPosition(ContentAssistInvocationContext context) { + final int contextPosition= context.getInvocationOffset(); + + IDocument document= context.getDocument(); + CHeuristicScanner scanner= new CHeuristicScanner(document); + int bound= Math.max(-1, contextPosition - 200); + + // try the innermost scope of parentheses that looks like a method call + int pos= contextPosition - 1; + do { + int paren= scanner.findOpeningPeer(pos, bound, '(', ')'); + if (paren == CHeuristicScanner.NOT_FOUND) + break; + int token= scanner.previousToken(paren - 1, bound); + // next token must be a method name (identifier) or the closing angle of a + // constructor call of a template type. + if (token == Symbols.TokenIDENT || token == Symbols.TokenGREATERTHAN) { + return paren - 1; + } + pos= paren - 1; + } while (true); + + return context.getInvocationOffset(); + } + + private List addContextInformations(CContentAssistInvocationContext context, int offset, IProgressMonitor monitor) { + List proposals= internalComputeCompletionProposals(offset, context, monitor); + List result= new ArrayList(proposals.size()); + + for (Iterator it= proposals.iterator(); it.hasNext();) { + ICompletionProposal proposal= (ICompletionProposal) it.next(); + IContextInformation contextInformation= proposal.getContextInformation(); + if (contextInformation != null) { + ContextInformationWrapper wrapper= new ContextInformationWrapper(contextInformation); + wrapper.setContextInformationPosition(offset); + result.add(wrapper); + } + } + return result; + } + + private List internalComputeCompletionProposals(int offset, CContentAssistInvocationContext context, IProgressMonitor monitor) { + IEditorPart editor= context.getEditor(); + if (editor != null) { + fCompletionProcessor= new CCompletionProcessor2(editor); + ICompletionProposal[] proposals= fCompletionProcessor.computeCompletionProposals(context.getViewer(), offset); + if (proposals != null) { + return Arrays.asList(proposals); + } + } + return Collections.EMPTY_LIST; + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#getErrorMessage() + */ + public String getErrorMessage() { + if (fCompletionProcessor != null) { + return fCompletionProcessor.getErrorMessage(); + } + return null; + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#sessionEnded() + */ + public void sessionEnded() { + fCompletionProcessor= null; + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#sessionStarted() + */ + public void sessionStarted() { + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/TemplateCompletionProposalComputer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/TemplateCompletionProposalComputer.java new file mode 100644 index 00000000000..c203cf53ba0 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/contentassist/TemplateCompletionProposalComputer.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2007 Wind River Systems, Inc. and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Anton Leherbauer (Wind River Systems) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.cdt.internal.ui.text.contentassist; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.TextUtilities; +import org.eclipse.jface.text.templates.TemplateContextType; + +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.text.ICPartitions; +import org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext; +import org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer; + +import org.eclipse.cdt.internal.corext.template.c.CContextType; +import org.eclipse.cdt.internal.corext.template.c.CommentContextType; + +import org.eclipse.cdt.internal.ui.text.CHeuristicScanner; +import org.eclipse.cdt.internal.ui.text.template.TemplateEngine; + +/** + * A completion proposal computer for templates. + * + * @since 4.0 + */ +public class TemplateCompletionProposalComputer implements ICompletionProposalComputer { + + private final TemplateEngine fCTemplateEngine; + private final TemplateEngine fCommentTemplateEngine; + + /** + * Default constructor is required (executable extension). + */ + public TemplateCompletionProposalComputer() { + TemplateContextType contextType= CUIPlugin.getDefault().getTemplateContextRegistry().getContextType(CContextType.ID); + if (contextType == null) { + contextType= new CContextType(); + CUIPlugin.getDefault().getTemplateContextRegistry().addContextType(contextType); + } + if (contextType != null) + fCTemplateEngine= new TemplateEngine(contextType); + else + fCTemplateEngine= null; + contextType= CUIPlugin.getDefault().getTemplateContextRegistry().getContextType(CommentContextType.ID); + if (contextType == null) { + contextType= new CommentContextType(); + CUIPlugin.getDefault().getTemplateContextRegistry().addContextType(contextType); + } + if (contextType != null) + fCommentTemplateEngine= new TemplateEngine(contextType); + else + fCommentTemplateEngine= null; + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#computeCompletionProposals(org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor) + */ + public List computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor) { + ITextViewer viewer= context.getViewer(); + int offset= context.getInvocationOffset(); + TemplateEngine engine= null; + try { + String partition= TextUtilities.getContentType(viewer.getDocument(), ICPartitions.C_PARTITIONING, offset, true); + if (partition.equals(ICPartitions.C_MULTI_LINE_COMMENT)) { + engine= fCommentTemplateEngine; + } else { + if (isValidContext(context)) { + engine= fCTemplateEngine; + } + } + } catch (BadLocationException x) { + return Collections.EMPTY_LIST; + } + + if (engine != null && context instanceof CContentAssistInvocationContext) { + CContentAssistInvocationContext cContext= (CContentAssistInvocationContext)context; + ITranslationUnit tUnit = cContext.getTranslationUnit(); + if (tUnit == null) { + return Collections.EMPTY_LIST; + } + engine.reset(); + engine.complete(viewer, offset, tUnit); + + List result= engine.getResults(); + + return result; + } + return Collections.EMPTY_LIST; + } + + /** + * Checks whether the given invocation context looks valid for template completion. + * + * @param context the content assist invocation context + * @return false if the given invocation context looks like a field reference + */ + private boolean isValidContext(ContentAssistInvocationContext context) { + CHeuristicScanner scanner= new CHeuristicScanner(context.getDocument()); + int start= context.getInvocationOffset(); + return !scanner.looksLikeFieldReferenceBackward(start, Math.max(0, start-100)); + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#computeContextInformation(org.eclipse.cdt.ui.text.contentassist.ContentAssistInvocationContext, org.eclipse.core.runtime.IProgressMonitor) + */ + public List computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor) { + return Collections.EMPTY_LIST; + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#getErrorMessage() + */ + public String getErrorMessage() { + return null; + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#sessionStarted() + */ + public void sessionStarted() { + } + + /* + * @see org.eclipse.cdt.ui.text.contentassist.ICompletionProposalComputer#sessionEnded() + */ + public void sessionEnded() { + if (fCommentTemplateEngine != null) { + fCommentTemplateEngine.reset(); + } + if (fCTemplateEngine != null) { + fCTemplateEngine.reset(); + } + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/template/CTemplateCompletionProcessor.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/template/CTemplateCompletionProcessor.java deleted file mode 100644 index 134b0d87546..00000000000 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/template/CTemplateCompletionProcessor.java +++ /dev/null @@ -1,182 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2007 Wind River Systems, Inc. and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * Anton Leherbauer (Wind River Systems) - initial API and implementation - *******************************************************************************/ - -package org.eclipse.cdt.internal.ui.text.template; - -import java.util.List; - -import org.eclipse.core.runtime.Assert; -import org.eclipse.jface.text.BadLocationException; -import org.eclipse.jface.text.ITextViewer; -import org.eclipse.jface.text.TextUtilities; -import org.eclipse.jface.text.contentassist.ICompletionProposal; -import org.eclipse.jface.text.contentassist.IContentAssistProcessor; -import org.eclipse.jface.text.contentassist.IContextInformation; -import org.eclipse.jface.text.contentassist.IContextInformationValidator; -import org.eclipse.jface.text.templates.TemplateContextType; -import org.eclipse.ui.IEditorPart; - -import org.eclipse.cdt.core.dom.ast.ASTCompletionNode; -import org.eclipse.cdt.core.model.IWorkingCopy; -import org.eclipse.cdt.ui.CUIPlugin; -import org.eclipse.cdt.ui.text.ICPartitions; -import org.eclipse.cdt.ui.text.contentassist.ICompletionContributor; - -import org.eclipse.cdt.internal.corext.template.c.CContextType; -import org.eclipse.cdt.internal.corext.template.c.CommentContextType; - -/** - * A completion processor for templates. - * Can be used directly as implementation of {@link IContentAssistProcessor} or - * as implementation of the extension point interface {@link ICompletionContributor}. - * - * @since 4.0 - */ -public class CTemplateCompletionProcessor implements IContentAssistProcessor, ICompletionContributor { - - private static final ICompletionProposal[] NO_PROPOSALS= new ICompletionProposal[0]; - private static final IContextInformation[] NO_CONTEXTS= new IContextInformation[0]; - - private IEditorPart fEditor; - - private final TemplateEngine fCTemplateEngine; - private final TemplateEngine fCommentTemplateEngine; - - /** - * Create a new template completion processor to be used as IContentAssistProcessor. - * - * @param editor the editor, may not be null - */ - public CTemplateCompletionProcessor(IEditorPart editor) { - this(); - Assert.isNotNull(editor); - fEditor= editor; - } - - /** - * Default constructor is required (executable extension). - */ - public CTemplateCompletionProcessor() { - TemplateContextType contextType= CUIPlugin.getDefault().getTemplateContextRegistry().getContextType(CContextType.ID); - if (contextType == null) { - contextType= new CContextType(); - CUIPlugin.getDefault().getTemplateContextRegistry().addContextType(contextType); - } - if (contextType != null) - fCTemplateEngine= new TemplateEngine(contextType); - else - fCTemplateEngine= null; - contextType= CUIPlugin.getDefault().getTemplateContextRegistry().getContextType(CommentContextType.ID); - if (contextType == null) { - contextType= new CommentContextType(); - CUIPlugin.getDefault().getTemplateContextRegistry().addContextType(contextType); - } - if (contextType != null) - fCommentTemplateEngine= new TemplateEngine(contextType); - else - fCommentTemplateEngine= null; - } - - /* - * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeCompletionProposals(org.eclipse.jface.text.ITextViewer, int) - */ - public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) { - Assert.isNotNull(fEditor); - TemplateEngine engine; - try { - String partition= TextUtilities.getContentType(viewer.getDocument(), ICPartitions.C_PARTITIONING, offset, true); - if (partition.equals(ICPartitions.C_MULTI_LINE_COMMENT)) - engine= fCommentTemplateEngine; - else - engine= fCTemplateEngine; - } catch (BadLocationException x) { - return NO_PROPOSALS; - } - - if (engine != null) { - IWorkingCopy workingCopy = CUIPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(fEditor.getEditorInput()); - if (workingCopy == null) - return NO_PROPOSALS; - - engine.reset(); - engine.complete(viewer, offset, workingCopy); - - List result= engine.getResults(); - - return (ICompletionProposal[]) result.toArray(new ICompletionProposal[result.size()]); - } - return NO_PROPOSALS; - } - - /* - * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#computeContextInformation(org.eclipse.jface.text.ITextViewer, int) - */ - public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) { - return NO_CONTEXTS; - } - - /* - * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getCompletionProposalAutoActivationCharacters() - */ - public char[] getCompletionProposalAutoActivationCharacters() { - return null; - } - - /* - * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationAutoActivationCharacters() - */ - public char[] getContextInformationAutoActivationCharacters() { - return null; - } - - /* - * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getContextInformationValidator() - */ - public IContextInformationValidator getContextInformationValidator() { - return null; - } - - /* - * @see org.eclipse.jface.text.contentassist.IContentAssistProcessor#getErrorMessage() - */ - public String getErrorMessage() { - return null; - } - - /* - * @see org.eclipse.cdt.ui.text.contentassist.ICompletionContributor#contributeCompletionProposals(org.eclipse.jface.text.ITextViewer, int, org.eclipse.cdt.core.model.IWorkingCopy, org.eclipse.cdt.core.dom.ast.ASTCompletionNode, java.lang.String, java.util.List) - */ - public void contributeCompletionProposals(ITextViewer viewer, int offset, - IWorkingCopy workingCopy, ASTCompletionNode completionNode, - String prefix, List proposals) { - // TODO We should use the completion node to determine the proper context for the templates - // For now we just keep the current behavior - TemplateEngine engine; - try { - String partition= TextUtilities.getContentType(viewer.getDocument(), ICPartitions.C_PARTITIONING, offset, true); - if (partition.equals(ICPartitions.C_MULTI_LINE_COMMENT)) - engine= fCommentTemplateEngine; - else - engine= fCTemplateEngine; - } catch (BadLocationException x) { - return; - } - - if (engine != null) { - engine.reset(); - engine.complete(viewer, offset, workingCopy); - - List result= engine.getResults(); - proposals.addAll(result); - } - } - -} 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 67e5c87c63c..14ea99b153d 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2006 IBM Corporation and others. + * Copyright (c) 2005, 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * QNX Software System + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.ui; @@ -25,8 +26,7 @@ import org.eclipse.cdt.internal.ui.text.ICColorConstants; * preference store programmatically. * * @since 2.0 - */ - + */ public class PreferenceConstants { private PreferenceConstants() { @@ -671,6 +671,27 @@ public class PreferenceConstants { */ public static final String CVIEW_GROUP_INCLUDES= "org.eclipse.cdt.ui.cview.groupincludes"; //$NON-NLS-1$ + /** + * A named preference that controls which completion proposal categories + * have been excluded from the default proposal list. + *

+ * Value is of type String, a "\0"-separated list of identifiers. + *

+ * + * @since 4.0 + */ + public static final String CODEASSIST_EXCLUDED_CATEGORIES= "content_assist_disabled_computers"; //$NON-NLS-1$ + + /** + * A named preference that controls the order of the specific code assist commands. + *

+ * Value is of type String, a "\0"-separated list of identifiers. + *

+ * + * @since 4.0 + */ + public static final String CODEASSIST_CATEGORY_ORDER= "content_assist_category_order"; //$NON-NLS-1$ + /** * A named preference that controls whether folding is enabled in the C editor. *

@@ -985,5 +1006,9 @@ public class PreferenceConstants { store.setDefault(PreferenceConstants.EDITOR_ESCAPE_STRINGS, false); store.setDefault(PreferenceConstants.ENSURE_NEWLINE_AT_EOF, false); + + // 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$ } } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/contentassist/ContentAssistInvocationContext.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/contentassist/ContentAssistInvocationContext.java new file mode 100644 index 00000000000..a12dd887f96 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/contentassist/ContentAssistInvocationContext.java @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2005, 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.ui.text.contentassist; + +import org.eclipse.core.runtime.Assert; + +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.ITextViewer; + +/** + * Describes the context of an invocation of content assist in a text viewer. The context knows the + * document, the invocation offset and can lazily compute the identifier prefix preceding the + * invocation offset. It may know the viewer. + *

+ * Subclasses may add information to their environment. For example, source code editors may provide + * specific context information such as an AST. + *

+ *

+ * Clients may instantiate and subclass. + *

+ * + * @since 4.0 + */ +public class ContentAssistInvocationContext { + + /* state */ + private final ITextViewer fViewer; + private final IDocument fDocument; + private final int fOffset; + + /* cached additional info */ + private CharSequence fPrefix; + + /** + * Equivalent to + * {@linkplain #ContentAssistInvocationContext(ITextViewer, int) ContentAssistInvocationContext(viewer, viewer.getSelectedRange().x)}. + * + * @param viewer the text viewer that content assist is invoked in + */ + public ContentAssistInvocationContext(ITextViewer viewer) { + this(viewer, viewer.getSelectedRange().x); + } + + /** + * Creates a new context for the given viewer and offset. + * + * @param viewer the text viewer that content assist is invoked in + * @param offset the offset into the viewer's document where content assist is invoked at + */ + public ContentAssistInvocationContext(ITextViewer viewer, int offset) { + Assert.isNotNull(viewer); + fViewer= viewer; + fDocument= null; + fOffset= offset; + } + + /** + * Creates a new context with no viewer or invocation offset set. + */ + protected ContentAssistInvocationContext() { + fDocument= null; + fViewer= null; + fOffset= -1; + } + + /** + * Creates a new context for the given document and offset. + * + * @param document the document that content assist is invoked in + * @param offset the offset into the document where content assist is invoked at + */ + public ContentAssistInvocationContext(IDocument document, int offset) { + Assert.isNotNull(document); + Assert.isTrue(offset >= 0); + fViewer= null; + fDocument= document; + fOffset= offset; + } + + /** + * Returns the invocation offset. + * + * @return the invocation offset + */ + public final int getInvocationOffset() { + return fOffset; + } + + /** + * Returns the viewer, null if not available. + * + * @return the viewer, possibly null + */ + public final ITextViewer getViewer() { + return fViewer; + } + + /** + * Returns the document that content assist is invoked on, or null if not known. + * + * @return the document or null + */ + public IDocument getDocument() { + if (fDocument == null) { + if (fViewer == null) + return null; + return fViewer.getDocument(); + } + return fDocument; + } + + /** + * Computes the identifier (as specified by {@link Character#isJavaIdentifierPart(char)}) that + * immediately precedes the invocation offset. + * + * @return the prefix preceding the content assist invocation offset, null if + * there is no document + * @throws BadLocationException if accessing the document fails + */ + public CharSequence computeIdentifierPrefix() throws BadLocationException { + if (fPrefix == null) { + IDocument document= getDocument(); + if (document == null) + return null; + int end= getInvocationOffset(); + int start= end; + while (--start >= 0) { + if (!Character.isJavaIdentifierPart(document.getChar(start))) + break; + } + start++; + fPrefix= document.get(start, end - start); + } + + return fPrefix; + } + + /** + * Invocation contexts are equal if they describe the same context and are of the same type. + * This implementation checks for null values and class equality. Subclasses + * should extend this method by adding checks for their context relevant fields (but not + * necessarily cached values). + *

+ * Example: + * + *

+	 * class MyContext extends ContentAssistInvocationContext {
+	 * 	private final Object fState;
+	 * 	private Object fCachedInfo;
+	 * 
+	 * 	...
+	 * 
+	 * 	public boolean equals(Object obj) {
+	 * 		if (!super.equals(obj))
+	 * 			return false;
+	 * 		MyContext other= (MyContext) obj;
+	 * 		return fState.equals(other.fState);
+	 * 	}
+	 * }
+	 * 
+ * + *

+ *

+ * Subclasses should also extend {@link Object#hashCode()}. + *

+ * + * @param obj {@inheritDoc} + * @return {@inheritDoc} + */ + public boolean equals(Object obj) { + if (obj == null) + return false; + if (!getClass().equals(obj.getClass())) + return false; + ContentAssistInvocationContext other= (ContentAssistInvocationContext) obj; + return (fViewer == null && other.fViewer == null || fViewer != null && fViewer.equals(other.fViewer)) && fOffset == other.fOffset && (fDocument == null && other.fDocument == null || fDocument != null && fDocument.equals(other.fDocument)); + } + + /* + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return 23459213 << 5 | (fViewer == null ? 0 : fViewer.hashCode() << 3) | fOffset; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/contentassist/ICompletionProposalComputer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/contentassist/ICompletionProposalComputer.java new file mode 100644 index 00000000000..a54ca6ef9ed --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/ui/text/contentassist/ICompletionProposalComputer.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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 + * Anton Leherbauer (Wind River Systems) + *******************************************************************************/ +package org.eclipse.cdt.ui.text.contentassist; + +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; + +import org.eclipse.jface.text.contentassist.ICompletionProposal; +import org.eclipse.jface.text.contentassist.IContextInformation; + +/** + * Computes completions and context information displayed by the C/C++ editor content assistant. + * Contributions to the org.eclipse.cdt.ui.completionProposalComputer extension point + * must implement this interface. + * + * @since 4.0 + */ +public interface ICompletionProposalComputer { + /** + * Informs the computer that a content assist session has started. This call will always be + * followed by a {@link #sessionEnded()} call, but not necessarily by calls to + * {@linkplain #computeCompletionProposals(ContentAssistInvocationContext, IProgressMonitor) computeCompletionProposals} + * or + * {@linkplain #computeContextInformation(ContentAssistInvocationContext, IProgressMonitor) computeContextInformation}. + */ + void sessionStarted(); + + /** + * Returns a list of completion proposals valid at the given invocation context. + * + * @param context the context of the content assist invocation + * @param monitor a progress monitor to report progress. The monitor is private to this + * invocation, i.e. there is no need for the receiver to spawn a sub monitor. + * @return a list of completion proposals (element type: {@link ICompletionProposal}) + */ + List computeCompletionProposals(ContentAssistInvocationContext context, IProgressMonitor monitor); + + /** + * Returns context information objects valid at the given invocation context. + * + * @param context the context of the content assist invocation + * @param monitor a progress monitor to report progress. The monitor is private to this + * invocation, i.e. there is no need for the receiver to spawn a sub monitor. + * @return a list of context information objects (element type: {@link IContextInformation}) + */ + List computeContextInformation(ContentAssistInvocationContext context, IProgressMonitor monitor); + + /** + * Returns the reason why this computer was unable to produce any completion proposals or + * context information. + * + * @return an error message or null if no error occurred + */ + String getErrorMessage(); + + /** + * Informs the computer that a content assist session has ended. This call will always be after + * any calls to + * {@linkplain #computeCompletionProposals(ContentAssistInvocationContext, IProgressMonitor) computeCompletionProposals} + * and + * {@linkplain #computeContextInformation(ContentAssistInvocationContext, IProgressMonitor) computeContextInformation}. + */ + void sessionEnded(); +}