diff options
| author | jacqueline <me@jacqueline.id.au> | 2023-12-13 16:10:08 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2023-12-13 16:10:08 +1100 |
| commit | 64b106c13e18c33be0f2b0de532054e0ed3f731d (patch) | |
| tree | b54b1c90d941bc456b4d51e864970720bdf2d648 /lib/lua-repl/plugins.md | |
| parent | 5a2f0b08e0e3f20cda977b510b680d5843ae7283 (diff) | |
| download | tangara-fw-64b106c13e18c33be0f2b0de532054e0ed3f731d.tar.gz | |
add a cool lua repl
Diffstat (limited to 'lib/lua-repl/plugins.md')
| -rw-r--r-- | lib/lua-repl/plugins.md | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/lib/lua-repl/plugins.md b/lib/lua-repl/plugins.md new file mode 100644 index 00000000..2a3591b4 --- /dev/null +++ b/lib/lua-repl/plugins.md @@ -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. |
