kg —
a small text editor with Emacs keybindings
-R
- Open all files given on the command line in read-only mode.
-V
- Print version and exit.
-h
- Print a short usage message and exit.
kg is a minimal terminal text editor with
Emacs-style keybindings. It supports syntax highlighting for many programming
languages, multiple buffers, split windows, incremental search, a kill ring,
and undo/redo.
kg does not depend on curses; it uses VT100
escape sequences directly.
If no files are given,
kg opens a scratch
buffer. Multiple files each open in their own buffer.
| Key |
Action |
| C-f / → |
Move forward one character |
| C-b / ← |
Move backward one character |
| C-n / ↓ |
Move to next line |
| C-p / ↑ |
Move to previous line |
| C-a / Home |
Move to beginning of line |
| C-e / End |
Move to end of line |
| C-v / PgDn |
Scroll down one page |
| M-v / PgUp |
Scroll up one page |
| M-f / C-→ |
Move forward one word |
| M-b / C-← |
Move backward one word |
| M-e |
Move forward one sentence |
| M-a |
Move backward one sentence |
| C-↑ / M-{ |
Move to previous paragraph |
| C-↓ / M-} |
Move to next paragraph |
| C-Home / M-< |
Move to beginning of buffer |
| C-End / M-> |
Move to end of buffer |
| M-m |
Back to indentation on current line |
| M-r |
Cycle cursor through top/middle/bottom of window |
| M-g |
Go to line (prompts for line or line:col) |
| C-u |
Numeric prefix for the next command (see below) |
C-u captures a count that the next command
runs that many times. Type
C-u alone for 4,
repeat
C-u to multiply by 4 each time
(‘C-u C-u
⇒ 16, ‘C-u C-u C-u
⇒ 64’’), or follow
C-u with digits to set an explicit count
(‘C-u 8
⇒ 8, ‘C-u 1 0 0
⇒ 100’’).
C-g cancels an in-progress prefix. The
accumulated count is shown in the echo area while it is being built.
Cursor motion, character and word deletion, word-case commands, opening lines,
inserting newlines and inserting plain characters all honour the count;
‘C-u 80 -’ inserts eighty dashes, handy for header rules.
Commands without a meaningful repeat semantics ignore the count silently.
M-a and
M-e use a deliberately simple
sentence-boundary heuristic: a sentence ends at ‘.’,
‘?’, or ‘!’ immediately followed by whitespace.
Mid-text periods such as “Mr. Smith” or “e.g.”
will be mis-detected as sentence ends.
| Key |
Action |
| Enter |
Insert newline with auto-indent |
| Tab |
Insert tab (with bracket autocomplete) |
| Backspace |
Delete character before point |
| Delete |
Delete character at point |
| C-d |
Delete character before point |
| C-k |
Kill from point to end of line |
| C-o |
Open line (insert newline, keep cursor) |
| C-q |
Quoted-insert: insert the next byte literally |
| M-^ |
Join current line with the previous one |
| M-u |
Upcase word forward from point |
| M-l |
Downcase word forward from point |
| M-c |
Capitalize word forward from point |
| M-; |
Toggle line comment |
C-k at end of line joins it with the next
line (Emacs behavior). Killed text is appended to the kill ring when
C-k commands are consecutive.
C-q reads the next byte from the terminal
verbatim and inserts it, bypassing auto-indent and bracket completion. Used to
embed a literal Tab, Escape, or other control byte — handy when editing
terminfo, sendmail.cf or similar files that need a specific byte the regular
key bindings would otherwise consume.
M-; toggles a line comment on the current
line using the comment prefix defined by the current syntax (e.g.
‘//’ for C, ‘#’ for Python and Shell). If the mark
is set, every line between the mark and the cursor is toggled. When adding a
comment the prefix is inserted at column 0 followed by a space; when removing
one, the prefix and the optional following space are deleted.
| Key |
Action |
| C-Space |
Set mark at point |
| C-w |
Kill region (cut to kill ring) |
| M-w |
Copy region (copy to kill ring) |
| C-y |
Yank from kill ring (paste) |
Set the mark with
C-Space; the region between
point and mark renders in reverse video and tracks the cursor as it moves. Cut
it with
C-w, copy with
M-w, or paste from the kill ring anywhere
with
C-y. The highlight is per-buffer; it
survives a
C-x b switch and reappears when
you come back, and it follows Emacs' transient-mark convention: the region
deactivates on
C-g, on the first edit, and
after a region command (
C-w,
M-w) has consumed it. The mark itself stays
set for the next
C-x C-x. The kill ring is
shared across all buffers.
| Key |
Action |
| S-← / S-→ |
Extend region one character |
| S-↑ / S-↓ |
Extend region one line |
| S-Home / S-End |
Extend region to line bounds |
| Shift-Delete |
Cut region |
| Shift-Insert |
Paste from kill ring |
| Ctrl-Insert |
Copy region |
| Delete |
Delete active region (or character if none) |
Shift+motion drops the mark at point and extends the region as the cursor moves,
so anyone coming from a modern GUI editor no longer has to remember
C-Space first. The region is transient: a
non-shifted key tears it down after the command runs, while region-consuming
commands (
C-w,
M-w,
C-x
C-x) still see the mark intact during their dispatch. Mixing styles
works: an explicit
C-Space mark stays
sticky when extended with shift+motion.
The CUA clipboard trio maps to the same kill ring as
C-w / M-w / C-y. Note that some terminals
(Terminator, gnome-terminal, ...) intercept
Shift-Insert and
Ctrl-Insert for their own clipboard
handling; unbind them in the terminal preferences if you want
kg to see them.
| Key |
Action |
| C-x Space |
Rectangle mark mode |
| C-x r k |
Kill rectangle (cut) |
| C-x r y |
Yank rectangle (paste at point) |
| C-x r d |
Delete rectangle (no save) |
| C-x r c |
Clear rectangle (spaces) |
C-x Space sets a rectangle mark at point and
turns the highlight rectangular: every row in the row range gets reverse-video
on the same visual column span, lined up across tabs and UTF-8 content. The
same transient-mark tear-down rules apply:
C-g, an edit, or a region command ends the
mode.
C-x r y pastes the last killed rectangle at
point, padding short target rows with spaces and appending new rows when the
buffer is too short.
C-x r c overwrites
every char in the rectangle with a space, padding short rows out to the right
edge so the rectangle “exists” everywhere it should. Each
rectangle command is one undo step;
C-_
restores the entire affected range, including any padding or row appends.
| Key |
Action |
| C-s |
Incremental search forward |
| C-r |
Incremental search backward |
| M-% |
Query replace |
While searching: type to extend the query;
C-s or
→ or
↓ finds the next match;
C-r or
← or
↑ finds the previous match;
Enter accepts the match;
Esc cancels and returns to the original
position;
Backspace removes the last
character from the query.
M-% prompts for a search string and a
replacement string, then steps through every match from the current position
to the end of the buffer. At each match:
y
or
Enter replaces the match and moves to
the next;
n skips the match;
! replaces the current match and all
remaining matches without prompting;
q,
Esc, or
C-g stops the search. Each replacement is
independently reversible with
C-_.
| Key |
Action |
| C-_ / C-/ |
Undo last change |
Undo history is per-buffer. Each buffer keeps up to 1000 operations. The buffer
is marked unmodified when undo returns to the last-saved state.
| Key |
Action |
| C-x C-s |
Save current buffer |
| C-x s |
Save all modified buffers |
| C-x C-x |
Exchange point and mark |
| C-x C-w |
Write buffer to a different file (save as) |
| C-x i |
Insert file contents at point |
| C-x C-f |
Open file in new buffer |
| C-x C-r |
Open file read-only in new buffer |
| C-x C-q |
Toggle read-only mode on current buffer |
| C-x b |
Switch to buffer (interactive) |
| C-x k |
Kill (close) current buffer |
| C-x C-b |
Open buffer list |
| C-x C-c |
Quit |
The filename prompts for
C-x C-f,
C-x C-r,
C-x
C-w and
C-x i open an ido-style
picker. Matching directory entries render as a {pipe-delimited} pick list in
the echo area, with the currently selected entry shown in bold.
What you type narrows the list by substring, the way Emacs' ido-mode does it:
typing ‘buf’ matches both ‘bufmgr.c’ and
‘editor_buffer.c’. Prefix matches still rank above mid-name
ones, so a leading letter prioritises the obvious candidate. Files already
open in a buffer get pushed to the back of the list, matched by basename the
way Emacs does it, so the default selection lands on something not yet open.
Handy when walking a directory to open several files in turn.
Left/Right arrows cycle the selection;
Enter
on a directory descends into it;
Enter on a
file completes the typed path and submits.
Backspace at a trailing ‘/’
walks you up one level in a single keystroke.
Tab extends the typed text to the longest
common prefix of the prefix- matched group, or appends a ‘/’
when the sole match is a directory.
C-x b opens the same picker shape against the
active buffers (everything except the current one). What you type narrows the
list by basename; Left/Right cycles;
Enter
switches to the selected buffer.
| Key |
Action |
| C-x 2 |
Split window horizontally |
| C-x 3 |
Split window vertically |
| C-x o |
Switch to next window |
| C-x 0 |
Delete current window |
| C-x 1 |
Delete all other windows |
| Key |
Action |
| C-x (/ F3 |
Start recording a keyboard macro |
| C-x) / F4 |
Stop recording |
| C-x e / F4 |
Execute (replay) last macro |
While recording,
C-x) or
F4 stops the macro. When not recording,
F4 (or
C-x
e) replays it. Every keystroke during recording is captured, including
characters typed into prompts (search strings, goto-line numbers, etc.), so
the macro replays the full interaction faithfully. The macro buffer holds up
to 1024 keystrokes.
| Key |
Action |
| C-g |
Cancel current operation |
| C-l |
Recenter: cycle current line to center, top, then bottom of window |
| C-h |
Open *help* buffer (q closes) |
| M-x |
Execute named command |
| M-! |
Run shell command, insert output at point |
| M-| |
Pipe region through shell command, replace it with the output |
M-! prompts for a shell command line, runs it
with ‘
/bin/sh -c’, and inserts the
command's standard output at the cursor position. The command's standard error
is discarded so it cannot disturb the editor.
M-| extracts the active region, prompts for a
shell command, runs it with the region piped to standard input, and replaces
the region with the command's standard output. The original region text is
left in the kill ring so
C-y recovers it if
the result was not what was wanted.
Both commands are intended for non-interactive filters such as
sort(1),
fmt(1),
column(1),
jq(1) or
sed(1); interactive
programs that require a terminal will not work.
C-h opens a special
*help* buffer with the full key-binding
table. It is a regular buffer. Scroll with the usual motion keys, then press
q to dismiss it (or
C-x k, or
C-x
b back to wherever you were).
M-x prompts for a command name and executes
it. The completion prompt is the same ido-style picker as
C-x C-f: substring matching, Left/Right to
cycle,
Tab to extend to the longest common
prefix. Available commands:
- auto-revert-mode
- Toggle automatic re-reading of the current buffer's file when it changes
on disk. A buffer is only auto-reverted while it is unmodified; a buffer
with unsaved edits is never silently overwritten.
- capitalize-word
- Capitalize the word forward from point (first letter upper, rest lower).
Equivalent to
M-c.
- delete-trailing-space
- Remove trailing whitespace from the current line only. Reversible with
C-_.
- downcase-word
- Convert the word forward from point to lower case. Equivalent to
M-l.
- global-auto-revert-mode
- Toggle automatic re-reading for every buffer at once. Equivalent to
enabling
auto-revert-mode in every
buffer.
- goto-line
- Go to a specific line (prompts for line or line:col). Equivalent to
M-g.
- join-line
- Join the current line with the previous one, stripping leading whitespace
from the current line and inserting a space at the join point. Equivalent
to
M-^.
- not-modified
- Clear the modified flag without saving.
- revert-buffer
- Re-read the current file from disk, discarding all unsaved changes. If the
buffer is modified a confirmation prompt is shown.
- save-buffer
- Save the current buffer to its file. Equivalent to
C-x C-s.
- shell-command
- Run a shell command and insert its standard output at point. Equivalent to
M-!.
- shell-command-on-region
- Pipe the region through a shell command and replace it with the command's
standard output. Equivalent to
M-|.
- toggle-read-only
- Toggle read-only mode on the current buffer. Equivalent to
C-x C-q.
- upcase-word
- Convert the word forward from point to upper case. Equivalent to
M-u.
- version
- Print the editor version string in the status bar.
- what-cursor-position
- Show current line, column, and percentage through the buffer.
- whitespace-cleanup
- Remove trailing whitespace from every line in the buffer. Each modified
line is independently reversible with
C-_.
kg supports up to 20 open buffers. Each
buffer has its own undo history, dirty flag, and read-only state. Special
system buffers (the buffer list with
C-x
C-b, the help buffer with
C-h) have
names enclosed in ‘*’ and can be closed with
q when another buffer is available to
switch to.
The kill ring is global and shared across all buffers, so text killed in one
buffer can be yanked in another.
kg keeps track of each open file's
modification time and size as last seen. A background poll catches files that
have been rewritten by another process and tags the affected buffer with a
‘(changed)’ marker in the mode line. On
C-x C-s against such a buffer the user is
prompted to confirm before overwriting.
With
auto-revert-mode (or its global
counterpart
global-auto-revert-mode) turned
on, a clean buffer whose file has changed on disk is silently reloaded so the
view always matches the file. Modified buffers are never reverted
automatically — the ‘(changed)’ marker stays visible
until either the user saves (and is prompted) or explicitly runs
M-x revert-buffer to discard local edits.
The terminal can be divided into multiple windows with
C-x 2 (horizontal split) or
C-x 3 (vertical split). Each window shows
one buffer and maintains its own scroll position and cursor. Multiple windows
may show the same buffer. Use
C-x o to
cycle between windows. Up to 8 windows may be open simultaneously.
Syntax highlighting is detected automatically from the file extension or type,
e.g., Makefile. Hash-bang lines (#!) are used as a fallback when the extension
is not recognised.
| Language |
Extensions |
| C/C++ |
.c .h .cpp .hpp .cc |
| Python |
.py .pyw .pyi .pyx |
| Shell |
.sh .bash .zsh .profile .bashrc .zshrc |
| JavaScript |
.js .mjs .cjs |
| TypeScript |
.ts .tsx .d.ts |
| Rust |
.rs .rlib |
| Java |
.java |
| C# |
.cs .csx |
| PHP |
.php .phtml |
| Ruby |
.rb .rbw .rake |
| Swift |
.swift |
| SQL |
.sql .ddl .dml |
| Dart |
.dart |
| HTML |
.html .htm .xhtml |
| JSX |
.jsx |
| Vue |
.vue |
| Angular |
.component.ts .service.ts .module.ts .directive.ts .pipe.ts |
| Svelte |
.svelte |
| Makefile |
Makefile .mk .mak |
| Markdown |
.md .markdown .mkd |
kg automatically inserts matching closing
brackets when an opening bracket is typed, provided the cursor is at the end
of a line. Recognised pairs are ‘()’, ‘[]’, and
‘{}’. Autocomplete is suppressed when text arrives quickly, such
as during a paste operation.
M-g prompts for a line number, optionally
followed by a colon and a column number (e.g.
42:10). The cursor is moved to that position
and the view is centred vertically.
kg uses the controlling terminal
/dev/tty for raw-mode input and output.
Terminal size changes (SIGWINCH) are handled gracefully.
- /dev/tty
- Terminal device used for raw-mode I/O.
mg(1),
emacs(1),
vi(1)
kg is derived from
kilo by
Salvatore Sanfilippo. The name is a nod to
mg(1) (Micro GNU Emacs),
suggesting a kilo-gram-weight minimal editor with Emacs keybindings.
The
kg editor was created by
Joachim Wiberg (troglobit) based on the
original
kilo by
Salvatore Sanfilippo (antirez).