v0.1.19 — Written in Rust

ASAT

// A spreadsheet editor that speaks Vim

Edit spreadsheets at the speed of thought. Modal keybindings, live formula evaluation, multi-sheet workbooks, and a full undo stack — all in your terminal.

asat budget.xlsx
ASAT v0.1.19 — Advanced Spreadsheet Alteration Tool B3 =SUM(B1:B2) ┄┄┄┄┄┄ A ┄┄┄┄┄┄┄ ┄┄┄┄┄┄ B ┄┄┄┄┄┄ 1 Revenue 84,200 2 Expenses -31,450 3 Net 52,750 4 NORMAL budget.xlsx · B3 / Sheet1

Everything you need.
Nothing you don't.

Built from the ground up for keyboard-driven workflows.

Modal Editing

Normal, Insert, Visual, Command, Search, and Recording modes — just like Vim. Every keystroke has purpose.

ƒ
Formula Engine

50+ built-in functions across math, text, logic, lookup, statistical, finance, and date. Volatile functions recalculate every frame — =NOW() auto-displays as YYYY-MM-DD HH:MM with no formatting needed. Tab-completion as you type.

Multi-Sheet

Tab bar navigation, :tabnew, :tabclose, and quick-switch with gt / gT.

File Formats

Read and write CSV, TSV, XLSX, and ODS (OpenDocument). Native .asat format with bincode + zstd compression.

Undo / Redo

1000-command undo stack with merge support. Row/column operations, cell edits, pastes — all undoable.

Macro Recording

Record key sequences into named registers with q{a-z}. Replay with @{reg} or N@@.

🎨
Theming

18 built-in themes selectable with :theme. Set theme_name = "nord" in config for a clean one-liner — no hex values needed. Full custom color override support.

🐍
Plugin System

Python plugins via PyO3 — enabled by default. Hook into cell changes, mode transitions, and file events. Register custom formula functions via ~/.config/asat/init.py. Manage with :plugins.

?
Searchable Help

:help opens a full-screen overlay with Keybindings and Formulas tabs. Type to filter entries instantly. Tab switches tabs, j/k scroll, q closes.

Circular Ref Detection

Formula dependency cycles are detected before evaluation. Cells in a cycle display #CIRC! — no hanging, no crash. Break the cycle to restore normal evaluation.

Go-to Definition

gd in Normal mode jumps to the first cell referenced in the current formula — like IDE go-to-definition for your spreadsheet.

:
Visual Mode Commands

Press : from any Visual mode to run an ex-command against the entire selection. Style commands like :bold, :fg #hex, :sort all apply to the range.

Sort & Replace

Sort rows by any column letter (:sort A, :sort B! = descending). Regex find & replace (:s/pat/repl/g). Both are fully undoable.

Filter & Freeze

Hide rows matching a condition with :filter col op val. Freeze top rows or left columns as sticky pane headers.

Named Ranges

Define named ranges with :name SALES A1:C10 and use them directly in formulas — =SUM(SALES).

Cell Notes

Attach comments to any cell with :note. A corner marker (▸) indicates noted cells at a glance.

Conditional Formatting

Live rules via :cf A1:C10 > 100 bg=#ff0000 — re-evaluated every frame. Supports > < = != contains blank error. :cf clear removes all rules.

📋
System Clipboard

Yank operations copy as TSV to your OS clipboard. Ctrl+V in Insert mode pastes from the system clipboard.

Repeat Last Change

. in Normal mode replays your last insert or destructive edit — just like Vim. Works for cell edits, deletes, and style changes.

Auto-Fill Series

Select a range in Visual mode and press Ctrl+F to extend a pattern: arithmetic sequences, weekday names (Mon→Tue→…), month names (Jan→Feb→…), or cyclic repeat.

Formula Intelligence

Live formula preview shows → result in the formula bar as you type. Referenced cells highlight in the grid. Formula cells render in muted blue to distinguish them from data.

ci
Text Objects

ci" ci( ci[ ci{ — change content inside delimiter pairs, just like Vim. Works on any cell containing quoted or bracketed text.

Install in seconds.

Pick your platform — pre-built binaries, package managers, or build from source.

Arch Linux — via AUR helper
$yay -S asat-bin
or with paru
$paru -S asat-bin
macOS & Linux — via Homebrew
$brew tap okt4v/tap
$brew install asat
Debian / Ubuntu — install via apt
$curl -LO https://github.com/okt4v/ASAT/releases/download/v0.1.19/asat_0.1.19-1_amd64.deb
$sudo apt install ./asat_0.1.19-1_amd64.deb

Resolves dependencies automatically. Uninstall with sudo apt remove asat.

Requires Rust 1.75+ with Cargo
$git clone https://github.com/okt4v/ASAT && cd ASAT
$bash install.sh
or manually
$cargo build --release

The install script copies the binary to ~/.local/bin/asat and warns if it isn't on your $PATH.

Pre-built binaries for Linux x86_64, Linux aarch64, macOS arm64, macOS x86_64, and Windows x86_64 are available on the v0.1.19 GitHub Release.

Run ASAT
Pass a file to open it, or omit for a blank workbook.
$asat myfile.csv
$asat report.xlsx
$asat spreadsheet.ods
Make sure ~/.local/bin is on your $PATH.

Spreadsheet formulas
from first principles.

Everything you need to write powerful formulas — syntax, references, functions, and the interactive cell picker.

Writing a formula

Start any cell with = to enter formula mode. Formulas are evaluated live — the result appears in the cell, and the raw formula shows in the formula bar.

Examples
=42+8→ 50
="Hello "&"World"→ Hello World
=A1*B1→ product of A1 and B1
=SUM(A1:A10)→ sum of 10 cells
Cell references

Reference individual cells, ranges, or cells on other sheets. Use $ to make a reference absolute (it won't shift when copying).

Reference types
A1relative — shifts when copied
$A$1absolute — never shifts
$A1col fixed, row relative
A1:C10range from A1 to C10
Sheet2.B4cell B4 on Sheet2
Interactive cell picker ✦

While typing a formula, press Ctrl+R to enter F-REF mode. Navigate the grid with hjkl. Your formula stays visible in the formula bar the whole time. Press Enter to insert a single-cell reference, or use v to anchor a range then navigate to the end and press Enter.

Single cell
i start editing a cell
=SUM(type the start of a formula
Ctrl+Renter F-REF mode
hjklnavigate to desired cell
Enterinsert cell ref (e.g. B3) and resume editing
Range selection
Ctrl+Renter F-REF mode
hjklnavigate to range start
vanchor range start (→ F-RANGE)
hjklextend selection to range end
Enterinsert range ref (e.g. B1:D5)
Esccancel without inserting
Operators

Standard arithmetic, comparison, and text operators work across all formula contexts.

Arithmetic
+ - * /add, subtract, multiply, divide
^power   =2^10 → 1024
Comparison
= <> < <= > >=return TRUE or FALSE
Text
&concatenate   ="Hi"&" "&A1
Math functions
SUM(range)total of a range
AVERAGE(range)mean value
MIN(range)smallest value
MAX(range)largest value
ROUND(n, d)round to d decimal places
ABS(n)absolute value
MOD(n, d)remainder after division
POWER(n, e)n raised to the power e
SQRT(n)square root
INT(n)truncate to integer
FLOOR(n, s)round down to multiple of s
CEILING(n, s)round up to multiple of s
Text functions
LEN(text)character count
LEFT(text, n)first n characters
RIGHT(text, n)last n characters
MID(text, s, n)n chars starting at s
TRIM(text)remove leading/trailing spaces
UPPER(text)convert to uppercase
LOWER(text)convert to lowercase
PROPER(text)Title Case
CONCATENATE(…)join multiple values
SUBSTITUTE(t,o,n)replace old text with new
FIND(find, in)position of substring (case sensitive)
TEXT(n, fmt)number to formatted string
Logic & conditional
IF(cond, t, f)branch on condition
AND(a, b, …)true if all are true
OR(a, b, …)true if any is true
NOT(cond)invert a boolean
IFERROR(val, alt)fallback if val is an error
ISBLANK(ref)true if cell is empty
ISNUMBER(val)true if val is numeric
ISTEXT(val)true if val is text
ISERROR(val)true if val is an error
Common patterns
Running total
=SUM($A$1:A1)expanding range
Conditional sum
=IF(B1>0, B1, 0)clamp negatives to zero
Percentage
=ROUND(A1/B1*100, 1)% to 1 decimal
Safe division
=IFERROR(A1/B1, 0)0 instead of #DIV/0!
Cross-sheet
=Sheet2.B4*1.2reference + calculation

Every key.
Every command.

The complete reference for all modes, keybindings, ex-commands, and formula functions.

🔍
No keybinds match your search.
KeysAction
KeysAction
h / Move cursor left
j / Move cursor down
k / Move cursor up
l / Move cursor right
wJump to next non-empty cell (horizontal)
bJump to previous non-empty cell (horizontal)
WJump to next non-empty cell (vertical)
BJump to previous non-empty cell (vertical)
{Jump to previous paragraph boundary (empty row)
}Jump to next paragraph boundary (empty row)
0 / HomeJump to first column
$ / EndJump to last column with data
ggJump to first row
GJump to last row with data
HJump to top of visible area
MJump to middle of visible area
LJump to bottom of visible area
Ctrld / Ctrlf / PgDnPage down
Ctrlu / Ctrlb / PgUpPage up
zzScroll so cursor is vertically centered
ztScroll so cursor is at top of screen
zbScroll so cursor is at bottom of screen
f{A–Z}Jump to column by letter (e.g. fC jumps to column C)
{N} + motionRepeat motion N times (e.g. 5j moves down 5 rows)
KeysAction
NORMAL MODE — enter / modify cells
i / Enter / F2Edit current cell (cursor at end of content)
aAppend: edit current cell with cursor at end
sSubstitute: clear cell and enter edit mode
rReplace mode: overwrite characters
ccChange: clear cell and enter edit mode
~Toggle case of text cell (upper ↔ lower)
x / Del / DDelete cell content (clear to empty)
JJoin: concatenate cell below into current cell (space-separated), then clear the cell below
CtrlaIncrement number by 1 — or cycle date forward (day → month name → weekday)
CtrlxDecrement number by 1 — or cycle date backward
gdGo-to definition — jump to the first cell referenced in the current formula
gwToggle line wrap on current cell (text reflows into merged rows below for vertical merges)
UUnmerge the merged region under the cursor
INSERT MODE — while editing a cell
EscConfirm edit and return to Normal mode
EnterConfirm edit and move down one row
TabConfirm edit and move right one column
Move edit cursor left / right within cell text
HomeMove edit cursor to start of cell text
EndMove edit cursor to end of cell text
BackspaceDelete character before edit cursor
DeleteDelete character after edit cursor
CtrlaMove edit cursor to start of cell text
CtrleMove edit cursor to end of cell text
CtrlwDelete word backward (to previous whitespace)
CtrluDelete everything from edit cursor to start of text
CtrlkDelete everything from edit cursor to end of text
CtrlrEnter F-REF mode to pick a cell reference (formulas only)
KeysAction
oInsert row below cursor and enter insert mode
OInsert row above cursor and enter insert mode
ddDelete current row (undoable)
dCDelete current column (undoable)
>>Increase current column width (+2 chars)
<<Decrease current column width (-2 chars, min 3)
=Auto-fit current column to its widest content
+Increase current row height (+1 terminal line)
-Decrease current row height (-1 terminal line, min 1)
_Reset row height to auto (1 line)
:ic / :insertcolInsert column to the left of cursor (undoable)
:icr / :insertcolrightInsert column to the right of cursor (undoable)
:dc / :deletecolDelete current column (undoable)
:ir [N] / :insertrow [N]Insert row at cursor or line N (undoable)
:dr [N] / :deleterow [N]Delete row at cursor or line N (undoable)
:cw <N> / :colwidth <N>Set current column width to exactly N chars
:rh <N> / :rowheight <N>Set current row height to exactly N lines
:sort [asc|desc]Sort all rows by cursor column (undoable)
:s/pat/repl/[g][i]Find & replace in text cells (undoable)

All yank operations copy to the system clipboard as tab-separated text, in addition to ASAT's internal register. Paste into any external app with your usual Ctrl+V.

KeysAction
yy / yrYank entire current row → register + system clipboard
ycYank current cell value only → register + system clipboard
y (in visual)Yank selected cells → register + system clipboard (as TSV)
pPaste after cursor (next row for line yanks)
PPaste before cursor (current row for line yanks)
{N}pPaste N times
ySCopy current cell's style to style clipboard
pSPaste style clipboard onto current cell / visual selection
KeysAction
vEnter character/cell visual mode (rectangular selection)
VEnter line (full-row) visual mode
CtrlvEnter column block (vertical) visual mode
Esc / v (again)Exit visual mode without action
V (in char visual)Switch to line visual mode
MOVEMENT (while selecting)
hjkl / arrowsExtend selection
w / bExtend to next / previous non-empty cell (horizontal)
W / BExtend to next / previous non-empty cell (vertical)
0 / $Extend selection to first / last column
g / GExtend selection to first / last row
ACTIONS ON SELECTION
MMerge selection into one spanning cell (undoable)
d / x / DelDelete all cells in selection (undoable)
c / sClear selection and enter insert mode at top-left cell
yYank selection → register + system clipboard (tab-separated)
SInsert = SUM(range) formula into the cell immediately below the selection
>Widen all columns in the selection (+2 chars each)
<Narrow all columns in the selection (−2 chars each, min 3)
:Enter command mode with the selection range pre-loaded — style commands apply to all selected cells
KeysAction
uUndo last change (up to 1000 deep)
CtrlrRedo undone change
{N}uUndo N changes at once
m{a–z}Set named mark at current position (e.g. ma)
'{a–z}Jump to named mark (e.g. 'a)
''Jump back to position before last mark jump
KeysAction
Tab / gt / CtrltSwitch to next sheet
⇧Tab / gT / CtrlTSwitch to previous sheet
:tabnew [name]Create a new sheet (optional name)
:tabedit [name]Alias for :tabnew
:tabcloseClose current sheet (must have at least one remaining)
KeysAction
q{a–z}Start recording macro to named register (e.g. qa). Mode shows REC.
q (while recording)Stop recording, save key sequence to register
@{a–z}Play macro from named register (e.g. @a)
@@Replay the most recently played macro
{N}@{reg}Play macro N times in a row (e.g. 5@a)
Macros record raw key events including mode transitions. Recursive macro playback is safely prevented.
CommandAction
:Enter command mode
EscCancel command mode
:q Quit — warns if there are unsaved changes
:q!Force quit, discarding all unsaved changes
:w [file]Save current workbook (optionally to a new path)
:wq / :xSave and quit
:e <file>Open a file, replacing current workbook
:tabnew [name]Create new sheet
:tabcloseClose current sheet
:ic / :insertcolInsert column at cursor (left)
:icr / :insertcolrightInsert column to the right of cursor
:dc / :deletecolDelete current column (undoable)
:ir [N] / :insertrow [N]Insert row at cursor or at line N
:dr [N] / :deleterow [N]Delete row at cursor or at line N (undoable)
:cw <N> / :colwidth <N>Set current column width to exactly N characters
:rh <N> / :rowheight <N>Set current row height to exactly N terminal lines
:boldToggle bold on current cell / visual selection
:italicToggle italic on current cell / visual selection
:underlineToggle underline on current cell / visual selection
:strikeToggle strikethrough on current cell / visual selection
:fg <color>Set foreground (text) colour — hex #rrggbb or named (red, blue, green…)
:bg <color>Set background colour — hex or named
:hl <color>Highlight: set background with auto-contrast foreground
:hlClear highlight (remove fg and bg colours)
:align <l/c/r>Set cell alignment: left, center, or right
:fmt <spec>Number format: %, $, 0.00, int, date, none
:csClear all styles from current cell / visual selection
:copystyleCopy current cell's style to style clipboard (same as yS)
:pastestylePaste style clipboard to cell / selection (same as pS)
:themeOpen the interactive theme picker
:theme <name>Apply a named theme directly (Tab-completes available names)
:set <option>Set a configuration option at runtime
:sort [asc|desc]Sort all rows by the current cursor column (undoable; default asc)
:s/pat/repl/[g][i]Find & replace in text cells — g = all occurrences, i = case-insensitive, undoable
:goto <addr>Jump to a cell address, e.g. :goto B15
:name <NAME> <range>Define a named range usable in formulas, e.g. :name SALES A1:C10
:filter <col> <op> <val>Hide rows where column doesn't match — ops: = != > < >= <= ~
:filter offUnhide all filtered rows
:freeze rows <N>Freeze top N rows as a sticky header
:freeze cols <N>Freeze left N columns as a sticky header
:freeze offClear all frozen panes
:note [text]Attach a comment to the current cell; :note shows it; :note! clears it
:colfmt <op> <val> <color>Conditional format — apply background color to matching cells in the column
:transposeTranspose rows and columns in the visual selection
:dedupRemove duplicate rows by the current cursor column
:filldownFill the anchor cell's value down through the selection
:fillrightFill the anchor cell's value right through the selection
:fmt thousandsThousands-separator number format (#,##0); :fmt t2 adds 2 decimal places
:mergeMerge visual selection (or current cell) into one spanning cell
:unmergeUnmerge the merged region under the cursor
:wrap / :wwToggle line wrap on current cell or visual selection
:help / :hOpen full-screen searchable help (Keybindings + Formulas tabs)
:pluginsOpen plugin manager TUI (engine status, custom functions, reload)

Tip: press Tab in command mode to cycle completions. Shift+Tab goes backwards. All structural operations are undoable with u.

Start any cell value with = to enter a formula. Formulas are re-evaluated automatically after every edit. Reference cells with A1, ranges with A1:B10, and other sheets with Sheet2.C4. Both absolute ($A$1) and relative references are supported.

Math
Statistical
Lookup & Reference
Finance
Date & Time
Random (volatile)
Text
Logic & Conditionals
Constants