开发者

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开发者_如何学Go 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:

  • Overwriting the file open action to check a setting in my project and open the appropriate editor. I did not find a solution that covers how to overwrite all file open actions (File → Open File in the main menu, Open in the Project Navigator, …)
  • Implementing an own editor which then opens the appropriate editor. This seems to be a hackish approach which would also cause some delay.
  • In the "open with" context menu on a file, the user can change the editor. Changing it programmatically would be OK, but I cannot find an API or the file in which this choice is stored.
  • Implementing different Natures for a project, then associate the file types differently in the context of each nature. Would only provide a project-specific association, not a per-file one.

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


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);
    }
}


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜