Fork of Tangara with customizations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
tangara-fw/lib/lua-repl/plugins.md

285 lines
10 KiB

# Plugins
As of version 0.3, lua-repl supports a plugin mechanism. For an example, please download the source code for
lua-repl and look at `repl/plugins/example.lua`.
# Available Plugins
lua-repl 0.3 ships with several plugins. If you use the example script `rep.lua`, some of them are loaded automatically;
these are marked with an asterisk.
## autoreturn (\*)
Automatically returns the the results of the evaluated expression. So instead of having to type 'return 3', you may
simply type '3'.
## completion (\*)
Provides completion facilities via the 'completion' feature. Other plugins may hook into this to provide tab completion.
## example
Simply an example plugin.
## history (\*)
Provides history facilities (and the 'history' feature), storing each line entered as an individual history entry,
and persisting the history in `$HOME/.rep.lua.history`. Other plugins may hook into this.
## keep\_last\_eval
Retains the results of the previously evaluated expression in global variables. The first result is stored in `_1`, the
second in `_2`, etc. For brevity's sake, the first result is also stored in `_`.
## linenoise (\*)
Hooks into the linenoise library. Allows the use of tab completion and history.
## pretty\_print
'Pretty prints' return values. Tables are printed in expanded form. Colors are provided if lua-term is installed.
## rcfile (\*)
Loads Lua code in `$HOME/.rep.lua` if the file exists. The repl object is provided to the file in a variable named `repl`, so
users may load plugins of their choosing.
## semicolon_suppress_output
Suppresses automatic printing of an expression's result if the expression ends in a semicolon.
# Creating a Plugin
If you would like to create your own plugin, it must be in a file under `repl/plugins/` in your `package.path`.
# Plugin Objects
When a plugin is loaded, it is provided with five special objects that are used to affect the behavior of the REPL
object loading the plugin.
## repl
The `repl` object is a proxy object for the REPL object loading the plugin; you can invoke methods on it, create methods on
it, set properties on it, etc. However, if you try to create a method on the `repl` object that already exists, an error
will occur. This is to keep plugin authors from stepping on each others' toes.
## before
The `before` object is another proxy object from the plugin environment. If you add methods to the `before` object, the original
method remains intact; however, the method you added will be called before the original. For example, if you wanted to print
"I got some results!" before you display them on the command line, your plugin could do this:
```lua
function before:displayresults(results)
print 'I got some results!'
end
```
This is called *advice*, and is stolen from Moose, an object system for the Perl programming language.
When you apply multiple pieces of advice via `before`, they are called in last-in-first-out order:
```lua
function before:method()
print 'Second!'
end
function before:method()
print 'First!'
end
```
`before` also receives all of the parameters to the original method. If they are tables, userdata, etc, you may alter them,
which can alter the behavior of the original method, for better or for worse.
If the method you are applying advice to does not exist on the current REPL object, an error will occur. This way, developers
can find out about API changes quickly, albeit noisily.
## after
The `after` object is another proxy object that attaches advice to the loading REPL object. As you can likely tell from its name,
advice applied via the `after` object occurs after the original method. Advice applied via after is called in first-in-first-out order:
```lua
function after:method()
print 'First!'
end
function after:method()
print 'Second!'
end
```
Like `before`, if you try applying advice to a method that doesn't exist, an error will occur. Also like `before`, after advice receives
all of the parameters passed to the original method.
## around
The `around` object is another advice object, but it works a little differently than `before` or `after`. `around` replaces the current
method will the advice, and like `before` and `after`, receives all of the parameters that would be passed to the original. However,
`around` also receives an additional parameter immediately before the parameters: the original method. This way, you can invoke
the method's original functionality if needed. For example:
```lua
function around:displayresults(orig, results)
print "I'm displaying some results!"
orig(self, results) -- don't forget self!
print "Now I'm done!"
end
```
Like the other advice objects, you can't apply advice to a method that doesn't exist. Also, be warned: the `around` advice does nothing
to make sure that the parameters are passed to the original function, and it doesn't make sure that the return values from the original
function are returned. You need to do that yourself.
## override
The `override` object isn't really an advice object; adding methods to it will replace the methods in the REPL object itself. However,
it will fail if that method does not already exist. The rule of thumb is if you want to add new methods to the REPL object, use
`repl`; if you want to completely override an existing method, use `override`. Keep in mind this will blow away all advice applied to
a method from other plugins; use with caution!
# Features
Sometimes, different plugins will want to provide a method, but implemented in a different way. For example, the completion plugin
included with lua-repl implements a tab completion method; however, if you are embedding lua-repl into your own environment, you may
have a more sophisicated way to provide completions. Other plugins (like the linenoise plugin) may want to hook into the completion
feature itself, without being tied to a particular implementation. So plugins may advertise a list of *features* that they provide,
so that they can develop loose relationships between one another. To advertise features for your plugin, simply set the features variable:
```lua
features = 'completion' -- make sure you're not setting a local!
```
If you wish you provide multiple features, simply use a table:
```lua
features = { 'completion', 'something_else' }
```
Obviously, plugins providing a feature need to agree on a standard interface of methods that they provide. No framework is in place for this as
of yet.
REPL objects may provide features as well; for example, `repl.console` provides the 'console' feature. You can use this to make sure your
plugins are only loaded in certain environments.
# REPL Methods
Now that you know how to affect the behavior of lua-repl with plugins, let's go over the methods you may advise/override, or call yourself from
within your advised/overridden methods. Please keep in mind that since lua-repl is still a young project, this API is subject to change.
## repl:getprompt(level)
Returns the prompt string displayed for the given prompt level, which is either 1 or 2.
1 signifies that the REPL is not in a multi-line expression (like a for loop); 2 signifies
otherwise.
## repl:prompt(level)
Actually displays the prompt for the given level. You more likely want to deal with
getprompt or showprompt.
## repl:name()
Returns the name of the REPL, used when compiling the chunks for evaluation.
## repl:traceback(err)
Returns a stack trace, prefixed by the given error message.
## repl:detectcontinue(err)
Detects whether or not the given error message means that more input is needed for a complete
chunk. You probably shouldn't touch this.
## repl:compilechunk(code)
Compiles the given chunk of code, returning a function, or a falsy value and an error message.
## repl:getcontext()
Returns the function environment that the REPL evaluates code in.
## repl:handleline(line)
Handles a line of input, returning the prompt level (1 or 2). Note that if this method is
called, an evaluation does not necessarily occur.
## repl:showprompt(prompt)
Displays the given prompt.
## repl:displayresults(results)
Displays the results from an evaluation. `results` is a table with the individual values in
the integer indices of the table, with the `n` key containing the number of values in the table.
## repl:displayerror(err)
Displays an error from an evaluation.
## repl:hasplugin(plugin)
Returns `true` if the given plugin has been loaded, `false` otherwise.
## repl:hasfeature(feature)
Returns `true` if the given feature has been loaded, `false` otherwise.
## repl:requirefeature(feature)
If the given feature has been loaded, do nothing. Otherwise, raise an error.
## repl:ifplugin(plugin, action)
If the given plugin has been loaded, call `action`. Otherwise, if the plugin
is ever loaded in the future, call `action` after that loading occurs.
## repl:iffeature(feature, action)
If the given feature has been loaded, call `action`. Otherwise, if the feature
is ever loaded in the future, call `action` after that loading occurs.
## repl:loadplugin(plugin)
Loads the given plugin. If the plugin returns a value, that value is returned.
## repl:shutdown()
Called when the REPL is exited. Don't call this yourself!
## repl:lines() -- repl.sync only
Returns an iterator that yields a line of input per invocation.
# The Future
This is the first release of lua-repl with plugins. The future will bring various
refinements to the plugin interface, along with the following planned features:
## Feature Interfaces
Earlier I mentioned that features have a sort of "gentlemen's aggreement" on what
methods they will provide. It would be nice if the plugin system had a way of
enforcing that.
## Attribute Storage
Currently, if a plugin wants to store some information between method calls, it needs
to store it on the REPL object (`self`) and hope no other plugins (or REPL clone) will
use the same name. Plugin-specific storage is a high priority.
## Configuration
Currently, plugins don't have any sort of configuration mechanism. I plan to change that.
## Library Plugins
Some plugins may want to leverage functionality of others without loading those others into
the REPL itself. I call these *library plugins*.
## Better Diagnostics
If you try to add a method that has already been added, or provide a feature that has already been
provided, you receive no information on which plugin provided the method or feature in question.
It would be nice to know.