AIT(1) General Commands Manual AIT(1)

ait - small yet mighty GNU Emacs style editor

ait [-hv]
ait [-b backup_dir] [-s switch_command] [-r register_dir] [-S shell_command] [-O open_command] [-c key=command] [file [+/-number] ...]

ait is intended to be small, portable, and powerful Emacs-like text editor. While those are the top 3 main goals, ait also is intended to be simple in both implemetation and use, support the most important GNU Emacs keybindings, support UTF8 and unicode, to not reinvent the wheel, and to be stable. You will find many differences between GNU Emacs and ait as ait is not intended to be an Emacs clone. Some of the most prominent differences are: the lacks of a config, of lisp, of 100% custom window layouts, of major syntax highlighting, and of modes in general. You'll also find that ait doesn't treat the MSGLINE as its own buffer. This keeps the interface simple and less complicated. ait instead is simple enough that you can change the source to change the keybindings, uses the existing system as the extension language (see SHELL COMMANDS), uses a simple static-window system that works for 99% of all editing purposes, and supports the bare-minimum syntax highlighting (see SYNTAX HIGHLIGHTING). Think of ait as a microEMACS implementation of GNU Emacs with concepts from Plan 9's acme editor.

+/- number
Go to the line specified by number (do not insert a space between the '+' or '-' sign and the number). If a negative number is specified, the line number counts backwards from the end of the file i.e. -1 will be the last line of the file, -2 will be second last, and so on.
Print version and exit
Print help and exit
Supply a directory to place the backups.
Supply a custom command, in your PATH, to switch buffers with. This command should accept the list of buffers in a newline seperated list and should return just the buffer name, exactly as it was, followed by a newline character. The newline charcter generally will be added automatically by most programs. This option is useful if you wish to have fuzzy-searching or something else special that isn't supported by the minimal interface provided by the msgline. Generally, speaking this command isn't used all the time as it requires another keybinding just to access it. See section KEYBINDINGS sub-section C-x b for how to use it.
Supply a default shell command. The command must be in your PATH. This command will show as the default option when using esc x or shell-command. See section SHELL COMMANDS for more information.
Supply a default open command. The command must be in your PATH. This command will show as the default option when using esc o or open-command. See section SHELL COMMANDS for more information.
Supply a directory for registers. See register keybinds and REGISTERS for more information.
Supply a C-c prefixed key to execute a command. See COMMANDS for more information.

ait is written using a gap buffer and therefore some of the lingo used to describe various behaviors come from this. The point is the location of the cursor in the buffer. The mark is a point that is set by the user to define either the beginning or end of the region. The region is used for a variety of functions such as cut, copy, and shell-command. The point and mark are both buffer specific. Each time the a mark is added, it is added to a mark history. The poptomark command will allow you to jump to previous marks using this history.

There are some note-worthy special cases that may confuse users at first. Firstly, persistent column. If you haven't explicitly changed the current column using something fwd-word, back-char, etc. ait will remember which column you're on. This makes editing things that are in the same column but seperated by short lines a lot easier. Secondly, brack-matching is supported for all heterogeneous bracket types (), {}, <>, and []. It is not supported for homogeneous ones ("", '', ``). You can, however, still use forward-bracket and backward-bracket to jump to them. Matched brackets are highlighted in magenta. If you've just typed the closing or opening bracket, ait will highlight the match in green until you move the cursor or the editor is repainted. Thirdly, regarding the forward/backward-bracket functions, if there is a mark they will overshoot to allow you to select the entire enclosed text and its brackets. For example, if you have the code (+ 1 2) and the point is on ( and you place a mark there and run forward-bracket, the point will actually go one character to the right of the), effectively allow you to kill that entire block of code. If the point were on the ) and you place a mark there and run backward-bracket, the mark will be moved one character to the right of the ) and then the point moved to the (. The two previous cases only work when the point is on the bracket and not next to it like in GNU Emacs. If a line goes over the allotted column wide for a window, it will automatically line-wrap. To show this, the last character of the row will be highlighted yellow.

If you have a region, and you press input a space character it will indent the entire region by 1 space character. If you press backspace/C-h, it will un-indent the region by 1 space character. This allows you to carefully indent regions to exactly where you need them.

When a file is loaded into ait, it is stored in a buffer. This buffer may be displayed on the screen in more than one window. Each window is delineated by a modeline at the bottom. The modeline contains important information about the buffer inside the window. The second position in the modeline will contain an "O" if that buffer is in overwrite mode. If changes are made to a buffer, you will see an asterisk in the third position of that buffer's window's modeline. If a file is changed outside ait and its buffer is about to be changed, ait prompts if the change should go ahead (y), not go ahead (n) or if the buffer should be reverted (r) to the latest file on disk. The default buffer is called *scratch* and is not saved when you close the program. In the modeline you will also find the buffer name. This name is usually the same as the file's name unless there is another buffer loaded with the same file name. In that case, the buffer name will contain the directory name in the name i.e. dir/foo.txt. If another buffer contains the same previous directory, the sync will continue until a non-match is found. The file name usually contains the entire path to that file and is seen when you save the buffer. Next in the modeline is the row and column inside of parenthesis. Lastly, there is the percentage of the buffer you're viewing. If you're at the top and you can't page up anymore, you'll see TOP. If you can't page down anymore you'll see BOT. Otherwise, you'll see the percent.

Unlike GNU Emacs, ait doesn't allow the user to make endless window configurations. There are only 8 supported window modes: one, horizontal, vertical, triple horizontal, triple vertical, Fibonacci right, Fibonacci left, and quad. Horizontal and vertical mode are 2 window splits in the respective direction. The triple modes are the same as the previous just with 3 windows. Fibonacci modes are modes that have 2 small windows that make up the height of the third large window. It is called this because it resembles the first 3 squares in the Fibonacci sequence. Lastly, quad mode is a 4 window mode with 4 windows, one in each quadrant. Also unlike GNU Emacs, close-window doesn't exist. You can only change window modes and so the keybinding C-x 0 will take you back to one window mode. When you change modes ait will attempt to fill the windows by following the buffer trail (explained in the next parapgraph). This isn't always right but is extremely handy.

The order of buffers and windows is not handled by any array or list. They are handled by pointers that point to other pointers, thus creating a "trail" of sorts. The buffer trail is the path to the order of the open buffers i.e. current-buffer(foo.txt->b_next(bar.txt)->b_next(README)->b_next(NULL). The list must always end with NULL.

Under all windows and modlines is the prompt area, namely, the msgline. This is where all non-editing input is handled. Unlike GNU Emacs, the msgline is not a buffer but a special place for messages and prompts. In most prompts, most of the basic movement keybindings are usable: backward-char, forward-char, back-word, fwd-word, delete, backspace, kill-line, beginning-of-line, end-of-line, back-word-delete, fwd-word-delete, and insert-control-char.

There are some unique special things that you may seen while using ait that may spark a question. First, a tab character is denoted by a 4 space line yellow UTF-8 character. This makes it easy to see whether spaces or actual tabs are being used. This character doesn't show correctly when you're in the tty. Second, if control characters make it into the file they are denoted, as GNU Emacs does, by a ^ followed by the letter that corresponds to that control character in red foreground cololr. For example, the form feed control character (ASCII 0x0C) would show up as ^L because 0x4C is an L in ASCII. Third, trailing whitespace is denoted by a red background color but only shows when you are not at the end of the trailing space. Fourth, completely empty lines that contain no buffer data are denoted by a cyan tilde (~) similarly to how vi(1) does it. This makes it easy to see when you're at the bottom of the file visually. Alternatively one could use the modeline BOT string to obtain the same conclusion.

Keybindings in ait are written similarly to other Emacs clones. "C" means control and "M" means meta. Therefore, "C-x" means control plus the x key and "M-x" means meta/alt plus the x key. Since ait is usable on pretty much any terminal, it was selected to use esc instead of "M" to describe meta. Therefore, "esc x" means the same as "M-x". The below list has the keybinding in bold, followed by the common name for the function that the keybinding runs, followed by a description on how that function works.

beginning-of-line, move the point to the beginning of the line.
backward-char, move the point to the left by 1 character.
delete, delete the character that the point is currently pointing to.
end-of-line, move the point to the end of the line.
foward-char, move the point to the right by 1 character.
backspace, delete the character directly to the left of the point.
indent, insert TAB_SPACE_SIZE spaces. If a mark is set, indent the entire region. See POINT AND MARK for information on indenting via a single space.
kill-to-eol, cut from the point to the end of the line.
recenter, jump the page from top, middle, and end of the window following this cycle: middle, top, end, repeat.
undo, unlimited linear undo. See UNDO & REDO for more information.
next-line, move the point down by 1 line.
newline, insert a newline character at the point.
previous-line, moved the point up by 1 line.
insert-control-char, prompts you in insert a control character. If you insert an invalid one, it will put '^@' (string terminator). This command also will work with regions if you want to tab-indent an entire region you can use C-q C-i and if there is a region, it will indent the entire thing using tab characters.
reverse-isearch, prompt the user for a search query and search start at the point going up. See the section ISEARCH for more information.
newline-below, insert a newline character at the end of the current line.
reverse-isearch, prompt the user for a search query and search start at the point going down. See the section ISEARCH for more information.
transpose, flip the position of the character at the point with the character directly to the left of it.
universal-argument, at the moment all this does is run certain commands 4^(number of C-u presses) times. In Emacs, universal-argument does much more and ait does have a framework to do more with it but isn't fully implemented due to lack of necessity.
forward-page, move the page by one full page down.
kill-region, cut the region. See POINT AND MARK and KILL RING for more information.
yank, insert the scrap at the point. If you apply the universal argument to this command it will yank from the kill ring. See POINT AND MARK and KILL RING for more information.
suspend, suspend ait
set-mark, set the point as the current mark.
remove-mark, remove the current mark. C-g is also used to quit any command in ait
delete-other-window, return to one window mode.
split-window, split into horizontal window mode.
chop-window, split into vertical window mode.
tri-split, split into triple horizontal window mode.
tri-chop, split into triple vertical window mode.
fib-right, split into Fibonacci right mode.
fib-left, split into Fibonacci left mode.
quad-window, split into quad window mode.
other-window, jump cursor to the next window in the window trail. See WINDOWS AND BUFFERS for more information.
cursor-position, print information on current cusor location to the msgline.
insert-file, insert a file into the current buffer.
kill-buffer, kill the current buffer. If unsaved, prompt to save.
next-buffer, switch to the next buffer in the buffer trail. See WINDOWS AND BUFFERS for more information.
last-buffer, switch to the last buffer you previous had as the current.
switch-to-buffer, prompt the user to select which buffer they'd like to switch to. If the the user inputs C-c while being prompted to switch buffer, it ait will attempt to run the switch command. If there is not one supplied, a simple numerical selection will occur. The switch command can handled the ! character at the start of the command if you need to control the command more. Unlike shell and open commands, these commands cannot use the @, #, and $ special characters.
start-kbd-macro, begin a keyboard macro. See KEYBOARD MACROS for more information.
end-kbd-macro, end a keyboard macro. See KEYBOARD MACROS for more information.
run-kbd-macro, execute a keyboard macro. See KEYBOARD MACROS for more information.
find-file, prompt the user to select a file to open.
save-buffer, save the current buffer to disk.
write-file, save the current buffer to a new file.
exit, quit ait
pop-to-mark, jump point to previous mark points.
comment, if there is a single line comment string for the current file extension, add it to the beginning of the line. If there is a region and there are multi-line comment strings for the current file extension, put the start comment delimiter at the top and the end at the bottom of the region. If the current line is a comment, remove it. If the point is within a multi-line comment, remove the multi-line comment. If you supply a universal argument while a region is set, it will comment out the region using single line comments. If you supply a universal argument and attempt to comment a line, it will create an empty multi-line comment. This is handy for making documentation above functions. This command functions much differently than GNU Emacs.
comment-at-eol, put a single line comment string at the end of the line
save-region-to-register, save the region into a register
insert-from-register, insert the value in a register at the point
save-point-to-register, save the point into a register
save-file-name-to-register, save the current buffer's filename into a register
jump-from-register, jump to the point found in the supplied register or if the register contains a non-number attempt to open a file of that name
compose-registers, prompts the user to input register functions and registers to compose together. This can run many registers after each other like a macro. For example, if you have some text in register a and a keyboard macro in b that works on the text in a you would input "iaeb" in the prompt.
save-kbd-macro-to-register, save the current keyboard macro into a register
execute-kbd-macro-from-register, execute the keyboard macro in the supplied register
save-kill-ring-to-register, save the scrap into a register. You can also use the universal argument to select a value from the kill ring
help-register, display command options for registers in msgline.
numeric-arg-0, see NUMERIC ARGUMENT for more information.
numeric-arg-1, see NUMERIC ARGUMENT for more information.
numeric-arg-2, see NUMERIC ARGUMENT for more information.
numeric-arg-3, see NUMERIC ARGUMENT for more information.
numeric-arg-4, see NUMERIC ARGUMENT for more information.
numeric-arg-5, see NUMERIC ARGUMENT for more information.
numeric-arg-6, see NUMERIC ARGUMENT for more information.
numeric-arg-7, see NUMERIC ARGUMENT for more information.
numeric-arg-8, see NUMERIC ARGUMENT for more information.
numeric-arg-9, see NUMERIC ARGUMENT for more information.
back-word, move point to the left by one word.
back-word-delete, delete one word to the left. See WORD DELETE and KILL RING for more information.
fwd-word, move point to the right by one word.
fwd-word-delete, delete one word to the right. See WORD DELETE and KILL RING. for more information.
execute-shell-cmd, execute a shell command. See SHELL COMMANDS for more information.
execute-control-cmd, execute a control command. See SHELL COMMANDS for more information.
goto-line, prompt the user to select which line to jump to.
goto-column, prompt the user to select which column to jump to.
jump-to-row, jump to a line on the current page by pressing the combination of chars displayed on that line.
jump-word, jump to a word on the current page starting with the input char by pressing the combination of chars displayed at the start of that word.
un-indent, remove an indent. If a mark is set, un-indent the entire region. This works for tab characters or spaces. See POINT AND MARK for information on un-indenting by a single space.
back-to-indentation, jump point to the next non-whitespace character.
negate, set the negate flag. This isn't used much and almost no commands use it. I've found it more valuable to have custom keybindings to run commands in reverse.
open-shell-cmd, execute a shell command to open a new buffer. See SHELL COMMANDS for more information.
query-replace, prompt the user to replace something in the buffer. See QUERY REPLACE for more information.
indent-to, prompt the user to indent the current line to the column of the input char on the line above. For example, if you had this:

switch(foo) {

case 1: }

and your point is where the colon is. You press return and now you're under the 'c'. If you wanted the point to be under the colon, you could use this function to jump to that level of indent. If negated, this will apply to the line below. Numerical argument is also supported but universal argument is not. The above example isn't the best but it does explain how this feature works.

backward-page, move the page by one full page up.
copy-region, copy the region. See POINT AND MARK and KILL RING for more information.
beg-of-buf, set point to the beginning of the buffer.
end-of-buf, set point to the end of the buffer.
delete-between, delete all whitespace to the right and left of the point. If the point is in the middle of a word (any where but the first and last char of the word), it will delete the word. It acts as if you typed `esc f esc backsp`. If you use the universal argument, this command can be used like vim(1) `i` text object selection and will delete the contents inside the bracket. You can jump over matching brackets by adding more universal arguments. For example, if you wanted to jump in-between a string and delete its contents but you aren't inside it. You'd want to have at least 2 universal arguments. This is due to the quotes being homogeneous.
redo, redo an undo. You an redo as many undos as there are. See UNDO & REDO for more information.
dynamic-expand, perform dynamic expansion on the word that the point is to the right of. This will search the buffer from point backwards until it reaches the point again for anything word in the buffer that contains the word to the right of the point. If there are no matches, the msgline will say so. If you've looped around back to the point, you will get the same message as if there are no matches. If you press the keybinding again, it will go around again. Note that it will only match words one time and skip over any duplicates. dynamic-expand will check the syntax mode's valid symbols and allow the expansion to use those symbols. For example, in lisp languages a hyphen (-) and punctuation (i.e. !?) are valid in variable names but not in C-like languages. Therefore, dynamic-expand will allow expansion on something like: res -> reset!, *fo -> *foo-bar* but it wouldn't allow that in C.
transpose word, flip the word the point is currently in the the word to the left.
lowercase-word, make the next word (starting at the point) lowercase.
capitalize-word, capitalize the next word (starting at the point).
uppercase-word, make the next word (starting at the point) uppercase.
jump-to-char, prompt the user to select a character to the right of the point to jump the point to.
negated-jump-to-char, prompt the user to select a character to the left of the point to jump the point to.
zap-to-char, delete all characters to the right until the point reaches the insert character. If the universal argument is applied, it will zap to the char but not the char.
negated-zap-to-char, delete all characters to the left until the point reaches the insert character. If the universal argument is applied, it will zap to the char but not the char.
toggle-overwrite-mode, toggle between insert and overwrite mode.
foward-bracket, jump the point to the match of the bracket at the point going to the right.
backward-bracket, jump the point to the match of the bracket at the point going to the left.

isearch stands for incremental search and is the normal way to search for something in a buffer. isearch has two modes: isearch and isearch-reverse. isearch goes down the buffer and reverse goes up. It is paramount that you understand how the prompt for isearch works to use it to it's best ability. While in the isearch function is running, you have a few keybindings at your disposal other than the normal msgline keybinds:

Quit. This will take you back to the original start point.
Jump to next match. If in isearch-reverse, switch to isearch.
Jump to next match. If in isearch, switch to isearch-reverse.
Accept match, quit isearch, and stay at that point.

Once you've reached a point where there are no more matches, pressing the respective keybind (C-s in isearch, C-r in isearch-reverse) will continue the search from the beginning or end of the buffer respectively. Lastly, if you type an all lowercase query it will search for matches regardless of case; meaning that it searches with case insensitivity. If you put any uppercase letter into the query, the search now becomes case sensitive.

It's not overtly obvious when a undo set happens, the explanation is quick. An undo set happens whenever you break a chain of similar commands - if you are typing a big paragraph but don't manually move the cursor, delete anything, or run any other commands you'll find the undo will remove that entire paragraph. This is because you haven't broken the chain of commands. A redo only becomes available once you've undone something.

Numerical argument is a way to run a keybinding many times. It is most useful when used in combination with keyboard macros but can also be nice when doing normal editting as well. When you begin entering a numeric argument you'll see "C-u x", where x is the number you've added, in the msgline. Upon entering the next number you will find that it doesn't add to the original number but rather shifts the original number into the next most significant digit. This makes it very easy to do massive recurring edits.

The query-replace function is useful to replace multiple occurances of something with another something. This function is very straightforward on how to use so an explanation isn't needed. Once in the search, 'y' will accept the replace, replace the query with the replacement, and move to the next match; 'n' will skip the current match, '!' will accept all occurances without asking, and 'q' will quit. You may also use C-g to quit before you get to searching part or C-g and enter in the searching part. If there are more instances of the query, you may use 'l' (stands for last) to replace the current result and then quit. This is useful when you want to just replace a handful but don't want to be jumped to the next result.

Similarly to GNU Emacs, if you consecutively execute the fwd-/back-word-delete commannds, it will add each new cut word to the scrap. Also, the undo/redo commands will undo these consecutive commands in 1 use similarly to how it works with consecutive character input or deletion.

Also similar to GNU Emacs (and many other editors), ait can collect keyboard data into a keyboard macro. This feature does not record everything but most commands can be used with it. You must first start recording by executing start-kbd-macro. You will see a K in the modeline of the current buffer where the O for overwrite mode would be. Then perform the actions you wish to run later. All editing and movement commands can be recorded. File, buffer, suspend, and the like commands cannot be recorded, for obvious reasons. Once recording is finished, run the end-kbd-macro command. You can then execute it with execute-kbd-macro.

Every time you cut (sometimes referred to as kill) some text, it gets placed into the scrap buffer. This buffer is placed into the kill ring once it is overwritten. The kill ring is a list of previously cut, or killed, data from the scrap buffer. You can recall this data by using the universal argument on the yank command. Each universal argument represents the next item in the kill ring. Most of the time you'll need something that you recently yanked and will only have to use 1, 2 or, at most 3 universal arguments. This feature is very handy and allows you to kill text without having to worry about yanking it somewhere else while you do some other editing.

Not to be confused with the next section SHELL COMMANDS , this section is about a special type of shell command that is to be used via a keybinding. All command keybindings are C-c prefixed, meaning you have to press C-c first and then the remaining key/s. These are only set by the -c argument. I.e. "ait -c c=/foo/bar/compiler", then you can run /foo/bar/compiler by pressing C-c c. If you want to use another control keybinding, just prefix it with a ^: "ait -c ^c=/foo/bar/complier" and now C-c C-c will compile. There isn't any support for meta/esc yet.

One thing that makes these commands different than the shell commands is that this a specfic for ait them as commands. All commands passed here must accept piped data, just like shell commands used with a region. The reason for this is that ait will pipe either the region or the entire file into the command. Unlike shell commands these commands will also be passed 4 arguments, in this order: the current line number, the current column, the current point, and the entire path for the current buffer's file. Commands can also be run as shell commands and ait will automatically pass in those arguments. This is just in case you forget that you have it assigned and run it as a shell command. You can do all the normal shell command stuff with a command that is input this way such as redirecting the output to another buffer with prefixing < and handling stdout interfaces and manually placing the output into the temp file (more on this in SHELL COMMANDS). Let's say you wanted difference behavior when you run your command in the msgline via shell command versus when you run it via a keybind. This just requires your command to be a little more argument-aware. When ait goes to run a shell command, it will check the list of commands and if there is a match. But before doing that it checks if there are any prefixes on it such as ! and <. That being said, we can tell ait we want our command to _not_ pass in the arugments by prefixing it with %. If you have a program that you want to pass the entire buffer into it as if it's a command, prefix it with |. For example, if you have a script that you run every once in a while but don't really want it as a command, you could just run it occasionally like: |my_script.

The last thing to note about commands is that these are used for modifying the current buffer, not for controlling ait piped data and don't do anything to it. When doing this you could run a compile command, view top(1), something important, or something dumb and your buffer won't be effected.

One of the most powerful features in ait is the support to open files using custom commands and running shell commands. When running a shell command (esc x) there are 2 types: input and replace. Input happens when you have no region and you want to input the output of a command. One of the best uses of this is with xclip(1), xsel(1) or pbpaste(1) (on macOS) allowing you to paste in the editor. Replace happens when there is a region. In a region command the region is passed into the shell command and the output of that command, unless empty (just contains a null terminator or newline), is then placed where the region was. One of the best uses of this is a spell checker or, once again, by using system clipboard commands. ait ships with an example script called "spell" that uses this technique, however, it requires pick(1) and aspell(1) to be installed. Shell command also allows you to input the current buffer's filename (including its path), line number, and column number by placing a single @, #, or $, respectively. For example, "cat @" would translate to "cat /path/to/foo.txt". ait will not get confused if you supply an environment variable such as $LANG or if you escape the symbol via backslash( such as @ or One last thing to note about shell-command is that these programs are run synchronously and will block the use of ait until it's finished. If you want to open a command in a new buffer, you can use a < at the beginning of the command such as "< ls $HOME/Documents" and the output will be output into the *Shell Command Output* buffer.

open-command (esc o) is very straightforward, use anything you want to find the file you want to open and make a script that returns just the file and path. You can have ait open the file at a certain line number if the filename is appended with a colon, followed by the line number. This makes it easier to work with standard commands such as grep(1)'s -n option. The caveat to this is that you cannot use open-command to open files that have a colon in the file name. While this is non-standard, it's worth mentioning. ait ships with a few examples of this "ff" (find file) and "gg" (git grep) which both require pick(1) and git(1) to be installed. There may, in the future, be a way to have commands that don't effect the buffer or commands that effect the entire buffer added in later version.

One very important note is that ait uses the system(3) function and directs the output into a temp file at the env variable AIT_TEMP. This variable is set when ait starts up and its a random temp file. The file is then read by ait and that's how it gets the output. Using system(3) is really handy because it allow certain programs to work better than when ait used popen(3) outputs something via echo(1) but in doing so the echoed text will be output to the temp file. You can prevent this from happening by writing the script like normal and then just echoing the output you want directly to AIT_TEMP. Then, in ait when you wish to run the script, prefix the entire command with !. This special character tells ait to not write the output to the temp file and expects the script to handle it itself. For example:

#!/bin/sh

echo "1. option one"

echo "2. option two"

echo "3. option three"

read -p "> " output

echo "$output" > "$AIT_TEMP"

EOF

Prefixing a shell command with % will do nothing as % is only used for commands. When you run a shell/open command, the command is placed into a history array. You can tranverse this list by using up/down arrows or C-p/C-n.

While ait supports syntax highlighting, it only supports the bare minimum of it. The reason for this is that syntax highlighting that hightlights too much isn't helpful. Instead ait uses an extremely simple and straight-forward syntax highlighting approach that is designed to be simple in implementation to avoid complexity in the code. Most of the time, the syntax highlighting just highlights keywords in the language. It does so by looking at the file extension or filename so if the file lacks an extension or name found in the list, you sadly won't get highlighting. How to create new modes can be found in the syntax.h file of the project and will not be explained here.

While prompting for a file, buffer, or shell command one can use the tab key or C-i to preform some basic tab completion. In the file prompt, one you're in a directory tabbing will cause the next alphabetic file or directory to populate. You can continue to press tab to go down the line. Note that you can type more and it will change the completion to fit that newly typed text. Once you get to the end, it will loop back. If you've come across a directory, you will need to press return or another character to use the tab completion inside of that directory. When prompted for a buffer, it works exactly the same except there are no directories. When prompted for a shell command via the shell-command or open-command commands, it will search your PATH environment variable for matches and loop around again if nothing is found. It's very important to note that tab completion only ever works when you're at the end of the msgline buffer.

Backups are created right before the buffer is written to disk. They contain the file's contents before it is overwritten.

Backup files usually end in ~ and are, by default, placed in the working directory. You can optionally set a backup directory with the -b option. Backups that are sent to the the backup directory contain the entire path with the slashes replaced with exclamation points i.e. /home/foobar/foo.txt -> !home!foobar!foo.txt. The best way to use this is by setting an alias for in your .profile for each user.

Registers are a handy way to save data that you might want to recall later, quickly. Registers are only available if you have the register_dir set via the -r option. You can change the location of the register directory so that you can point only the registers that matter for the particular work you're working on. For example, if you're logged in as root you might have commonly edited file paths saved to quickly open and edit them but if they require root you don't need them when underprivileged.

ait is a fork of an editor called atto. Atto was a fork of an editor called AE. From Atto's README: "Atto is based on the public domain code of Anthony Howe's editor (commonly known as Anthony's Editor or AE, [2])..." That being said, parts of ait are written by all three of us: Anthony Howe, Hugh Barney, and Kevin Bloom.

Kevin Bloom is the current maintainer.

You may view and track issues in the ISSUE.txt file that comes with the source of ait

Combined emojis will also cause the modeline to claim you're column number is 1 more than it really is. This is caused by the extra emoji and invisible combinator character. This bug is so minimal I don't find the need to fix it at this point.

As of right now FreeBSD, OpenBSD, and GNU/Linux with muslc are not actively tested. There may or may not be many bugs on those systems.

Report bugs to ktnb at NetBSD dot org.

Public Domain 1991, 1993 by Anthony Howe. No warranty.

Public Domain 2014-2022 by Hugh Barney. No warranty

Copyright © 2023-2026 Kevin "The Nuclear" Bloom. ait comes with ABSOLUTELY NO WARRANTY. You may redistribute copies of ait under the terms of the BSD 3-Clause License. For more information about these matters, see the file named LICENSE.

mg(1), emacs(1). vi(1).

May 2026 ait 1.15