This section outlines the new deferred loading plugin API. Also see the section called “Common API”.
Note that most plugins written for jEdit 4.1 and earlier will continue to work, however they will not take advantage of the new features of the plugin API outlined in this section.
In jEdit 4.1 and earlier, plugins are always fully loaded at program startup, and unloaded at program shutdown. While the plugin loader and API was very simple as a result, this scheme had two main disadvantages:
Having a large number of plugins generally slowed down jEdit startup, since many plugins performed a lot of lengthy initialization. Even if all plugins made their start() methods as quick as possible, there would still be the overhead of scanning the JAR files, loading the plugin classes, and so on.
There was no way to reload plugins in a running jEdit instance. This made plugin development and use of the plugin manager a bit cumbersome.
Through a stroke of insight, one notices that the only functions called from most plugins' start() methods fall into two categories:
Registration of virtual file systems, fold handlers, macro handlers, Console shells, SideKick parsers, ...
Arbitriary initialization that only needs to be done when the plugin is first invoked by the user, and not at program startup.
jEdit 4.2 moves the former task out of the start() method and into a file within the JAR that can be parsed quickly. This allows the plugin core class to only be loaded, and its start() method called, only when the plugin is first invoked. Note that the start() method is always called from the event dispatch thread (or from the main thread if the GUI has not yet been loaded). Therefore you do not need to worry about thread-safety issues.
Also, plugins can now be loaded and unloaded at runtime. Note that in 4.2pre1, this code has not been fully implemented yet, so the only way of doing this is by calling BeanShell APIs. Look for a nice UI for this in the 4.2pre2 plugin manager.
Documentation for the properties mentioned below, and their possible values, can be found in the documentation for the EditPlugin class.
jEdit distinguishes between jEdit 4.1 and 4.2-style plugins by checking for the presence of a single property, plugin.class name.activate. If this property is present, the plugin is loaded using the new API.
Since the plugin's menu might need to be displayed before its core class is fully loaded, there is a new way of specifying the plugin menu using properties; the createMenuItems() method of the EditPlugin class has been deprecated.
For example, the jEdit 4.1 version of the QuickNotepad plugin had the following createMenuItems() method:
public void createMenuItems(Vector menuItems) { menuItems.addElement(GUIUtilities.loadMenu("quicknotepad.menu")); } |
Additionally, the following two properties were defined in QuickNotepad.props:
quicknotepad.menu.label=QuickNotepad quicknotepad.menu=quicknotepad - quicknotepad.choose-file \ quicknotepad.save-file quicknotepad.copy-to-buffer |
The jEdit 4.2 version of this plugin no longer has a createMenuItems() method, and instead defines the following property:
plugin.QuickNotepadPlugin.menu=quicknotepad \ - \ quicknotepad.choose-file \ quicknotepad.save-file \ quicknotepad.copy-to-buffer |
Note that specifying a .label property for the menu is no longer necessary, as the label becomes the name of the plugin.
If the content of your plugin's menu is determined at runtime, you must use the new dynamic menu API by defining a property like so:
plugin.MyPlugin.menu.code=new MyPluginMenuProvider(); |
The value of the property is a BeanShell snippet that should evaluate to a DynamicMenuProvider instance.
Similarly, option panes should are now specified using properties, and the createOptionPanes() method of the EditPlugin class has been deprecated.
In QuickNotepad's case, the createOptionPanes() method has been removed:
public void createOptionPanes(OptionsDialog od) { od.addOptionPane(new QuickNotepadOptionPane()); } |
The new properties look like this:
plugin.QuickNotepadPlugin.option-pane=quicknotepad options.quicknotepad.code=new QuickNotepadOptionPane(); |
The syntax of the actions.xml file has not changed.
A few methods of the EditAction class were made final, since these three values must be known even if the action instance in question has not been loaded:
getLabel()
isToggle()
getMouseOverText()
This change does not affect plugins that define actions using an actions.xml file. However, if your plugin creates action instances dynamically, you will need to make calls like the following instead of overriding these methods:
jEdit.setTemporaryProperty("action name.label", "Hello World"); jEdit.setTemporaryProperty("action name.toggle", "true or false"); jEdit.setTemporaryProperty("action name.mouse-over", "some string"); |
Make sure to use jEdit.setTemporaryProperty() and not jEdit.setProperty() since the latter will save the values to the user properties file, which is probably not what you want.
The jEdit.getActions() method has been deprecated, since it must load all plugin actions.xml files in order to return an array of EditAction instances. Use jEdit.getActionNames() instead, which returns an array of strings.
A new abstract method was added to the InputHandler class:
void addKeyBinding( | String | keyBinding, |
String | action) ; |
The removeKeyBinding() method of the DefaultInputHandler class has finally been implemented. Previously it always threw an InternalError when invoked.
The syntax of the dockables.xml file has not changed.
Instead of overriding the deprecated JComponent.requestDefaultFocus() method, dockable windows can now implement the new DefaultFocusComponent interface and its focusOnDefaultComponent() method.
A new services.xml file can be provided in the plugin JAR file. The preferred way of adding virtual file systems, fold handlers and many other types of extensions is through this file. Its syntax is described in the documentation for the ServiceManager class.
Instead of calling VFSManager.registerVFS() in your plugin's start() method, add entries in the services.xml file that look like so:
<SERVICE CLASS="org.gjt.sp.jedit.io.VFS" NAME="ftp"> new ftp.FtpVFS(false); </SERVICE> |
A new VFS.DirectoryEntryCompare class was added, for sorting lists of VFS directory entries.
Fields named canRead and canWrite were added to the VFS.DirectoryEntry class.
There is a new extended attribute API to go with the details view in the file system browser. Pass an array of extended attribute names to the VFS constructor, then provide an implementation of the getExtendedAttribute() method in your subclass of VFS.DirectoryEntry.
The following two methods in the VFSManager class have been deprecated since they can no longer be implemented for file systems using the deferred loading API
getVFSByName() - use getVFSForProtocol() instead.
getFilesystems() - use getVFSs(), which returns a string array, instead.
Instead of calling FoldHandler.registerFoldHandler() in your plugin's start() method, add entries in the services.xml file that look like so:
<SERVICE CLASS="org.gjt.sp.jedit.buffer.FoldHandler" NAME="sidekick"> new sidekick.SideKickFoldHandler() </SERVICE> |
The API for text area extensions is still the same, however you must make sure your plugin correctly adds itself to existing text areas when it is loaded after jEdit startup, and that it removes itself from open views if it is unloaded before jEdit exits.
A new method was added to the TextAreaExtension class:
void paintScreenLineRange( | Graphics2D | gfx, |
int | firstLine, | |
int | lastLine, | |
int[] | physicalLines, | |
int[] | start, | |
int[] | end, | |
int | y, | |
int | lineHeight) ; |
See the class documentation for information on each parameter.
You can override this method to paint a range of lines at once, instead of having to respond to each individual paintValidLine() and paintInvalidLine call.
While for many text area extensions using this method will result in no speedup, some like the error highlight in the ErrorList plugin become more efficient. The error highlight searches an array each time it has to paint a line. Previously the array was searched for each line painted in screen. Using this method, it is possible to search the array only once when painting a range of lines.