canmove, Confirmed users, Bureaucrats and Sysops emeriti
1,093
edits
No edit summary |
|||
| (4 intermediate revisions by the same user not shown) | |||
| Line 61: | Line 61: | ||
* Plugins should be loaded lazily to improve startup time. For example, a plugin that provides a syntax highlighter shouldn't be loaded until that highlighter is needed. | * Plugins should be loaded lazily to improve startup time. For example, a plugin that provides a syntax highlighter shouldn't be loaded until that highlighter is needed. | ||
== | == Plugin Definition and Loading == | ||
Here we will talk about the first goal, the | Here we will talk about the first goal, the ability for plugins to load and be activated. | ||
=== A one-file plugin === | === A one-file plugin === | ||
| Line 76: | Line 76: | ||
// referred to by name | // referred to by name | ||
name: "luaHighlighter", | name: "luaHighlighter", | ||
// up to the first "." would be used as the short description. Everything | // up to the first "." would be used as the short description. Everything | ||
// else is viewed in a long description context. | // else is viewed in a long description context. | ||
description: "Syntax highlighter for the Lua programming language.", | description: "Syntax highlighter for the Lua programming language.", | ||
// version numbers will be good for automatic updates. | // version numbers will be good for automatic updates. | ||
version: "1.0", | version: "1.0", | ||
// core parts of Bespin (and even plugins) can query for metadata | // core parts of Bespin (and even plugins) can query for metadata | ||
// and request that a plugin is loaded. In this case, | // and request that a plugin is loaded. In this case, | ||
| Line 90: | Line 90: | ||
// If it's a lua file, it will see that this plugin can handle | // If it's a lua file, it will see that this plugin can handle | ||
// lua. | // lua. | ||
"bespin.syntax.simple" | provides: [ | ||
["bespin.syntax.simple", { | |||
extensions: ["lua"], | |||
// in the single file version of a plugin, you just refer to the | |||
// functions in the plugin itself | |||
load: "luaHighlighter" | |||
] | |||
], | |||
subscribes: [ | |||
["file:savefile", "savehandler"] | |||
] | |||
} | } | ||
exports.luaHighlighter = function() { | |||
// return a lua highlighter instance | |||
exports. | } | ||
// | |||
exports.savehandler = function(event) { | |||
// do whatever it is we do on save | |||
} | } | ||
Plugin modules are implemented not as Dojo modules, but rather as [https://wiki.mozilla.org/ServerJS/Modules/SecurableModules ServerJS Securable Modules]. This would allow seamless interop for plugins that have both client and server side JS components. | Plugin modules are implemented not as Dojo modules, but rather as [https://wiki.mozilla.org/ServerJS/Modules/SecurableModules ServerJS Securable Modules]. This would allow seamless interop for plugins that have both client and server side JS components. | ||
Here's an example of a single file plugin that adds commands and adds/removes a DOM element: | |||
exports.info = { | |||
name: "Something Fun", | |||
provides: [ | |||
["bespin.command", { | |||
name: "woot", | |||
pointer: ":woot" | |||
}], | |||
["bespin.command", { | |||
name: "hello", | |||
takes: ["name"], | |||
pointer: ":hello" | |||
}] | |||
] | |||
} | |||
exports.woot = function(instruction) { | |||
instruction.addOutput("woot!"); | |||
} | |||
exports.activate = function() { | |||
dojo.create("div", { | |||
id: "fun", | |||
style: "opacity: 0; position: absolute; top: 0; left: 0; padding: 2em; background: white" | |||
}, document.body); | |||
} | |||
exports.deactivate = function() { | |||
dojo.query("#fun").orphan(); | |||
} | |||
exports.hello = function(instruction, name) { | |||
instruction.addOutput("Hi there, " + name); | |||
} | |||
=== Plugin installation === | |||
New commands will allow you to work with plugins: | |||
;plugin install URL|Bespin file path:Install a plugin. You can point it at a single file plugin, or at a zip file or tarball. If it's a zip or tgz, it needs to have a single .js file in it or contain a directory that includes a plugin.json. plugin.json provides the equivalent of the info object above. You can also give it a Bespin file path pointing to a directory or file where there is a plugin. | |||
;plugin uninstall PluginName:If installed from a remote location, the plugin files will be deleted. If it was a Bespin path, the plugin is just removed from the installed plugin data. | |||
;plugin reload:generally used in development of a plugin. | |||
;plugin list:list the installed plugins. | |||
Bespin keeps track of installed plugins in a file called BespinSettings/plugins.metadata. The plugins themselves are installed into BespinSettings/plugins/. The metadata file includes all of the info objects from all of the plugins. Additionally, it stores where the plugin came form and where it is located within Bespin. | |||
When a plugin is installed, the module is loaded and the info object is extracted. For this reason, plugin modules should not actually *do* anything when they are loaded. The call to activate() is the time when the plugin should actually do something. | |||
=== Plugin loading === | |||
The idea behind this plugin system is to load as little as possible. Bespin will always load the plugins.metadata file. Based on the metadata, Bespin will decide when to load a plugin. The exact timing of loading the code will depend on what the plugin does. For example, syntax highlighters are loaded when a file of the appropriate type is opened. | |||
It will be possible to have a plugin load immediately, but the goal is to seek out ways to allow plugins to load more lazily. | |||
=== Singletons === | |||
Plugins are singletons; there is only one instance of a plugin in memory at a time. | |||
=== Disabling Plugins === | |||
Adding ?disable=ALL to the editor URL will turn off all plugins. Adding ?disable=PluginName will disable a single plugin. This can be used as an escape hatch if a plugin is ill-behaved and renders Bespin inoperable. | |||
== Plugin API == | |||
Functions available within the plugin's loading scope: | |||
=== | // require() loads a module. Modules are loaded only once, so calling | ||
// require a second time will simply return the same object. | |||
// var module = require("./modulename"); Example: | |||
var narcissus = require("narcissus"); | |||
= | var ast = narcissus.parse("1+1"); | ||
// resourceURL() computes URLs to resources relative to code modules. | |||
// If only one parameter is provided, the URL is relative to the plugin.json file. | |||
// Example: | |||
var url = resourceURL("bespin", "../../images/foo.png"); | |||
var url = resourceURL("images/foo.png"); // images directory at same level as plugin.json | |||
// subscribe() is like bespin.subscribe, except the plugin handler automatically | |||
// keeps track of subscriptions and will unsubscribe if the plugin needs to | |||
// be reloaded or deactivated. | |||
subscribe("plugin:activated", function(plugin) { | |||
// do something | |||
}); | |||
=== Bespin Extension API === | |||
The sections above describe the basic framework for handling plugins. The API for extending Bespin will be developed over time, based entirely upon the development of real-world plugins. Documentation in this spot will grow along with that API. | |||