Eclipse plugin development : How to listen events in Eclipse editor

4.5k views Asked by At

I am trying to develop an eclipse plug-in. I am aware about the basics of this thing.

In a sample plugin template when we click the menu entry (or button with eclipse icon in below image in this case) in testing instance of eclipse, execute method of sampleHandler.java is executed and a pop-up shown in image below appears.

enter image description here

I want to invoke 'execute' method whenever I press some key (lets say backspace) in code editor instead of clicking any menu entry (or button).

SampleHandler.java

 public class SampleHandler extends AbstractHandler {

    public SampleHandler() {
    }

    /**
     * the command has been executed, so extract extract the needed information
     * from the application context.
     */
    public Object execute(ExecutionEvent event) throws ExecutionException {
        IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
        MessageDialog.openInformation(
                window.getShell(),
                "Sdfsdfsadf",
                "Hello, Eclipse world");
        return null;
    }
   }

I tried suggestions given in other posts but I am unable to achieve the desired functionality.

As per my understanding from referenced post in above line, I tried below code -

    package eventlisten.handlers;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;


import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;

/**
 * Our sample handler extends AbstractHandler, an IHandler base class.
 * @see org.eclipse.core.commands.IHandler
 * @see org.eclipse.core.commands.AbstractHandler
 */
public class SampleHandler extends AbstractHandler {
    /**
     * The constructor.
     */
    public SampleHandler() {
    }

    /**
     * the command has been executed, so extract extract the needed information
     * from the application context.
     */
    public Object execute(ExecutionEvent event) throws ExecutionException {

        IWorkbenchWindow window = HandlerUtil.getActiveWorkbenchWindowChecked(event);
        MessageDialog.openInformation(window.getShell(),"EventListen","Trying event listen");
        IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
        IEditorPart editor = page.getActiveEditor();
        IEditorInput input = editor.getEditorInput();  
        IDocument document=(((ITextEditor)editor).getDocumentProvider()).getDocument(IDocument.class);

        document.addDocumentListener(new IDocumentListener() //**this is line 45**
        {
            @Override
            public void documentAboutToBeChanged(DocumentEvent event) {
                // TODO Auto-generated method stub
                System.out.println("Hello");
            }

            @Override
            public void documentChanged(DocumentEvent event) {
                // TODO Auto-generated method stub
                System.out.println("Hello second");

            }
        });

        return null;
    }
}

But after showing the pop-up , it throws the exception -

java.lang.NullPointerException
at eventlisten.handlers.SampleHandler.execute(SampleHandler.java:45)
at org.eclipse.ui.internal.handlers.HandlerProxy.execute(HandlerProxy.java:290)
at org.eclipse.core.commands.Command.executeWithChecks(Command.java:499)
at org.eclipse.core.commands.ParameterizedCommand.executeWithChecks(ParameterizedCommand.java:508)
at org.eclipse.ui.internal.handlers.HandlerService.executeCommand(HandlerService.java:169)
at org.eclipse.ui.internal.handlers.SlaveHandlerService.executeCommand(SlaveHandlerService.java:241)
at org.eclipse.ui.menus.CommandContributionItem.handleWidgetSelection(CommandContributionItem.java:829)
at org.eclipse.ui.menus.CommandContributionItem.access$19(CommandContributionItem.java:815)
at org.eclipse.ui.menus.CommandContributionItem$5.handleEvent(CommandContributionItem.java:805)
at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1276)
at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:3562)
at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3186)
at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2701)
at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2665)
at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2499)
at org.eclipse.ui.internal.Workbench$7.run(Workbench.java:679)
at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:668)
at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:124)
at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:353)
at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:180)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:629)
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:584)
at org.eclipse.equinox.launcher.Main.run(Main.java:1438)
at org.eclipse.equinox.launcher.Main.main(Main.java:1414)

Can someone guide me through the process? Let me know in case more information is required.

1

There are 1 answers

0
kjlubick On BEST ANSWER

Your first problem seems to be that you only interact with the documents when the user presses the button. There are better ways to set this up.
As far as I can tell, you want to either detect when a user modifies a document (probably by typing), any key is typed anywhere, or when the AST of the code is modified. You will probably only find one of the solutions below useful or relevant.

Listening to Document Changes

The solution you attempted is closest to the first one, so I'll start there. I did something like this (in the post you linked, as it turns out). Start by making your plugin extend the org.eclipse.ui.startup extension point, and define a class to override IStartup

That earlyStartup() will look something like:

@Override
public void earlyStartup() {
    IWorkbench wb = PlatformUI.getWorkbench();
    wb.addWindowListener(generateWindowListener());
}

We'll listen for windows to open, and when they do,

private IWindowListener generateWindowListener() 
{
    return new IWindowListener() {
        @Override
        public void windowOpened(IWorkbenchWindow window) {
            IWorkbenchPage activePage = window.getActivePage(); 
            activePage.addPartListener(generateIPartListener2());
        }

        @Override
        public void windowDeactivated(IWorkbenchWindow window) {}

        @Override
        public void windowClosed(IWorkbenchWindow window) {}

        @Override
        public void windowActivated(IWorkbenchWindow window) {}
    };
}

This part listener is where you should get the EditorPart, which means you can add the document listener:

private IPartListener2 generateIPartListener2() 
{
    return new IPartListener2() {

        private void checkPart(IWorkbenchPartReference partRef) {
        IWorkbenchPart part = partRef.getPart(false);
            if (part instanceof IEditorPart)
            {
                IEditorPart editor = (IEditorPart) part;
                IEditorInput input = editor.getEditorInput();
                if (editor instanceof ITextEditor && input instanceof FileEditorInput)  //double check.  Error Editors can also bring up this call
                {
                    IDocument document=(((ITextEditor)editor).getDocumentProvider()).getDocument(input);
                    document.addDocumentListener(/* your listener from above*/);
                }
            }
        }

        @Override
        public void partOpened(IWorkbenchPartReference partRef) {
            checkPart(partRef);
        }

        @Override
        public void partInputChanged(IWorkbenchPartReference partRef) 
        {
            checkPart(partRef);
        }           

        @Override
        public void partVisible(IWorkbenchPartReference partRef){}

        @Override
        public void partHidden(IWorkbenchPartReference partRef) {}

        @Override
        public void partDeactivated(IWorkbenchPartReference partRef)  {}

        @Override
        public void partClosed(IWorkbenchPartReference partRef) {}

        @Override
        public void partBroughtToTop(IWorkbenchPartReference partRef) {}

        @Override
        public void partActivated(IWorkbenchPartReference partRef) {}
    };
}

Listening to just the keypressess

This ends up being simpler to implement, but may be very noisy. We'll be looking at the Display and the Listener, which gets right into the SWT event loop.

You'll want to do the earlyStartup() extension again, and have something like:

@Override
public void earlyStartup() {
    Display display = Display.getDefault();
    display.setFilter(SWT.KeyUp, new Listener() {
        @Override
        public void handleEvent(Event event) {
            //do stuff here.  Be careful, this may cause lag
        }
     });
}

Listening to Java AST changes The final one has the simplicity of the raw keyup approach, but will probably be as semantically useful as the first one I suggested. We will be listening to the JavaCore directly.

Again, in the earlyStartup method()

 JavaCore.addElementChangedListener(new IElementChangedListener() {

        @Override
        public void elementChanged(ElementChangedEvent event)
        {
            //do stuff with the event
        }
    });

Conclusion: With luck, one of these three methods is useful to you. I've had reason to use each in my Eclipse development career -- each is useful in its own way.

I hope this helps.