summaryrefslogtreecommitdiff
path: root/lib/lua-repl/plugins.md
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2023-12-13 16:10:08 +1100
committerjacqueline <me@jacqueline.id.au>2023-12-13 16:10:08 +1100
commit64b106c13e18c33be0f2b0de532054e0ed3f731d (patch)
treeb54b1c90d941bc456b4d51e864970720bdf2d648 /lib/lua-repl/plugins.md
parent5a2f0b08e0e3f20cda977b510b680d5843ae7283 (diff)
downloadtangara-fw-64b106c13e18c33be0f2b0de532054e0ed3f731d.tar.gz
add a cool lua repl
Diffstat (limited to 'lib/lua-repl/plugins.md')
-rw-r--r--lib/lua-repl/plugins.md285
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.