Rust core · Ruby commands · Terminal

Rust keeps your text safe. Ruby makes the editor yours.

Rust owns the text, the edit history and the terminal. Ruby defines commands, keymaps and configuration. Change a command, reload it, and keep editing the same buffer.

Ruru logo: a stylised owl in night blues with amber eyes

01

Architecture

Ruby never holds the text. A command reads a bounded view from the Rust core and returns a change; the core checks it and publishes it, or rejects it whole.

Rust core
Immutable UTF-8 snapshots with exact byte, scalar and UTF-16 positions, LF lines and Unicode 17 grapheme boundaries. Owns revisions, undo and redo.
Ruby runtime
Embedded CRuby 3.4. Commands read bounded pages of a snapshot and compose private edits, with nested rollback, that commit as one change.
Terminal
The first frontend. Renders only what changed, and restores the terminal's screen, modes and settings on exit, including after errors.

02

Commands are Ruby files

A command is a Ruby callable registered by name. A keymap binds it to keys for one interaction profile. This file wraps the current line in brackets and binds it to Control-b.

examples/terminal-commands.rb
module Ruru::UserCommands
  def self.surround_current_line(context)
    line = context.current_line
    context.replace(line.merge('replacement' => "[#{line.fetch('text')}]"))
  end
end

configuration = Ruru.configuration
configuration.command('surround-current-line',
  Ruru::UserCommands.method(:surround_current_line), {
    'description' => 'Surround the current logical line with brackets',
    'kind' => 'compound', 'requires' => 'view',
    'effects' => ['text', 'selection']
  })
configuration.keymap('user-editing', {
  'profile' => 'nonmodal', 'states' => 'editing',
  'class' => 'user', 'priority' => 10
})
configuration.bind('user-editing', ['control-b'], 'surround-current-line')
  • One undo step. The whole command is undone with Control-z, however many edits it made.
  • Reload in place. Edit the file and run reload-configuration. The buffer and its history stay.
  • Failed reloads change nothing. If the new file is invalid, the previous commands and profile keep working.
  • Inspectable. describe-key shows which binding wins and where it was declared.

03

What it does

Everything below is a command, so all of it can be rebound, inspected or replaced from Ruby.

Editing
Cursor movement, selection and deletion work on graphemes, so emoji and combining marks behave as one character. Nonmodal or Normal/Insert profiles. Undo and redo treat each command as one step, whether it ran in Rust or Ruby.
Files and buffers
Open, switch and save local files. Saves are atomic: a failed write never leaves a half-written file. Closing or exiting with unsaved changes asks first. A buffer list saves or closes marked buffers together.
Search
Search and replace with plain text or regular expressions, across one buffer or a whole project.
Version control
Inspect Git status and history, stage files or hunks, commit, and work with branches and conflicts.
Structured documents
Outlines, tables, tasks, agendas, source blocks and export.
Language support
Ruby, Rust, Go, TypeScript, JavaScript, HTML and CSS. Syntax works without a language server; a configured server adds completion, diagnostics, navigation and formatting.
Programming
Write commands in Ruby, find them with Alt-x, bind them to keys and reload them during a session. describe-key and describe-command show what runs and where it was declared.
Packages
Install packages from catalogs, choose their permissions, and enable, update or roll them back.

04

Build it

Build from source with Rust 1.98, CRuby 3.4 with its headers and shared library, a C compiler and libclang. No gems are needed.

shell
$ git clone https://forge.adrianpk.com/ruru/ruru.git
$ cd ruru
$ env -u RUBYOPT -u RUBYLIB -u GEM_HOME -u GEM_PATH \
    cargo run --locked -p ruru-editor --bin ruru-terminal-proof -- \
    --config examples/terminal-commands.rb

Ruby environment overrides are rejected at startup, so the command clears them for this process only. Control-q exits and restores the terminal.