Skip to content

Plugins

Luke edited this page Nov 3, 2024 · 35 revisions

Introduction

If there is a feature you would like to integrate into Ox that doesn't currently come as standard, then the plug-in API may allow you to implement this feature.

As with the rest of the configuration, the language used is Lua and plug-ins are imported in the same configuration file (~/.oxrc)

Official Plug-ins

You can also use other people's plug-ins as well as making your own. See the plugins folder in the repository to see some pre-made plugins, including auto bracket pairs and automatic indentation. To enable these plug-ins, please see the Plugin Distribution section below.

The below sub-sections outline the available plug-ins and how to enable them and use them

Pairs

Helps you out when programming by inserting and deleting bracket or quote pairs automatically. For example, if you type ( it will automatically insert a ) after the cursor so that you can immediately start typing between the brackets. You can also insert a space whilst inside a pair to space both of them out e.g. (|) becomes ( | ) (the cursor is shown as |)

☑️ Enabled by default

You can install your own local copy in ~/.config/ox by running the command plugin install pairs and then entering y at the prompt asking to reinstall.

You can also disable and uninstall it by running plugin uninstall pairs.

Autoindent

Helps you out when dealing with indentation, it will detect language-specific indicators of when an indent / decent is required, and it will also correct any indentation when you move lines up and down. Another feature is being able to expand brackets nicely, demonstrated below:

{|}

becomes

{
    |
}

when the return key is pressed between two brackets

☑️ Enabled by default

You can install your own local copy in ~/.config/ox by running the command plugin install autoindent and then entering y at the prompt asking to reinstall.

You can also disable and uninstall it by running plugin uninstall autoindent.

Usage:

You can use the key binding Ctrl + Tab to indent a line / selection of lines.

You can use the key binding Shift + Tab to dedent a line / selection of lines.

Quickcomment

Helps you to quickly comment and uncomment lines of code.

☑️ Enabled by default

You can install your own local copy in ~/.config/ox by running the command plugin install quickcomment and then entering y at the prompt asking to reinstall.

You can also disable and uninstall it by running plugin uninstall pairs

Usage:

By default, the key Alt + C will toggle comment / uncomment the current line

Pomodoro

image

This is a simple Pomodoro timer that can be used to break up your work with breaks. It uses the default 25 minute work followed by 5 minute break format, but can be modified using the parameters at the top of the ~/.config/ox/pomodoro.lua file.

❌ Not installed by default

To install: run the command plugin install pomodoro

To uninstall: run the command plugin uninstall pomodoro

Usage:

The first thing you want to do is add the output of the pomodoro somewhere in your editor (it is recommended to put it in the status line)

To do this, go to your .oxrc file and in your status line configuration section, add {pomodoro_show} to a part of your status line.

E.g. status_line.parts = { " {file_name} | {pomodoro_show} " } will render the file name of the file currently in view and will show the output of the pomodoro timer to the right.

You may need to restart Ox for it all to work correctly

Once everything is up and running, you should now see No Pomodoro Active

To start the pomodoro timer, run the command pomodoro start

To end the pomodoro timer, run the command pomodoro stop

Update Notification

image

This is a very simple plug-in that will notify you if your currently installed Ox version differs from the latest one

This plug-in uses networking so will require curl on all systems, including Windows.

To install: run the command plugin install update_notification

To uninstall: run the command plugin uninstall update_notification

Typing Speed

image

A plug-in to track how fast you are typing.

It will use how quickly you press the space bar.

It is limited in accuracy due to Lua's limited date and time API, which only allows measurement of seconds, but it will be roughly correct.

To install: run the command plugin install typing_speed

To uninstall: run the command plugin uninstall typing_speed

Usage:

You will need to add it somewhere to the editor

You can do this in the greeting message, help message, status line or even the tab line.

To do this, simply add in {typing_speed_show} into the format strings of any of the lines / messages in your .oxrc file.

E.g.

status_line.parts = { " {typing_speed_show} " }

Todo Lists

A plug-in that adds support for todo files.

To install: run the command plugin install todo

To uninstall: run the command plugin uninstall todo

Usage:

Todo files have the extension .todo.

They are formatted like so:

- [ ] This task is not done
- [X] This task is completed

If you put your cursor over one of the lines, and press Ctrl + Enter you can change a todo from done to not done (and vice versa).

If you change the state of a todo, it will also show you the statistics on how many todos you have done at the bottom of the editor.

Live HTML

If you find yourself writing a lot of web pages, then this plug-in will help you visualise your changes instantly, as soon as you type them.

⚠️ Please note this is only confirmed to be working on Linux as of now.

Install:

Run the command plugin install live_html

You will also need a browser installed, along with Python and the flask module, which can be installed through the command pip install Flask

Uninstall:

Run the command plugin uninstall live_html

You can also uninstall python and the flask module if you would like to

Usage:

To use this plug-in, open up the file that is the entry point of your application e.g. index.html and then run the command html start.

By default, you will be able to see your website by navigating to localhost:5000 in your browser.

To stop the HTML live server, run the command html stop.

When you close the editor, the HTML server will automatically stop for you.

Be default, every time you press Ctrl + S (mirroring the default save command), all browsers currently on localhost:5000 will reload the page, but you can also have it refresh whenever the document is changed in-editor so it updates as you type. To do this, add the line live_html = { refresh_when = "keypress" }.

If you have a CSS file, for example, and you want livehtml to update the preview when you edit it, you can run the command html track [file name] e.g. html track style.css, and when you update style.css, it will refresh the main website too so you can see changes in style instead of waiting for you to edit your main entry point i.e. index.html where you ran html start

Emmet

This is a plug-in that will help you in web development through the Emmet library.

Install:

Run the command plugin install emmet

You need to have python and the py-emmet library installed. You can install the py-emmet library through the command pip install py-emmet.

You also need the autoindent plug-in installed and enabled so that it can correctly provide indented expanded code. Run plugin install autoindent to ensure that it is enabled

Uninstall:

Run the command plugin uninstall emmet

You can then uninstall python or the py-emmet module if you desire.

Usage:

E.g. if you type ul>li*5, you can use the key binding Ctrl + M to activate the emmet plug-in, which will look on the line the cursor is on, take it as emmet code and then expand and insert the expanded HTML as

<ul>
	<li></li>
	<li></li>
	<li></li>
	<li></li>
	<li></li>
</ul>

Git

image

image

A plug-in that will help you interact with git without needing to leave the editor

It features the following features:

  • See and change the current branch
  • See which files are staged or modified, and stage/unstage them
  • Create commits
  • Push and pull changes
  • View diffs

Install:

Make sure you have git installed and accessible on the command line. Then you can run the command plugin install git

Uninstall:

Run the command plugin uninstall git and then you can uninstall git if you'd like.

Usage:

This is a complicated plug-in and there are multiple commands and additions you can put in.

This plug-in provides {git_branch} and {git_status} for your status line / tab line.

My personal recommendation is to add {git_status} to your tab line to see which files are staged / modified. Also add {git_branch} to your status line see the current branch you're on.

By default, the git plug-in will not use nerd font icons. If you want to enable these, edit the git.lua file where your extensions are (~/.config/ox/git.lua on UNIX based systems, ~/ox/git.lua on windows where ~ is the root of your user home directory).

Set icons = true in the following table and save your file changes:

git = {
    status = {},
    icons = false, -- change this to true to enable icons
}

As for the commands that you can use:

  • git add all - Stage all files
  • git add - Stage currently open file
  • git reset all - Unstage all files
  • git reset - Unstage currently open file
  • git diff all - View the diff before all files in the repository
  • git diff - View the diff in this file in particular
  • git stat all - View the overall statistics of the changes in the repository
  • git stat - View the statistics of the changes of the currently open file
  • git commit - Create a commit
  • git push - Push local commits upstream
  • git pull - Pull commits from upstream
  • git checkout [branch name] - Switch to a different branch

Discord RPC

image

A plug-in that shows your Ox activity to other discord users.

⚠️ Please note this is only confirmed to be working on Linux as of now.

Install:

Make sure this setting is enabled for your Discord account.

Run the command plugin install discord_rpc

After this, you should also make sure you have Python installed and the discord-rpc module installed or the plug-in will not work.

You can install the discord-rpc module through the command pip install discord-rpc

Uninstall:

Run the command plugin uninstall discord_rpc

You can also uninstall python / the discord-rpc module if you would like.

Built-in Plug-ins

Some of the above plug-ins are "built-in" which means that you don't actually need to install them as they come with the Ox binary as standard and are enabled.

The built-in plug-ins are: autoindent and pairs and quickcomment.

If you want to run a modified version of a built-in plug-in, then you can place the modified lua file of these plug-ins, and install them just as you would any other plug-in (see distribution of plug-ins).

At the bottom of your user configuration file, add load_plugin("name_of_plugin.lua").

If you want to disable a built-in plug-in because you don't like the feature, just create a user configuration and don't include the load_plugin call for the plug-in you want to disable and ox will recognise your preference and not load it in.

APIs

Editor API

When writing plug-ins, you are very likely going to want to interact with the editor somehow, whether it's displaying a message to the user, or writing some text for them. The way to interact with the editor is through the API. There are a wide range of methods available for you to call, attached to an editor table.

Here are all the commands that you can run that may come in handy:

Command Description Example usage
editor:display_error This command will display an error message to the user (red by default) editor:display_error("file not found") which will display an error message saying "file not found"
editor:display_warning This command will display a warning message to the user (yellow by default) editor:display_warning("backup failed") which will show a warning message saying "backup failed"
editor:display_info This command will display an information message to the user (blue by default) editor:display_info("time to take a break") which will show info to the user asking them to take a break
editor:prompt This command will ask the user for something and return what they entered name = editor:prompt("What's your name") which will ask the user their name and store their answer in the name variable
editor:insert This command will insert specified text at the place where the cursor currently is in the document editor:insert("3.141") which will insert PI (the number) where the user's cursor currently is
editor:remove This command will essentially just simulate the user pressing the backspace key, and will delete whatever is behind the cursor editor:remove() which will simulate the user pressing backspace
editor:insert_line This command will insert a line at the current position of the cursor, it essentially simulates the user pressing the enter key editor:insert_line() which will simulate the user pressing enter
editor:remove_line This command will delete the line that the cursor is currently on editor:remove_line() which will remove the line where the cursor is, e.g. line 3 if the cursor is on line 3
editor:move_to This command will move the cursor to a specified x and y coordinate in the document editor:move_to(3, 1) which will move the cursor to the third character (x coordinate) on the first line (y coordinate)
editor:move_up This command will move the cursor one upwards editor:move_up() which will move the cursor to the line above where it currently is
editor:move_down This command will move the cursor one downwards editor:move_down() which will move the cursor to the line below where it currently is
editor:move_left This command will move the cursor one to the left editor:move_left() which will move the cursor to the left
editor:move_right This command will move the cursor one to the right editor:move_right() which will move the cursor to the right
editor:move_home This command will move the cursor to the beginning of the current line editor:move_home() which will move the cursor to the beginning of the line it is currently on
editor:move_end This command will move the cursor to the end of the current line editor:move_end() which will move the cursor to the very end of the line it is currently on
editor:move_page_up This command will scroll up a page e.g. if the viewport is 20 lines in height, then it will scroll 20 lines upwards editor:move_page_up() which will move up one page
editor:move_page_down This command will scroll down a page e.g. if the viewport is 20 lines in height, then it will scroll 20 lines downwards editor:move_page_down() which will move down one page
editor:move_top This command will move the cursor to the first line in the document editor:move_top() which will move to the top of the document
editor:move_bottom This command will move the cursor to the last line in the document editor:move_bottom() which will move to the bottom of the document
editor:move_previous_word This command will move the cursor to the previous word (separated by spaces) editor:move_previous_word() which will move the cursor to the previous word
editor:move_next_word This command will move the cursor to the next word (separated by spaces) editor:move_next_word() which will move the cursor to the next word
editor:insert_at This command will insert text at a certain x and y location editor:insert_at("Hello", 2, 3) which will insert the text "hello" at the 2nd character of the 3rd line
editor:remove_at This command will remove the a character at a certain x and y location editor:remove_at(4, 1) which will remove the 4th character on the 1st line
editor:insert_line_at This command will insert a line at a certain line number editor:insert_line_at("new line", 4) which will create a new line on line 4 of the document with the text "new line"
editor:remove_line_at This command will remove the line at a certain index editor:remove_line_at(4) which will remove the 4th line in the document
editor:open_command_line This command will open the command line editor:open_command_line() which will open the command line
editor:previous_tab This command will move to the previous document that is currently opened in the editor editor:previous_tab() which will move focus to the previous document that is currently opened
editor:next_tab This command will move to the next document that is currently opened in the editor editor:next_tab() which will move focus to the next document that is currently opened
editor:new This command will create and open a new document for the user to write in editor:new() which will create a new document
editor:open This command will ask the user for a file name and then open that file editor:open() which will ask the user for a file to open, and then open it
editor:save This command will save the document the user is currently on editor:save() which will save the current document
editor:save_as This command will ask the user for a file name and save the current document to that path editor:save_as() which will ask the user for a path to save the current document to
editor:save_all This command will save all currently opened documents editor:() which will save all opened documents
editor:quit This command will quit the current document (and the whole editor if all documents are quit out of) editor:quit() which will close the current document (and the whole editor if all documents have been closed)
editor:undo This command will trigger an undo action editor:undo() which will undo the last change the user made
editor:redo This command will trigger a redo action editor:redo() which will redo the last change the user made
editor:commit This command will create a snapshot to restore to on undo/redo editor:commit() which will commit changes to the event stack
editor:search This command will trigger the search wizard editor:search() which will ask the user for what they wish to search for and move the cursor along all matches
editor:replace This command will open the replace wizard editor:replace() which will ask the user what they want to replace and what to replace with, and then provide options to replace certain instances or all instances
editor:get This command will get the entire contents of the file editor:get() which will get the entire contents of the currently opened document
editor:get_character This command will get the character that the cursor is currently over editor:get_character() which will return e, for example, if the cursor in the editor is next to the letter e in the document
editor:get_character_at This command will get a character at a certain location within the document editor:get_character_at(3, 1) which will get the 3rd character on the first line
editor:get_line This command will get the line the cursor is currently on in the document editor:get_line() which will get line 2, for example, if the cursor in the document is currently at line 2
editor:get_line_at This command will get the line at a certain index editor:get_line_at(5) which will get the fifth line in the document
editor:move_to_document This command will move to a document that is currently open editor:move_to_document(0) which will move to the first document that is open in the editor
editor:move_previous_match This command will move to a previous instance of a certain string editor:move_previous_match("self") which will search backwards until it finds the text self, and then move the cursor to where that text is in the document
editor:move_next_match This command will move to the next instance of a certain string editor:move_next_match("bar") which will search forwards until it finds the text bar, and then move the cursor to where that text is in the document
editor:hide_help_message This command will hide the in-editor help message editor:hide_help_message() which will hide the help message
editor:show_help_message This command will show the in-editor help message editor:show_help_message() which will show the help message
editor:set_read_only This command will set the file's read only status editor:set_read_only(true) which will make the file that is currently open read only
editor:set_file_type This command will set the file type of the file that is currently open editor:set_file_type("Python") which will set the file type of the file currently open to a python file, and enable language-specific features such as syntax highlighting. Note the argument is the file type name in version 0.6.6 and up, and the file extension (e.g. py) in lower versions
editor:select_up This command will move the cursor up whilst selecting editor:select_up() which will move the cursor up whilst selecting
editor:select_down This command will move the cursor down whilst selecting editor:select_down() which will move the cursor down whilst selecting
editor:select_left This command will move the cursor left whilst selecting editor:select_left() which will move the cursor left whilst selecting
editor:select_right This command will move the cursor right whilst selecting editor:select_right() which will move the cursor right whilst selecting
editor:select_all This command will select all the text in the currently open document editor:select_all() which will select all the text in the currently open document
editor:select_to This command will select to a certain position within the document editor:select_to(x, y) which will move to the x position x and the y position y.
editor:cancel_selection This command will cancel the current selection. editor:cancel_selection() which will cancel the current selection.
editor:cursor_to_viewport This command will move the viewport over the cursor. editor:cursor_to_viewport() which will move the viewport over the cursor position.
editor:copy This command will copy the currently selected text editor:copy() which will copy the currently selected text
editor:reload_config This command will reload the config file for when you want to load in any changes editor:reload_config() which will reload the configuration file
editor:reload_plugins This command will reload the plugins for when you want to load in any changes editor:reload_plugins() which will reload the plugins
editor:rerender This command will force the editor to re-render its interface editor:rerender() which will force a rerender
editor:rerender_feedback_line This command will force the editor to re-render just the feedback line editor:rerender_feedback_line() which will force a rerender on just the feedback line
editor:rerender_status_line This command will force the editor to re-render just the status line editor:rerender_status_line() which will force a rerender on just the status line
editor:reset_terminal This command will reset the terminal to ensure that everything works correctly after another TUI program hijacks the terminal. editor:reset_terminal() which resets the terminal
editor:cursor_snap This command will cache the current y position of the cursor such that when the user moves directly up or down, it will retain that y position. editor:cursor_snap() which caches the y position of the cursor.
editor:move_line_up This command will move the current line the user is on upwards. editor:move_line_up() which swaps the current line with the line above.
editor:move_line_down This command will move the current line the user is on downwards. editor:move_line_down() which swaps the current line with the line below.
editor:panic This command will immediately halt the editor and print the string provided in the argument, this should only be used for debugging purposes. editor:panic("This was run") which will purposefully crash the editor and print the text [Error] This was run
editor.cursor This command will return the current cursor position of the document editor.cursor.x which will return the x coordinate of the cursor (the character) and editor.cursor.y which will return the y position of the cursor (line number)
editor.selection This command will return the current position of where the selection starts or ends within the document editor.selection.y which will return the line that the selection currently ends / starts on
editor.file_name This command will return the name of the file currently open editor.file_name which will return the name of the file that is currently being looked at
editor.file_extension This command will return the extension of the file currently open editor.file_extension which will return the extension of the file that is currently being looked at
editor.file_path This command will return the path of the file currently open editor.file_path which will return the path of the file that is currently being looked at
editor.document_name This command will return the name of the document editor.document_name which will return the name of the document that is currently being looked at
editor.document_length This command will return the number of lines in the document currently open editor.document_length which will return 300 if the document currently being looked at is 300 lines in length
editor.document_type This command will return the type of document you're looking at editor.document_type which will return "Python", for example, if you are looking at a .py file
editor.version This command will return the version of the editor editor.version which will return "0.4.3" if you're using version 0.4.3 of Ox
editor.current_document_id This command will give the ID of the document that the user is currently looking at editor.current_document_id which will return 1 for example, if the user is looking at the second document open and 0 if the user is looking at the first one
editor.document_count This command will return the number of documents currently open editor.document_count which will return 5 if there are 5 documents currently open in the editor
editor.pasting This command will return true if the editor is currently having text pasted into it editor.pasting will return true if the user is currently pasting something into the editor and vice versa for false
editor.cwd Returns the current working directory editor.cwd will get the current working directory based on the file the user is currently looking at.

Now you know how to interact and control the editor from your plug-ins, you can get to work writing them.

Networking API

You can make http get, post, put and delete requests from within your plug-ins.

Every plug-in will have the http table available to it.

Here is an example of a http get request that checks the version of the latest release of Ox

-- Get the contents of the latest Cargo.toml
local cargo_latest = http.get("https://raw.githubusercontent.com/curlpipe/ox/refs/heads/master/Cargo.toml")
-- Extract the version from the build file
local version = cargo_latest:match("version%s*=%s*\"(%d+.%d+.%d+)\"")
-- Display it to the user
if version ~= nil then
    editor:display_info("The latest version of Ox is: " .. version)
end

In full, the available http method calls:

  • http.get(url)
  • http.post(url, data)
  • http.put(url, data)
  • http.delete(url)

Under the hood, these call either curl across all operating systems, including windows.

Make sure you have curl installed if you wish to use networking on Linux or macOS or windows, although most of the time this binary is pre-installed on these systems.

Concurrency API

There is a very very simple concurrency API included everywhere you write plug-ins.

There are two functions you can make use of:

  • after(delay, name) - runs a function called "name" after delay seconds
  • every(interval, name) - runs a function called "name" every interval seconds

Be warned - name must be a string referring to the name of a globally-available function.

Also, if you make changes that will result in the interface itself updating - run editor:rerender() to tell the editor to rerender the UI and show any changes.

You can also use editor:rerender_status_line() or editor:rerender_feedback_line() to speed up rendering (as it only rerenders a small part of the editor rather than the whole thing)

Here is an example:

function hello()
    editor:display_info("Hello!")
    -- Rerender just the feedback line as this is the only part that will have changed
    editor:rerender_feedback_line()
end

every(1, "hello")

this defines a function hello that is globally available, which will display hello in the status line, rerender the editor to show the new UI change. hello is called every second, essentially spamming "Hello!" into the status line.

Another handy trick is this - if you want to run a function that doesn't lock up the entire editor, then you can use after(0, name), which itself isn't blocking, however the function given as name will be run immediately.

This should be enough to get lua code running in the background.

Shell API

If you want to run shell commands, it is highly recommended that you use the shell API. This API allows for cross-platform commands and it silences commands so they don't mess around with the editor's rendering. This API is built 100% in Lua, it's just a wrapper around Lua's built-in os.execute and io.popen methods.

There are two basic methods available to all plug-ins:

  • shell:run(command) - this will run the command given as a parameter, and return the exit code (0 if successful)
  • shell:output(command) - this will run the command given as a parameter, and return a string of the resulting stdout and stderr

Using these two commands, you can cover 90% of what you'd want to do with shell commands

You can also make use of forked processes and have them run alongside the editor without locking up the editor itself.

There are two main commands here:

  • shell:spawn(command) - this will run the command given as a parameter, fork off the process and return the PID of the process (store this PID in a variable if you want to kill the process at a later time)
  • shell:kill(pid) - this will kill the process spawned by the shell:spawn command.

Here is an example:

-- Run an external script
local process = shell:spawn("some_script.sh")

-- Kill the external script when the user presses ctrl + shift + k
event_mapping["ctrl_shift_k"] = function()
    shell:kill(process)
end

Other APIs

Please note that you can use all APIs available in the standard library of Lua, e.g. array manipulation / os interaction

You can also access anything you would be able to access in the configuration file (in case you want your plug-in to modify settings)

Note that you will not be able to use external lua libraries without downloding the code for these libraries themselves. Therefore, if you're looking for certain functionality that is just not provided, then be sure to look above at the available APIs for if the functionality has already been implemented, and worst case, open an issue on github to request a certain feature to be provided to plug-ins.

Here is a random list of internal functions that you might find useful:

  • dir_exists("path/to/dir") - returns true if the directory exists, otherwise false
  • file_exists("path/to/file") - returns true if the file exists, otherwise false

Examples of Plug-ins

Displaying the date and time to the user in the editor

local date = shell:output("date") -- run the "date" command to get the date and time

-- Now display to the user the date and time
editor:display_info("The date and time is currently: " .. date)

-- Alternatively, you could use Lua's standard library `os.date()`

Auto HTML tags

Here, we ask the user for their HTML tag name e.g. body or h1 and then insert both the starting and ending tags for them to use

tag_name = editor:prompt("Tag name")
editor:insert("<" .. tag_name .. ">\n\n</" .. tag_name .. ">" )

For example, if the user puts in style, it would write:

<style>

</style>

in their document (at the location of where the cursor currently is)

This would be a quick way to write HTML tags without having to repeat yourself all the time

Running your plug-ins

Now you've been able to understand how to write your plug-ins, we must go over how to start them when in the editor.

There are three ways to trigger plug-ins:

  1. On key press
  2. When a command is run (by default, you can access the command line by using the key combination Ctrl + K and then typing in a command there)
  3. When rendering the status line, greeting message or help message

Key Bindings

The format of keys

In the configuration file, key presses are represented with strings.

Modifiers, such as ctrl, alt and shift come first, followed by key codes e.g. backspace or z or 4

Here are some examples:

  • a - the a key on the keyboard is pressed
  • ctrl_a - the control key is held down, followed by the a key being pressed
  • ctrl_alt_s - the control key is held down, then the alt key is held, then the s key is pressed
  • esc - the escape key is pressed

Mapping key bindings to plug-ins

When the user presses a key, Ox will send that key press through to your configuration file. It will check in a table called event_mapping for a key combination corresponding to the key the user pressed and then run the function that was attached to that key combination.

It's probably better demonstrated with an example:

event_mapping = {
    ["ctrl_h"] = function()
        editor:display_info("You pressed CTRL and H")
    end,
}

here you can see the binding of ctrl h to a function which will display an info message to the user.

in the default config file (in config/.oxrc in the repository) you can see a list of all the key bindings that are used by default.

You are free to use any key binding for whatever purpose you please.

Before vs After editor applies an event

By default, when you bind a key in the event mapping, the function you provide will be executed after the editor has applied it's own edits.

E.g. binding to the a key means that the code you provide to the event_mapping will inherit an editor state with the a already present. If you were to run editor:get_line() you would see that a in there already.

This is not always ideal. For example, say you want to check the existing state of the document before any edits are applied so that you can run some code after the edit that takes into account the previous state of the document.

To do this, you can use the before: syntax before the key you are binding to:

event_mapping["before:a"] = function()
    -- Is there an a already in the document?
    if editor:get_character() == "a" then
        -- Yes, there is, delete it to avoid duplicate As
        editor:remove_character_at(editor.cursor.x, editor.cursor.y)
    end
end

This code checks if there is an existing a in the document, and will remove it if there is (before the editor inserts its own a as per the user's key press)

This is used heavily in the pairs plug-in because pairs needs to see if you're in a bracket pair when pressing backspace.

E.g. say our cursor is like this between two brackets: (|) if we press backspace, it'll look like this |), by which point if we had bound a traditional non-before: event, we would be missing that ( that was just deleted, and would have absolutely no idea what was just deleted, hence we couldn't verify if we had just deleted the starting pair of a bracket pair, hence we wouldn't be able to tell to delete the corresponding end pair.

Additional Events

You can also bind an event for when the editor exits. This is useful for any cleanup your plug-in needs to do.

event_mapping["exit"] = function()
    -- Run code when the editor exits
end

By default, paste events are not given to normal bindings e.g. if you paste the string test into the editor, it won't issue events for t, e and s and t (again). To react to paste events, you must use the following:

event_mapping["paste"] = function(paste_text)
    -- paste_text contains the text that the user has pasted
end

Custom Commands

So, perhaps you don't want to run a plugin on a key press, or you would like your plug-in to act differently depending on what the user inputs. This can be done through custom commands.

Just like with key bindings, you can add custom commands into the configuration file at ~/.oxrc.

When a command is run, it is shown to the configuration file, which can then determine how to deal with that command.

The command table defines how to handle various commands to the user. The keys are the command name, and the values are functions.

Here is an example:

commands = {
    ["greet"] = function(arguments)
        editor:display_info("Hello, " .. arguments[1] .. ", nice to meet you")
    end,
}

When the user writes a command that starts with greet, followed by some arguments (separated by spaces), it will take the first argument the user provided, and display a message to greet the user.

For example, if I were to open the command line using Ctrl + K (which is the default key binding for opening the command line), and run the command greet James then the first argument would be James, and the editor would display Hello, James, nice to meet you. Arguments are a list of what the user has put in (after the initial command name, which is greet in this case)

You could extend on this (to demonstrate multiple arguments) as such:

commands = {
    ["greet"] = function(arguments)
        first_name = arguments[1]
        surname = arguments[2]
        editor:display_info("Nice to meet you, " .. first_name .. " " .. surname)
    end,
}

where we use the first argument as the first name and the second as the surname.

E.g. greet James Smith would display Nice to meet you, James Smith to the console (here using both the first and second argument)

On The Status Line, Greeting Message or Help Message

In your plugin, if you were to write a function that returned a string, such as:

function show_pomodoro()
    return "Time remaining: " .. pomodoro.time_left .. " minutes"
end

then you would be able to put the return value of this function into the status line, by adding {show_pomodoro} in one of the parts of the status line, greeting message or help message.

Now, every time the editor renders, it will call your lua function, take the string that was returned and then display on the status line.

This makes your plug-ins feel just like a built-in feature!

Distribution of Plug-ins

There may come a time where you want to distribute your plug-in or download someone else's plug-in.

Using the plug-in manager

Ensure you have updated to at least Ox 0.6.3 to use the plug-in manager

This is a very simple way to install and uninstall and check which plug-ins are installed.

This can be accessed in the command line (Ctrl + K).

Within the command line, you can use one of the following commands:

  • plugin install [plugin name] - install a plug-in (make sure you substitute the correct name of the plug-in in without the .lua extension)
  • plugin uninstall [plugin name] - uninstall a plug-in (make sure you substitute the correct name of the plug-in without the .lua extension)
  • plugin status - check which plug-ins are enabled
  • plugin update - check for and update any out-of-date plug-ins

Please open a pull request if you would like to add your own plug-in to Ox, add it to the /plugins folder at the root of the repository. This folder is where the plugin command gets the code from. By adding your plug-in to that folder, you add it to the plug-in manager within Ox.

Manual Method

If the plug-in manager isn't working, you can use this method, which is essentially doing what the plug-in manager above will do for you.

Downloading a Plug-in

You can see a list of plug-ins you might want to use in the plugins folder in the repository. This includes plug-ins such as auto bracket pairs (for quick editing) and auto indentation in code files.

You may also find another plug-in elsewhere, such as on github.

Plug-ins should be in a single lua file (or multiple lua files with a central one that imports the others).

Download all lua files and place them in your ~/.config/ox/ folder (on unix based systems) or in an ox folder in your user's home directory on windows (e.g. C:/Users/Boris/ox/).

Then, in your configuration file, you can import plugins by using the load_plugin function, which takes as argument the file name of the plug-in e.g. pomodoro.lua: load_plugin("pomodoro.lua").

Example

Say you want to install the pairs.lua plug-in, which is included in the plugins folder.

  1. Download it and put it in the ~/.config/ox/ folder as ~/.config/ox/pairs.lua
  2. Load it in your configuration file (it is best to put it at the bottom of the config file)
    load_plugin("pairs.lua")
    Ox will look in the directory for the plug-in pairs.lua, which we downloaded in step 1
  3. Now when the editor is opened, the plugin should be up and running

Packaging a Plug-in

If you want to set key bindings in your plug-in, you can do it, for example, like this:

event_mapping["ctrl_h"] = function()
    -- Your plugin code goes here
end

This just adds to the same event_mapping object the user has in their config file.

Similarly, to add in a command, you can do the following:

commands["name"] = function(arguments)
    -- Your plugin code goes here
end

To package a plug-in, simply take the lua files where you have written your plug-in and then you are free to distribute them on github or through other means where users can download and import them.

If you want to add it to the plug-in manager (so that users can install your plug-in by simply running the command plugin install [your plugin name] then please feel free to create a pull request with your plug-in lua code in the plugins directory in the Ox repository)