Currently I am developing a plugin for Eclipse CDT. I have successfully made an action declaration in my plugin.xml file.
<extension
point="org.eclipse.ui.popupMenus">
<objectContribution
id="MyOwnPlugin.contribution1"
nameFilter="*.c"
objectClass="org.eclipse.ui.IEditorInput">
<action
class="myownplugin.popup.actions.DoTestsAction"
enablesFor="1"
id="myownplugin.doTests"
label="Do Tests"
menubarPath="additions">
</action>
</objectContribution>
</extension>
This successfully declares a popup action when I right-click on the editor and I can select the action.
However, what I would like to achieve is that the action would only appear when I right-click on a function name/function declaration. Is there a way how to achieve this? I have been trying the filter XML tag in the plugin.xml, but with no success.
Thanks.
I haven't tried this myself, but I believe what you want is doable (modulo the click position vs. caret position issue described in the comments). You are on the right track with using the
<filter>element, but there are some other pieces required to get this to work.The first thing to realize is that a context menu pertains to an object, and in the case of the editor's context menu, that object is the
IEditorInputrepresenting the contents of the editor tab.Any mechanism to make the presence of a context menu action dependent on some condition, will only have that object available as input. It follows that the condition must be based on the state of the object (only). This is why we can base it on caret position but not on the location of the click itself: "the current caret position in the editor" is part of the state of the
IEditorInput, but "the location of the current click within the editor" is not (that I know of).The documentation of the
<filter>element says:The reason this talks about a "selection" is that in some views a context menu can be invoked with multiple objects selected (e.g. in the Project Explorer view, with multiple files / folders selected). In an editor context that doesn't apply; there will only be one object in the "selection", of type
IEditorInput.Unfortunately,
IEditorInputdoes not implementIActionFilter. However, it does implementIAdaptable, so we can use the adapter mechanism to have our plugin support adapting it toIActionFilter.That will involve adding something like this to your
plugin.xml:where
your.plugin.EditorInputAdapterFactoryis a type you'll write in your plugin. Its implementation will look something like this:where
EditorInputActionFilteris, again, a type we'll write.Ok, now we have an action filter that works with
IEditorInputobjects, enabling us to use the<filter>element.The
<filter>element uses an "attribute name" and an "attribute value", which will be passed it to theIActionFilter. As the author of the action filter, we get to invent these. For example, we can invent an attribute name calledselectedElementType(where by "selected element" I mean the type of C++ element the caret is currently over), and a value namedfunction.Then our filter declaration will look like this:
Finally, we need to implement our action filter so it evaluates the property we defined for an
IEditorInputobject. I won't write up the complete implementation here, but in broad strokes:CDTUITools.getWorkingCopyManager()to map theIEditorInputto anIWorkingCopy, which implementsITranslationUnit.CUIPlugin.getActivePage().findEditor(editorInput).getEditorSite().getSelectionProvider().getSelection()(with appropriate null checks in between). There may be an easier way to do this, but that's what comes to mind. Since you're in an editor, the returned selection should have typeITextSelection.SharedASTJobto get access to the editor's shared AST (IASTTranslationUnit). Note that you'll need to block on the job, and the action filter will (I assume) be invoked on the UI thread, which is not ideal. (More on that below.)IASTTranslationUnit.getNodeSelector(null).findEnclosingName(offset, length), with the offset and length from theITextSelection, to get anIASTNamerepresenting the name under the caret (if any).IASTName.resolveBinding()to get the binding (C++ semantic model object) that the name refers to.IFunction.All of this will go in your implementation of
IActionFilter.testAttribute(). Thetargetparameter to that function will be theIEditorInput. For good measure, you should check that thenameandvalueparameters correspond to the attribute names you invented (selectedElementTypeandfunction) before doing any of this (initially, your action filter will only be invoked by your<filter>element, so they'll always match, but you can imagine extending this mechanism in the future to e.g. support other selected element types.)Finally, a note on performance: what you're doing here is conditioning the responsiveness of a UI element (the appearance of a popup) on a property of C++ code, which can be slow to parse and analyze. This necessarily means your popup may take longer to appear as a result (which is reflected in your action filter needing to block on the
SharedASTJob). By usingSharedASTJob, you are minimizing this effect by re-using an already-parsed AST if there is one, but e.g. if you've just opened an editor and you right-click, and the initial AST takes several seconds to build, your popup will take several seconds to show up. Caveat emptor.