Metadata-Version: 2.4
Name: candat
Version: 1.5.2
Summary: A terminal text editor with emacs keybindings, built on Textual — file tree, markdown preview, CSV table viewer, a large-file pager, and a real PTY terminal
Keywords: editor,terminal,tui,emacs,textual,markdown
Author: Petr Novak
Author-email: Petr Novak <petr@umbr.cas.cz>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: MacOS
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Text Editors
Requires-Dist: pyte>=0.8.2
Requires-Dist: tomli>=2 ; python_full_version < '3.11'
Requires-Dist: textual[syntax]>=8.2.8,<9
Requires-Dist: tree-sitter-language-pack>=1.12.2
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/kavonrtep/candat
Project-URL: Repository, https://github.com/kavonrtep/candat
Project-URL: Issues, https://github.com/kavonrtep/candat/issues
Description-Content-Type: text/markdown

# candat

A terminal text editor with emacs keybindings, built on [Textual](https://textual.textualize.io/).

## Demo

![candat — a terminal editor with emacs keybindings](https://raw.githubusercontent.com/kavonrtep/candat/main/docs/brag.gif)

More than the basics, without leaving the terminal — emacs keys, split windows
on the same file, live markdown preview with smart markdown editing, a CSV
table viewer, a large-file pager, and a real shell inside.

## Features

- **Emacs editing** — kill ring, mark/region, incremental search with match
  highlighting, query-replace, line moving, comment toggle, and an `M-x`
  command palette; fixed emacs bindings by design.
- **File tree** — live path filter, dired-style refresh and copy-path,
  resizable by keys or mouse, configurable icons.
- **Buffers and windows** — tabs, side-by-side or stacked splits showing the
  same buffer as linked views with independent cursors.
- **Markdown** — live side-by-side preview (scroll-synced, rendered in the
  background so big documents never block typing), plus structural editing:
  smart Enter for lists/quotes, `M-q` paragraph fill and table alignment,
  table cell navigation, checkbox and emphasis helpers.
- **Table viewer** — `.csv`/`.tsv` (or any delimited buffer) as a table with
  no row limit, background search and regex row filter.
- **Large-file pager** — `less`-style streaming view for multi-GB files,
  with follow mode and background search; binary files are guarded.
- **Terminal inside** — a real PTY panel with scrollback, and `C-c C-c` to
  send the region or line to it as a REPL.
- **System clipboard** — copies mirror over OSC 52 (works through SSH and
  tmux) with a local wl-copy/xclip fallback, configurable.
- **Safety** — atomic saves, encoding/line-ending preservation, disk-change
  watching, session restore per project, and crash recovery snapshots.
- **Syntax highlighting** — python, markdown, json, yaml, bash, html, xml,
  css, toml, js, sql, go, rust, java, R, and config formats: INI/`.cfg`/
  `.conf`, Makefiles, Dockerfiles, and shell dotfiles (`.bashrc`, `.env`, …).

## Install

From [PyPI](https://pypi.org/project/candat/), as a standalone tool
(recommended — gives you a global `candat` command in its own isolated
environment):

```sh
uv tool install candat
# or, with pipx (needs Python >= 3.10):
pipx install candat
```

Or with plain pip into the environment of your choice: `pip install candat`.
To try it once without installing anything permanently: `uvx candat`.

The development version installs straight from GitHub:

```sh
uv tool install git+https://github.com/kavonrtep/candat
```

Requires Python >= 3.10, on Linux or macOS.

## Run

```sh
candat [FILE|DIR ...]
```

Passing a directory sets the file-tree root; files are opened in buffers.
Started bare, candat shows a welcome screen with the essential keys — type
to turn it into a scratch buffer, or `C-x C-f` straight into a file.

## Keys (so far)

| Key | Action |
| --- | --- |
| `C-x C-f` | find file (opens new file if it doesn't exist) |
| `C-x C-s` | save buffer |
| `C-x C-w` | write buffer to another file |
| `C-x C-q` | toggle read-only (status bar shows `%%`) |
| `C-x w` | toggle soft wrap for this buffer |
| `C-x C-r` | open a file read-only |
| `C-x k` | kill buffer |
| `C-x b` | buffer list (Enter switches; next buffer preselected) |
| `C-x o` | move focus: tree → window(s) → terminal |
| `C-x 3` / `C-x 2` | split window side-by-side / stacked (same buffer, linked view) |
| `C-x 0` / `C-x 1` | close this window / the others |
| `C-x C-c` | quit (confirms if unsaved buffers) |
| `C-x C-x` | exchange point and mark |
| `C-x h` | mark whole buffer |
| `C-x u`, `C-/`, `C-z` | undo |
| `C-f` `C-b` `C-n` `C-p` `C-a` `C-e` | char/line movement |
| `M-f` `M-b` | word movement |
| `C-v` `M-v` | page down / up |
| `M-<` `M->` | beginning / end of buffer |
| `C-space` | set mark (movement extends region) |
| `C-k` | kill line (consecutive kills accumulate) |
| `C-w` / `M-w` | kill / copy region |
| `C-y` / `M-y` | yank / yank-pop |
| `M-d` / `M-backspace` | kill word forward / backward |
| `M-up` / `M-down` | move current line (or marked block) up / down |
| `C-s` / `C-r` | incremental search (smart case, wraps) |
| `M-%` | query-replace (y/n/!/q) |
| `C-x g` | project-wide regex search (results list, Enter jumps) |
| `M-;` | toggle line comment |
| `C-c C-c` | send region or current line to the terminal REPL |
| `C-x t` | toggle terminal panel (keys pass through raw; only `C-x` is reserved) |
| `Shift+PgUp/PgDn`, wheel | terminal scrollback (typing snaps back) |
| `C-c C-v` | alternate view: markdown preview cycle, or table view of the buffer |
| `M-q` | fill paragraph to `fill_column`; in a markdown table, align the columns |
| `Tab` / `Shift-Tab` | markdown: next / previous table cell, or nest / un-nest a list item |
| `C-c C-t` | markdown: toggle a `[ ]`/`[x]` checkbox |
| `C-c b` / `C-c i` / `C-c c` | markdown: bold / italic / inline-code the region or word |
| `C-x {` / `C-x }` | narrow / widen the file tree (or drag the divider) |
| `C-x d` | show / hide the file tree (remembered across runs) |
| `M-x`, `Ctrl+Shift+P` | command palette |
| `C-g` / `Esc` | cancel chord / prompt / search / mark |

`ESC` acts as the Meta prefix, so `ESC w` == `M-w`, `ESC x` == `M-x`, etc.
`Tab` completes paths in the find-file and write-file prompts, listing the
choices (navigable with arrows, Enter to pick) when more than one matches.

Open files are watched for external changes: clean buffers reload
automatically; buffers with local edits ask before discarding them.

Text files over 10 MB open in a `less`-style pager instead of the editor:
the file stays on disk behind a sparse line index (multi-GB files open with
bounded memory, with progress shown while indexing), and searches scan in
the background so the UI never freezes — `C-g` cancels one mid-flight.
Scroll (keys or mouse wheel), `g`/`G`, `/`/`?` and `C-s`/`C-r` search with
all visible matches highlighted, `C-x w` wrap, `M-g` goto line. `F` follows
a growing file like `less +F` (rotation-aware; any key stops), and `e`
loads the file into a real editor buffer if you insist. Binary files are
shown as a placeholder, and the truncated/binary views refuse to save, so a
partial view can never overwrite the real file.

`.csv` and `.tsv` files open in a table viewer (inspired by
[csvlens](https://github.com/YS-L/csvlens)): a sticky header, row cursor,
and original file line numbers in the gutter. There is **no row limit**:
big files get a windowed table backed by a sparse index (built in the
background — the table shows and scrolls immediately), so ten million rows
cost the same as a hundred, `G` jumps straight to the last row, and search
scans the whole file once and then steps match-to-match instantly. And it isn't just for CSV: **any** buffer
— a `.tab`, `.gff`, a log, even an unsaved one — switches to a table with
`C-c C-v`. The delimiter is auto-detected; press `d` in the table to re-pick
it (`,` `;` `|` `tab` `space` or any character) if the guess was wrong, and
add suffixes to `table_suffixes` in the config to make more file types open
as tables automatically. In the table: `/` (or `C-s`) searches — literal
with smart case, the same dialect as everywhere else — with the matched text
highlighted in the cells (`Esc` clears), `C-s`/`n` and `C-r`/`N` step to the
next / previous match, `&` filters rows by regex, `g`/`G` jump to
top/bottom. `C-c C-v` switches back to the raw text. The table is read-only.

Markdown buffers edit structurally, not just visually. `Enter` continues
lists — bullets, numbered items (the block renumbers itself), `- [ ]` task
boxes, `>` quotes — and `Enter` on an empty item ends the list; after an
unclosed ` ``` ` it closes the fence. `M-q` reformats by context: paragraphs,
list items, and quotes wrap to `fill_column` (works in plain-text buffers
too), and inside a pipe table it aligns the columns, honouring `:---:`
alignment markers. `Tab`/`Shift-Tab` hop cell to cell through a table
(keeping it aligned; `Tab` past the last cell adds a row) or nest/un-nest a
list item. Pasting a URL over selected text makes a `[link](url)`. Fenced
code blocks are exempt from all of it.

The file-tree icons are emoji by default; if your terminal renders them poorly
(Konsole, some others), set `CANDAT_TREE_ICONS=nerd` (needs a Nerd Font) or
`=ascii`, or switch live with `M-x cycle-tree-icons` — the choice is saved.

## Configuration

`~/.config/candat/config.toml` (XDG aware), all keys optional:

```toml
tree_icons = "emoji"     # or "nerd" / "ascii"; cycle-tree-icons saves here
tree_width = 32          # file-tree width; C-x {/} and the splitter save here
tree_visible = true      # show the file tree; C-x d toggles and saves here
pager_wrap = false       # start the large-file pager with soft wrap on
tabstop = 8              # tab width in the pager
restore_session = true   # reopen last session's files (see below)
table_suffixes = [".csv", ".tsv"]   # files that open straight into the table
terminal_history = 2000  # scrollback lines kept by the terminal panel
fill_column = 80         # wrap width for M-q (fill-paragraph)
system_clipboard = "copy"   # mirror copies to the system clipboard (see below)
```

### System clipboard

Copies can be mirrored to the system clipboard, controlled by
`system_clipboard`: `"copy"` (the default) mirrors explicit copies — `M-w`
and `w` in the file tree; `"all"` mirrors every kill (`C-k`, `C-w`, `M-d`,
…, like emacs' `select-enable-clipboard`); `"off"` keeps everything in the
app-internal kill ring only. Pasting *from* the system clipboard needs no
setup — the terminal's own paste (usually `Ctrl+Shift+V`) already arrives
in the buffer.

Two channels are used at once, since neither can report success:

- **OSC 52**, an escape sequence the terminal turns into a clipboard write.
  This is what makes **SSH work**: the sequence travels back through the
  connection and your *local* terminal does the copy, so text copied in a
  remote candat lands on your local clipboard. Support depends on the
  terminal: Konsole has it (any recent release; if it doesn't work, look
  for "remote clipboard" in the profile settings), kitty / WezTerm /
  Alacritty / foot / xterm / Windows Terminal work out of the box, and
  GNOME Terminal and other VTE terminals only from VTE 0.76 (≈ Ubuntu
  24.04). **Inside tmux** add `set -g set-clipboard on` to `~/.tmux.conf`,
  or tmux swallows the sequence — that applies to local *and* SSH'd tmux
  sessions.
- **A local clipboard tool** — `wl-copy`, `xclip`, `xsel`, or `pbcopy`,
  whichever is installed and matches the session type — as a fallback for
  terminals without OSC 52. This channel only helps on the machine candat
  runs on; over SSH it would set the *remote* clipboard, so there OSC 52
  is the one that matters.

Unsupported terminals silently ignore OSC 52 — if a copy doesn't reach the
clipboard, it's the terminal or tmux configuration, and the text is still
on the kill ring (`C-y`) regardless.

Starting candat without file arguments reopens the files you had open the
last time you quit in that directory — tabs, cursor positions, scroll, and
the active buffer (per project root, kept in `~/.local/state/candat/`).
Passing files on the command line skips the restore.

Files are read and written in whatever encoding and line ending they arrive
in: UTF-8 by default, a UTF-8/UTF-16 byte-order mark is honoured, and anything
else falls back to latin-1 so the bytes round-trip untouched (the status bar
shows the encoding and `CRLF`/`CR` when they aren't plain UTF-8/LF). Saves are
atomic — written to a temp file and renamed over the original — so a crash or
full disk never leaves a half-written file.

Unsaved edits are snapshotted to `~/.cache/candat/recovery/` every 20 seconds
and on a crash; a clean quit clears them, and if any survive they are reported
(never auto-applied) on the next launch. Crash logs (including hard faults
caught by `faulthandler`) land in `~/.cache/candat/`.

The file tree has a filter box on top: press `/` while the tree is focused (or click it), type to narrow the tree to files whose path matches, `Esc` clears it. `r` (or `g`, dired-style) refreshes the tree from disk — deliberately manual, since re-walking a large tree can take a moment. `w` (or `M-w`, dired-style again) copies the selected file or directory's absolute path to the kill ring, ready to yank into any buffer with `C-y`. The file tree opens files on selection. The default theme is `candat-light`
(high-contrast dark-on-white). The markdown preview is linked: it follows
the editor's scroll position.

## Stability

The keybindings are fixed emacs bindings by design — there is no rebinding
layer, and that is a deliberate choice, not a missing feature. `candat` is a
comfortable emacs-muscle-memory editor, not a configurable one.

From 1.0 onward the project follows semantic versioning: the config-file keys,
the `CANDAT_TREE_ICONS` variable, the command-line interface, and the on-disk
locations of the config, session, and recovery files are treated as stable and
will not change incompatibly within a major version. The keybindings and the
`M-x` command names are equally stable. Behaviour details not listed here
(exact status-bar wording, colours, internal module layout) may still change.

## Development

```sh
uv run pytest
```

Release history is in [CHANGELOG.md](CHANGELOG.md).
