===============================================================================
 Writing a plugin for LinuxNoteText in Lua
===============================================================================

 The five folders beside this one are working plugins, each kept short and
 each showing a different part of what a plugin can do. Read hello_lua
 first; it is nine lines.

     hello_lua         the smallest plugin that does anything
     change_case       three commands from one plugin, editing the selection
     sum_numbers       reading only, and Lua patterns
     align_equals      a real transformation over many lines
     stamp_modified    running on save rather than from the menu

 To write your own, copy one of these folders into

     ~/.config/LinuxNoteText/plugins/

 and change it. A plugin there replaces a bundled one of the same name, and
 arrives switched off until you switch it on in the plugin manager.


-------------------------------------------------------------------------------
 A plugin is two files
-------------------------------------------------------------------------------

 plugin.ini says what it is and what it needs. Reading it never runs
 anything, which is why the editor can list a plugin it has never executed:

     [plugin]
     name = Sum Numbers
     version = 1.0
     api = 1
     author = your name
     description = What it does, in a sentence.
     entry = plugin.lua

     [capabilities]
     read_text = yes

 plugin.lua is the plugin. It must define one function:

     function register(editor)
         editor.add_command("Do the thing", function()
             editor.status("did the thing")
         end)
     end

 The editor calls register once, when the plugin is switched on, and never
 again. Everything the plugin adds, it adds in there.


-------------------------------------------------------------------------------
 The one mistake worth warning about
-------------------------------------------------------------------------------

 Use a dot, not a colon:

     editor.text()        yes
     editor:text()        no

 The editor is a table of plain functions, not an object with methods, so
 there is no self to pass. The colon form hands the table in as the first
 argument and the error you get will not obviously say so.


-------------------------------------------------------------------------------
 What a plugin is given
-------------------------------------------------------------------------------

 READING            needs read_text
     editor.text()                the whole document, as a string
     editor.selected_text()       the selection, or "" if there is none
     editor.file_path()           the file, or "" if it has not been saved
     editor.language()            what it is being coloured as
     editor.line_count()          how many lines

 WRITING            needs change_text
     editor.set_text(s)           replace the whole document
     editor.replace_selection(s)  replace what is selected
     editor.insert_at_caret(s)    put text in at the cursor

 THE MENU AND THE HOOKS
     editor.add_command(label, fn)              add a menu entry
     editor.add_command(label, fn, "Ctrl+K")    ...with a shortcut
     editor.on_save(fn)           run fn(path) every time a file is saved
     editor.on_open(fn)           run fn(path) every time one is opened

 SAYING SOMETHING
     editor.status(s)             one line, in the status bar
     editor.message(s)            a box the person has to dismiss
     editor.message(s, title)     ...with a title

 ABOUT ITSELF
     editor.plugin_folder()       the plugin's own folder
     editor.api_version()         the plugin API this build speaks

 Whatever a command does to the document is one undo step, however many
 separate changes it made.


-------------------------------------------------------------------------------
 What a plugin is NOT given, and why
-------------------------------------------------------------------------------

 A Lua plugin runs in an interpreter the editor builds for it. These are
 not in it:

     io          package     require     load        loadstring
     dofile      loadfile    debug       python      collectgarbage

 and most of os. What is left of os is the part that only tells the time:

     os.date     os.time     os.clock    os.difftime

 Everything else in Lua is there and normal — strings, tables, math,
 pcall, coroutines, metatables.

 This is a real restriction rather than a promise. A Lua plugin has no
 import and no io, so it cannot read your files, write to them, use the
 network or start a program — not because it undertook not to, but because
 there is no way to from inside. That is the difference between a Lua
 plugin and a Python one: a Python plugin's capabilities are what it asked
 the editor for, and Python code that goes around the editor's API is not
 stopped by it.

 What this is still not: a security boundary of the kind a separate process
 would give you. A Lua plugin can loop forever and hang the editor, and it
 can do anything it was granted. Run plugins you have reason to trust.


-------------------------------------------------------------------------------
 When it goes wrong
-------------------------------------------------------------------------------

 A plugin that raises has the error recorded and shown in the plugin
 manager, and the editor carries on. One that fails three times in a row is
 switched off rather than allowed to interrupt every keystroke.

 A capability you did not ask for gives a refusal that names it and says
 which line to add to plugin.ini.

 Two things about Lua specifically:

   * Lua checks os.date format strings and rejects anything outside
     standard C. %-d, which drops a leading zero on glibc, raises here.
     tonumber(os.date("%d")) does the same job everywhere.

   * Lua patterns are not regular expressions. There is no \b, no |, and
     no +? — see the second half of sum_numbers and align_equals for what
     the usual ways round them look like.
