Ele: Extendable Lua Editor

Ele is an Extendable Lua Editor written to be easy to understand, extend and modify. It is the primary editor and shell for the Civstack project, and also the core framework for the devleopment of Civstack's text-based learning games.

To install: follow civ.html#install, ele will be installed by default.

Learn Ele in Y Minutes

To learn ele (or vim for that matter) just install civstack and run:
ele cmd/ele/README.cxt

Pressing ^q ^q (cntrl+q twice) will exit ele from any mode. You can move the cursor with the keys h j k l which are mapped to h=left j=down k=up l=right (yes they are weird but also easy to access).

The top left will say mode:command, meaning you are in command mode. In command mode, ele's key bindings are in a memonic "language" that is relatively quick to learn but does take some effort. Stringing multiple keys together is called a "chord", and the language of chords are typically of the form: * amount: for instance 2. * action/verb: for instance delete. * noun/movement: for instance word.

So typing 2 d w will delete two words. Typing actions twice tends to trigger a specialized action, for instance d d is delete line and 4 d d deletes four lines. Capital letters are also typically special, e.g. D deletes from the cursor to the end of the line.

You can press ? ? at any point to see what keys are available. You can also type ? <key> to see what that key would have done in that mode.

Architecture

Ele is architected using the MVI (model-view-intent) architecture, also known as the "React architecture" from the web library of the same name.

   ,_____________________________________________
   | intent(): keyboard, timer, executor, etc    |
   `~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
   /\                                    || Data + events
   || Data + scheduled                   \/
  ,__________________   Data + scheduled ,____________________________
  | view(): paint    | <================ | model(): keybind, actions  |
  `~~~~~~~~~~~~~~~~~~'                   `~~~~~~~~~~~~~~~~~~~~~~~~~~~~'

In practice, this is accomplished in four coroutines spawned by ele.lua's main function:

This roughly implements the MVI architecture because ALL actions are performed sequentially based on the ordering in the events channel.

Actions or plugins have the option to spawn their own coroutine. However, this behavior should be extremely rare, and reserved mostly for things that really can happen concurrently with no user feedback needed, such as saving a file, finding file lints, or updating syntax highlighting. Most real-world editor operations can block the user while they happen, and if they can't then they should consider not being included as an editor operation. Some exceptions such as searching for patterns in a recursive tree should be spawned as a coroutine but be cancelled if the user modifies the buffer that the results are being written to in any way.

Adding Bindings

Adding simple bindings is easy. Simply insert the space-separated chord of keys you want to go to your binding to one of ele's default modes, or create your own.

Mod ele.bindings

's command, insert and/or system entries are where you will find the default modes. For instance, the following will insert the expected text at the cursor position from command mode:

local B = require'ele.bindings'
B.command['y y'] = {action='insert', 'Why oh why did I bind this?\n'},

To write your own action you must:

Editor API

ele.Editor is the main object most custom actions or Ele scripts will interact with. It has several fields, but the fields and methods that most folks will care about are:

Most plugins will simply get edit and then insert/remove/search its buf data using its APIs and/or change its l,c (line,column) values. They are free to use any lua API to do so, but should avoid large amounts of work as much as possible.

Command ele

Usage: ele path/to/file.txt
The ele commandline editor. Arguments:

Mod ele.types

Core ele types.

Types: BufState PaneState State BasePane VSplit HSplit EditLoc

Functions

Record BufState

Cached buffer state

Fields:

Record PaneState

Cached window/pane state.

Fields:

Methods

Record State

Editor state for caching/reloading the current editor state.

Fields:

Record BasePane

The base record for Edit/Game.

These should be implemented:

function M.BasePane:state() --> PaneState
function M.BasePane.fromState(T, ed, s) --> new self

Fields:

Methods

Record VSplit

A container with windows split vertically (i.e. tall windows)

Fields:

Methods

Record HSplit

A container with windows split horizontally (i.e. wide windows)

Fields:

Methods

Record EditLoc

The location the cursor was at, to be in a stack.

Fields:

Methods

Mod ele.bindings

Types: KeySt KeyBindings

Functions

Record KeySt

The state of the keyboard input (chord). Some bindings are a simple action to perform, whereas callable bindings can update the KeySt to affect future ones, such as decimals causing later actions to be repeated a num of times.

Fields:

Methods

Record KeyBindings

A map of key -> binding. The name and doc can be provided for the user.

Other "fields" must be valid chords. They will be automatically split (by whitespace) to create sub-KeyBindings as-needed.

The value must be one of:

Fields:

Methods

Mod ele.edit

Types: Edit

Record Edit

Ele Edit View for viewing and editing text files in a pane.

Fields:

Methods

Mod ele.actions

Types: nav

Functions

Mod ele.actions.nav

Functions

Record Editor

Fields: Methods

Record Session

Fields: Methods

Mod ele.game

Ele game library.

Types: Game

Record Game

A Game window which renders a list of sprites.

Fields:

Methods

Mod ele.testing

Functions