Michael
Michael

Reputation: 816

Eclipse Plugin to handle same file extension with different editors

I am writing a plugin for Eclipse which provides syntax highlighting for a specific preprocessor directive that is used in our software stack. Before compiling the actual code, these get replaced by an external program (imagine a tag like <% FOO %> which gets replaced with something else, say a version string).

So the plugin provides annotations for each tag to quickly identify these. Instead of implementing an own editor, the annotations are provided for already existing editors, like the JavaEditor or PHP-Editor.

Now the problem is that the files which get processed by the external program all have the same file extension, say .inc. They can contain Java code or PHP code (other languages also possible in the future).

I have successfully added a content type to my plugin and I can distinguish the different files based on some criteria. So, when I have a java .inc file and assign the content type inc file (java).

However, the user should be able to overwrite this auto-detection (also, sometimes the auto-detection might fail). So, I want to be able to open one file (foo.inc) with different editors (Java Editor, PHP Editor, …) and be able to save this association.

The approaches I am currently thinking of:

Are there better solutions? Do you know more about any of the approaches I have listed?

Upvotes: 4

Views: 2383

Answers (2)

Yann TM
Yann TM

Reputation: 2075

You could attack directly the IEditorRegistry programmatically, by registering specific file names rather than just extensions.

Look at : "Help on IEditorRegistry"

http://help.eclipse.org/luna/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fui%2FIEditorRegistry.html

The method :

void    setDefaultEditor(String fileNameOrExtension, String editorId) 
      Sets the default editor id for the files that match
      that specified file name or extension.

accepts full names and/or wildcards. Most of the openEditor calls (menus, toolbars etc...) end up in calls to this registry to get the appropriate editor. Set up a hook when opening your editor that registers this file name specifically.

It's not foolproof if you have two files with same name and different language but it's fast and easy to implement compared to your approaches.

Upvotes: 0

Michael
Michael

Reputation: 816

I found out how to overwrite the file open action: by registering an actionProvider which overrides org.eclipse.ui.navigator.resources.OpenActions. I provide all the code because it is relatively hard to put all these different things together and have it working.

Let’s start with the plugin.xml entry:

<!-- overwrite OpenActions -->
<extension
      point="org.eclipse.ui.navigator.navigatorContent">
   <actionProvider
         class="myplugin.navigator.OpenActionProvider"
         id="myplugin.navigator.actions.open"
         overrides="org.eclipse.ui.navigator.resources.OpenActions"
         priority="highest">
      <enablement>
         <and>
            <instanceof
                  value="org.eclipse.core.resources.IFile">
            </instanceof>
            <test
                  property="org.eclipse.core.resources.extension"
                  value="frm">
            </test>
         </and>
      </enablement>
   </actionProvider>
</extension>
<extension
      point="org.eclipse.ui.navigator.viewer">
   <viewerActionBinding
         viewerId="org.eclipse.ui.navigator.ProjectExplorer">
      <includes>
         <actionExtension
               pattern="myplugin.navigator.actions.open">
         </actionExtension>
      </includes>
   </viewerActionBinding>
</extension>

The OpenActionProvider looks like this:

package myplugin.navigator;

import org.eclipse.jface.action.IMenuManager;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.navigator.CommonActionProvider;
import org.eclipse.ui.navigator.ICommonActionConstants;
import org.eclipse.ui.navigator.ICommonActionExtensionSite;
import org.eclipse.ui.navigator.ICommonViewerWorkbenchSite;

public class OpenActionProvider extends CommonActionProvider {
    private OpenEditorActionGroup fOpenGroup;

    @Override
    public void init(ICommonActionExtensionSite site) {
            ICommonViewerWorkbenchSite workbenchSite = null;
            if (site.getViewSite() instanceof ICommonViewerWorkbenchSite) {
                    workbenchSite = (ICommonViewerWorkbenchSite) site.getViewSite();
            }
            if (workbenchSite != null) {
                    if (workbenchSite.getPart() != null && workbenchSite.getPart() instanceof IViewPart) {
                            IViewPart viewPart = (IViewPart) workbenchSite.getPart();

                            fOpenGroup = new OpenEditorActionGroup(viewPart);
                    }
            }
    }

    @Override
    public void dispose() {
            if (fOpenGroup != null) {
                    fOpenGroup.dispose();
                    fOpenGroup = null;
            }
            super.dispose();
    }

    @Override
    public void fillActionBars(IActionBars actionBars) {
            if (fOpenGroup == null)
                    return;

            fOpenGroup.updateActionBars();
            actionBars.setGlobalActionHandler(ICommonActionConstants.OPEN, fOpenGroup.getOpenAction());
    }

    @Override
    public void fillContextMenu(IMenuManager menu) {
            if (fOpenGroup == null)
                    return;
            fOpenGroup.fillContextMenu(menu);
    }

    @Override
    public void setContext(ActionContext context) {
            super.setContext(context);
            if (fOpenGroup == null)
                    return;
            fOpenGroup.setContext(context);
    }
}

The OpenEditorActionGroup looks like this:

package myplugin.navigator;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.actions.OpenFileAction;
import org.eclipse.ui.actions.OpenWithMenu;
import org.eclipse.ui.navigator.ICommonMenuConstants;


public class OpenEditorActionGroup extends AbstractActionGroup {
    private OpenFileAction fOpenFileAction;

    public OpenEditorActionGroup(IViewPart viewPart) {
            super(viewPart);
    }

    @Override
    protected void makeActions() {
            fOpenFileAction= new OpenGenElementAction(getViewPart().getSite().getPage());
    }

    public static IStructuredSelection convertSelectionToResources(ISelection s) {
            List<Object> converted = new ArrayList<Object>();
            if (s instanceof StructuredSelection) {
                    Object[] elements = ((StructuredSelection) s).toArray();
                    for (int i = 0; i < elements.length; i++) {
                            Object e = elements[i];
                            if (e instanceof IResource) {
                                    converted.add(e);
                            } else if (e instanceof IAdaptable) {
                                    IResource r = (IResource) ((IAdaptable) e).getAdapter(IResource.class);
                                    if (r != null) {
                                            converted.add(r);
                                    }
                            }
                    }
            }
            return new StructuredSelection(converted.toArray());
    }


    @Override
    public void fillContextMenu(IMenuManager menu) {
            System.out.println("fillcontextmenu");
            if (getContext() == null)
                    return;
        IStructuredSelection celements = (IStructuredSelection)getContext().getSelection();
            IStructuredSelection selection = convertSelectionToResources(celements);

            fOpenFileAction.selectionChanged(celements);
            if (!fOpenFileAction.isEnabled())
                    return;
            menu.appendToGroup(ICommonMenuConstants.GROUP_OPEN, fOpenFileAction);
            fillOpenWithMenu(menu, selection);
    }

The AbstractActionGroup is just a wrapper, should you want to implement more of these:

package myplugin.navigator;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.actions.ActionGroup;

public abstract class AbstractActionGroup extends ActionGroup {
    private final IViewPart fViewPart;

    public AbstractActionGroup(IViewPart viewPart) {
            Assert.isNotNull(viewPart);
            fViewPart = viewPart;
            makeActions();
    }

    protected IViewPart getViewPart() {
            return fViewPart;
    }

    protected ImageDescriptor getImageDescriptor(String relativePath) {
            return ImageDescriptor.createFromURL(null);
    }

    protected abstract void makeActions();
    @Override
    public abstract void fillContextMenu(IMenuManager menu);
    @Override
    public abstract void fillActionBars(IActionBars actionBars);
    @Override
    public abstract void updateActionBars();
}

And finally the OpenGenElementAction itself:

package myplugin.navigator;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.OpenFileAction;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.part.FileEditorInput;

public class OpenGenElementAction extends OpenFileAction {
    private IFile selectedFile = null;
    private final IWorkbenchPage workbenchPage;

    public OpenGenElementAction(IWorkbenchPage page) {
            super(page);
            workbenchPage = page;
    }

    @Override
    public boolean isEnabled() {
            return true;
    }

    @Override
    public void run() {
            System.out.println("RUN");
    }

    @Override
    protected boolean updateSelection(IStructuredSelection selection) {
            if (selection.size() != 1)
                    return super.updateSelection(selection);
            Object element = selection.getFirstElement();
            if (element instanceof IFile) {
                    selectedFile = (IFile)element;
            }

            return selectedFile != null || super.updateSelection(selection);
    }
}

Upvotes: 3

Related Questions