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.
285 lines
10 KiB
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.
|
|
|