add a cool lua repl
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user