ActionHooks Plugin

Ollie Rutherfurd


Table of Contents

About

ActionHooks enables one to execute actions or macros in reponse to specific EditBus messages. The EditBus is jEdit's mechanism for event notification. jEdit "sends" EditBus messages when buffers are opened, closed, properties changed; when views are created or destroyed; when you switch between edit panes and when other events occur. As an example, using ActionHooks, you could write a macro to hide all docking areas when you split a view into multiple edit panes. An example macro for this is provided below. It would be bound to EditPane.CREATED.

This allows for powerful customization of jEdit.

Other Examples include setting custom buffer properties depending on the buffer's path or automatically displaying SideKick's Structure Browser when you switch to a buffer containing an XML file.

Usage

To use ActionHooks you must do 2 things.

  1. Enable it.

    By default ActionHooks is not enabled. To enable ActionHooks select Plugins > ActionHooks. To enable ActionHooks by default, select Plugins > Plugin Options > ActionHooks > Enable ActionHooks by Default.

  2. Associate actions or macros with events.

    To associate actions or macros with events, go to Plugins > Plugin Options > ActionHooks. You'll see a dropdown box with a list of available events (EditBus messages). Use the buttons below to add, remove, or change the execution order for actions or macros associated with the selected event.

Examples

Showing Structure Browser for XML buffers

If you associate the following macro with EditPaneUpdate.BUFFER_CHANGED, when you open or switch to an xml or xsl buffer, SideKick's Structure Browser will be displayed. If SideKick's Structure Browser is open and you switch to a buffer of another type, it will be hidden.

void toggleStructureBrowser(View view)
{
    DockableWindowManager wm = view.getDockableWindowManager();
    Buffer buffer = view.getBuffer();

    // can't get mode until done loading
    if(!buffer.isLoaded())
    {
        VFSManager.waitForRequests();
    }

    String mode = buffer.getMode().getName();
    if(mode.equals("xml")
        || mode.equals("xsl"))
    {
        // only show if view not split
        if(view.getEditPanes().length == 1)
            wm.showDockableWindow("sidekick-tree");
    }
    else
    {
        // hide
        if(wm.isDockableWindowVisible("sidekick-tree"))
            wm.hideDockableWindow("sidekick-tree");
    }
}

toggleStructureBrowser(view);

// :noTabs=true:lineSeparator=\n:indentSize=4:deepIndent=false:

Setting Buffer Properties

With the following macro bound to EditPaneUpdate.BUFFER_CHANGED, custom buffer properties are set when you switch to a buffer whose filename contains project_x or project_y. This can be handy when working on projects with different editing conventions from your defaults if coding conventions prevent the use of buffer local properties.


void setProperties(Buffer buffer){

    String name = buffer.getName();
    String path = buffer.getPath();

    if((path.indexOf("project_x") > 0)
        || (path.indexOf("project_y") > 0)
    )
    {
        if(!buffer.isLoaded())
            VFSManager.waitForRequests();
        
        String mode = buffer.getMode().getName();
        
        buffer.setProperty("lineSeparator","\n");
        buffer.setProperty("noTabs",true);
        
        if(mode.equals("xml"))
        {
            buffer.setProperty("tabSize",2);
            buffer.setProperty("indentSize",2);
            buffer.setProperty("folding","sidekick");
        }
        else if(mode.equals("java"))
        {
            buffer.setProperty("tabSize",4);
            buffer.setProperty("indentSize",4);
            buffer.setProperty("deepIndent",true);
        }
        else if(mode.equals("jsp"))
        {
            buffer.setProperty("tabSize",2);
            buffer.setProperty("indentSize",2);
        }
    
        buffer.propertiesChanged();
    }

}

setProperties(buffer);

// :noTabs=true:lineSeparator=\n:indentSize=4:deepIndent=false:

Hiding All Docking Areas

If you bind the following macro to EditPane.CREATED, when you split a view all docking areas will be collapsed to make room for viewing the edit panes.

void hideDockingAreas(View view)
{
    // left
    dock = view.getDockableWindowManager().getLeftDockingArea();
    if(dock.getCurrent() != null)
        dock.show(null);
    
    // top
    dock = view.getDockableWindowManager().getTopDockingArea();
    if(dock.getCurrent() != null)
        dock.show(null);

    // right
    dock = view.getDockableWindowManager().getRightDockingArea();
    if(dock.getCurrent() != null)
        dock.show(null);

    // bottom
    dock = view.getDockableWindowManager().getBottomDockingArea();
    if(dock.getCurrent() != null)
        dock.show(null);

    view.getTextArea().requestFocus();
}

hideDockingAreas(view);

// :noTabs=true:lineSeparator=\n:indentSize=4:deepIndent=false:

Tips

  • Use Utilities > Troubleshooting > Activity Log (along with jEdit's API Documentation) to see when EditBus messages are sent.

  • Remember that any actions or macros you bind to events may be run a lot and/or frequently. For example, if you bind a macro to BufferUpdate.LOADED it will be run for each buffer that jEdit re-opens on startup (if enabled). With a large number of buffers open, then may increase startup time.

  • If you write macros to bind to EditBus messages, test them well. It's easy to make a bit of a mess of things if jEdit starts invoking macros which have errors. Remember you can always disable ActionHooks via Plugins > ActionHooks.

  • Depending on the event, some operations may not work as expected. For example, while a buffer is being saved, the textarea is read-only, so the following macro bound to BufferUpdate.SAVING wouldn't work:

    textArea.selectAll();
    textArea.removeTrailingWhiteSpace();

    In this specific example, you could work around the issue by modifying the buffer itself, but make sure to read jEdit's API docs or code so your actions don't produce unwanted results.

Extending ActionHooks

By default ActionHooks supports the following EditBus messages:

  • BufferUpdate

  • EditPaneUpdate

  • PropertiesChanged

  • ViewUpdate

Plugins can add support for EditBus messages they provide by registering EBMessageHandler subclasses with ActionHooks.

EBMessageHandler

An EBMessageHandler does the following:

  • Tells ActionHooks the name of the EditBus it handles. For example, org.gjt.sp.jedit.msg.BufferUpdate.

  • Tells ActionHooks the list of "events" associated with the EditBus message it handles. For example, BufferUpdate.LOADED, BufferUpdate.CLOSED, BufferUpdate.SAVING, BufferUpdate.SAVED.

  • When an EditBus message is sent an EBMessageHandler tells ActionHooks which event it corresponds to. For example, a BufferUpdate message is sent for BufferUpdate.LOADED, BufferUpdate.CLOSED, BufferUpdate.SAVING, BufferUpdate.SAVED. The EBMessageHandler must specify which "event" the specific EditBus is for.

  • Tells ActionHooks which view to use for invoking EditActions or Macros for an event.

EBMessageHandler Example

Writing an EBMessageHandler is actually pretty simple. Here's the source for PropertiesChangedHandler:

package actionhooks.handler;

import org.gjt.sp.jedit.EBMessage;
import org.gjt.sp.jedit.View;
import org.gjt.sp.jedit.msg.PropertiesChanged;

import actionhooks.EBMessageHandler;

public class PropertiesChangedHandler extends EBMessageHandler
{

    static String[] events = {"PropertiesChanged"};

    public String getMessageName()
    {
        return "org.gjt.sp.jedit.msg.PropertiesChanged";
    }

    public View getView(EBMessage msg)
    {
        return null;
    }

    public String[] getEventNames()
    {
        return this.events;
    }

    public String getEventName(EBMessage msg)
    {
        return "PropertiesChanged";
    }

}

See the source code for ActionHooks for more examples.

Registering an EBMessageHandler

Plugins register an EBMessageHandler using:

ActionHooksPlugin.registerHandler(<handler>);

and unregister a handler using:

ActionHooksPlugin.unregisterHandler(<handler>);

Version History

  • 0.6

    Plugin updated for compatibility with latest apis.

  • 0.5

    Plugins can register EBMessageHandler classes to add support for custom EditBus messages.

  • 0.4.1, released 2004-01-10

    First public release

Feedback

Send bug reports or feature requests to:

License

The source code for this plugin is release under the GPL. Please see http://www.fsf.org/copyleft/gpl.html.