diff --git a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF index d4d49f138cd..b9814044cd5 100644 --- a/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF +++ b/core/org.eclipse.cdt.core/META-INF/MANIFEST.MF @@ -53,7 +53,7 @@ Export-Package: org.eclipse.cdt.core, org.eclipse.cdt.internal.core.model.ext;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.parser;x-internal:=true, org.eclipse.cdt.internal.core.parser.problem;x-internal:=true, - org.eclipse.cdt.internal.core.parser.scanner;x-internal:=true, + org.eclipse.cdt.internal.core.parser.scanner;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.parser.scanner2;x-internal:=true, org.eclipse.cdt.internal.core.parser.token;x-friends:="org.eclipse.cdt.ui", org.eclipse.cdt.internal.core.parser.util;x-internal:=true, diff --git a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/CodeFormatter.java b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/CodeFormatter.java index 1ba10b31de7..4968f3e77be 100644 --- a/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/CodeFormatter.java +++ b/core/org.eclipse.cdt.core/src/org/eclipse/cdt/core/formatter/CodeFormatter.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,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.core.formatter; @@ -24,22 +25,22 @@ public abstract class CodeFormatter { public static final String EMPTY_STRING = ""; //$NON-NLS-1$ /** - * Unknown kind + * Unknown kind. */ public static final int K_UNKNOWN = 0x00; /** - * Kind used to format an expression + * Kind used to format an expression. */ public static final int K_EXPRESSION = 0x01; /** - * Kind used to format a set of statements + * Kind used to format a set of statements. */ public static final int K_STATEMENTS = 0x02; /** - * Kind used to format a set of class body declarations + * Kind used to format a set of class body declarations. */ public static final int K_CLASS_BODY_DECLARATIONS = 0x04; @@ -54,11 +55,11 @@ public abstract class CodeFormatter { public static final int K_TRANSLATION_UNIT = 0x08; /** - * Kind used to format a single-line comment + * Kind used to format a single-line comment. */ public static final int K_SINGLE_LINE_COMMENT = 0x10; /** - * Kind used to format a multi-line comment + * Kind used to format a multi-line comment. */ public static final int K_MULTI_LINE_COMMENT = 0x20; @@ -71,7 +72,7 @@ public abstract class CodeFormatter { * caller to get rid of preceeding whitespaces. * * @param kind Use to specify the kind of the code snippet to format. It can be any of these: - * K_EXPRESSION, K_STATEMENTS, K_CLASS_BODY_DECLARATIONS, K_COMPILATION_UNIT, K_UNKNOWN + * K_EXPRESSION, K_STATEMENTS, K_CLASS_BODY_DECLARATIONS, K_TRANSLATION_UNIT, K_UNKNOWN * @param file - file associated with this source (null if no file is associated) * @param source the document to format * @param offset the given offset to start recording the edits (inclusive). diff --git a/core/org.eclipse.cdt.ui/plugin.properties b/core/org.eclipse.cdt.ui/plugin.properties index fb51b89932c..18f0196d693 100644 --- a/core/org.eclipse.cdt.ui/plugin.properties +++ b/core/org.eclipse.cdt.ui/plugin.properties @@ -128,6 +128,11 @@ ActionDefinition.gotoMatchingBracket.description= Moves the cursor to the matchi ActionDefinition.toggleSourceHeader.name= Toggle Source/Header ActionDefinition.toggleSourceHeader.description= Toggles between corresponding source and header files +ActionDefinition.backwardMacroExpansion.name= Backward Macro Expansion +ActionDefinition.backwardMacroExpansion.description= Step backward in macro expansions +ActionDefinition.forwardMacroExpansion.name= Forward Macro Expansion +ActionDefinition.forwardMacroExpansion.description= Step forward in macro expansions + CEditor.name=C/C++ Editor CPluginPreferencePage.name=C/C++ @@ -322,6 +327,8 @@ ViewCommand.typeHierarchy.description= Show the Type Hierarchy view CEditorTextHoversName=C Editor Text Hovers sourceHover= Source sourceHoverDescription= Shows the source of the selected element. +macroExpansionHover= Macro Expansion +macroExpansionHoverDescription= Shows the expansion of the selected macro. cdocHover= Documentation cdocHoverDescription= Shows the documentation of the selected element. sequentialHover= Combined Hover @@ -437,4 +444,8 @@ TemplatePreferencePage.name=Template Default Values Template.Engine.Wizard=template entries contributor ConfigManager=Dialog for Configurations management -HelpInfo=Allows contributing the map files to the map-file-based CDT CHelpProvider. \ No newline at end of file +HelpInfo=Allows contributing the map files to the map-file-based CDT CHelpProvider. + +# Macro Expansion Hover key binding context +macroExpansionHoverScope.name= In Macro Expansion Hover +macroExpansionHoverScope.description= In Macro Expansion Hover diff --git a/core/org.eclipse.cdt.ui/plugin.xml b/core/org.eclipse.cdt.ui/plugin.xml index 22a955db0a5..c7d13f480b6 100644 --- a/core/org.eclipse.cdt.ui/plugin.xml +++ b/core/org.eclipse.cdt.ui/plugin.xml @@ -179,6 +179,12 @@ class="org.eclipse.cdt.internal.ui.text.c.hover.CSourceHover" id="org.eclipse.cdt.ui.CSourceHover"> + + @@ -1437,6 +1450,16 @@ contextId="org.eclipse.cdt.ui.cEditorScope" commandId="org.eclipse.cdt.ui.edit.text.c.toggle.source.header" schemeId="org.eclipse.ui.defaultAcceleratorConfiguration"/> + + @@ -1580,6 +1603,16 @@ categoryId="org.eclipse.cdt.ui.category.source" id="org.eclipse.cdt.ui.edit.text.c.toggle.source.header"> + + 0) { + edit.moveTree(-prefix.length()); + } + return edit; } /** diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/IndentUtil.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/IndentUtil.java index c7f62aaa5a5..d2093314ae4 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/IndentUtil.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/editor/IndentUtil.java @@ -398,7 +398,7 @@ public final class IndentUtil { int minIndent= Integer.MAX_VALUE; for (int line= 0; line < numberOfLines; line++) { int length= computeVisualLength(getCurrentIndent(document, line + first, indentInsideLineComments), tabSize); - if (length < minIndent) { + if (length < minIndent && document.getLineLength(line + first) > 0) { minIndent= length; minLine= line; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/AbstractInformationControl.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/AbstractInformationControl.java index 79c5e072d81..0875e6fccf0 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/AbstractInformationControl.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/AbstractInformationControl.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 @@ -11,8 +11,6 @@ package org.eclipse.cdt.internal.ui.text; import org.eclipse.core.runtime.CoreException; -import org.eclipse.jface.action.Action; -import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.dialogs.IDialogSettings; @@ -48,7 +46,6 @@ import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Item; -import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.Tree; @@ -112,8 +109,6 @@ public abstract class AbstractInformationControl extends PopupDialog implements private TreeViewer fTreeViewer; /** The current string matcher */ protected StringMatcher fStringMatcher; -// private ICommand fInvokingCommand; -// private KeySequence[] fInvokingCommandKeySequences; /** * Fields that support the dialog menu @@ -123,11 +118,6 @@ public abstract class AbstractInformationControl extends PopupDialog implements private CustomFiltersActionGroup fCustomFiltersActionGroup; -// private IKeyBindingService fKeyBindingService; -// private String[] fKeyBindingScopes; - private IAction fShowViewMenuAction; -// private HandlerSubmission fShowViewMenuHandlerSubmission; - /** * Field for tree style since it must be remembered by the instance. */ @@ -145,15 +135,6 @@ public abstract class AbstractInformationControl extends PopupDialog implements */ public AbstractInformationControl(Shell parent, int shellStyle, int treeStyle, String invokingCommandId, boolean showStatusField) { super(parent, shellStyle, true, true, true, true, null, null); - if (invokingCommandId != null) { -// ICommandManager commandManager= PlatformUI.getWorkbench().getCommandSupport().getCommandManager(); -// fInvokingCommand= commandManager.getCommand(invokingCommandId); -// if (fInvokingCommand != null && !fInvokingCommand.isDefined()) -// fInvokingCommand= null; -// else -// // Pre-fetch key sequence - do not change because scope will change later. -// getInvokingCommandKeySequences(); - } fTreeStyle= treeStyle; // Title and status text must be set to get the title label created, so force empty values here. if (hasHeader()) @@ -329,24 +310,6 @@ public abstract class AbstractInformationControl extends PopupDialog implements return fFilterText; } - protected void createHorizontalSeparator(Composite parent) { - Label separator= new Label(parent, SWT.SEPARATOR | SWT.HORIZONTAL | SWT.LINE_DOT); - separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - } - - protected void updateStatusFieldText() { - setInfoText(getStatusFieldText()); - } - - /** - * Handles click in status field. - *

- * Default does nothing. - *

- */ - protected void handleStatusFieldClicked() { - } - protected String getStatusFieldText() { return ""; //$NON-NLS-1$ } @@ -514,13 +477,10 @@ public abstract class AbstractInformationControl extends PopupDialog implements */ public void setVisible(boolean visible) { if (visible) { - addHandlerAndKeyBindingSupport(); open(); } else { - removeHandlerAndKeyBindingSupport(); saveDialogBounds(getShell()); getShell().setVisible(false); - removeHandlerAndKeyBindingSupport(); } } @@ -539,42 +499,8 @@ public abstract class AbstractInformationControl extends PopupDialog implements *

*/ public void widgetDisposed(DisposeEvent event) { - removeHandlerAndKeyBindingSupport(); fTreeViewer= null; fFilterText= null; -// fKeyBindingService= null; - } - - /** - * Adds handler and key binding support. - */ - protected void addHandlerAndKeyBindingSupport() { -// // Remember current scope and then set window context. -// if (fKeyBindingScopes == null && fKeyBindingService != null) { -// fKeyBindingScopes= fKeyBindingService.getScopes(); -// fKeyBindingService.setScopes(new String[] { IWorkbenchContextSupport.CONTEXT_ID_WINDOW }); -// } -// -// // Register action with command support -// if (fShowViewMenuHandlerSubmission == null) { -// fShowViewMenuHandlerSubmission= new HandlerSubmission(null, getShell(), null, fShowViewMenuAction.getActionDefinitionId(), new ActionHandler(fShowViewMenuAction), Priority.MEDIUM); -// PlatformUI.getWorkbench().getCommandSupport().addHandlerSubmission(fShowViewMenuHandlerSubmission); -// } - } - - /** - * Removes handler and key binding support. - */ - protected void removeHandlerAndKeyBindingSupport() { -// // Remove handler submission -// if (fShowViewMenuHandlerSubmission != null) -// PlatformUI.getWorkbench().getCommandSupport().removeHandlerSubmission(fShowViewMenuHandlerSubmission); -// -// // Restore editor's key binding scope -// if (fKeyBindingService != null && fKeyBindingScopes != null) { -// fKeyBindingService.setScopes(fKeyBindingScopes); -// fKeyBindingScopes= null; -// } } /** @@ -683,26 +609,6 @@ public abstract class AbstractInformationControl extends PopupDialog implements getShell().removeFocusListener(listener); } -// final protected ICommand getInvokingCommand() { -// return fInvokingCommand; -// } - -// final protected KeySequence[] getInvokingCommandKeySequences() { -// if (fInvokingCommandKeySequences == null) { -// if (getInvokingCommand() != null) { -// List list= getInvokingCommand().getKeySequenceBindings(); -// if (!list.isEmpty()) { -// fInvokingCommandKeySequences= new KeySequence[list.size()]; -// for (int i= 0; i < fInvokingCommandKeySequences.length; i++) { -// fInvokingCommandKeySequences[i]= ((IKeySequenceBinding) list.get(i)).getKeySequence(); -// } -// return fInvokingCommandKeySequences; -// } -// } -// } -// return fInvokingCommandKeySequences; -// } - /* * @see org.eclipse.jface.dialogs.PopupDialog#getDialogSettings() */ @@ -729,26 +635,6 @@ public abstract class AbstractInformationControl extends PopupDialog implements fFilterText= createFilterText(parent); } - // Create a key binding for showing the dialog menu - // Key binding service -// IWorkbenchPart part= CUIPlugin.getActivePage().getActivePart(); -// IWorkbenchPartSite site= part.getSite(); -// fKeyBindingService= site.getKeyBindingService(); - - // Create show view menu action - fShowViewMenuAction= new Action("showViewMenu") { //$NON-NLS-1$ - /* - * @see org.eclipse.jface.action.Action#run() - */ - public void run() { - showDialogMenu(); - } - }; - fShowViewMenuAction.setEnabled(true); - fShowViewMenuAction.setActionDefinitionId("org.eclipse.ui.window.showViewMenu"); //$NON-NLS-1$ - - addHandlerAndKeyBindingSupport(); - return fViewMenuButtonComposite; } diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/AbstractSourceViewerInformationControl.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/AbstractSourceViewerInformationControl.java new file mode 100644 index 00000000000..0ea94761a33 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/AbstractSourceViewerInformationControl.java @@ -0,0 +1,460 @@ +/******************************************************************************* + * 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; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.jface.action.IMenuManager; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.dialogs.PopupDialog; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlExtension; +import org.eclipse.jface.text.IInformationControlExtension2; +import org.eclipse.jface.text.source.ISourceViewer; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.StyledText; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.KeyListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.PreferenceConstants; +import org.eclipse.cdt.ui.text.ICPartitions; + +import org.eclipse.cdt.internal.ui.editor.CSourceViewer; + +/** + * Abstract class for "quick" views in light-weight controls. + * + * @since 4.0 + */ +public abstract class AbstractSourceViewerInformationControl extends PopupDialog implements IInformationControl, IInformationControlExtension, IInformationControlExtension2, DisposeListener { + + private int fTextStyle; + + private ISourceViewer fSourceViewer; + + private Color fBackgroundColor; + + private boolean fIsSystemBackgroundColor; + + private int fMaxWidth; + + private int fMaxHeight; + + private List fColorExclusionControls= new ArrayList(); + + /** + * Creates a source viewer information control with the given shell as parent. The given + * styles are applied to the shell and the source viewer. + * + * @param parent the parent shell + * @param shellStyle the additional styles for the shell + * @param textStyle the additional styles for the source viewer + * @param takeFocus flag indicating whether to take the focus + * @param showViewMenu flag indicating whether to show the "view" menu + * @param persistBounds flag indicating whether control size and location should be persisted + */ + public AbstractSourceViewerInformationControl(Shell parent, int shellStyle, int textStyle, boolean takeFocus, boolean showViewMenu, boolean persistBounds) { + super(parent, shellStyle, takeFocus, persistBounds, showViewMenu, false, null, null); + fTextStyle= textStyle; + // Title and status text must be set to get the title label created, so force empty values here. + if (hasHeader()) + setTitleText(""); //$NON-NLS-1$ + setInfoText(""); // //$NON-NLS-1$ + + // Create all controls + create(); + } + + private void initializeColors() { + RGB bgRGB= getHoverBackgroundColorRGB(); + if (bgRGB != null) { + fBackgroundColor= new Color(getShell().getDisplay(), bgRGB); + fIsSystemBackgroundColor= false; + } else { + fBackgroundColor= getShell().getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND); + fIsSystemBackgroundColor= true; + } + } + + private RGB getHoverBackgroundColorRGB() { + IPreferenceStore store= CUIPlugin.getDefault().getPreferenceStore(); + return store.getBoolean(PreferenceConstants.EDITOR_SOURCE_HOVER_BACKGROUND_COLOR_SYSTEM_DEFAULT) + ? null + : PreferenceConverter.getColor(store, PreferenceConstants.EDITOR_SOURCE_HOVER_BACKGROUND_COLOR); + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#createContents(org.eclipse.swt.widgets.Composite) + */ + protected Control createContents(Composite parent) { + Control contents= super.createContents(parent); + for (Iterator it= fColorExclusionControls.iterator(); it.hasNext(); ) { + Control ctrl = (Control) it.next(); + ctrl.setBackground(fBackgroundColor); + } + return contents; + } + + /** + * Create the main content for this information control. + * + * @param parent The parent composite + * @return The control representing the main content. + * + */ + protected Control createDialogArea(Composite parent) { + fSourceViewer= createSourceViewer(parent, fTextStyle); + + final StyledText text= fSourceViewer.getTextWidget(); + text.addKeyListener(new KeyListener() { + public void keyPressed(KeyEvent e) { + if (e.character == 0x1B) // ESC + dispose(); + } + public void keyReleased(KeyEvent e) { + // do nothing + } + }); + + addDisposeListener(this); + return ((Viewer)fSourceViewer).getControl(); + } + + protected ISourceViewer createSourceViewer(Composite parent, int style) { + IPreferenceStore store= CUIPlugin.getDefault().getCombinedPreferenceStore(); + SourceViewer sourceViewer= new CSourceViewer(parent, null, null, false, style, store); + CTextTools tools= CUIPlugin.getDefault().getTextTools(); + sourceViewer.configure(new SimpleCSourceViewerConfiguration(tools.getColorManager(), store, null, ICPartitions.C_PARTITIONING, false)); + sourceViewer.setEditable(false); + + StyledText styledText= sourceViewer.getTextWidget(); + GridData gd= new GridData(GridData.BEGINNING | GridData.FILL_BOTH); + styledText.setLayoutData(gd); + initializeColors(); + styledText.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND)); + styledText.setBackground(fBackgroundColor); + fColorExclusionControls.add(styledText); + + Font font= JFaceResources.getFont(PreferenceConstants.EDITOR_TEXT_FONT); + styledText.setFont(font); + + return sourceViewer; + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#createInfoTextArea(org.eclipse.swt.widgets.Composite) + */ + protected Control createInfoTextArea(Composite parent) { + Control infoText= super.createInfoTextArea(parent); + fColorExclusionControls.add(infoText); + return infoText; + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#createTitleControl(org.eclipse.swt.widgets.Composite) + */ + protected Control createTitleControl(Composite parent) { + Control titleText= super.createTitleControl(parent); + fColorExclusionControls.add(titleText); + return titleText; + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#getBackgroundColorExclusions() + */ + protected List getBackgroundColorExclusions() { + List exclusions= super.getBackgroundColorExclusions(); + exclusions.addAll(fColorExclusionControls); + return exclusions; + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#getForegroundColorExclusions() + */ + protected List getForegroundColorExclusions() { + List exclusions= super.getForegroundColorExclusions(); + exclusions.addAll(fColorExclusionControls); + return exclusions; + } + + /** + * Returns the name of the dialog settings section. + *

+ * The default is to return null. + *

+ * @return the name of the dialog settings section or null if + * nothing should be persisted + */ + protected String getId() { + return null; + } + + /** + * Returns the source viewer. + * + * @return the source viewer. + */ + protected final ISourceViewer getSourceViewer() { + return fSourceViewer; + } + + /** + * Returns true if the control has a header, false otherwise. + *

+ * The default is to return false. + *

+ * + * @return true if the control has a header + */ + protected boolean hasHeader() { + // default is to have no header + return false; + } + + /** + * {@inheritDoc} + */ + public void setInformation(String content) { + if (content == null) { + fSourceViewer.setDocument(null); + return; + } + + IDocument doc= new Document(content); + CUIPlugin.getDefault().getTextTools().setupCDocument(doc); + fSourceViewer.setDocument(doc); + } + + /** + * {@inheritDoc} + */ + public void setInput(Object input) { + if (input instanceof String) + setInformation((String)input); + else + setInformation(null); + } + + /** + * Fills the view menu. + * Clients can extend or override. + * + * @param viewMenu the menu manager that manages the menu + */ + protected void fillViewMenu(IMenuManager viewMenu) { + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#fillDialogMenu(IMenuManager) + */ + protected void fillDialogMenu(IMenuManager dialogMenu) { + super.fillDialogMenu(dialogMenu); + fillViewMenu(dialogMenu); + } + + /** + * {@inheritDoc} + */ + public void setVisible(boolean visible) { + if (visible) { + open(); + } else { + saveDialogBounds(getShell()); + getShell().setVisible(false); + } + } + + /** + * {@inheritDoc} + */ + public final void dispose() { + if (!fIsSystemBackgroundColor) { + fBackgroundColor.dispose(); + } + close(); + } + + protected Point getInitialLocation(Point initialSize) { + if (!getPersistBounds()) { + Point size = new Point(400, 400); + Rectangle parentBounds = getParentShell().getBounds(); + int x = parentBounds.x + parentBounds.width / 2 - size.x / 2; + int y = parentBounds.y + parentBounds.height / 2 - size.y / 2; + return new Point(x, y); + } + return super.getInitialLocation(initialSize); + } + + /** + * {@inheritDoc} + * @param event can be null + *

+ * Subclasses may extend. + *

+ */ + public void widgetDisposed(DisposeEvent event) { + fSourceViewer= null; + } + + /** + * {@inheritDoc} + */ + public boolean hasContents() { + return fSourceViewer != null && fSourceViewer.getDocument() != null; + } + + /** + * {@inheritDoc} + */ + public void setSizeConstraints(int maxWidth, int maxHeight) { + fMaxWidth= maxWidth; + fMaxHeight= maxHeight; + } + + /** + * {@inheritDoc} + */ + public Point computeSizeHint() { + // compute the preferred size + int x= SWT.DEFAULT; + int y= SWT.DEFAULT; + Point size= getShell().computeSize(x, y); + if (size.x > fMaxWidth) + x= fMaxWidth; + if (size.y > fMaxHeight) + y= fMaxHeight; + + // recompute using the constraints if the preferred size is larger than the constraints + if (x != SWT.DEFAULT || y != SWT.DEFAULT) + size= getShell().computeSize(x, y, false); + + return size; +// return getShell().getSize(); + } + + /** + * {@inheritDoc} + */ + public void setLocation(Point location) { + if (!getPersistBounds() || getDialogSettings() == null) + getShell().setLocation(location); + } + + /** + * {@inheritDoc} + */ + public void setSize(int width, int height) { + if (!getPersistBounds() || getDialogSettings() == null) { + getShell().setSize(width, height); + } + } + + /** + * {@inheritDoc} + */ + public void addDisposeListener(DisposeListener listener) { + getShell().addDisposeListener(listener); + } + + /** + * {@inheritDoc} + */ + public void removeDisposeListener(DisposeListener listener) { + getShell().removeDisposeListener(listener); + } + + /** + * {@inheritDoc} + */ + public void setForegroundColor(Color foreground) { + applyForegroundColor(foreground, getContents()); + } + + /** + * {@inheritDoc} + */ + public void setBackgroundColor(Color background) { + applyBackgroundColor(background, getContents()); + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#getFocusControl() + */ + protected Control getFocusControl() { + return fSourceViewer.getTextWidget(); + } + + /** + * {@inheritDoc} + */ + public boolean isFocusControl() { + return fSourceViewer.getTextWidget().isFocusControl(); + } + + /** + * {@inheritDoc} + */ + public void setFocus() { + getShell().forceFocus(); + fSourceViewer.getTextWidget().setFocus(); + } + + /** + * {@inheritDoc} + */ + public void addFocusListener(FocusListener listener) { + getShell().addFocusListener(listener); + } + + /** + * {@inheritDoc} + */ + public void removeFocusListener(FocusListener listener) { + getShell().removeFocusListener(listener); + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#getDialogSettings() + */ + protected IDialogSettings getDialogSettings() { + String sectionName= getId(); + if (sectionName == null) { + return null; + } + IDialogSettings settings= CUIPlugin.getDefault().getDialogSettings().getSection(sectionName); + if (settings == null) + settings= CUIPlugin.getDefault().getDialogSettings().addNewSection(sectionName); + + return settings; + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CHoverMessages.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CHoverMessages.java index c4053f5dfe9..c62a08c685a 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CHoverMessages.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CHoverMessages.java @@ -1,91 +1,34 @@ /******************************************************************************* - * Copyright (c) 2002, 2006 QNX Software Systems and others. + * Copyright (c) 2002, 2007 QNX Software Systems 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: - * QNX Software Systems - Initial API and implementation + * QNX Software Systems - Initial API and implementation + * Anton Leherbauer (Wind River Systems) *******************************************************************************/ - package org.eclipse.cdt.internal.ui.text.c.hover; -import java.text.MessageFormat; -import java.util.MissingResourceException; -import java.util.ResourceBundle; +import org.eclipse.osgi.util.NLS; +public final class CHoverMessages extends NLS { -/** - * CHoverMessages - */ -public class CHoverMessages { - - private static final String RESOURCE_BUNDLE= CHoverMessages.class.getName(); - - private static ResourceBundle fgResourceBundle= ResourceBundle.getBundle(RESOURCE_BUNDLE); + private static final String BUNDLE_NAME = "org.eclipse.cdt.internal.ui.text.c.hover.CHoverMessages";//$NON-NLS-1$ private CHoverMessages() { + // Do not instantiate } - public static String getString(String key) { - try { - return fgResourceBundle.getString(key); - } catch (MissingResourceException e) { - return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$ - } - } - /** - * Gets a string from the resource bundle and formats it with the argument - * - * @param key the string used to get the bundle value, must not be null - * @since 3.0 - */ - public static String getFormattedString(String key, Object arg) { - String format= null; - try { - format= fgResourceBundle.getString(key); - } catch (MissingResourceException e) { - return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$ - } - if (arg == null) - arg= ""; //$NON-NLS-1$ - return MessageFormat.format(format, new Object[] { arg }); - } - /** - * Gets a string from the resource bundle and formats it with the arguments - * - * @param key the string used to get the bundle value, must not be null - * @since 3.0 - */ - public static String getFormattedString(String key, Object arg1, Object arg2) { - String format= null; - try { - format= fgResourceBundle.getString(key); - } catch (MissingResourceException e) { - return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$ - } - if (arg1 == null) - arg1= ""; //$NON-NLS-1$ - if (arg2 == null) - arg2= ""; //$NON-NLS-1$ - return MessageFormat.format(format, new Object[] { arg1, arg2 }); - } - - /** - * Gets a string from the resource bundle and formats it with the argument - * - * @param key the string used to get the bundle value, must not be null - * @since 3.0 - */ - public static String getFormattedString(String key, boolean arg) { - String format= null; - try { - format= fgResourceBundle.getString(key); - } catch (MissingResourceException e) { - return "!" + key + "!";//$NON-NLS-2$ //$NON-NLS-1$ - } - return MessageFormat.format(format, new Object[] { new Boolean(arg) }); - } + public static String CMacroExpansionControl_statusText; -} + public static String CMacroExpansionControl_title_expansion; + public static String CMacroExpansionControl_title_fullyExpanded; + public static String CMacroExpansionControl_title_macroExpansion; + public static String CMacroExpansionControl_title_original; + + static { + NLS.initializeMessages(BUNDLE_NAME, CHoverMessages.class); + } +} \ No newline at end of file diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CHoverMessages.properties b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CHoverMessages.properties index 673db3a7006..9193bc9a323 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CHoverMessages.properties +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CHoverMessages.properties @@ -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,12 +7,11 @@ # # Contributors: # IBM Corporation - initial API and implementation +# Anton Leherbauer (Wind River Systems) ############################################################################### -TypeHover.more_to_come=\ ... - -CTextHover.createTextHover= Could not create c text hover - -CTextHover.makeStickyHint= Press ''{0}'' for focus. - -NoBreakpointAnnotation.addBreakpoint= Add a breakpoint +CMacroExpansionControl_statusText=Press {0} or {1} to step through macro expansion +CMacroExpansionControl_title_expansion=Expansion \#{0) of {1} +CMacroExpansionControl_title_fullyExpanded=Fully Expanded +CMacroExpansionControl_title_macroExpansion=Macro Expansion +CMacroExpansionControl_title_original=Original diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CMacroExpansionControl.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CMacroExpansionControl.java new file mode 100644 index 00000000000..4dd5d06bb6d --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CMacroExpansionControl.java @@ -0,0 +1,224 @@ +/******************************************************************************* + * 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.c.hover; + +import java.util.ArrayList; +import java.util.Collection; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.contexts.IContextActivation; +import org.eclipse.ui.contexts.IContextService; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.keys.IBindingService; + +import org.eclipse.cdt.ui.CUIPlugin; + +import org.eclipse.cdt.internal.ui.text.AbstractSourceViewerInformationControl; + +/** + * Information control for macro expansion exploration. + * + * @since 5.0 + */ +public class CMacroExpansionControl extends AbstractSourceViewerInformationControl { + + private static final String COMMAND_ID_EXPANSION_BACK= "org.eclipse.cdt.ui.hover.backwardMacroExpansion"; //$NON-NLS-1$ + private static final String COMMAND_ID_EXPANSION_FORWARD= "org.eclipse.cdt.ui.hover.forwardMacroExpansion"; //$NON-NLS-1$ + private static final String CONTEXT_ID_MACRO_EXPANSION_HOVER= "org.eclipse.cdt.ui.macroExpansionHoverScope"; //$NON-NLS-1$ + + private IHandlerService fHandlerService; + private Collection fHandlerActivations; + private IContextService fContextService; + private IContextActivation fContextActivation; + private int fIndex; + private CMacroExpansionInput fInput; + + /** + * Creates a new control for use as a "quick view" where the control immediately takes the focus. + * + * @param parent parent shell + * @param shellStyle shell style bits + * @param style text viewer style bits + * @param input the input object, may be null + */ + public CMacroExpansionControl(Shell parent, int shellStyle, int style, CMacroExpansionInput input) { + super(parent, shellStyle, style, true, false, true); + setMacroExpansionInput(input); + } + + /** + * Creates a new control for use as a hover which does not take the focus. + * + * @param parent parent shell + * @param statusFieldText text to be displayed in the status field, may be null + */ + public CMacroExpansionControl(Shell parent, String statusFieldText) { + super(parent, SWT.NO_TRIM | SWT.TOOL, SWT.NONE, false, false, false); + if (statusFieldText != null) { + setInfoText(statusFieldText); + } + setTitleText(CHoverMessages.CMacroExpansionControl_title_macroExpansion); + } + + /* + * @see org.eclipse.cdt.internal.ui.text.AbstractSourceViewerInformationControl#hasHeader() + */ + protected boolean hasHeader() { + return true; + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#open() + */ + public int open() { + int result= super.open(); + + if (fInput != null) { + IHandler fBackwardHandler= new AbstractHandler() { + public Object execute(ExecutionEvent event) throws ExecutionException { + backward(); + return null; + } + }; + IHandler fForwardHandler= new AbstractHandler() { + public Object execute(ExecutionEvent event) throws ExecutionException { + forward(); + return null; + } + }; + + IWorkbench workbench= PlatformUI.getWorkbench(); + fHandlerService= (IHandlerService) workbench.getService(IHandlerService.class); + fContextService= (IContextService) workbench.getService(IContextService.class); + fContextActivation= fContextService.activateContext(CONTEXT_ID_MACRO_EXPANSION_HOVER); + fHandlerActivations= new ArrayList(); + fHandlerActivations.add(fHandlerService.activateHandler(COMMAND_ID_EXPANSION_BACK, fBackwardHandler)); + fHandlerActivations.add(fHandlerService.activateHandler(COMMAND_ID_EXPANSION_FORWARD, fForwardHandler)); + + String infoText= getInfoText(); + if (infoText != null) { + setInfoText(infoText); + } + } + + return result; + } + + protected void forward() { + ++fIndex; + if (fIndex >= fInput.fExpansions.length) { + fIndex= 0; + } + showExpansion(fIndex); + } + + protected void backward() { + --fIndex; + if (fIndex < 0) { + fIndex += fInput.fExpansions.length; + } + showExpansion(fIndex); + } + + /** + * Returns the text to be shown in the popups's information area. + * May return null. + * + * @return The text to be shown in the popup's information area or null + */ + protected String getInfoText() { + IWorkbench workbench= PlatformUI.getWorkbench(); + IBindingService bindingService= (IBindingService) workbench.getService(IBindingService.class); + String formattedBindingBack= bindingService.getBestActiveBindingFormattedFor(COMMAND_ID_EXPANSION_BACK); + String formattedBindingForward= bindingService.getBestActiveBindingFormattedFor(COMMAND_ID_EXPANSION_FORWARD); + + String infoText= null; + if (formattedBindingBack != null && formattedBindingForward != null) { + infoText= NLS.bind(CHoverMessages.CMacroExpansionControl_statusText, formattedBindingBack, formattedBindingForward); + } + return infoText; + } + + /* + * @see org.eclipse.jface.dialogs.PopupDialog#close() + */ + public boolean close() { + if (fHandlerService != null) { + fHandlerService.deactivateHandlers(fHandlerActivations); + fHandlerActivations.clear(); + fHandlerService= null; + } + if (fContextActivation != null) { + fContextService.deactivateContext(fContextActivation); + } + return super.close(); + } + + /* + * @see org.eclipse.cdt.internal.ui.text.AbstractSourceViewerInformationControl#getId() + */ + protected String getId() { + return "org.eclipse.cdt.ui.text.hover.CMacroExpansion"; //$NON-NLS-1$ + } + + /* + * @see org.eclipse.cdt.internal.ui.text.AbstractSourceViewerInformationControl#setInput(java.lang.Object) + */ + public void setInput(Object input) { + if (input instanceof String && fInput == null) { + super.setInput(input); + return; + } + if (input instanceof CMacroExpansionInput) { + setMacroExpansionInput((CMacroExpansionInput) input); + } + } + + /** + * Set the input for this information control. + * @param input + */ + private void setMacroExpansionInput(CMacroExpansionInput input) { + fInput= input; + fIndex= input.fExpansions.length - 1; + showExpansion(fIndex); + } + + private void showExpansion(int index) { + if (fIndex == 0) { + setTitleText(CHoverMessages.CMacroExpansionControl_title_original); + } else if (fIndex < fInput.fExpansions.length - 1) { + setTitleText(NLS.bind(CHoverMessages.CMacroExpansionControl_title_expansion, + String.valueOf(fIndex), String.valueOf(fInput.fExpansions.length - 1))); + } else { + setTitleText(CHoverMessages.CMacroExpansionControl_title_fullyExpanded); + } + IDocument document= getSourceViewer().getDocument(); + if (document == null) { + document= new Document(fInput.fExpansions[index]); + CUIPlugin.getDefault().getTextTools().setupCDocument(document); + getSourceViewer().setDocument(document); + } else { + document.set(fInput.fExpansions[index]); + } + } +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CMacroExpansionHover.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CMacroExpansionHover.java new file mode 100644 index 00000000000..5e46a99574d --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CMacroExpansionHover.java @@ -0,0 +1,675 @@ +/******************************************************************************* + * 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.c.hover; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.IDocument; +import org.eclipse.jface.text.IInformationControl; +import org.eclipse.jface.text.IInformationControlCreator; +import org.eclipse.jface.text.IRegion; +import org.eclipse.jface.text.ITextViewer; +import org.eclipse.jface.text.Position; +import org.eclipse.jface.text.Region; +import org.eclipse.jface.text.TextUtilities; +import org.eclipse.jface.text.information.IInformationProviderExtension2; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.text.edits.MalformedTreeException; +import org.eclipse.text.edits.TextEdit; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.texteditor.ITextEditor; + +import org.eclipse.cdt.core.dom.ast.ASTVisitor; +import org.eclipse.cdt.core.dom.ast.IASTDeclaration; +import org.eclipse.cdt.core.dom.ast.IASTExpression; +import org.eclipse.cdt.core.dom.ast.IASTFileLocation; +import org.eclipse.cdt.core.dom.ast.IASTFunctionStyleMacroParameter; +import org.eclipse.cdt.core.dom.ast.IASTMacroExpansion; +import org.eclipse.cdt.core.dom.ast.IASTName; +import org.eclipse.cdt.core.dom.ast.IASTNode; +import org.eclipse.cdt.core.dom.ast.IASTNodeLocation; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorFunctionStyleMacroDefinition; +import org.eclipse.cdt.core.dom.ast.IASTPreprocessorMacroDefinition; +import org.eclipse.cdt.core.dom.ast.IASTStatement; +import org.eclipse.cdt.core.dom.ast.IASTTranslationUnit; +import org.eclipse.cdt.core.dom.ast.IBinding; +import org.eclipse.cdt.core.dom.ast.IMacroBinding; +import org.eclipse.cdt.core.dom.parser.IScannerExtensionConfiguration; +import org.eclipse.cdt.core.dom.parser.c.GCCScannerExtensionConfiguration; +import org.eclipse.cdt.core.dom.parser.cpp.GPPScannerExtensionConfiguration; +import org.eclipse.cdt.core.formatter.CodeFormatter; +import org.eclipse.cdt.core.model.ICProject; +import org.eclipse.cdt.core.model.ILanguage; +import org.eclipse.cdt.core.model.ITranslationUnit; +import org.eclipse.cdt.core.model.IWorkingCopy; +import org.eclipse.cdt.core.parser.CodeReader; +import org.eclipse.cdt.core.parser.EndOfFileException; +import org.eclipse.cdt.core.parser.IGCCToken; +import org.eclipse.cdt.core.parser.IScannerInfo; +import org.eclipse.cdt.core.parser.IToken; +import org.eclipse.cdt.core.parser.ParserLanguage; +import org.eclipse.cdt.core.parser.ParserUtil; +import org.eclipse.cdt.core.parser.ScannerInfo; +import org.eclipse.cdt.ui.CUIPlugin; +import org.eclipse.cdt.ui.IWorkingCopyManager; + +import org.eclipse.cdt.internal.core.dom.NullCodeReaderFactory; +import org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable; +import org.eclipse.cdt.internal.core.parser.scanner.CPreprocessor; +import org.eclipse.cdt.internal.corext.util.CodeFormatterUtil; + +import org.eclipse.cdt.internal.ui.editor.ASTProvider; + +/** + * A hover to explore macro expansion. + * + * @since 5.0 + */ +public class CMacroExpansionHover extends AbstractCEditorTextHover implements IInformationProviderExtension2 { + + /** + * AST visitor to find a node (declaration, statement or expression) enclosing a given source range. + */ + private static class FindEnclosingNodeAction extends ASTVisitor { + { + shouldVisitTranslationUnit= true; + shouldVisitDeclarations = true; + shouldVisitStatements = true; + shouldVisitExpressions = true; + } + + private final int fOffset; + private final int fEndOffset; + private final String fFilePath; + private IASTNode fBestMatch= null; + private int fBestOffset= -1; + private int fBestEndOffset= Integer.MAX_VALUE; + + public FindEnclosingNodeAction(String filePath, int offset, int length) { + fFilePath= filePath; + fOffset= offset; + fEndOffset= offset + length; + } + + private int processNode(IASTNode node) { + IASTFileLocation location= node.getFileLocation(); + if (location != null && fFilePath.equals(location.getFileName())) { + final int startOffset = location.getNodeOffset(); + if (startOffset <= fOffset) { + int endOffset= startOffset + location.getNodeLength(); + if (endOffset >= fEndOffset) { + if (startOffset > fBestOffset || endOffset < fBestEndOffset) { + fBestMatch= node; + fBestOffset= startOffset; + fBestEndOffset= endOffset; + boolean isPerfectMatch= startOffset == fOffset || endOffset == fEndOffset; + if (isPerfectMatch) { + return PROCESS_ABORT; + } + } + } else { + return PROCESS_SKIP; + } + } else { + return PROCESS_ABORT; + } + } + return PROCESS_CONTINUE; + } + + public int visit(IASTTranslationUnit tu) { + return processNode(tu); + } + + public int visit(IASTDeclaration declaration) { + return processNode(declaration); + } + + public int visit(IASTExpression expression) { + return processNode(expression); + } + + public int visit(IASTStatement statement) { + return processNode(statement); + } + + public IASTNode getNode() { + return fBestMatch; + } + } + + /** + * AST visitor to collect nodes (declaration, statement or expression) enclosed in a given source range. + */ + private static class CollectEnclosedNodesAction extends ASTVisitor { + { + shouldVisitDeclarations = true; + shouldVisitStatements = true; + shouldVisitExpressions = true; + } + + private final int fOffset; + private final int fEndOffset; + private final String fFilePath; + private List fMatches= new ArrayList(); + + public CollectEnclosedNodesAction(String filePath, int offset, int length) { + fFilePath= filePath; + fOffset= offset; + fEndOffset= offset + length; + } + + private int processNode(IASTNode node) { + IASTFileLocation location= node.getFileLocation(); + if (location != null && fFilePath.equals(location.getFileName())) { + final int startOffset = location.getNodeOffset(); + if (startOffset >= fOffset) { + int endOffset= startOffset + location.getNodeLength(); + if (endOffset <= fEndOffset) { + fMatches.add(node); + } else { + return PROCESS_SKIP; + } + } else if (startOffset >= fEndOffset) { + return PROCESS_ABORT; + } + } + return PROCESS_CONTINUE; + } + + public int visit(IASTTranslationUnit tu) { + return processNode(tu); + } + + public int visit(IASTDeclaration declaration) { + return processNode(declaration); + } + + public int visit(IASTExpression expression) { + return processNode(expression); + } + + public int visit(IASTStatement statement) { + return processNode(statement); + } + + public Collection getNodes() { + return fMatches; + } + } + + /** + * Computes the source location for a given identifier. + */ + private static class ComputeExpansionRegionRunnable implements ASTRunnable { + private final Position fTextRegion; + private IASTNode fEnclosingNode; + private Map fMacroDict; + private List fExpansionNodes= new ArrayList(); + + /** + * @param tUnit + * @param textRegion + */ + private ComputeExpansionRegionRunnable(ITranslationUnit tUnit, IRegion textRegion) { + fTextRegion= new Position(textRegion.getOffset(), textRegion.getLength()); + } + + /* + * @see org.eclipse.cdt.internal.core.model.ASTCache.ASTRunnable#runOnAST(org.eclipse.cdt.core.dom.ast.IASTTranslationUnit) + */ + public IStatus runOnAST(ILanguage lang, IASTTranslationUnit ast) { + if (ast != null) { + // try object style macro expansion first + IASTNode node= ast.selectNodeForLocation(ast.getFilePath(), fTextRegion.getOffset(), fTextRegion.getLength()); + if (node instanceof IASTName) { + IASTName macroName= (IASTName) node; + IBinding binding= macroName.getBinding(); + if (binding instanceof IMacroBinding) { + IMacroBinding macroBinding= (IMacroBinding) binding; + if (!macroBinding.isFunctionStyle()) { + addExpansionNode(node); + buildMacroDictionary(ast); + return Status.OK_STATUS; + } + } + } + // function style macro or selection + FindEnclosingNodeAction nodeFinder= new FindEnclosingNodeAction(ast.getFilePath(), fTextRegion.getOffset(), fTextRegion.getLength()); + ast.accept(nodeFinder); + fEnclosingNode= nodeFinder.getNode(); + if (fEnclosingNode != null) { + boolean macroOccurrence= false; + IASTNodeLocation[] locations= fEnclosingNode.getNodeLocations(); + for (int i = 0; i < locations.length; i++) { + IASTNodeLocation location= locations[i]; + if (location instanceof IASTMacroExpansion) { + IASTFileLocation fileLocation= location.asFileLocation(); + if (fileLocation != null && ast.getFilePath().equals(fileLocation.getFileName())) { + if (fTextRegion.overlapsWith(fileLocation.getNodeOffset(), fileLocation.getNodeLength())) { + nodeFinder= new FindEnclosingNodeAction(ast.getFilePath(), fileLocation.getNodeOffset(), fileLocation.getNodeLength()); + ast.accept(nodeFinder); + addExpansionNode(nodeFinder.getNode()); + macroOccurrence= true; + } + } + } + } + if (macroOccurrence) { + CollectEnclosedNodesAction nodeCollector= new CollectEnclosedNodesAction(ast.getFilePath(), fTextRegion.getOffset(), fTextRegion.getLength()); + ast.accept(nodeCollector); + fExpansionNodes.addAll(nodeCollector.getNodes()); + buildMacroDictionary(ast); + return Status.OK_STATUS; + } + } + } + return Status.CANCEL_STATUS; + } + + private void buildMacroDictionary(IASTTranslationUnit ast) { + Map macroDict= new HashMap(500); + IASTPreprocessorMacroDefinition[] macroDefs; + final IASTPreprocessorMacroDefinition[] localMacroDefs= ast.getMacroDefinitions(); + for (macroDefs= localMacroDefs; macroDefs != null; macroDefs= (macroDefs == localMacroDefs) ? ast.getBuiltinMacroDefinitions() : null) { + for (int i = 0; i < macroDefs.length; i++) { + IASTPreprocessorMacroDefinition macroDef= macroDefs[i]; + StringBuffer macroName= new StringBuffer(); + macroName.append(macroDef.getName().toCharArray()); + if (macroDef instanceof IASTPreprocessorFunctionStyleMacroDefinition) { + macroName.append('('); + IASTPreprocessorFunctionStyleMacroDefinition functionMacro= (IASTPreprocessorFunctionStyleMacroDefinition)macroDef; + IASTFunctionStyleMacroParameter[] macroParams= functionMacro.getParameters(); + for (int j = 0; j < macroParams.length; j++) { + if (j > 0) { + macroName.append(','); + } + IASTFunctionStyleMacroParameter param= macroParams[j]; + macroName.append(param.getParameter()); + } + macroName.append(')'); + } + macroDict.put(macroName.toString(), macroDef.getExpansion()); + } + } + fMacroDict= macroDict; + } + + private void addExpansionNode(IASTNode node) { + if (node != null) { + fEnclosingNode= computeCommonAncestor(node, fEnclosingNode); + fExpansionNodes.add(node); + } + } + + private IASTNode computeCommonAncestor(IASTNode node, IASTNode other) { + if (node == null) { + return null; + } + if (other == null) { + return node; + } + if (node == other) { + return other; + } + List ancestors= new ArrayList(); + while (node != null) { + node= node.getParent(); + ancestors.add(node); + } + while (other != null) { + if (ancestors.contains(other)) { + return other; + } + other= other.getParent(); + } + return null; + } + + IRegion getExpansionRegion() { + if (fEnclosingNode != null) { + int startOffset= Integer.MAX_VALUE; + int endOffset= 0; + for (Iterator it= fExpansionNodes.iterator(); it.hasNext(); ) { + IASTNode node= (IASTNode) it.next(); + if (node != fEnclosingNode) { + while (node != null && node.getParent() != fEnclosingNode) { + node= node.getParent(); + } + } + if (node != null) { + IASTFileLocation location= node.getFileLocation(); + if (location != null) { + startOffset= Math.min(startOffset, location.getNodeOffset()); + endOffset= Math.max(endOffset, location.getNodeOffset() + location.getNodeLength()); + } + } + } + if (endOffset > startOffset) { + return new Region(startOffset, endOffset - startOffset); + } + } + return null; + } + + Map getMacroDictionary() { + return fMacroDict; + } + + IASTNode getEnclosingNode() { + return fEnclosingNode; + } + } + + private static class Cache { + ComputeExpansionRegionRunnable fComputer; + IWorkingCopy fTranslationUnit; + String fExpansionText; + String fExpandedText; + } + + private WeakReference fCache; + + /* + * @see org.eclipse.cdt.internal.ui.text.c.hover.AbstractCEditorTextHover#getHoverInfo(org.eclipse.jface.text.ITextViewer, org.eclipse.jface.text.IRegion) + */ + public String getHoverInfo(ITextViewer textViewer, IRegion hoverRegion) { + IEditorPart editor = getEditor(); + if (editor == null || !(editor instanceof ITextEditor)) { + return null; + } + IEditorInput input= editor.getEditorInput(); + IWorkingCopyManager manager= CUIPlugin.getDefault().getWorkingCopyManager(); + IWorkingCopy tu = manager.getWorkingCopy(input); + if (tu == null) { + return null; + } + + IProgressMonitor monitor= new NullProgressMonitor(); + ComputeExpansionRegionRunnable computer= new ComputeExpansionRegionRunnable(tu, hoverRegion); + IStatus status= ASTProvider.getASTProvider().runOnAST(tu, ASTProvider.WAIT_ACTIVE_ONLY, monitor, computer); + if (!status.isOK()) { + return null; + } + IRegion region= computer.getExpansionRegion(); + if (region == null) { + return null; + } + ITextEditor textEditor= (ITextEditor)editor; + IDocument document= textEditor.getDocumentProvider().getDocument(input); + region= alignRegion(region, document); + + String expansionText; + try { + expansionText= document.get(region.getOffset(), region.getLength()); + } catch (BadLocationException exc) { + CUIPlugin.getDefault().log(exc); + return null; + } + // expand + String expandedText= expandMacros(computer.getMacroDictionary(), expansionText, tu.isCXXLanguage()); + + // format + final String lineDelimiter= TextUtilities.getDefaultLineDelimiter(document); + final int nodeKind = getNodeKind(computer.getEnclosingNode()); + String formattedText= formatSource(nodeKind, expandedText, lineDelimiter, tu.getCProject()); + + // cache objects for later macro exploration + Cache cache= new Cache(); + cache.fComputer= computer; + cache.fExpansionText= expansionText; + cache.fTranslationUnit= tu; + cache.fExpandedText= formattedText; + fCache= new WeakReference(cache); + return formattedText; + } + + private static int getNodeKind(IASTNode node) { + if (node instanceof IASTDeclaration) { + return CodeFormatter.K_TRANSLATION_UNIT; + } + if (node instanceof IASTStatement) { + return CodeFormatter.K_STATEMENTS; + } + if (node instanceof IASTExpression) { + return CodeFormatter.K_EXPRESSION; + } + if (node instanceof IASTTranslationUnit) { + return CodeFormatter.K_TRANSLATION_UNIT; + } + return CodeFormatter.K_UNKNOWN; + } + + private static final IRegion alignRegion(IRegion region, IDocument document) { + try { + int start= document.getLineOfOffset(region.getOffset()); + int end= document.getLineOfOffset(region.getOffset() + region.getLength()); + if (start == end) { + return region; + } + int offset= document.getLineOffset(start); + if (document.get(offset, region.getOffset()- offset).trim().length() > 0) { + offset= region.getOffset(); + } + int endOffset; + if (document.getNumberOfLines() > end + 1) { + endOffset= document.getLineOffset(end + 1); + } else { + endOffset= document.getLineOffset(end) + document.getLineLength(end); + } + int oldEndOffset= region.getOffset() + region.getLength(); + if (document.get(oldEndOffset, endOffset - oldEndOffset).trim().length() > 0) { + endOffset= oldEndOffset; + } + return new Region(offset, endOffset - offset); + + } catch (BadLocationException x) { + return region; + } + } + + /** + * Format given source content according given options. + * + * @param kind + * one of {@link CodeFormatter#K_TRANSLATION_UNIT}, + * {@link CodeFormatter#K_STATEMENTS}, + * {@link CodeFormatter#K_EXPRESSION} + * @param content + * the source content + * @param lineDelimiter + * the line delimiter to be used + * @param project + * @return the formatted source text or the original if the text could not + * be formatted successfully + */ + private static String formatSource(int kind, String content, String lineDelimiter, ICProject project) { + TextEdit edit= CodeFormatterUtil.format(kind, content, 0, lineDelimiter, project.getOptions(true)); + if (edit != null) { + IDocument doc= new Document(content); + try { + edit.apply(doc); + content= doc.get().trim(); + } catch (MalformedTreeException exc) { + CUIPlugin.getDefault().log(exc); + } catch (BadLocationException exc) { + CUIPlugin.getDefault().log(exc); + } + } + return content; + } + + private String expandMacros(Map macroDict, String expansionText, boolean isCpp) { + final IScannerInfo scannerInfo= new ScannerInfo(macroDict); + final CodeReader codeReader= new CodeReader(expansionText.toCharArray()); + final IScannerExtensionConfiguration configuration; + final ParserLanguage parserLanguage; + if (isCpp) { + configuration= new GPPScannerExtensionConfiguration(); + parserLanguage= ParserLanguage.CPP; + } else { + configuration= new GCCScannerExtensionConfiguration(); + parserLanguage= ParserLanguage.C; + } + CPreprocessor preprocessor= new CPreprocessor(codeReader, scannerInfo, parserLanguage, ParserUtil.getParserLogService(), configuration, NullCodeReaderFactory.getInstance()); + StringBuffer expandedText= new StringBuffer(expansionText.length()); + IToken token= null; + IToken prevToken; + while (true) { + try { + prevToken= token; + token= preprocessor.nextToken(); + } catch (EndOfFileException exc) { + break; + } + if (requireSpace(prevToken, token)) { + expandedText.append(' '); + } + expandedText.append(token.getImage()); + } + return expandedText.toString(); + } + + private static boolean requireSpace(IToken prevToken, IToken token) { + if (prevToken == null) { + return false; + } + if (prevToken.isOperator() && token.isOperator()) { + return true; + } + switch (prevToken.getType()) { + case IToken.tLPAREN: case IToken.tLBRACKET: + return false; + + // bit operations + case IToken.tAMPERASSIGN: + case IToken.tBITOR: case IToken.tBITORASSIGN: + case IToken.tSHIFTL: case IToken.tSHIFTLASSIGN: + case IToken.tSHIFTR: case IToken.tSHIFTRASSIGN: + case IToken.tXOR: case IToken.tXORASSIGN: + + // logical operations + case IToken.tAND: case IToken.tOR: + + // arithmetic + case IToken.tDIV: case IToken.tDIVASSIGN: + case IToken.tMINUS: case IToken.tMINUSASSIGN: + case IToken.tMOD: case IToken.tMODASSIGN: + case IToken.tPLUS: case IToken.tPLUSASSIGN: + case IToken.tSTARASSIGN: + case IGCCToken.tMAX: case IGCCToken.tMIN: + + // comparison + case IToken.tEQUAL: case IToken.tNOTEQUAL: + case IToken.tGT: case IToken.tGTEQUAL: + case IToken.tLT: case IToken.tLTEQUAL: + + // other + case IToken.tASSIGN: case IToken.tCOMMA: + return true; + } + + switch (token.getType()) { + case IToken.tRPAREN: case IToken.tRBRACKET: + return false; + + // bit operations + case IToken.tAMPER: case IToken.tAMPERASSIGN: + case IToken.tBITOR: case IToken.tBITORASSIGN: + case IToken.tSHIFTL: case IToken.tSHIFTLASSIGN: + case IToken.tSHIFTR: case IToken.tSHIFTRASSIGN: + case IToken.tXOR: case IToken.tXORASSIGN: + + // logical operations + case IToken.tAND: case IToken.tOR: + + // arithmetic + case IToken.tDIV: case IToken.tDIVASSIGN: + case IToken.tMINUS: case IToken.tMINUSASSIGN: + case IToken.tMOD: case IToken.tMODASSIGN: + case IToken.tPLUS: case IToken.tPLUSASSIGN: + case IToken.tSTAR: case IToken.tSTARASSIGN: + case IGCCToken.tMAX: case IGCCToken.tMIN: + + // comparison + case IToken.tEQUAL: case IToken.tNOTEQUAL: + case IToken.tGT: case IToken.tGTEQUAL: + case IToken.tLT: case IToken.tLTEQUAL: + return true; + + // other + case IToken.tASSIGN: + return true; + + case IToken.tCOMMA: + return false; + } + + char lastChar= prevToken.getCharImage()[prevToken.getLength() - 1]; + char nextChar= token.getCharImage()[0]; + if (Character.isJavaIdentifierPart(lastChar) && Character.isJavaIdentifierPart(nextChar)) { + return true; + } + return false; + } + + /* + * @see org.eclipse.jface.text.ITextHoverExtension#getHoverControlCreator() + */ + public IInformationControlCreator getHoverControlCreator() { + return new IInformationControlCreator() { + public IInformationControl createInformationControl(Shell parent) { + return new CMacroExpansionControl(parent, getTooltipAffordanceString()); + } + }; + } + + /* + * @see IInformationProviderExtension2#getInformationPresenterControlCreator() + */ + public IInformationControlCreator getInformationPresenterControlCreator() { + return new IInformationControlCreator() { + public IInformationControl createInformationControl(Shell parent) { + int shellStyle= SWT.RESIZE; + int style= SWT.V_SCROLL | SWT.H_SCROLL; + return new CMacroExpansionControl(parent, shellStyle, style, createMacroExpansionInput()); + } + }; + } + + protected CMacroExpansionInput createMacroExpansionInput() { + // TODO compute all expansion steps + Cache cache= (Cache) fCache.get(); + fCache= null; + if (cache != null) { + CMacroExpansionInput input= new CMacroExpansionInput(); + input.fExpansions= new String[] { cache.fExpansionText, cache.fExpandedText }; + return input; + } + return null; + } + +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CMacroExpansionInput.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CMacroExpansionInput.java new file mode 100644 index 00000000000..586d4bcb1e6 --- /dev/null +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/CMacroExpansionInput.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * 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.c.hover; + +/** + * An input object to the {@link CMacroExpansionControl}. + * + * @since 5.0 + */ +public class CMacroExpansionInput { + String[] fExpansions; +} diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/SourceViewerInformationControl.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/SourceViewerInformationControl.java index e596d103986..62e9128a851 100644 --- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/SourceViewerInformationControl.java +++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/text/c/hover/SourceViewerInformationControl.java @@ -193,7 +193,7 @@ public class SourceViewerInformationControl implements IInformationControl, IInf fontDatas[i].setHeight(fontDatas[i].getHeight() * 9 / 10); fStatusTextFont= new Font(fStatusField.getDisplay(), fontDatas); fStatusField.setFont(fStatusTextFont); - GridData gd2= new GridData(GridData.FILL_VERTICAL | GridData.FILL_HORIZONTAL | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_BEGINNING); + GridData gd2= new GridData(GridData.FILL_HORIZONTAL | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_BEGINNING); fStatusField.setLayoutData(gd2); // Regarding the color see bug 41128 @@ -245,7 +245,7 @@ public class SourceViewerInformationControl implements IInformationControl, IInf * or null if the status field should be hidden * @since 3.0 */ - public SourceViewerInformationControl(Shell parent,int style, String statusFieldText) { + public SourceViewerInformationControl(Shell parent, int style, String statusFieldText) { this(parent, SWT.NO_TRIM | SWT.TOOL, style, statusFieldText); } @@ -313,7 +313,7 @@ public class SourceViewerInformationControl implements IInformationControl, IInf * @see IInformationControl#setVisible(boolean) */ public void setVisible(boolean visible) { - fShell.setVisible(visible); + fShell.setVisible(visible); } /**