yumete

A terminal text editor for CJK users

Helix and vim keys, cursor motions by Chinese word, vertical editing, and the Yume input method built in.

  • Written for Chinese w moves by real Chinese words, found with a dictionary rather than spaces. Sentences, clauses and Chinese brackets are things you can select and change.
  • Vertical layout Columns run top to bottom and right to left, punctuation turns with them, and the keys follow the column.
  • An input method inside the editor The Yume engine is built in, so typing Chinese needs no system input method. In Normal mode every key stays a command, even with Chinese on.
  • Tables you can edit as tables Markdown and CSV tables are drawn as grids. Move by cell, sort by a column, and add or drop rows without counting pipes.
  • A book of many files Links between chapters, a wiki of the book's people and places, a daily word target, and checks that run over the whole project.
  • Code too Tree-sitter highlighting, language servers, and helix's keys, with a vim keymap for vim hands.

Select, then act

yumete works the way helix does. Every move leaves a selection: w walks forward one word and selects the word it walked over, and x selects a line. You see the text you are about to change before you change it, and then d deletes it, c replaces it and y copies it. The character under the cursor is part of the selection, so what the block covers is what d takes.

Where yumete differs from helix, it is mostly for people who write in 漢字. Editors made for English find words by spaces, and Chinese has none, so w here asks a dictionary. English is only written across the page, and Chinese is also written in columns, so the page can turn. And an input method that sits outside the terminal cannot tell Normal mode from Insert mode, so this one is inside the editor. If your fingers know vim rather than helix, see Coming from vim.

Installation

yumete is one program with two names: yumete, and ye, the short one you type. Every package below installs both. There are builds for macOS on Apple Silicon, Linux on x86_64 and aarch64, and Windows on x86_64.

Homebrew (macOS and Linux)

brew install forfudan/tap/yumete

This puts yumete and ye on your PATH. brew upgrade keeps them current. The tap has no build for Intel Macs; on one of those, build from source.

Scoop (Windows)

scoop bucket add forfudan https://github.com/forfudan/scoop-bucket
scoop install yumete

Scoop makes a shim for each name, so yumete and ye both work in any terminal.

Release archives

Every release on the releases page carries one archive per platform:

PlatformArchive
macOS, Apple Siliconyumete-<version>-darwin-arm64.tar.gz
Linux, x86_64yumete-<version>-linux-x86_64.tar.gz
Linux, aarch64yumete-<version>-linux-aarch64.tar.gz
Windows, x86_64yumete-<version>-windows-x86_64.zip

Each has a .sha256 file beside it. Inside you find bin/yumete (or bin\yumete.exe) and the licence.

The archive holds one name only. To install it on macOS or Linux:

tar -xzf yumete-*-linux-x86_64.tar.gz
cp yumete-*/bin/yumete ~/.local/bin/
ln -s yumete ~/.local/bin/ye

Use any directory on your PATH. On Windows, put yumete.exe in a directory on your PATH and copy it to ye.exe beside it. The Windows binary needs no runtime library installed.

Check that it worked:

ye --version

Typing Chinese: the input method data

Every build carries small tables of two schemes, 靈明 and 星陳, so the built-in input method types Chinese characters as soon as it is installed (see The input method). Whole words, the other schemes, and the language model behind whole-sentence input come from the data of the Yume input method.

If the Yume input method is installed on the same machine, yumete finds its data on its own. Nothing else to do.

If the data is somewhere else, name the directory in your configuration:

[ime]
data_dirs = ["~/yume-data"]

or, for one run, in the environment variable YUMETE_DATA_DIR. It takes a list of directories, separated the way PATH is (: on macOS and Linux, ; on Windows).

:yume-where lists every place yumete looked and what it found there. See The input method for the rest.

Building from source

You need:

  • a stable Rust toolchain (rustup is the usual way to get one);
  • a checkout of the yume repository (the input method engine) beside this one, as ../yume. The input method crate depends on it by that path, fixed in Cargo.toml, so nothing builds without it there. (YUME_ROOT only tells scripts/build.sh where yume's source tables are.)

The yume repository is not public. If you cannot clone it, use one of the packages above.

git clone https://github.com/forfudan/yumete
cd yumete
scripts/build.sh --no-data

scripts/build.sh builds a release binary and leaves it at the repository root as ./yumete. It then links ~/.local/bin/yumete and ~/.local/bin/ye to it, so both names run the build you just made, from any directory. It prints a warning if that directory is not on your PATH.

OptionWhat it does
--no-dataskip compiling the input method tables (this needs yume's source tables, which most checkouts do not have)
--no-linkleave the global commands alone
YUMETE_BIN_DIR=/usr/local/binlink there instead of ~/.local/bin

On macOS and Linux the script never replaces a real file at either path. If ~/.local/bin/ye belongs to another program, it says so and skips that name.

The first build downloads the 靈明 and 星陳 tables it carries and caches them in ~/.cache/yumete/builtin. A build with no network and no cached table still compiles, but the binary cannot type Chinese characters. Build again once you are online.

On Windows

Run the same script under Git Bash (it comes with Git for Windows) or MSYS2, not PowerShell. The binary is yumete.exe, data goes to %APPDATA%\yumete, and the global yumete and ye are copies rather than links, since a link needs Developer Mode, and each build overwrites them. A copy is only as new as the last build that made it.

Next

Getting started walks through the first few minutes: modes, saving, and where to look when you forget a key.

Getting started

Open a file by naming it:

ye notes.md

Name several to open them all, or none to start on a blank page. A file that does not exist yet opens empty and is created when you save it.

CommandWhat it opens
ye ch01.md ch02.mdboth files, one buffer each
yea blank, unnamed buffer
ye -rthe files that were open last time in this project, each at the line you left
ye --tutorthe lesson (see below)

The fastest way in is the lesson. If you have five minutes, type :tutor and Enter now, and come back to this page later.

Modes

yumete is a modal editor, like helix and vim. The same key does different things depending on the mode you are in.

ModeHow to get inWhat keys do
NormalEscmove, select, and act on the selection. The mode you start in
Inserti a I A o O, or ctype text. The input method works here
Selectvmotions grow the selection instead of replacing it
Command:type a command, such as :w
Command search: again, on an empty command linedescribe what you want to do and find the command
Search/ forward, ? backwardtype what to look for

The first word on the status line names the mode: NOR, INS or SEL. The cursor shape says the same thing. A block means the next key is a command; a bar means the next key is text. Every place where you type, including the command line and the search box, shows a bar.

You will press Esc hundreds of times a day. Most systems let you turn Caps Lock into a second Esc in their keyboard settings; it is worth doing once.

Moving

These work in Normal mode.

KeysMove
h j k lleft, down, up, right. The arrow keys work too
wnext word
e bto the end of the clause, back to its start
gg gestart and end of the file
gh glstart and end of the line
C-d C-uhalf a page down and up
/ then n Nsearch, then next and previous match

w knows where a Chinese word ends. In 我們明天去爬山, w takes 我們, then 明天, 去 and 爬山, not one character at a time. e and b go from one punctuation mark to the next. In English text they move by word, as in vim. See CJK motions.

A number in front repeats a key: 3w moves three words, 10j ten lines. Type 30G or :30 to go to line 30.

Selecting and changing

yumete follows helix: you select first, then say what to do with the selection. The cursor is a selection one character wide.

KeysWhat it does
xselect the whole line. In a text file, a line is usually a paragraph
vstart extending the selection with motions
ddelete the selection
cdelete the selection and start typing in its place
y p Pcopy, paste after, paste before
u Uundo, redo
.repeat the last insert (under the vim keymap, the last change)

So w then d deletes a word, and x then c rewrites a paragraph. If your hands know vim instead, see Vim keys below.

Typing Chinese

The input method is part of the editor, and it starts off. Turn it on with :yume (:y) or Space z z, or set [ime] start = true in the config. Once it is on, tap Shift on its own in Insert mode to switch between Chinese and ABC. The status line shows which one is on: the name of the scheme in brackets for Chinese, [ABC] for Latin letters. Type a code, and a bar of candidates appears; Space takes the first.

Space z z in Normal mode turns it off again.

A tap of Shift needs a terminal that reports Shift on its own. On one that does not, C-^ switches instead; the setting language_key changes which key that is. The full story is in The input method.

Saving and quitting

CommandWhat it does
:wsave
:w other.mdwrite a copy to other.md, and stay on this file
:qclose this file, and leave when it was the last one
:q!the same, throwing away unsaved changes
:wqsave, then leave
:xsave only if something changed, then leave
:wasave every file that changed
:qaclose every file and leave

:q refuses while there are unsaved changes, and says so on the line under the status line. A [+] after the file name on the status line means unsaved changes.

With the window split (C-w s), :q closes only the region you are in, and the file stays open. See Windows and the sidebar.

Nothing is lost if the terminal closes before you save. yumete keeps a rescue copy of every unsaved change. The next time you open the file it asks whether to recover it, and :recover asks again.

When you forget a key

The editor tells you what comes next

Many keys wait for a second one. When you press one, yumete shows what can follow:

  • g, m and Space open a table of their keys, each with a few words on what it does. Press one of them, or Esc to let go.
  • f, t and the other keys that take a character say so in a small box.
  • A number opens a table of the keys where a count means something other than "do it this many times". 10 then gg goes to line 10, for example.
  • Under the vim keys, d, c and y list the motions they accept.

What you have typed so far shows at the right end of the status line and beside the cursor. :view-hud basic makes the one beside the cursor smaller, and :view-hud off removes it.

The line below the status line answers in words: what just happened, why a key was turned away, or what keys the panel you are in accepts.

:tutor

:tutor copies a lesson into a file of your own and opens it. You learn by editing it, so every key it teaches is safe to press there, and :w saves your notes. Running :tutor again gives you a fresh copy and keeps the old one. The lesson is in English when the interface is, and in Chinese otherwise.

:help

:help (or :h) opens the keys you will use most, in a buffer of its own. Every motion and / work in it, so you can search it like any file. Most of it is drawn from the key tables the editor itself runs on.

CommandWhat it opens
:helpthe common keys
:help commandsevery command
:help chineseChinese text: punctuation, readings, the input method
:help verticalvertical layout
:help tabletable mode

:q closes the help buffer.

:: searches commands by what they do

Command names are short English words, and you may remember only what you want done. Press : twice. The command line now takes a description, in English or Chinese:

::sort         → :table-sort
::竖排          → :vertical

The list mixes commands and keys: whichever does the job comes up. Tab moves down the list, Enter runs the highlighted entry, and the entry shows what to type next time. Letters only need to be in order, so tbsort finds table-sort. Backspace past the start returns to an ordinary : line.

Space ? lists every command as a menu.

: completes as you type

An ordinary : line shows the commands that match what you have typed so far, above the command line. Tab cycles through them, and once a command is chosen, through the words it accepts.

Vim keys

If your fingers know vim, switch the keymap:

:keymap vim

d, c and y then wait for a motion (dw, cc, y$), x deletes a character, and V selects a line. :keymap helix switches back, and :keymap alone says which one is in use.

:keymap lasts for this session. To keep it, set it in the configuration:

[keys]
preset = "vim"

or open :settings and change it under Keys. Coming from vim lists what is different.

Next

Search and pickers

yumete has four ways to find things, from small to large:

KeysWhere it looksWhat you get
/ ?this fileone match at a time, with n and N
g/ g?this filethe word under the cursor, without typing it
Space /this file, the open buffers, a directory or the projecta panel listing every match, with find and replace
Space f Space e Space bfile names, folders, open buffersa picker that opens what you choose

Searches can treat simplified and traditional characters as one (書 finds 书), and full pinyin can find the characters it spells (shuzhai finds 書齋). Where each is on is under Chinese in a search.

Searching in the file: /

KeyAction
/search forwards
?search backwards
n Nnext / previous match
g/search for the selection, or the word under the cursor
g?the same, shown in the other editor region
*helix keymap: put the selection in the search register (n then finds it)

/ opens a search: prompt at the bottom of the screen. You can type Chinese there with the input method. Enter jumps to the first match after the cursor, and the match becomes the selection, so c right after a search changes the word you found.

The pattern is a regular expression. /第.+章 finds every chapter heading. To find a character that means something in a regex, such as . * ( or [, escape it with \.

The last pattern you searched for waits on an empty prompt in grey. Enter on its own searches for it again, and Tab turns it into text you can edit. C-p and C-n (or Up and Down) walk your search history.

Case

A pattern with no capital letters ignores case; one capital makes case count. This is the same smart case helix uses. Since Chinese has no case, most searches never notice. To turn it around once, start the pattern with (?-i). To turn it off for good:

[editor]
smart_case = false

Searching for the word you are on

g/ takes the selection, or the word under the cursor, and jumps to the next place it appears. The text is taken as it stands, so brackets and dots need no escaping. g? shows the same match in the other editor region, so the place you are standing stays on screen (see Windows and the sidebar).

Under the helix keymap, * works as in helix: it puts the selection into the search register without moving, with word boundaries when the selection is a whole word (A-* without them). Press n to go.

Two kinds of matching are added on top of the plain text. Each can be on or off on its own.

Glyphs (繁簡). A character also matches its other written forms: simplified, traditional, and the regional variants. 書齋 finds 书斋, and 书斋 finds 書齋. The rule is one-sided on purpose: a vague character finds more, a precise one stays precise. 发 stands for both 發 and 髮, so it finds both; 發 does not find 髮.

Glyph folding works inside a regex too. Only the characters you typed plainly are folded, so 書.*齋 still means "書, anything, 齋". One thing to watch: inside a negated class it excludes more than you typed. [^書] also refuses 书. Turn glyph matching off when you need the exact class.

Pinyin. A run of Latin letters also finds the characters it spells. shuzhai finds 書齋 and 书斋. Every syllable must be spelled in full: sz and shuzh find nothing. Every reading of a character counts, so both chang and zhang find 長. Letters and characters can be mixed: zhongguo很大, zhong国 and 中guo all find 中國很大. The plain-text match still runs, so hello keeps finding hello.

Where each kind is used:

WhereGlyphsPinyin
/ and ?off until you press A-joff until you press A-j
:sthe j flagthe y flag
the search panelon (row 2)on (row 2)
the file pickeronon
ye --greponon

On the / prompt, A-j turns both on together, and the prompt changes from search: to wide search: so you can see which you have. It stays as you left it. It starts off because English text is full of short words like de, shi and an, and every one of them is also a syllable.

The search panel: Space /

Space / opens the search panel in the left sidebar. Where / gives you one match at a time, the panel lists all of them, across as many files as you ask for.

╔Search══════════════════1/2═╗
║  Search: shuzhai           ║
║1 case [smart]              ║
║2 Chinese [glyphs+pinyin]   ║
║3 matching [literal]        ║
║4 whole word [ ]            ║
║5 replace [off]             ║
║6 In: this file             ║
╠════════════════════════════╣
║3  他走進書齋,看見桌上的書…║
║5  书斋裏很安靜。           ║
╚Tab Explorer>Buffers>Outlin…╝

The panel opens with the keys in the search box. A selection longer than one character is searched for at once; otherwise your last search waits in grey, and Enter runs it again. In "this file" the list updates as you type. The wider scopes wait for Enter, since reading a hundred files per keystroke would be too slow; the top right corner says Enter to search until then.

Keys in the panel

While you type in a box:

KeyAction
Entersearch, then leave the box (you stay in the panel)
Down Upnext / previous box
Escleave the box; what you typed stays

Out of the box:

KeyAction
j kwalk the boxes, then the results; on a result, the text follows
h lmove inside a box; on the results, fold or open a file
i a I Astart typing in this box
d D c Cdelete in the box, as in the text
/back to the search box
1 to 7flip that row's switch
Entersearch again; on a result, go there
wmake the sidebar wider or narrower
qgive the keys back to the text, leaving the panel open
Tabthe next view in this sidebar

j on a result shows that match in the text while the keys stay in the panel. Enter goes there and hands the keys to the text. Once you edit the text the list is out of date, and Enter searches again before it jumps.

The switches

Each row is a number, a name and the state it is in now. Press the number to step to the next state.

KeyRowStates (first is the default)
1casesmart, match, ignore
2Chineseglyphs+pinyin, glyphs, pinyin, off
3matchingliteral, regex, fuzzy
4whole wordoff, on
5replaceoff, as typed, keep case
6Inthe scope, see below
7skiphidden+ignored, hidden, ignored, nothing

Matching is literal by default: in ordinary text you look for (注) or A.B more often than for a pattern. "Whole word" only works on Latin words; Chinese has no word boundaries, so with it on a Chinese search finds nothing. The switches last until you quit; [editor] fuzzy_search = true starts on fuzzy.

Fuzzy matching

Fuzzy finds the characters you typed, in order and close together: 他説 finds 他輕輕地説. The whole match must fit in twice the number of characters you typed, plus four, or nearly every long line of text would match. This is not the file picker's fuzzy matching, which ranks short names and ignores case.

Fuzzy is for finding only. Turning replace on sets matching back to literal, and while replace is on, 3 steps between literal and regex.

Scopes

The 6 row says where to look. Press 6 (or h / l while standing on it) to step around:

ScopeWhat it searchesCommand
this filethe buffer in front of you:search (:s, Space /)
open buffersevery open buffer, unnamed ones included:search-buffers (:sb)
working directorythe directory you started ye in; :cd moves it:search-working (:sw)
the projectthe nearest folder above with .yumete, or else .git, .jj or .svn:search-project (:sp)
a folderthe folder you name:search <folder>

The commands take where to look, never what to find: :search 稿 searches the folder 稿. A relative path counts from the working directory. An open file is searched in its buffer, so unsaved changes are found. The scope does not follow the cursor into another repository; :cd there first.

Which files: only, except, skip

Scopes that read from disk add three more rows.

RowExampleMeaning
only*.md, docs/*.pysearch only files that match
except**/舊稿/*never search files that match
7 skip[hidden+ignored]what is left out

"only" and "except" take comma-separated globs, written as in .gitignore. "Hidden" is dot-files; "ignored" is what .gitignore and .ignore name. A search that comes back short is usually missing an ignored folder, and the first press of 7 lets those in. Binary files are never searched, and a very long search stops after five seconds; the count then ends in +.

Replacing across files

:replace (:r) opens the same panel with a "replace with" box below the search box. :replace-buffers (:rb), :replace-working (:rw), :replace-project (:rp) and :replace <folder> choose the scope, as the search commands do. You can also press 5 in an open panel.

On the results:

KeyAction
j kstep through the matches; the text follows
r on a matchreplace this one
r on a file's rowreplace every match in that file (asks first)
Rreplace every match (asks first when more than one file is involved)
uundo the last replacement, in every file it touched (asks)

Standing on a match, a preview under the list shows the line after the change, with what goes out struck through and what comes in in green.

Nothing is written to disk. Each file with a match is changed in a buffer, and :wa saves them all. u in the panel takes the whole run back, except in a file you have typed in since, which it leaves alone and names.

keep case (the third state of 5) follows the case of what it replaces: server → daemon turns SERVER_DEBUG into DAEMON_DEBUG. Capture groups such as $1 work only with matching set to regex. A replacement that would change the number of cells in a table row is refused; :replace! does it anyway.

Substitute: :s

For a quick change in this file, vi's substitute is there. :s/find/replace/ changes the lines the selection covers; :%s changes the whole file. With a slash, :s is substitute; without one, it opens the search panel.

A range goes before s: :1-40s is lines 1 to 40, :1,5,9s is those three lines only, and :.-$s is this line to the end. Note that , lists single lines here, so :1,40s is two lines, not forty.

FlagMeaning
gevery match on a line, not just the first
iignore case
cask at each match: y yes, n no, a all the rest, l this one then stop, q stop
ncount the matches, change nothing
fthe pattern and replacement are literal text
jglyph matching (繁簡)
ypinyin matching
tallow a change that breaks a table row

The separator is whatever follows s, so :s#2024/01#2025/02# needs no escaping. In the replacement, $1 is the first capture group, \n a newline and $$ a dollar sign. However many matches a substitution changes, one u undoes all of it.

Pickers

A picker opens in the middle of the screen, with a list on the left and a preview on the right. You type straight away to narrow the list.

KeyPicker
Space ffiles under the project
Space Ffiles under the working directory
Space ebrowse folders, starting at the project
Space Ebrowse folders, starting at this file's folder
Space bopen buffers

:open with no file name opens the file picker too.

KeyAction
typingnarrow the list
Tab S-Tabdown / up (also C-n C-p, Down Up)
PageDown PageUpa page at a time
A-hstep what is skipped: hidden + ignored, hidden, ignored, nothing
Enteropen
Escclose

The file picker matches loosely: the letters you type must appear in order, so ch63 finds 卷三/ch63.md. It reads pinyin too, and digits and characters mix: di120 finds 第120章.md. The matched characters are drawn in gold. Before you type, files you opened this session come first, then the rest in path order. A file that is already open is previewed from its buffer.

Space e shows one folder at a time, folders first, with ../ at the top. Enter on a folder opens it in the same picker; Enter on a file opens the file. The preview shows what a folder holds.

From the shell: ye --grep

The search panel can be asked from the command line, without opening the editor. Pinyin and 繁簡 are on, which grep and rg cannot do.

$ ye --grep shuzhai
ch1.md
3:4:他走進書齋,看見桌上的書。

ch2.md
3:1:书斋裏很安靜。

On a terminal, hits are grouped under their file, as rg prints them. In a pipe each hit is one line, file:line:column:text, so awk and xargs can read it. --heading and --no-heading choose one. The text is sixty characters either side of the match, since a line of text can be a whole paragraph. The exit code is 0 if anything was found, 1 if nothing was, and 2 if the pattern was wrong.

ye --files finds file names the way Space f does:

$ ye --files di120
卷一/第120章.md

Both search the current directory. Name a directory or a file to search that instead, or pass --project to search from the project root.

--open (-O) opens the editor on the answer instead of printing it: --grep with the panel already run, --files with the picker already filtered. -G is short for --grep, and short names string together:

$ ye -GO zhongguo

The search word is always its own argument (-G zhongguo, not -Gzhongguo).

The other switches carry rg's names: --ignore-case, --case-sensitive, --word, --regex, --fixed (the default), --fuzzy, --hidden (-.), --no-ignore (-u; -uu adds the dot-files), --glob=, --exclude= and --color=, plus --chinese=both|glyphs|pinyin|off. --files takes only --hidden, --no-ignore, --glob=, --exclude= and --project. ye --help lists everything.

Windows and the sidebar

The screen holds up to four regions:

  • the main editor region, where you write;
  • a second editor region below it, when you split;
  • the left sidebar;
  • the right sidebar.

Editor regions and sidebars are moved between, opened and closed with one group of keys. The group has two doors, C-w and Space w, with the same letters after either, as in helix.

Region keys

KeyAction
C-w wgo to the next open region
C-w h C-w lgo to the left / right sidebar, if it is open
C-w k C-w jgo to the main / second editor region, if it is open
C-w e C-w iopen or close the left / right sidebar; the keys stay where they are
C-w E C-w Iopen the left / right sidebar and go into it
C-w s C-w vsplit: open the second editor region and go there
C-w qclose the region you are in
C-w okeep only this editor region; close the rest

You can keep Ctrl held: C-w C-w is the same as C-w w, and so on for the other letters. The arrow keys work in place of h j k l.

Going and opening are separate. C-w w and C-w h j k l only move among regions that are open; where nothing is open, they do nothing. To open a sidebar use C-w e or C-w E, and to open a second editor region use C-w s.

Lower case leaves the keys where they are. C-w e folds the left sidebar away or brings it back while you keep typing in the text. Upper case C-w E opens the sidebar and moves into it, and never closes it.

C-w q in the only region left does nothing but say so: closing it would mean leaving, and that is :q.

The second editor region

C-w s opens a second editor region below the first, showing the same file. C-w j and C-w k move between the two, and C-w q closes the one you are in. :q does the same while the window is split: it closes this region and leaves the file open, changes and all. In the last region :q works as usual (see Saving and quitting).

Some commands use this region to show you something without moving you:

C-w v does the same as C-w s for now: the second region always sits below the first.

There is one sidebar on each side, and each holds one panel at a time. The left sidebar holds four panels, and Tab steps through them:

PanelShowsOpened with
Explorerthe project's files, as a tree:sidebar explorer
Buffersthe open files; a changed one is marked +:sidebar buffers
Outlinethe headings of the current fileSpace s, :sidebar outline
Searchthe search panelSpace /, :sidebar search

The right sidebar holds one panel, Info, described below.

:sidebar on its own says what is open, :sidebar off closes both, and :sidebar info opens the right one. Space e and Space f are pickers, not the Explorer panel; see Pickers.

Keys in a sidebar

When a sidebar has the keys, the status line lists the keys it takes, including how to get back.

KeyAction
j kdown / up a row
J Kdown / up twelve rows (also PageDown PageUp)
g Gthe first / last row
hfold a folder or heading
l Enteropen: a folder, a file, a heading, a buffer
Tab S-Tabthe next / previous panel on this side
wmake this sidebar wider, one step
qgive the keys back to the text; the sidebar stays open
C-wthe region keys, as in the text
: Spacethe command line and the Space menu, as in the text

q and C-w w both leave the sidebar without closing it. The way to close it is C-w q (or C-w e / C-w i). Esc does not close a sidebar: it is kept for leaving a box you are typing in.

Choosing a file or a heading with Enter takes you there and gives the keys back to the text.

The frame

A sidebar that has the keys is framed in a double gold line. One that does not has a thin grey frame. The panel's name is on the top edge. When a side holds more than one panel, the bottom edge lists them in the order Tab walks, for example Tab Explorer>Buffers>Outline>Search.

Width

w in a sidebar steps its width through four sizes: 2/10, 3/10, 4/10 and 5/10 of the window, then back to 2/10. The default is 3/10. The status line shows the fraction after each press.

The width belongs to the side, not to the panel. Switching panels with Tab, or closing and reopening the sidebar, keeps it.

The text always keeps at least a third of the window, and never less than 24 columns. When both sidebars are open on a small window they shrink together to make room; they grow back to the width you chose when the window widens. A window narrower than 36 columns has no room for a sidebar at all.

The outline: Space s

Space s asks for the outline, and what it does depends on where you are:

  • sidebar closed: open it on the outline and go in;
  • open on another panel: switch to the outline;
  • open on the outline, keys in the text: go back into it;
  • already in the outline: close it.

From a closed sidebar, pressing Space s twice brings you back to where you started; from any other state the second press closes the sidebar.

The outline lists Markdown headings (# followed by a space) and Typst headings (=). In a text file it shows at the right edge how much each heading's section holds, its subsections included. h folds the sections under a heading and l opens them again; h on a heading with nothing under it folds its parent. In a Typst file, the outline also reads the files the document #includes, and Enter on one of their headings opens that file.

The Info panel

The right sidebar holds one panel, Info, which shows one kind of answer at a time about what is under the cursor. Its title says which one it is showing. Each kind has a lower-case key that floats a small window beside the text and an upper-case key that puts the same answer in the sidebar:

KindFloatSidebar
dictionary: the character's decomposition, readings, code pointSpace nSpace N
documentation from the language serverSpace kSpace K
diagnosticsSpace dSpace D
the change here: what this block was beforeSpace xSpace X
the record (table row details)Space iSpace I
the wiki entry for the name under the cursor:wiki-panel

None of these take the keys: the cursor stays in the text and you keep typing. When an answer is too long to read at a glance, C-w w walks into the sidebar, and j k J K g G scroll it. In the change view, u puts back what the block was before and y copies the old text.

A dictionary answer is about the character you asked about. Move the cursor off it and the answer goes away.

Files, saving and changes

This page covers what happens between the buffer and the disk: saving, files that change underneath you, the recovery copy that protects unsaved work, and the tools that show what you have changed.

Saving and quitting

CommandAction
:write (:w)save this file
:w <path>write a copy to <path>; you keep editing this file
:write-as <path>save under a new name and edit that one
:write-all (:wa)save every file that changed
:update (:up)save only if changed
:write-quit (:wq)save, then quit
:exit (:x, :xit)save only if changed, then quit
:write-quit-all (:wqa, :xa)save every file that changed, then quit
:quit (:q)close this file; quit when it was the last one; in a split, close this region
:quit-all (:qa)close every file and quit

A ! after :q or :qa throws away unsaved changes. Without it, quitting with unsaved changes stops and says so.

With the window split, :q, :q!, :wq and :x close only the region you are in. The file stays open with its changes, so :q! throws nothing away there. In the last region they work as above.

:w always writes, even when nothing changed, and that moves the file's modification time. Build tools and sync programs read that time, so an idle :wq over a dozen files you only looked at can make them all look rewritten. :x and :up write nothing when nothing changed; the status line says unchanged; nothing written. Given a path, :x <path> always writes.

What a save writes

Files are read and written as UTF-8. A byte-order mark at the start is kept out of the text you edit, and saving puts it back. A file in another encoding, such as Big5 or GB18030, is refused with a note to convert it first, for example with iconv -f big5 -t utf-8.

A saved file ends with a line break, as in vim and helix. It uses the file's own line ending, \r\n or \n, and an empty file stays empty. Line endings already in the file are never rewritten. To write the file back byte for byte instead:

[editor]
insert_final_newline = false

The row after the final line break shows ~ in the gutter rather than a line number: the break ends the line above, it does not start a new one. Under the helix keymap the cursor can stand on that row to start a new paragraph; under the vim keymap it cannot.

A save that makes the file much bigger

One key, such as aligning a table whose widest cell is a paragraph, can multiply the size of a file. When a save would leave a file both at least twice as big and more than 256 KB bigger, it stops and asks:

safety check

saving would take 《notes.md》 from 25 KB to 1.1 MB — 44.1× as big.

y  save it
d  see what changed
n  cancel

y saves. d opens :diff and does not save; look, then save again if it was right. n or Esc cancels. Both conditions must hold, so a short draft that doubles, or a long book that gains a chapter, is never stopped. A file's first save never asks.

Every save goes through this check: :w!, :wq, :wa and :wqa! included. The ! overrides a change made outside, not this question.

When the file changes outside

Another program can change a file you have open: a git checkout, a sync folder, a formatter, :!sed -i, or the same file open somewhere else. yumete checks the disk every two seconds.

  • No unsaved changes here: the new version is read in, and the status line says so.
  • Unsaved changes here: nothing is read. The status line tells you once, and the choice is yours.
  • The file was deleted: nothing is read, and you keep the text you had.

If you then try to save over a file that changed outside, the save stops:

the file changed on disk — `:reload!` takes its version, `:w!` keeps yours
CommandAction
:reloadread the file again; refuses when you have unsaved changes
:reload!read the file again and throw away your changes
:w!write your version over the one on disk

A file whose time changed but whose bytes did not, as after a touch or a sync that rewrote the same content, does not count as changed. A file that was deleted does.

Reading changed files back by itself is on by default. :reload-auto off turns it off for this run and :reload-auto on its own says which it is. To turn it off for good:

[editor]
reload_auto = false

The recovery copy

While a file has unsaved changes, yumete writes a copy of it beside the file every few seconds, at most five, and also when you stop typing. The copy of chapter.md is a hidden file beside it, .chapter.md.yumete.<number>, where the number identifies the running yumete, so two yumetes never write over each other's copy. Saving or quitting normally deletes it, so you only see it after something went wrong.

On macOS and Linux, closing the terminal window (SIGHUP) or a SIGTERM makes yumete write the copy of every unsaved file on the way out, so nothing typed since the last copy is lost.

If yumete, the terminal or the machine stops before you saved, the next time you open the file it asks:

a draft was kept

《chapter.md》 has a draft from 10-29 23:56, left by a run that ended
before it saved; the file itself was saved at 10-29 23:55.

y  recover
d  throw the draft away
n  leave it for now

The draft is never loaded without asking. y asks once more, with y recover it, d open the comparison, n cancel, so no single key replaces your text. After recovering, u goes back to the version on disk, and :w keeps the draft.

n leaves the draft where it is. The file shows [draft] after its name in the status line, and :recover asks the question again whenever you like. Typing does not overwrite a draft nobody has claimed, and :q! does not delete it; only d does.

Each running yumete keeps its own copy, so two windows on the same file do not delete each other's.

Unnamed buffers

A buffer with no file name, such as the one ye opens with no arguments, has nowhere to put a copy beside it. Its copy goes to the drafts folder in yumete's data directory instead, once something has been typed in it. After a crash the next start says:

1 unsaved draft(s) — :recover opens them

:recover opens each one as a buffer. Save it with :w <name> to keep it.

To turn recovery copies off altogether:

[editor]
autosave = false

Seeing what changed

The change bar

The column between the line numbers and the text marks lines that differ from the last commit (HEAD):

MarkMeaning
greena new line
bluea changed line
a thin line at the top of a linelines were deleted just above it

A file outside git, or one git does not track, is compared with its saved copy instead, so the bar shows what you changed since the last save.

The bar follows the buffer, unsaved changes included, and updates a moment after you stop typing. It is on by default; :view-diff off hides it, and [editor] diff_gutter = false turns it off for good.

KeyAction
]g [gnext / previous change
Space xshow what this block was before, in a float
Space Xthe same, in the Info sidebar; there u puts the old text back
mi gselect the changed lines around the cursor

:diff, word by word

Most diff tools compare lines, and a Chinese paragraph is one long line: change one word and the whole paragraph is reported. :diff compares words, using the same word boundaries as w.

:diff with no argument compares the buffer with the saved file: what you have changed since you last saved. :diff <path> compares with another file, such as an earlier draft; a relative path counts from this file's folder.

The report opens as a read-only buffer, one pair of rows per change:

3-  她伸手去碰,指尖一涼。
3+  她伸手去碰了碰,指尖一涼,那朵花就化了。

The - row is the old text, the + row the new. Removed words are drawn on red, added words on green. Each row carries its own line number in its own file. gf on either row goes to that place in the file you are editing.

:git-diff is the same comparison against git: HEAD by default, or the commit you name (:git-diff HEAD~3, :git-diff v1.0). Both compare the buffer, unsaved changes included.

:git-show

:git-show opens the committed copy of this file (HEAD, or the commit you name) read-only in the second editor region, at the line you are on. The keys stay in the file you are writing, so you can read the two side by side. Asking again replaces that copy. When git has no answer, such as for a file never committed, git's own message is shown.

Sessions: -r and :resume

yumete records which files are open, separately for each project (see A book of many files). Sessions live in the data directory, so nothing is added to the project. To pick up where you left off:

$ ye -r

-r (--resume) reopens those files, each at the line you left it on. :resume does the same from inside the editor, adding them to what is already open. At most 24 files are kept. Files that have moved or been deleted since are skipped quietly. Quitting with no file open leaves the last session alone.

The session is written whenever the open files change, not only when you quit, so -r also brings them back after a crash.

The session also keeps how the page looked: the layout, the :view-* settings and the theme, where they differ from your config. :reload-config puts everything back the way the config says.

To resume every time you start without a file name:

[editor]
session = true

Then ye --new starts on an empty buffer.

[editor] remember = false keeps no memory at all: no session, no recent files, no line to return to. --private does the same for one run. Recovery copies are kept either way.

Copying to and from other programs

The terminal keeps Cmd+C and Cmd+V for itself. Use the Space menu:

KeyAction
Space ycopy the selection to the system clipboard
Space p Space Ppaste from the system clipboard after / before

Pasting with the terminal's own paste also works; the text arrives as text, not as keys. Copying goes through the terminal, so it works over ssh and tmux. Pasting reads the local clipboard (pbpaste on macOS, wl-paste or xclip on Linux).

Recent yanks and deletes are kept too; Space " lists them. See Registers.

Configuration

yumete reads its settings from TOML files. You need none of them: every setting has a default, and the editor runs the same with no file at all. When you do want to change something, you can write the file by hand or use the settings panel, which writes it for you.

This page covers where the files are, how they combine, and what happens when one is wrong. For what each setting does, see Settings. Its full list is the settings panel's; the few keys not on the panel are explained on the pages that own them, and that page links to each.

Where the files are

There are two files, and both are optional.

FileWhere
Global~/.config/yumete/config.toml on macOS and Linux ($XDG_CONFIG_HOME/yumete/config.toml when that variable is set), %APPDATA%\yumete\config.toml on Windows
Project.yumete/config.toml at the root of the project, or .yumete.toml in the same place

The global file holds your own habits. The project file holds what belongs to one book or one repository: its layout, its markup, its language servers. It travels with the project, so a collaborator who opens the same folder gets the same page.

The project file overrides the global one item by item. A project that sets only layout = "vertical" keeps everything else from your global file.

Which project you are in

The project is worked out from the working directory, not from the file you open: ye ~/book/ch01.md still reads the project config from where you stand. See A book of many files.

An example

# ~/.config/yumete/config.toml

[editor]
line_numbers = "relative"
indent = 2           # first-line indent of a paragraph, in cells
ruler = 40           # mark text past column 40

[theme]
name = "moxiang"
mode = "auto"        # follow the terminal's dark or light background

[ime]
scheme = "lingming"

[keys]
preset = "vim"
# ~/book/.yumete/config.toml

[editor]
layout = "vertical"
zong_length = 24     # characters to a column

The sections

SectionWhat it holds
[editor]layout, line numbers, indent, wrapping, search, saving, the interface language. Most settings live here
[theme]the theme and its colours. See Themes
[panel]the candidate bar of the input method
[ime]the input method: scheme, commit style, where its data is
[keys]the keymap preset, and your own bindings under [keys.normal]. See Key remapping
[syntax]which markup a file is read as, by extension or by file name
[language.<name>]the programs that format or preview a kind of file
[lsp.<name>]the language server for a kind of file. See Language servers
[export]the paper size for printing

What a project file cannot set

Two things are read from the global file only:

  • [keys.normal], because a binding can run a command line;
  • screenshot under [editor], because it runs through a shell.

A project folder can come from anyone, and opening it should not run their commands. If a project file sets either one, yumete ignores that part and says so at startup. [language.*] and [lsp.*] are allowed in a project file: they never pass through a shell, and each placeholder such as {file} is handed to the program as a single argument.

The settings panel

:settings (short: :set) opens every setting on one full page, each with its name and a line saying what it does. You do not need to know what a key is called, or which section it lives in.

 settings [global]                               ~/.config/yumete/config.toml
──────────────┬───────────────────────────────────────────────────────────────
  layout      │ layout                        [across]
  marks       │ column length                 0
  interface   │ indent                        2
  colours     │ measure                       0
  input       │ ruler                         40
  editing     │ soft wrap                     [x]
  files       │ hanging punctuation           [ ]
  keys        │
              ├───────────────────────────────────────────────────────────────
              │  cells at the head of a paragraph; the Chinese convention is 2
              │
              │  factory 0
              │  global  2   ← in force
              │  project unset
──────────────┴───────────────────────────────────────────────────────────────
  hl panes  jk move  space toggle  i edit  d drop  e open it  Tab which file  :w save  :q leave

The groups on the left follow what a setting changes on screen, not the TOML section it lives in.

KeyWhat it does
h lmove between the groups on the left and the settings on the right
j kmove up and down. g and G go to the top and bottom
Spaceflip a switch, step to the next choice, or add one to a number
itype a value. Enter keeps it, Esc leaves the old one
dremove this setting from the file, so the layer below applies again
Tabswitch the file your edits go to: global or project
eopen that file in the editor
:wsave
:qclose the panel. :wq saves first

Which file, which layer

The title says which file an edit goes to, global or project, and the path on the right is that file. A + after the path means the file does not exist yet; saving creates it. The panel opens on the global file; press Tab for the project one.

The block under the list explains the setting under the cursor and shows its three layers: the factory default, the global file, and the project file. The one in force is marked. If the project file sets a value and you change it in the global file, the line on screen does not move, because the project value still wins. The panel says so under the setting.

Saving

Nothing takes effect until you save with :w. Saving writes the file and applies it at once.

The panel changes only the lines it needs to. Your comments, the order of your settings and your blank lines stay as they were, so a config kept in git shows a one-line diff. If the file changed on disk while the panel was open, :w edits the current copy on disk, so it does not undo someone else's change.

When the file holds a key yumete does not know, saving comments that line out and leaves a note beside it. An unknown key would otherwise stop the whole file from applying (see below).

What the panel does not show

Some settings are not on the panel: the theme colours, [keys.normal], [syntax], [language.*], [lsp.*], [export] page, [ime] data_dirs, [editor] usage_groups and [editor] screenshot. Write those by hand; Settings says which page explains each. e on the panel opens the file it is editing, so you do not have to remember where it lives.

Reloading

If you edit a config file by hand while the editor is running, run :reload-config to apply it without restarting.

Commands such as :theme, :view-hud or :view-focus change a setting for this session only. :reload-config also puts those back to what the files say.

When the config is wrong

yumete checks the config every time it reads it.

A file that does not parse is ignored as a whole. This covers a broken quote, a value of the wrong type, and a key yumete does not know. Unknown keys count as errors so that a typo such as zong_lenght = 24 is reported instead of doing nothing in silence. The message names the file and the key, and lists the keys that would have been accepted there. The other file still applies: a broken project file leaves your global settings in force, and a broken global file leaves the factory defaults under the project file.

A value that parses but makes no sense is skipped on its own, with a warning: a number out of range, a word that is not one of the choices, or a binding whose right-hand side is not an action. The rest of the file applies.

Either kind shows on the line under the status line when the editor starts or reloads. To see them again:

  • :check-config reads both files and lists every problem as path:line: message, without opening either file. A clean config says so in one line.
  • Open the config file in yumete. Each line with a problem gets a mark in the gutter, the same way a language server marks code; Space d shows the message under the cursor. The marks update as you type, so a line goes quiet the moment it is right. Nothing needs to be installed for this.

Next

Key remapping

You can put any action, command or run of keys on a key of your own choosing. Bindings go under [keys.normal] in your global config file:

[keys.normal]
"C-s" = ":write"               # Ctrl-s saves
x = "delete_selection"         # x deletes the selection
"<tab>" = ":buffer-next"       # Tab goes to the next open file
"<S-tab>" = ":buffer-previous" # Shift-Tab back
"A-j" = "@kJ"                  # Alt-j joins this line onto the one above

Only Normal-mode keys can be remapped. Bindings are read from the global file only; a project's .yumete/config.toml cannot hold them, because a binding can run a command line. Run :reload-config after editing, or restart.

The right-hand side

Each right-hand side says which of three kinds it is.

Written asKindExample
a bare namean action"delete_selection"
: in fronta command line, run as if typed after :":write", ":view-wrap 40"
@ in fronta run of keys, played as if you pressed them"@kJ"

Prefer an action name when one exists. A name says what the key does, and it keeps meaning that even if the default key for it moves.

:keymap-actions lists every action in three columns: the name to write, what it does, and which key does it now. There are about eighty. The names follow helix's, so move_line_down, goto_file_start and yank are what you would guess.

A bare word that is not an action name is refused. yumete reports it at startup and does not install that binding, so a typo such as "delete_slection" never types its own letters into your text. To bind a run of keys, write the @.

The left-hand side

The left side is the key you press, written as itself: x, Q, ;, \\ (a backslash, escaped for TOML), or a Chinese punctuation mark such as ",".

Modifiers and named keys:

WriteFor
C-sCtrl and a letter
A-xAlt and a key
<tab> <S-tab> <enter> <space> <backspace> <delete>the keys with names
<up> <down> <left> <right>the arrows
<home> <end> <pageup> <pagedown>the rest

Names in angle brackets ignore case. A shifted letter is just the capital: Q, not S-q. The same spellings work inside an @ run on the right: "@<down>x".

The left side may also be a run of keys. "dj" = "@xd" makes d followed by j delete the line. yumete waits after the d to see what comes next; if the next key does not continue any of your runs, d does what it always did. That is also why named keys need the brackets: "tab" would be the three keys t, a, b.

Esc cannot be bound. It is how you let go of a run of keys you started by mistake, and a keymap without it would have no way out.

Prefix keys

Space, g, m and z open groups of keys. To move a whole group to another key, bind that key to the prefix itself, as a run of keys:

[keys.normal]
";" = "@ "             # ; opens the Space menu
"<space>" = "@l"       # Space moves one cell right
"<backspace>" = "@h"   # Backspace moves left

A run of keys on the right always means the editor's own keys. "@ " presses the real Space even when you have bound Space to something else.

Binding a prefix key to an action takes its whole group away. g = "delete_selection" makes g delete, and gg, ge and the rest of the g group are gone.

Presets and bindings together

[keys] preset = "vim" switches the whole keymap to vim's hands (see Getting started). Your [keys.normal] lines apply on top of whichever preset is in use, and beat it where both bind the same key.

The Yumete flavour is a short list of changes laid over either preset. Today it holds one: d and c delete without filling the register, and A-d and A-c become the keys that cut.

[keys]
flavor = true

:keymap-yumete-flavor on turns it on for this session, and :keymap-yumete-flavor alone says whether it is on and what it changes. Use the flavour rather than binding the four keys yourself. A binding stays in force whatever the defaults later become; the flavour follows them.

Checking your bindings

A binding that yumete cannot use is reported at startup and by :check-config. Opening the config file in yumete marks the line in the gutter; Space d shows why. See When the config is wrong.

The Keymap lists every default key.

Themes

yumete ships ten themes. Pick one for this session with :theme <name>, or set it for good in your config.toml:

[theme]
name = "gruvbox"

:theme with no name says which theme and which mode are in force. Press Tab after :theme to see the list. A theme chosen with :theme does not change the config file; it lasts for this run, and ye -r or :resume brings it back next time.

The themes

NameAlso acceptedLightDarkWhat it looks like
inkmoxiangyesyesThe default. Warm white ink on near-black paper, gold for headings, red for errors
bwheibai, monoyesyesBlack, white and grey only
morandimolandiyesyesMuted, greyed colours
githubgithub-themeyesyesGitHub Light Default and Dark Default
tokyo-nighttokyonight, tokyo_nightyesyesTokyo Night and Tokyo Night Light
catppuccinyesyesMocha when dark, Latte when light
gruvboxyesyesGruvbox Medium, dark and light
one-darkonedark, one_darknoyesOne Dark Pro
monokainoyesMonokai as VS Code ships it
draculanoyesDracula

In the config file the Chinese names also work: 墨香 for ink, 黑白 for bw, 莫蘭迪 for morandi. On the command line, use the names in the first column, since the input method is not active there.

The last seven are borrowed: their code and Markdown colours are copied value for value from the theme they are named after, so keywords and headings are where you expect them. The first three are yumete's own, described below.

Light and dark

Light or dark is a separate choice from the theme. Every theme with both modes has a light and a dark version; the three dark-only themes stay dark whatever you ask for.

[theme]
mode = "auto"   # "auto" (the default), "dark" or "light"

auto asks the terminal what colour its background is and follows it. This is the terminal's background, not the desktop's: a dark terminal on a light desktop gets the dark mode. A terminal that does not answer gets dark.

To switch for the moment, use :theme-mode:

CommandEffect
:theme-mode lightthe light version of the current theme
:theme-mode darkthe dark version
:theme-mode systemback to what the terminal said at startup
:theme-modesay which mode is in force

:theme only takes a theme's name, and :theme-mode only takes a mode. Neither writes to the config file.

How the house themes are built

ink, bw and morandi are each defined by a handful of colours. The two that matter most are the ink (the colour of the text) and the paper (the background). Every grey on the screen is a step between those two: line numbers, borders, the selection, the cursor's row in a table, a quoted block. Change the ink or the paper and every grey moves with them, so the relations between them still hold.

On top of the greys, each colour has one job:

ColourConfig keyUsed for
goldgoldheadings, table headers, labels in panels: text that is not the body text
redmarksomething wrong: a footnote marker, a table row with the wrong number of cells, a merge conflict
purplepurpleinline code and code blocks
greengreenquotations
blueazurelinks
yellowamber::: warning blocks, and types in code

Bold, italic and plain body text get no colour of their own. The order of the colours follows the ranks of official robes in imperial China: gold for the writer's own headings, purple for code where not one character may change, green for other people's words, blue for an address. Red never marks emphasis; it always means "this is wrong". Your own ==highlighted== text sits on a pale wash of the gold, not the red.

Code uses the same colours, plus four second tones that appear only in code:

In codeColourConfig key
keywordspurplepurple
functionsblueazure
stringsgreengreen
typesyellowamber
properties, attributesgoldgold
numbers and constantsorangeorange
built-ins such as self and thispinkpink
escapescyancyan
HTML tagslimelime
commentsgrey, italic

The five callouts each take one colour: ::: note blue, ::: tip green, ::: important purple, ::: warning yellow, ::: caution red. VitePress's names work too: ::: info and ::: details are drawn as note, ::: danger as caution.

bw has no hue at all. It shows what gold and red show in ink by brightness instead: gold text is brighter than the body text, and red is brighter still.

The [theme] keys

KeyValuesDefaultEffect
namea theme's name"ink"which theme
modeauto, dark, light"auto"light or dark (system is the same as auto)
groundpaint, terminal"paint"terminal leaves your terminal's background alone and only colours the text
status_barsunken, raised"sunken"sunken sets the status line further from the text colour than the page; raised sets it one step toward the text
filltrue, falsefalseput a background behind code and quotations

fill can also be changed for the session with :theme-fill on, :theme-fill off or :theme-fill toggle. :theme-fill alone says whether it is on.

With ground = "terminal", yumete cannot promise any contrast, because it does not know your background. Use it if you have tuned your terminal's colours and want to keep them.

The same keys are in the settings panel (:settings), under colours.

Changing the colours

There are no theme files. To make a theme of your own, start from the nearest one and override the colours you want to change. Each key from the tables above takes a #RRGGBB value for the dark mode, and the same key with _light takes the light one:

[theme]
name = "ink"
ink = "#D2CEC4"          # text, dark mode
paper = "#181A1D"        # background, dark mode
ink_light = "#2A2923"    # text, light mode
paper_light = "#F1EBD9"  # background, light mode
mark = "#EC5D4B"         # red, dark mode
gold_light = "#6B5426"   # gold, light mode

The colour keys are ink, paper, mark, gold, purple, green, azure, amber, orange, pink, cyan and lime, each with a _light twin. A value that is not #RRGGBB is reported when the config loads, and a name that is not a theme's is reported too. In both cases the rest of the config still applies.

Language servers

yumete understands code in two ways. Built-in grammars give every supported language its colours, indentation, comments and outline, with nothing to install. A language server is a separate program you install yourself; when one is running, yumete shows its errors and offers its completions, definitions, renames and fixes. The editor speaks the Language Server Protocol, the same one helix and VS Code use.

Supported languages

Each language below has a grammar compiled into yumete. A grammar gives you:

  • syntax colours, in code files and in Markdown code fences (```py, ```rust)
  • indentation after an opener, on Enter and on o
  • Space c and Space C to comment lines out with a line or a block comment
  • the outline (Space s), and ]f [f to the next or previous function, ]t [t to the next or previous class
  • text objects such as mi f (inside a function) and mi c (inside a comment); see match mode

The name in the first column is the one you write in [lsp.<name>] and [language.<name>].

Language[lsp.…] keyServer at the factory
adaada
bashbash
cc
cppcpp
csscss
gasgas
gogogopls
htmlhtml
javajava
javascriptjavascript
jsonjson
lualua
mojomojomojo-lsp-server
nasmnasm
pascalpascal
pythonpythonty, pylsp, jedi-language-server, ruff
rr
rustrustrust-analyzer
tomltoml
tsxtsx
typescripttypescript
yamlyaml

What a server gives you

KeyWhat it does
gdgo to the definition, across files. C-o comes back
grevery place the name is used: one is gone to, several open a picker showing the line around each
Space k / Space Kdocumentation for the name under the cursor, in a float / in the sidebar
Space d / Space Dthe diagnostics on this line, in a float / in the sidebar
]d / [dthe next / previous diagnostic; ]D / [D the last / first
Space rrename the name everywhere: opens :lsp-rename with the old name typed in. Edit it and press Enter
Space acode actions for the selection: quick fixes, refactors. Pick one from the list
C-n or C-x (Insert)open the completion list by hand
:diagnostics-allevery diagnostic from every file, as a path:line: listing; gf on a line goes there

In Insert mode, completion opens by itself as you type a word or after . and :. C-n and C-p (or the arrows) move through it, Tab takes an item and Esc closes it. Each letter you type narrows the list at once without asking the server again. When you type ( or , inside a call, the signature appears beside the cursor with the parameter you are filling in highlighted.

While a float is open, PageUp, PageDown, C-u and C-d scroll it instead of the page.

Diagnostics wait until you leave Insert mode, since a half-typed line is always broken. In code files they show in a two-cell column in the gutter: a solid red cell for an error, and a pale cell marked ! for a warning, i for information or · for a hint. The column is only drawn in code files, and only while line numbers are on.

Rename and code actions can change files you have not opened. Those files are opened, each gets one undo step, and nothing is saved until you save it.

gd in a text file follows footnotes and links instead; see the page.

Configuring a server

Servers are set in config.toml, one table per language:

[lsp.rust]
command = "rust-analyzer"

[lsp.typescript]
command = "typescript-language-server"
args = ["--stdio"]

command is the program and args its arguments, as a list. The program is started directly, not through a shell. Writing a language replaces its whole factory entry, and command = "" turns the language's server off.

A language can list several servers, as an array of tables:

[[lsp.python]]
command = "ty"
args = ["server"]

[[lsp.python]]
command = "pylsp"

Every server in the list that is installed is started. Diagnostics come from all of them, and Space a gathers code actions from all of them. Other questions (documentation, definitions, references, completion, renames) go to the first one in the list that is running, so put the one that answers those best first.

These come filled in:

LanguageServers
rustrust-analyzer
gogopls
mojomojo-lsp-server
pythonty server, pylsp, jedi-language-server, ruff server

For any other language, add a table yourself. tsx is its own name, separate from typescript, so a TSX file needs [lsp.tsx] too.

Where yumete looks for the program

Before PATH, yumete looks inside the project. Starting from the file's folder and going up to the outermost git repository, it checks .venv/bin, venv/bin, node_modules/.bin, and the environments under .pixi/envs/ and .direnv/. A copy found there wins over one on PATH, because it matches the project's own interpreter and dependencies, and the status line says so when it is used. A command with a / in it is taken as a path.

A server starts when you first open a saved file of its language. If none of the configured programs can be found, the status line says so once. :diagnostics-all names each running server and where its program is.

A project's own .yumete/config.toml can set [lsp.<name>] too, so a repository can choose its servers for everyone who opens it.

Servers for text files

Markdown, Typst and plain text have no server by default. Name one and it is attached, for example a spelling and grammar checker such as harper-ls:

[[lsp.markdown]]
command = "harper-ls"
args = ["--stdio"]

[[lsp.text]]
command = "harper-ls"
args = ["--stdio"]

The text-file names are markdown, typst and text. The server's complaints are under Space d and ]d, and its fixes under Space a.

Formatting and other commands

:format (or :fmt) does not ask the language server. It runs the command the config names for the file's language, under [language.<name>]:

[language.markdown]
format = { run = "rumdl check --fix {file}" }

[language.typst]
preview = { run = "tinymist preview --no-open {file}", kind = "server" }

format is used by :format, preview by :view-preview, and any other name you give by :run <name>. {file}, {dir} and {name} are replaced with the file's path, its folder and its name. kind says how the command runs:

kindEffect
once (default)run it, then read the file again
serverkeep it running; :view-preview off stops it
filtersend the text on standard input and replace it with the output; u undoes it

As with servers, the command is not passed to a shell.

The page

This page covers how your text is drawn: which markup the file is in, how much of that markup you see, where lines break, how paragraphs open, and the extras you can turn on while you write. None of it changes the file. Every setting here is a way of looking at the text.

Most of these are :view-* commands. Most of them, typed alone, tell you their current state, and on, off or a value changes it. To keep a setting, put it in your config (see Configuration) or save it from the settings panel, :settings.

Source, colour, or WYSIWYG: :render

:render has three levels:

LevelWhat you see
offthe source, uncoloured
basic (default)the source, coloured; not one character hidden
fullthe markup comes off the page, except where the cursor is

Under basic, the word in **bold** is drawn bold and the two ** stay on the page, dimmed. You always see what is really in the file.

Under full, the inline marks leave the page: **, backticks, the brackets and target of [text](target), [[ ]], and %%. Only the text they surround stays. A footnote mark [^1] is drawn as a superscript ⁽¹⁾, and so is the [^1]: that starts the note. Only all-digit footnote names are drawn this way; [^note] stays as written.

Block structure is never hidden. A heading keeps its #, a list keeps its marker, a quote keeps its >. A terminal cannot make a heading bigger, so the # is the only thing that shows its level. Typst code such as #import "ch1.typ" is never hidden either, only dimmed.

Wherever the cursor or the selection touches a construct, that construct opens and shows its source. So the cursor is never inside text you cannot see, and w, x and d work on the characters in front of you. Move away and it closes again.

:render alone tells you the level of the markup, tables and ruby. Tables have their own levels (see Tables), and so does ruby (see Ruby and annotations).

Markdown, Typst or plain text: :syntax

A .md file is Markdown and a .typ file is Typst. A file whose name does not say, usually a .txt, is read when it opens and guessed. A Typst keyword such as #let or #import, or a heading in either syntax, settles it. Without enough evidence the file is plain text, so the * and # in a novel are not mistaken for markup.

You can always say it yourself:

:syntax              which syntax this file is read as
:syntax typst        read it as Typst
:syntax markdown     read it as Markdown
:syntax text         no markup at all: every character is only itself

From the shell, ye --syntax typst draft.txt does the same, and it beats both the extension and the config.

For a whole project, write it into the project's .yumete/config.toml. An exact file name beats an extension:

[syntax]
txt = "typst"              # every .txt here is Typst
"notes.txt" = "markdown"   # except this one

[editor] syntax = "typst" says the same for every file whose name does not say, without listing extensions.

A chapter you open with gf from a Typst #include line is read as Typst whatever it is called.

Markdown colouring

Headings, **bold**, *italic*, ~~struck~~, ==highlight==, `code`, links, [[wiki links]], footnotes, quotes, lists, tasks, rules, tables, front matter and ::: tip boxes are all coloured. The marks themselves are dimmed, and the text they fence stands forward. Annotations to yourself, written %%like this%% or <!-- like this -->, are set back in a faint colour. Their words are never hidden, and export leaves them out.

A fenced code block is coloured by the grammar its fence names (```rust, ```python), with the theme's code colours. :view-code off turns this off; :view-code alone tells you whether it is on and why a block is not coloured. The list of languages is on the Language servers page.

A line longer than 50,000 characters is left plain, so a one-line data file does not slow the editor down. :view-long-line sets the limit, and :view-long-line off removes it.

Put the cursor anywhere on a link and press gL, or hold Ctrl and click it.

WrittenWhere it goes
[site](https://example.com/)opened in the system's browser
[next](02.md), [[Chapter 3]]opened here, as a buffer
[snow](#snow), [[Chapter 3#snow]]to that heading, opening its file first

A relative path is read from the folder of the file the link is in. [[Chapter 3]] names a title, not a file: yumete tries the current file's extension first, then .md. In [[Chapter 3|that night]] the part after | is only what the link shows.

Only http and https links are handed to the system. Any other scheme, such as mailto:, is shown on the status line and not run, and an absolute path is not followed.

On a footnote mark, gd goes to the note, and gd in the note brings you back to the character you left. See Checks and counts for the note that floats beside the sentence.

Soft wrap: :view-wrap

A paragraph too wide for the window wraps onto the next row. This is on by default, because a Chinese paragraph is often one line of several hundred characters.

:view-wrap off       one line per paragraph; the page scrolls sideways
:view-wrap on        wrap at the window's edge (also :view-wrap alone)
:view-wrap 50        wrap at column 50: a measure (below)
:view-wrap 0         drop the measure and wrap at the window again

A Latin word is never cut in half. A row never starts with closing punctuation such as 。 、 」 ), and never ends with an opener such as 「 (.

Under the helix keymap, j and k move by rows on screen and gj gk by lines in the file. Under the vim keymap it is the other way round, as in vim. The line number is printed only on the first row of a paragraph.

Wrap is for horizontal layout. A vertical column has its own length, set with :view-wrap-vertical; see Vertical layout.

The measure

A measure writes the page narrow. :view-wrap 50 wraps every line at column 50 and tints everything to the right of it. On a wide monitor this keeps lines at a length the eye can follow. If the window is narrower than the measure, the window wins.

To keep it, set measure in the config:

[editor]
measure = 50   # wrap here; 0 is the window's width
ruler = 80     # only a cue: tint whatever runs past column 80

ruler does not wrap anything. It tints what runs past the column, which helps if you break long sentences by hand. With wrap off it also draws a vertical line there.

First-line indent: :indent

A Chinese paragraph opens with a two-square indent, not a blank line. :indent draws that indent without changing the file.

:indent full     indent every paragraph and fold away the blank line between them
:indent basic    indent, and keep the blank lines
:indent off      no indent (default)
:indent 2        the width in squares, up to 8; turns the indent on if it was off
:indent          tell me the current level and width
[editor]
indent = 2       # a width here turns the indent on, at the full level

Under full, the blank line between two paragraphs stays in the file but leaves the screen, and the line numbers skip (1, 3, 5) to show that it is there. The paragraph the cursor is in is drawn as the file has it, with no indent and its blank line back above it. Two or more blank lines in a row stay on the page, as do blank lines inside a code fence or front matter. The cursor never stops inside an indent or on a folded line.

Headings, lists, quotes, tables, code fences and footnote bodies are not indented, and a line already indented gets nothing added.

The indent squares are blank by default. To see them:

:indent-hint color     a faint tint
:indent-hint symbol    a mark in the first square (↵, or [editor] indent_symbol)
:indent-hint none      blank (default)

Pages side by side: :view-pages-*

On a wide window you can cut the page into columns that run on from one to the next, like a newspaper. The text flows from the left page into the right one.

:view-pages-horizontal 2    two pages side by side
:view-pages-horizontal 1    one page, the whole window
:view-pages-vertical 2      in vertical layout, two pages one above the other
:view-pages-horizontal      how many there are now

The number is 1 to 4, and each layout keeps its own. Each horizontal page has its own line numbers and wraps at its own width, even with wrap off. Moving, scrolling, paging, zt zz zb and mouse clicks work across all the pages as if they were one. When the window is too narrow, you get fewer pages: each page needs at least 30 cells of text.

[editor]
pages_horizontal = 2
pages_vertical = 2

The margin: :view-margin

Ruby readings, hung punctuation, emphasis dots and 平仄 marks are drawn in the margin, a narrow strip beside the text: the row above a line, or the cell to the right of a vertical column. :view-margin decides when the margin is kept.

ValueThe margin is kept
nevernowhere
dense (default)on each row or column that carries something
looseon every row or column of a paragraph if any of it carries something
alwayseverywhere

loose keeps the spacing even through a paragraph; always looks most like squared paper. Under never, ruby tags are still hidden but the readings are not set. The indent is not part of the margin, so never does not turn it off. In config: [editor] margin = "dense".

Preview in a real typesetter: :view-preview

:render full is the most this screen can show. :view-preview hands the file to a real typesetter and shows the result in your browser.

FileWhat happens
Typsttinymist preview starts, and the browser opens its page
Markdownan HTML export goes to a temporary file, and the browser opens it

The Typst preview follows your typing without saving: each keystroke is sent to the typesetter, which recompiles only what changed. The first compile of a long book can take a while, and the page stays grey until it finishes. The preview stops when you close that file, when you type :view-preview off, or when you quit. The file must have a name; save a new buffer first.

Focus and typewriter

:view-focus on          the paragraph you are in stays lit; the rest steps back
:view-typewriter on     the row you are writing stays in the middle of the screen

Focus works by paragraph, not by row: a paragraph wrapped over three rows is lit as a whole. Both take on, off and toggle; typed alone, each says whether it is on.

The count as you write

The status line shows how long the file is, after the cursor position, and the outline sidebar (Space s) shows each heading's count. See Checks and counts for how words are counted and where the count is shown.

The page you left

ye -r and :resume bring back how the page looked along with the files. See Files, saving and changes.

CJK motions

Chinese is written without spaces, so an editor that finds words by spaces sees a whole paragraph as one word. yumete finds Chinese words with a dictionary, so the word keys step through Chinese the way they step through English. This page covers moving by word, clause and sentence, jumping to what you see, searching by pinyin and across simplified and traditional characters, and how yumete learns the names in your book.

Words, clauses and sentences

KeyMoves
wto the next word
eto the end of the clause
bback to the start of the clause
W B Ethe same, split on spaces only
H Lto the previous / next sentence; right after L, H first re-selects the same sentence backwards, as helix's b does after w
{ }to the previous / next paragraph

On 我們明天去圖書館借書。 with the cursor on 我, w selects 我們, then 明天, then 去, then 圖書館. e instead runs straight to 書, the last character before the 。.

w takes a word, e takes a clause. With a dictionary, a w and an e that both stopped at word ends would do almost the same thing, so e runs to the next punctuation mark instead. b is e's partner and walks back to where an e would have started. A run of punctuation marks such as :「 is one stop, as it is in vim and helix.

Under the vim keymap w, b and e all go by dictionary word, as vim's do by English word, and the sentence motions are ( and ). As in vim, w and b stop on an empty line, and e passes it by.

W, B and E split on whitespace and nothing else, so a run of Chinese with no spaces in it is one unit. Use them for URLs, paths and version numbers.

A sentence ends after 。 ! ? or …, and after any closing quote or bracket that follows: in 「不。」他說。 the first sentence ends after the 」. A half-width . ends a sentence only when a space or the end of the line follows, so 3.14 does not. When there is no next sentence in the paragraph, L goes to the first one of the next paragraph.

Text objects

The same units are text objects. With the cursor on 期 in 他說:「今天(星期三)要下雨。」:

KeysSelects
mi w星期三, the word
mi sthe sentence, from 他 to the closing 」
mi u星期三: the clause, up to any punctuation mark on either side
mi (星期三: inside the parentheses; ( covers () too
mi [inside the corner brackets; [ covers 「」 and 【】 too

Each ASCII bracket key covers its full-width and Chinese relatives, so your hand goes to the same key. The full list is in Match mode. Under the vim keymap these are iw, is and the rest.

How words are found

Word boundaries come from a word list. With Yume's data installed (see The input method), yumete uses Yume's language model. Otherwise it uses segmentation.txt in the data directory if there is one (:word-list-global opens it), and a built-in list if not. :word says which list is in use, the level and the mark.

:word-level sets how readily characters join into words:

LevelWords
offno dictionary: a run of Chinese counts as one word
strictonly the clearest words, more single characters
balancedas the dictionary has them (the default)
fulllong and unusual words too

The setting is word_level under [editor].

By default, every other word is drawn in a second colour of the same lightness, so you can see where yumete thinks the words are. :word-mark changes how: color (the default), ink, tint, line, or off. The setting is word_mark under [editor].

The book's own words: .yumete/words.txt

The name on every page of your novel is the one word no dictionary has. Left alone, 阿寧 is cut into 阿 and 寧: w stops twice, and the word colours draw two words. List such words in .yumete/words.txt at the root of your project, one per line. # starts a comment.

# People
阿寧
沈鶴年
# Places
落霞鎮

:word-list-local opens the file, even before it exists. The project is worked out from the working directory, not from the file; see A book of many files.

The list is read at startup, and again whenever you save it with :w; the status line says how many words are now in force. If you changed it outside yumete, :word-list-reload reads both lists again.

Your words are laid over the dictionary. The dictionary still decides everywhere else, and only where one of your words is spelled out does it become one word. The longer match wins, so 阿寧 beats 阿.

yumete only reads this file and never writes to it.

Words yumete finds by itself

You do not have to list every name. When you open a file, yumete reads it in the background and picks out strings that behave like words but are not in the dictionary, and from then on treats them as words. Nothing is shown and nothing waits: before the scan finishes, w follows the dictionary, and after it w steps over 落霞鎮 in one go.

It reads the file you opened first, then the folder it is in, subfolders included. Saving asks again: a different file is read at once, the same file only after five minutes. Very large files are skipped.

A string counts as a word when its characters occur together far more often than chance would give, and it turns up next to many different characters on both sides. The first test separates 阿寧 from 的時; the second separates 阿寧 from 阿寧說, which always has the same neighbour. Anything the dictionary already joins is left out.

This list lives only in memory. There is nothing to accept or reject: close yumete and it is gone, and the next file you open works it out again.

Seeing the list: :word-discover

CommandReads
:word-discoverthis file
:word-discover-workingthe working directory, subfolders included
:word-discover-projectthe project, subfolders included

The command works the list out on the spot, writes it to .yumete/discovered_words.txt, ordered by count, and opens it.

That file goes one way only: yumete writes it on every run and never reads it back, so editing it does nothing. To keep a word for good, copy it into .yumete/words.txt, which goes the other way: you write it and yumete reads it.

Jumping to what you see

Three keys jump to a place on the screen. C-o takes you back.

gw: any clause. Every clause on the screen gets a letter label on its first character; type the label to jump there. Up to 26 places and each label is one letter; more, and labels take two. The places are the units e stops at, so 那年冬天,雪下得早。 gets two labels, not one per character. It works the same on a vertical page, one label to a column.

gx: two letters of Latin text. Type the first two letters of a word you can see, and every place on the screen spelled that way gets a label. A one-letter word such as a is followed by a space, and the space is its second letter. gx looks only at Latin text; for Chinese use gz.

gz: a Chinese character by its reading. Type the pinyin of the character you are looking at. A panel lists the characters on the screen with that reading and how many times each appears; keep typing until the list is short. Then pick one with its number, or with Space, ; or ' for the first three. Each place it appears gets a label. - and = page through a long list. For example, on a screen with 雪, 下 and 見 on it, gz x lists all three, and ; then labels every 下.

To find one character on the line, f and t take Chinese as well. With Chinese input on, type the character's code and pick it as usual. f, does not find ,: they are different characters.

Searching by pinyin and across 繁簡

A search can be widened in two ways: simplified and traditional forms count as the same character, and pinyin finds the characters it spells. /shuzhai finds 書齋 and 书斋, and letters and characters can be mixed, as in zhong国. On the / prompt A-j turns both on; :s has a flag for each, and the search panel and ye --grep have both on. See Search and pickers for the rules.

Punctuation

Chinese punctuation is full-width, and yumete treats it that way:

  • Joining lines. J puts no space between two Chinese characters and one space between two Latin words, so joining a broken paragraph gives back a clean one. A-J does the same and leaves the seam selected, so you can see what was decided and type over it.
  • Line breaks. A wrapped row never starts with a closing mark such as 。 , or 」, and never ends with an opening one such as 「 or (.
  • Brackets. mi (, ma [ and the rest of the match group treat (), 「」, 《》 and their kin as the pairs they are. With auto_pairs = "full" under [editor], typing an opening Chinese bracket adds the closing one (see Settings).
  • Width. `f turns the selection full-width and `h half-width, for the characters Unicode pairs: , and ,, A and A. 「」 and 。 have no half-width form and are left alone.
  • Wrong marks. :view-punct shows the right mark beside a half-width one in Chinese text (see Ruby and annotations), and :check-punct lists the problems (see Checks and counts).

Vertical layout

yumete can set a page vertically, the way Chinese and Japanese books are set (縱書): characters run from top to bottom, and each new column stands to the left of the one before. It is a way of looking at the file. The file on disk stays an ordinary text file, and :w writes exactly what you typed.

A terminal cell is about twice as tall as it is wide, so one full-width character in two cells is a square. Stack those squares and you get a column that reads like a printed page.

Turning the page

ToType
set this page vertically:vertical
set it across again:horizontal
open a file verticallyye --vertical file.md
open it across, whatever the config saysye --horizontal file.md
make vertical the defaultlayout = "vertical" under [editor]

Two kinds of file are always set across:

  • Program files. Open a .rs or .py and the page stays horizontal. Indentation is part of the syntax there, and line numbers and diagnostics need one line to a row. :vertical says so on the status line and changes nothing else: switch back to your manuscript and it is still vertical.
  • Tables. A file opened with --table is a grid, and a grid is always horizontal.

On the status line and in messages, a column is called a 縱 (zong). Here "line" and "column" each have two meanings, so the column gets a name of its own. A paragraph is one line of the file. A long paragraph wraps into as many columns as it needs and runs on from the foot of one column to the head of the next.

   12  他  起那
   章  在  了年
   ︒1997  大冬
       年  雪天
       寫  ︒︐
       了    山
       第    下

Two paragraphs with an empty line between them, each wrapping into two columns. 1997 is set across in one cell (see Half-width characters).

How long a column is

By default a column is as long as the window is tall. To set your own length:

:view-wrap-vertical 24      24 characters to a column
:view-wrap-vertical 0       as long as the window again
:view-wrap-vertical         the same as 0

The length can be 8 to 64; a smaller number is raised to 8 and a larger one lowered to 64. The setting is zong_length under [editor] (0 follows the window). A column never runs past the bottom of the window, whatever number you give. :view-wrap is the horizontal measure and does nothing to a vertical page.

Two more settings shape the columns:

  • zong_gap under [editor]: half-width cells of white space between one column and the next, 0 to 4. The default is 0.
  • :view-pages-vertical 2 cuts the window into two pages, one above the other. You read the top page right to left, then the bottom page right to left. It takes 1 to 4, and 1 is one page. The setting is pages_vertical under [editor]. On a horizontal page the number waits until you turn the page.

Moving

The page has turned ninety degrees, and by default h j k l turn with it. Each key goes where it points:

KeyVertical page
j kdown / up the column
hto the next column, on the left
lto the previous column, on the right
↓ ↑ ← →the same as j k h l
gh glstart / end of the paragraph (0 $ under the vim keymap)
gj gknext / previous paragraph; under the vim keymap, next / previous column
C-d C-uhalf a page onward / back, across the columns
C-f C-ba whole page

j at the foot of a column carries on at the head of the next one, so you can walk a whole paragraph with one key.

The mouse wheel turns the page sideways, three columns a notch. :wheel 5 changes how many.

Keys that follow the text instead

:vertical-keys logical
:vertical-keys visual       the default
:vertical-keys              switch to the other one

With logical, the four letters mean what they mean on a horizontal page: h and l are a character back and a character on, j and k are the next and previous line. A line here is a column, so j steps to the column on the left. A count or an operator then means the same thing in both layouts: 3j is three lines and d2l is two characters, whichever way the page points. Choose this if you record macros or switch layouts often. The setting is vertical_keys under [editor].

The setting changes only those four letters:

  • The arrow keys always point. ↓ is one character down the column and ← is the column on the left, under either setting.
  • Key bindings you write mean the logical keys. "x" = "@dl" in [keys.normal] deletes one character under either setting. Only the keys your own hand presses are turned.
  • gh gl gj gk do not change. A paragraph and its two ends do not point anywhere.

Under the vim keymap, whether h and l cross the end of a paragraph follows which_wrap, as it does in vim.

Where the cursor is

The status line reads Zong 55, Heng 11, Chr 20 on a vertical page. These are the horizontal Ln, Col and Chr turned on their side:

FieldMeansAcross the page it is
Zongthe paragraph number, the same for every column it wraps intoLn
Henghow many cells down the paragraphCol
Chrhow many characters down the paragraphChr

Heng and Chr differ when several half-width characters share one cell (see Half-width characters). In 26年, 年 is the third character but stands in the second cell. When the status line is crowded, Chr is the part left out.

Paragraph numbers

Each paragraph's number stands above its first column, two digits to a row, so 1280 takes two rows. Two numbers side by side could run together, so every other number is a step dimmer. Odd and even follow the paragraph number, so the pattern does not crawl as you scroll. The number of the paragraph the cursor is in has the theme's accent colour. A ~ marks the empty line at the end of the file. :view-numbers-fill on gives the number band a ground of its own.

What the page does on its own

Punctuation turns

Punctuation is drawn in its vertical form:

In the fileOn the page
。 , 、︒ ︐ ︑
: ; ! ?︓ ︔ ︕ ︖
「」 『』﹁﹂ ﹃﹄
() 《》 【】︵︶ ︽︾ ︻︼
—— ……︱︱ ︙︙

Only the screen changes. Copying, searching and saving all see the original characters.

How much turns follows :render: under off the marks are drawn as the file has them; basic, the default, turns the marks above; full also sets the curly quotes, “” as ﹃﹄ and ‘’ as ﹁﹂.

The vertical forms live at U+FE10–FE48. Fonts with CJK coverage have them: Source Han and Noto CJK, Sarasa Gothic, LXGW WenKai Mono. A font with only Latin glyphs shows boxes instead.

Line breaking

A column does not begin with a mark that closes something (。 , 、 」 ) and the like), and does not end with a mark that opens something (「 (). When a break would fall there, the character moves to the next column with its neighbour. A two-cell mark such as —— or …… is never split between two columns. The horizontal page follows the same rules.

Half-width characters

Latin letters and digits stand one to a row, against the right side of the cell, so they line up with the right edge of the Chinese characters.

A short run of them is set across inside one cell instead, so a year reads as one number and not as a stack of digits. This is tatechuyoko (縱中橫):

[editor]
tatechuyoko = 4    # 0 is off; 2 to 8 is the longest run set across

The default is 4, which covers years (1997) and chapter numbers (第12章).

  • A run is set across whole, or not at all. Half of 1997 in one cell and half in the next would read as 19 and 97.
  • A run of 2 fits the cell exactly, so the grid is not disturbed. This is how Japanese books set two-digit numbers.
  • A run of 3 or more is wider than the cell. It borrows the gap to its left, and only that column moves over. A few years and acronyms on a page cost little; if a page is full of long runs, set it back to 2.
  • A run longer than the setting is stacked one character to a row.

The cursor still moves one character at a time. Press j on 1997 and Heng and Chr move four times while the cell stays boxed whole.

Paper ticks

[editor]
paper_ticks = 10

This puts a faint tick in the margin every ten characters, as on squared manuscript paper, so you can judge length at a glance. It is off by default (0). Turning it on gives every column a margin, so the whole page shifts one cell to the left. A reading or a hung mark takes the cell before a tick does.

The margin

One cell to the right of each column is the margin. Readings, hung punctuation, emphasis dots, 平仄 marks and paper ticks are drawn there. :view-margin decides when a column keeps that cell; see The page for its values, and Ruby and annotations for what goes in it.

Hanging punctuation

In old books, 。,、?!:;「」 do not take a square of their own. They hang in the margin beside the character they belong to, and the text runs on unbroken. A page of dialogue then stops looking half empty.

:view-hanging on
:view-hanging off
:view-hanging toggle
:view-hanging            say whether it is on

The setting is hanging_punctuation = true under [editor]. It needs a vertical page and a margin. If either is missing, the command says so; write :view-hanging! on to turn them on with it.

   off      on
   起那     了 那
   了年     大 年
   大冬     雪。冬
   雪天        天,
   ︒︐        山
     山        下
     下        起

The hung marks are half-width, because the margin is one cell wide:

MarkHangs as
。 、 「」。 、 「」
, ? ! : ; (), ? ! : ; ()

A full-width mark in that cell would spill over the column to its right.

Some marks always keep their square:

  • 《》 【】 『』 〔〕 have no half-width form.
  • —— and …… would break in two.
  • A mark at the start of a paragraph has nothing to hang on.
  • A mark whose character already has something in its margin.

An opening bracket hangs beside the character it opens onto. Every other mark hangs beside the character before it.

Two marks in a row do not hang. 。」 and 、」 squeeze together into one square of the text, 。」, half a cell each; print sets them the same way. ?」 and !」 have no true half-width form, so each of the two takes its own square at full size.

When a reading and a hung mark want the same cell, the mark wins and the reading moves up to the rows above its character.

One sentence to a column

When you proofread, you read one sentence at a time.

:view-sentence on       one sentence to a column
:view-sentence off      full columns again

Every sentence then starts at the head of a new column. A sentence ends at 。 ! ? or …, and a closing quote after it goes with it: 「你回來了。」 is one sentence, and the 」 is not pushed onto the next column. A sentence longer than a column still wraps into the next one; only the next sentence always starts fresh.

   我他﹁
   點問你
   頭︒回
   ︒  來
       了
       ︒
       ﹂

The breaks are the same ones the sentence motions jump between (H and L, or ( and ) under the vim keymap), so the head of every column is a place the cursor can jump to.

Nothing in the file changes. It needs a vertical page; :view-sentence! on turns that on too.

See also

Ruby and annotations

This page covers four things yumete draws beside the text:

  • readings (ruby): pinyin, bopomofo or kana over or beside a character;
  • emphasis dots (着重號) beside emphasised characters on a vertical page;
  • 平仄 marks for writing classical verse;
  • punctuation hints: the full-width mark a half-width one should have been.

Only readings are stored in the file, as markup. The other three are views and never change a byte.

Readings and 平仄 marks are drawn in the margin: the cell to the right of each column on a vertical page, the row above the line on a horizontal one. Emphasis dots go in the margin too, but only on a vertical page. Punctuation hints are drawn in the text, beside the mark they correct. See The page for when the margin is kept.

Writing a reading

A reading is written into the file as markup. yumete reads two kinds. HTML ruby, for Markdown, HTML and plain text:

<ruby>漢<rt>hàn</rt></ruby>

and Typst's ruby call, for Typst:

#ruby("漢", "hàn")

A browser shows the HTML form as ruby, and Typst's compiler typesets the Typst form, so the file stays useful outside yumete. Both kinds are read in every file, and one file may mix them.

One reading can cover a whole word, or each character can have its own:

<ruby>漢字<rt>hàn zì</rt></ruby>
<ruby>漢<rt>hàn</rt></ruby><ruby>字<rt>zì</rt></ruby>

The second form, one group per character, is how Chinese ruby is usually set.

How readings are shown

:ruby-render off        the markup is plain text
:ruby-render basic      the readings are known; the markup stays on the page
:ruby-render full       the readings are laid out and the markup is hidden
:ruby-render            say which one is on

The default is basic. The markup stays where you typed it, but yumete knows what is a reading: the word count, for example, counts 漢字 as two characters and the pinyin as nothing. off treats every tag as text.

With full, the readings are drawn beside their characters and the tags are hidden. Move the cursor into a group and its whole source comes back for you to edit, as with any other Markdown mark. To start in full, set show_ruby = true under [editor]. :render full (see The page) lays out readings along with all the other markup.

To stop reading one kind of markup, :ruby-html off or :ruby-typst off. :ruby-html on or :ruby-typst on reads it again and lays it out. With no word after it, either command says which kinds are being read. Both kinds are read from the start.

On a horizontal page

A reading sits on the row above, over the character it belongs to. Only a line that has a reading gets the extra row, so one annotated word does not double-space the whole chapter.

A reading wider than its characters runs over, and the next reading is pushed to the right:

   ㄩㄥˇ ㄏㄜˊ
   住在永和的人。

The characters themselves are not spread apart, because that would shove the rest of the line along. And no reading is dropped: a reading a cell out of place is one you can see.

On a vertical page

A reading stands in the margin to the right of its column, and the characters are spaced out down the column to match its length. Two readings can never collide this way.

   寫
     h
   漢à
     n
   字z
     ì
   給

The margin is one cell wide for pinyin. If any reading on the page is full-width (bopomofo, kana), the margin is two cells wide for the whole page, so the columns stay in line. Pinyin with a tone mark also takes two cells when ambiguous_width is "wide", because that is how the terminal draws it.

When a hung punctuation mark wants the same cell as a reading, the mark wins and the reading moves up into the rows above its character (see Hanging punctuation).

Editing a reading: :ruby

:ruby opens the command line, below the status line, for the reading alone:

  • On a character that has a reading, the line holds that reading. Change it and press Enter.
  • With characters selected, the line starts empty. Type the reading and press Enter.
  • An empty line removes the reading, markup and all. So Backspace on an empty line does not close it, as it would on the search line. Press Esc to leave without changing anything.
  • The input method works here, so you can type kana or bopomofo.

To give each character its own reading, split the reading with |, one part per character: hàn|zì on 漢字 writes two groups, while hàn zì writes one reading for the word.

:ruby has no key of its own. Its action name is ruby, so you can bind it to one; see Key remapping.

Changing the markup: :ruby-format

:ruby-format html       rewrite every reading in the file as HTML
:ruby-format typst      rewrite every reading as Typst

Fenced code blocks and the front matter at the top of the file are left as they are. A book about readings is full of markup examples, and those must not be rewritten.

Readings written for you: :ruby-auto

:ruby-auto          every word in the selection, or in the file
:ruby-auto-rare     only words with a rare character in them

:ruby-auto goes through the selection word by word, or the whole file when nothing is selected, and writes in every reading it can find. It writes Typst markup in a .typ file and HTML everywhere else, one group per character. The status line says how many words it annotated, and one u takes them all back.

It needs Yume's 拆分表, the table :yume-scheme loads (see The input method). Without it, the command says so and does nothing.

It reads by word, not by character. 了 is le in 為了 and liǎo in 了解. yumete looks the whole word up, so a character with several readings gets the one its word uses. A word it cannot find is left as it was; nothing is guessed.

Readings that differ only in tone are not told apart. The word table has no tones, so 為 (wéi in 認為, wèi in 為了) always gets the more common reading. 難, 好, 教 and 中 are the same. Check those by hand. Readings spelled differently, such as 了, 行, 和 and 長, are settled correctly.

Readings already in the file are left alone, so a reading you corrected by hand is never overwritten.

Only the rare characters

A novel with a reading on every character reads like a textbook. Usually you want readings only where a reader would stop, and that is :ruby-auto-rare.

It annotates a word only if one of its characters is in none of the four character standards in daily use: the mainland 通用規範漢字表, its traditional counterpart, Taiwan's and Hong Kong's. If any one of them has the character, it is not rare. When a word has a rare character, the whole word gets readings, not only that character.

Emphasis dots

Chinese does not slant its characters for emphasis. It puts a dot beside every emphasised character (着重號), and on a vertical page the dot goes to the right. So on a vertical page, yumete draws *這樣* as emphasis dots:

   只
   是
   一·
   個·
   詞·
   而
   已

There is no new markup: *…* is already emphasis, and dots are what emphasis looks like in Chinese. **…** gets no dots; bold is a different weight.

The dots go in the margin, and a column with emphasis keeps a margin for them. They come last: if a reading, a hung mark or a 平仄 mark wants the cell, the dot gives way. Nothing is drawn under :view-margin never or :render off; with :render off, * is just an asterisk. A horizontal page draws no dots.

平仄: :view-meter

When you write classical verse or fill in a 詞 pattern, you check 平仄 one character at a time. :view-meter marks it for you, in the notation 詞譜 use:

   春○
   眠○
   不●
   覺○
   曉▲
:view-meter on
:view-meter off
:view-meter toggle
:view-meter             say whether it is on
MarkMeans
○平
●仄
△平, at the end of a 句
▲仄, at the end of a 句

The triangles mark the last character of a 句, where a rhyme can fall. A 句 ends at 。 , ! ? ; : or …. 、 does not end one, and neither does ——. Punctuation inside brackets belongs to the aside and does not end the 句 around it.

On a vertical page the marks are in the margin; on a horizontal page they are on the row above. The command needs a margin; if :view-margin is never, write :view-meter! on to turn the margin on as well.

The tone of each character is looked up by word, from the same 拆分表 that :ruby-auto uses. Without it, there is nothing to look up, and the status line says so.

These are modern readings, and 入聲 is where they go wrong. The tone comes from Mandarin pinyin. The old 入聲 characters have spread over all four modern tones, so 竹 (zhú), 白 (bái) and 石 (shí) are 仄 by the classical rules but marked 平 here. The data has no Middle Chinese readings, and pinyin cannot be worked back to them.

  • Writing by 中華新韻, the marks are right.
  • Writing by 平水韻, they are a first pass: they catch the mistakes in the third and fourth tones and say nothing about 入聲.

A syllable in the neutral tone gets no mark; it is neither 平 nor 仄.

When a cell already holds a reading or a hung mark, the 平仄 mark gives way.

Punctuation hints: :view-punct

A half-width , in Chinese text is easy to type and hard to see. :view-punct writes the right mark beside the wrong one, where you are reading:

他説,,好吧...……我知道了!!
:view-punct on
:view-punct off
:view-punct toggle
:view-punct             say whether it is on

The hint is drawn in the colour the editor uses to point at things, not in the colour of the text, so you can tell them apart. On a vertical page it turns with the page, in the same cell.

The hint is not in the file. The cursor cannot land on it, x cannot delete it, and :w writes what you typed. Fix the mark and the hint goes away.

It shows two things: a half-width mark next to a Chinese character, and .... 3.14, 1,000 and English sentences are left alone, and so are code blocks, where , is correct. Nothing is drawn under :render off, which shows the file exactly as it is.

A missing 」 is not shown, because one line cannot see the next paragraph. :check-punct lists those; see Checks and counts.

The input method

yumete has an input method inside it. It is the engine of the 宇浩 input method, Yume, built into the editor. You type a code in Insert mode, a list of candidates appears, and the one you pick goes into the text. You do not need a system input method to write Chinese in yumete, and you do not need to switch anything at the operating system level.

This page is for someone who has never used it. It covers:

  • what the input method needs before it can type;
  • turning it on and off, and switching between Chinese and English;
  • choosing a scheme;
  • typing: codes, candidates, paging and committing;
  • the candidate panel: where it is drawn and how it looks;
  • the quick symbols on ;;
  • the Space z menu;
  • using your system input method instead;
  • the settings that matter, and what to do when something is wrong.

If you have used 宇浩 on macOS or Windows, most of this will feel familiar. The schemes, the codes, the candidate order and the commit rules are the same, because the engine is the same.

What it needs

An input method is two things: an engine and a code table (碼表) that says which code gives which character. The engine is always in yumete. The code tables are data, and where they come from decides which schemes you can use.

The two tables in every build

Every yumete binary carries two small tables of its own:

  • 靈明, 精華版. Every character in the CJK basic block and Extension A, the 宇浩 component block, and the rest of the characters in the seven character sets, plus the short codes. It has no words, so whole-sentence input falls back to one character at a time.
  • 星陳, 精簡版. The same idea for 星陳.

With nothing else installed, you can type 靈明 and 星陳 with these. The other schemes are not carried. If you ask for one and its table is not on the machine, yumete says the table is not installed and stays on the scheme you were using.

Installed data

When the 宇浩 input method is installed on the same machine, yumete reads its data. You get all of its schemes (靈明, 星陳, 卿雲, 日月 and 宇浩拼音), the full tables with words, and any schemes you added to 宇浩 yourself. There is nothing to configure: yumete looks where 宇浩 keeps its data on each system.

  • macOS: ~/Library/Application Support/Yume/ (and its data/compiled/), then the input method's app bundle, for one user or for the whole machine.
  • Windows: %APPDATA%\Yume\ (and its data\compiled\), and the Resources\ folder beside the installed program.
  • Linux: yume/ under $XDG_DATA_HOME (by default ~/.local/share), then $YUME_DATADIR, then yume/ under each of $XDG_DATA_DIRS (by default /usr/local/share and /usr/share).

An installed table always wins over the built-in one. The built-in tables are the fallback for a machine with nothing installed.

Naming a directory yourself

If your tables live somewhere else, such as a shared drive or a portable install, name the directory in the config. These directories are searched first, in the order you write them:

[ime]
data_dirs = ["~/tables/yume"]

For one run only, set YUMETE_DATA_DIR instead.

~/.local/share/yumete is yumete's own directory (your word list and sessions live there), but it is not searched for tables. A table copied there is ignored. Use data_dirs to name a directory.

To see every directory yumete searched, in order, and which files it found in each, run :yume-where (or press Space z W). The answer opens as a buffer, so you can search it and follow the paths with gf. The last line says which table is in use right now.

When the table is loaded

By default the code table is not loaded at start-up. Loading it takes a tenth of a second or so, and many people open yumete without wanting to type Chinese. The language model behind word motions (w, b, e) is loaded either way; it does not depend on the scheme.

The table loads the first time you turn the input method on. The status line says "loading the 碼表…" for that moment. If you type Chinese every day, load it at start-up instead:

[ime]
scheme = "lingming"
start = true

Turning it on and off

The sign on the status line

The status line always tells you what the input method will do with the next letter, in every mode:

Status lineStateWhat a letter does
[中 靈明]Chinese, with the scheme's namestarts a code
[ABC]Englishtypes itself
nothingofftypes itself, and the system input method is free to work

In Normal mode the sign tells you which one you will land in when you press i, before you type the wrong thing.

Two switches

There are two separate questions, and each has its own switch.

Is the input method in use at all? This is on or off.

  • :yume (or :y) turns it on, in Chinese. If no table is loaded yet, this loads the scheme named in the config first.
  • :yume off turns it off and hands the keyboard back to the system.
  • Space z z flips between the two: off becomes Chinese, and Chinese or English becomes off.

Chinese or English, while it is on? This is a tap of Shift on its own.

  • Tap Shift (press and release it, with no other key) to go from Chinese to English, and tap again to come back.
  • A Shift tap does nothing while the input method is off. Use :yume or Space z z to turn it on.
  • A Shift tap works in every mode, not only in Insert mode. Except in the settings panel, it sets what the next i will give you.

English and off look the same when you type: the letters type themselves. The difference is whether yumete is still holding the keyboard. In English, yumete keeps the system input method out of the way, and one tap of Shift brings Chinese back. When it is off, yumete lets the system input method work. See Using a system input method instead.

What the terminal must support

A Shift tap on its own is only visible to a program when the terminal speaks the Kitty keyboard protocol. Ghostty, kitty, WezTerm, foot, Alacritty and Konsole all do. Apple's Terminal does not.

When the terminal cannot report a lone Shift, yumete says so on the status line at start-up. In that case use the stand-in key, C-^ (Control and ^). It does exactly what a Shift tap does. Set it in the config:

[editor]
language_key = "C-^"   # or "C-<char>", "A-<char>", "F1"…"F12", or "off"

C-^ is the default, and it works in every terminal, not only the ones without the protocol. Older terminals send the same byte for C-^ and C-6, so on those the two names are one key.

Choosing a scheme

A scheme (方案) is a way of turning keys into characters: 靈明, 星陳, 卿雲 and 日月 are shape-based (形碼); 宇浩拼音 is phonetic.

:yume-scheme

:yume-scheme              load the scheme the config names (靈明 by default)
:yume-scheme xingchen     switch to 星陳
:yume-s r                 the same idea, short: 日月

The names are lingming, xingchen, qingyun, riyue and pinyin. Type :yume-scheme (with the space) and the menu lists every scheme yumete found on this machine, with its Chinese name beside it. Schemes you added to 宇浩 yourself appear at the end, under a tag such as custom.9a54773d and the name their author gave them.

You can shorten both the command and the scheme, as long as the short form matches only one thing. :yume-s is :yume-scheme, because no other :yume- command starts with s. l, x, q, r and p are each enough for a scheme. If a short form matches two things, yumete picks neither and shows you both in the menu: :yume-c could be :yume-chaifen or :yume-commit, so type :yume-ch or :yume-co.

If the scheme's table is not installed, yumete says so and stays on the scheme that was working. You are never left unable to type.

To set the scheme you start in, write it in the config:

[ime]
scheme = "xingchen"

Built-in or installed

When both a built-in and an installed table exist for 靈明 or 星陳, the installed one is used. Two commands switch between them. They are useful when a candidate surprises you and you want to know which table is answering.

  • :yume-builtin switches to the built-in 靈明 table, whatever is installed.
  • :yume-installed (or Space z t) switches back to the installed table for the current scheme.

:yume-which tells you which one is in use; see Troubleshooting.

A table of your own

:yume-table ~/tables/wubi86.dict.yaml

:yume-table loads any code table file. A Rime .dict.yaml works as it is: 五筆, 倉頡, 粵拼 and others. yumete strips the YAML header and works out the column order (字<TAB>碼 or 碼<TAB>字) and whether the columns are separated by tabs or spaces. A two-column text file you wrote by hand works too.

This changes the spelling only. The word frequencies and vocabulary still come from the installed data.

A code or a word longer than 255 bytes cannot be stored, so such rows are dropped and the reply ends with "N over-long rows skipped". A real code table has no rows that long. If you see this, check that the path points at the file you meant. If no row at all can be read, yumete says so.

Typing

Codes and candidates

In Insert mode with the sign at [中 …], type a code. As soon as there is a letter, the candidate panel appears, with the code you typed at its head and the candidates numbered below it. With 靈明, typing wo looks like this:

╭─────╮
│wo   │
│1 和│
│2 辶│
│3 𠂎│
│4 𠂒│
│5 𱍸│
│6 ⺧│
╰─────╯

Press Space to take the first candidate, or a digit to take the one with that number.

Keys while you type a code

KeyWhat it does
Spacecommit the highlighted candidate
1–9commit the candidate with that number on this page
; / 'commit the second / third candidate (靈明 and the other 宇浩 schemes)
= / -next page / previous page
PageDown / PageUpnext page / previous page
arrow keysmove the highlight and turn pages (see below)
Backspacedelete the last letter of the code
Entercommit the code itself, as letters
Esccancel the code
Tabshow the dictionary entry for the highlighted candidate
a Shift tapcommit the code as letters, then switch to English

A digit with no candidate under it on this page is swallowed. It does not type itself, and it does not pick a candidate you cannot see.

;, ', - and = are the scheme's own keys, not yumete's. If you load a table of your own that uses one of them as part of its codes, that key goes back to being a code key.

While a code is open, no key leaks through to the editor. The cursor does not move, so whatever you commit goes where you were typing.

Arrow keys

Two arrow keys move the highlight, and the other two turn the page. Which two depends on which way the candidates run:

Panel and layoutNext candidateNext page
box, horizontal text↓→
box, vertical text←↓
bottom, either layout→↓
inline, horizontal text→↓
inline, vertical text↓←

The opposite keys go back. Moving past the last candidate on a page goes on to the first one on the next page. Space then commits whichever candidate is highlighted.

The dictionary on Tab

Tab while a code is open shows the dictionary entry for the highlighted candidate: its 拆分 (how it splits into components), its code and more. For a word, it shows the first character. The entry floats beside the text, or appears in the sidebar if the sidebar's information panel is already open. It does not open the sidebar for you. This is the same entry Space n shows for the character under the cursor in Normal mode.

The 拆分 beside each candidate

:yume-chaifen on writes the 拆分 and code beside each candidate in the box. :yume-chaifen off removes them, :yume-chaifen toggle flips, and :yume-chaifen on its own says which it is. The strip panels (bottom and inline, below) have no room for this; use Tab there.

When a character goes into the text

When a finished code commits is the commit method (上屏方式). There are three, plus "let the scheme decide":

MethodNameWhat happens
delayed延遲上屏 (頂字)A finished code waits. The next key commits it when that key cannot continue the code.
unique唯一上屏A finished code with exactly one candidate commits at once. auto is another name for it.
fluency整句Type on. Space commits the whole sentence. Nothing commits on its own.
default預設The scheme decides. 靈明, for example, uses delayed.

Set it with :yume-commit delayed (and so on), or Space z m followed by d, u, f, or 0 for default. :yume-commit on its own says which method is in force.

These are the same methods as in 宇浩's own settings on macOS and Windows, with the same names. The one you pick stays when you change scheme.

宇浩拼音 is the exception. It has no code table to look up a segment in, so it always types whole sentences. If you set another method while in 拼音, yumete says so instead of pretending the setting took.

default is the way back. Once you set one of the other three, it stays until you choose default again. In the config, [ime] commit is the same setting; leave it out, or write "default", to let each scheme decide.

Prediction

Prediction (輸入預測) offers candidates before the code is complete. :yume-autocompletion on and :yume-autocompletion off set it, :yume-autocompletion toggle flips it, and :yume-autocompletion alone says whether it is on. Space z y flips it.

Candidates on a page

A page holds six candidates by default. :yume-menu-size 4 (any number from 1 to 9) changes it for this session, and so does Space z followed by a digit from 3 to 9. Nine is the limit because the selection keys are 1 to 9. To change the default, set [panel] page_size.

A short window or a narrow strip holds fewer candidates than this. The page size is a ceiling, and yumete never numbers a candidate it could not draw.

Where you can type Chinese

The input method works wherever you type text:

  • Insert mode;
  • the search line, /;
  • the command search, ::, which finds a command by what it does;
  • the pickers, such as Space f for files and Space b for buffers;
  • the :ruby line;
  • the keys that wait for one character of the text: f and F, r, and the surround keys such as ms and mr. Type f, then a code, and the character you pick is the one f looks for.

It does not work on the : command line, including the arguments. That line is always English. To open a file with a Chinese name, run :open with no path: that opens the file picker, where you can type Chinese to filter the list. The picker also matches pinyin and simplified or traditional forms of a name. If you need Chinese on the command line, you can still paste it.

When a prompt line opens, any code you were typing is cancelled first, because nothing on that line could finish it. : and :: always open in English, because what you type there first is a command name, and hand back Insert mode's Chinese or English when they close. /, the :ruby line and the pickers keep whatever you were using, since a search term or a file name is often Chinese. A Shift tap on a prompt line changes that line only.

The candidate panel

Two things about the panel are separate settings. Where the candidates are drawn is :yume-panel. Where the code you are typing is shown is :yume-inline-preedit. Any combination of the two works.

Where the candidates are drawn

:yume-panel box, bottom or inline, or Space z p followed by b, m or i. :yume-panel on its own says which one is on. In the config it is [panel] display.

box (the default) is the bordered list beside the cursor, one candidate per line. It has room for the 拆分 and code beside each candidate. It covers as much of the text as it has candidates.

bottom puts the candidates in a row on the last line of the window, the line where : and / are typed. They run left to right in both horizontal and vertical layout. The text loses nothing, because that line is reserved anyway:

1  第一行。
2  第二行。
~
 INS   [中 靈明] b.md   Ln 1, Col 1
wo  1 和  2 辶  3 𠂎  4 𠂒  5 𱍸  6 ⺧

If there is no command line ([editor] command_line = false, or a window too short for it), the candidates go on the status line while you type.

inline puts the same row on the line under the one you are writing, so the candidates sit where your eyes already are. It covers that one line. If there is no line below the cursor, it goes on the line above. In vertical layout it is a column instead of a row: the column to the left of the one you are writing, read downward.

1  第一行。
wo  1 和  2 辶  3 𠂎  4 𠂒  5 𱍸  6 ⺧

The two strips work like the box: the same numbers and colours, the same highlight, the same keys. They show only the number and the candidate, with no 拆分 or code beside it. Use Tab for those.

In a few places there is no row to spare, so the bordered box is used whatever the setting: while you type into /, :: or a picker, inside a panel's input field, and in a table grid.

Where the code is shown

:yume-inline-preedit off, composition or preview, or Space z e followed by o, c or p. On its own, the command says which one is on. In the config it is [panel] inline_preedit.

  • off (the default) keeps the code out of the text. It stands at the head of the candidates: the first line of the box, or the left end of the strip.

  • composition writes the code into the text, where the character will go. The candidate panel stops showing it a second time.

    1  wo第一行。
    
  • preview writes the highlighted candidate into the text, where it will go, with the cursor after it. You read the sentence, not the code. In this setting the code is not shown anywhere; choose composition if you want to see it.

    1  和第一行。
    

Three combinations people use most:

PanelCodeResult
boxoffthe bordered list with the code at its head
bottomcompositionthe code in the sentence, the candidates along the foot
bottompreviewthe candidate in the sentence, the candidates along the foot

What the preedit draws in the text is not in the file until you commit, but it takes up room on the page like real text. Wrapping, line-break rules and the cursor all count it, so the text after it never jumps somewhere you cannot see.

Two exceptions. In a / search there is no text to draw into, so the code stays at the head of the box. And with more than one selection, the preedit is not drawn into the text; the panel shows it instead, so the code does not appear in several places at once.

How the panel looks

  • Colours come from the theme, the same way the completion menu's do: the same background and border, the code in grey, the highlighted candidate in reverse. Change the theme and the panel follows. See Themes.
  • Numbers are the full-width digits 123… by default. [panel] markers sets your own, in order. Each must be full width (two cells), or the columns come apart: 123 and ㊀㊁㊂ work; 123 and ①②③ do not.
  • Corners are rounded by default. [panel] rounded = false makes them square.
  • In vertical layout the candidates run right to left, the same direction as the text they will join. The code reads downward in the rightmost column.
[panel]
display = "bottom"          # "box" | "bottom" | "inline"
inline_preedit = "composition"  # "off" | "composition" | "preview"
page_size = 6               # 1–9
markers = "123456789"
rounded = true

Quick symbols on ;

With no code open, press ;. Instead of the candidate list, a chart of punctuation appears. Each letter stands for one mark, and pressing the letter commits that mark. There is no choosing between duplicates and no paging.

╭────────────────────────────────────────────────────╮
│快捷符號 — press a letter to commit it              │
│a :「  b ~    c !    d 」    e ”     f 『    g 』│
│i ·     j 、    k 〔    l 〕    m 》    n 《    o ␣ │
│q :“   r ‘     s 「    t ’     v ?    w “     x ……│
│z ——    ; ;    , ,    . 。    $ ¥    ` `         │
╰────────────────────────────────────────────────────╯

The letters are laid out by where they sit on the keyboard:

KeysCommit
q w e r t:“ “ ” ‘ ’
a s d f g:「 「 」 『 』
z x c v b—— …… ! ? ~
i j k l· 、 〔 〕
m n o》 《 and a full-width space (shown as ␣)

Five more keys on the chart are fixed: ; , . give the full-width ;,。, $ gives ¥, and a backtick gives itself.

To leave the chart:

  • press a letter, which commits its mark;
  • press ; again, or Space, to commit ; itself;
  • press Esc or Backspace to close it without committing anything;
  • press any other key, which closes the chart and then acts as if you had just pressed it.

This chart is 宇浩's, shared with its macOS, Windows and Linux front ends, so the keys are the same in yumete as in the system input method. Schemes that use ; as a code key move the chart to another lead key; the scheme decides which.

While a code is open, ; is not the chart: in 靈明 it commits the second candidate.

The Space z menu

In Normal mode, Space z opens the input method's own menu. A popup lists the keys, so you do not have to remember them.

KeysWhat it doesSame as
Space z zturn the input method on (in Chinese) or off:yume, :yume off
Space z m then 0 d u fcommit method: default, delayed, unique, fluency:yume-commit
Space z p then b m icandidates: box, bottom, inline:yume-panel
Space z e then o c pcode shown: off, composition, preview:yume-inline-preedit
Space z yturn prediction on or off:yume-autocompletion toggle
Space z ipick a scheme from the list:yume-scheme
Space z nswitch to the next scheme that can load, installed or built in; the rest are skipped
Space z 3–9candidates on a page:yume-menu-size
Space z /show or hide the 拆分 beside each candidate:yume-chaifen toggle
Space z tuse the installed table:yume-installed
Space z wwhich scheme and table are in use:yume-which
Space z Wwhere the data is:yume-where

Where 宇浩 has a Control-Shift shortcut for the same thing, the letter after Space z is the same letter: m for ⌃⇧M (commit method), y for ⌃⇧Y (prediction), n for ⌃⇧N (next scheme), and the digits for ⌃⇧3 to ⌃⇧9 (page size). If you know one, you know the other.

Space z m, Space z y and Space z / run the :yume-* commands beside them, so without a loaded 碼表 they are refused the same way the commands are.

Esc closes the menu. A key it does not know shows the list again.

Using a system input method instead

The terminal does not know that yumete has its own input method. It sees a window where people type, so the system input method takes each key first. In Normal mode that means j opens a list of system candidates instead of moving down a line. With yumete's input method on, it means two input methods trying to compose the same letters.

So yumete asks the system input method to stand aside whenever yumete wants to read the keys itself, and gives the keyboard back when it does not.

When yumete stands it aside

Status lineNormal modeInsert mode and other typing places
[中 靈明]stand asidestand aside
[ABC]stand asidestand aside
nothingstand asideleave it alone

In Normal mode every letter is a command, so the system input method always stands aside. Where you type text, it stands aside only when yumete itself is typing for you.

The bottom row is how you use a system input method in yumete. Run :yume off (or never load a table: with the default [ime] start = false, nothing is loaded until you ask). Then in Insert mode the system input method works as usual, and in Normal mode it still stands aside so your commands work. This is also the row for anyone writing Japanese or another language in yumete.

While the input method is on, both top rows say "stand aside", so pressing i and Esc all day changes nothing. Only the bottom row changes with the mode.

What yumete does for 宇浩

What yumete sends is 宇浩's own "modal suspend" signal. It is the same signal yume-mode off and yume-mode on send for helix and vim. On macOS it is a system-wide notification; on Windows it is a window message to 宇浩.

While suspended, 宇浩 passes every key to the terminal untouched. It does not switch the input source, and it does not change its own Chinese or English state, so the menu bar does not change. 宇浩 also records which program asked, so yumete standing it aside does not mute it in your browser.

yumete gives the keyboard back:

  • when you quit, including after a crash, and on macOS when the terminal window closes;
  • when :! hands the screen to another program;
  • when the terminal loses focus, so that typing in another window works.

If yumete is killed without a chance to say so (for example, with kill -9), 宇浩 on macOS notices that the program is gone and resumes on its own.

Losing focus depends on the terminal reporting it. Most modern terminals do.

Other input methods, and Linux

Only 宇浩 understands the modal-suspend signal, so only 宇浩 can be stood aside. On Linux, 宇浩's fcitx5 add-on has no modal suspend yet, so yumete sends nothing there.

With any other input method, or on Linux, switch your system input method to English yourself before you use Normal mode, or turn it off while you work in yumete and use yumete's own.

To stop yumete from sending the signal at all, for example in a remote session or an unusual terminal:

[ime]
system = "keep"   # "auto" (default) or "keep"

Coming back to find the system input method on

Sometimes you type with 宇浩 in another window, come back to yumete, and the input method is still active: keys in Normal mode get swallowed. yumete thinks it has already asked 宇浩 to stand aside, and 宇浩 has come back on its own.

Press Esc once more in Normal mode. When Esc has nothing else to do (no selection to drop, no panel to close), it sends the request again.

Why off and English are two states

To see a Shift tap on its own, yumete turns on a Kitty keyboard protocol mode that reports every key as an escape code. That same mode stops a system input method from composing: what it commits arrives as a row of spaces. So yumete turns that mode on only while its own input method is on, and turns it off the moment you hand the keyboard back. That is why English (yumete still holds the keyboard) and off (yumete has let go) are different.

Settings

All of these can also be set in the settings panel (:settings), under the input method group, or in the config file. See Configuration and Settings.

SettingDefaultWhat it does
[ime] scheme"lingming"The scheme to load: lingming, xingchen, qingyun, riyue, pinyin, or a custom.… tag.
[ime] startfalseLoad the code table at start-up.
[ime] commit"default"Commit method: default, delayed, unique (or auto), fluency. default means the scheme decides.
[ime] data_dirs[]Directories to search for tables first, in order. ~ is expanded.
[ime] system"auto"auto stands the system input method aside; keep never touches it.
[panel] display"box"Where candidates are drawn: box, bottom, inline.
[panel] inline_preedit"off"Where the code is shown: off, composition, preview.
[panel] page_size6Candidates on a page, 1 to 9.
[panel] markers"123456789"The numbers in front of candidates; each must be full width.
[panel] roundedtrueRounded corners on the box.
[editor] language_key"C-^"Stand-in for a Shift tap, or "off".

The panel takes its colours from the theme. [panel] ink and [panel] paper are still accepted in old config files, but they have no effect.

A complete example for someone who types Chinese every day:

[ime]
scheme = "lingming"
start = true
commit = "default"

[panel]
display = "bottom"
inline_preedit = "composition"
page_size = 6

Commands

CommandWhat it does
:yume, :yTurn the input method on, in Chinese. Loads the table if needed.
:yume offTurn it off and give the keyboard back to the system.
:yume-scheme [name]Load a scheme; with no name, the one in the config.
:yume-builtinUse the built-in 靈明 table.
:yume-installedUse the installed table for the current scheme.
:yume-table <path>Use a code table file of your own, including Rime .dict.yaml.
:yume-commit [method]Set or show the commit method.
:yume-panel [where]Set or show where candidates are drawn.
:yume-inline-preedit [mode]Set or show where the code is shown.
:yume-menu-size [1–9]Set or show the candidates on a page.
:yume-chaifen [on|off|toggle]Set or show the 拆分 beside candidates.
:yume-autocompletion [on|off|toggle]Set or show prediction.
:yume-whichSay which scheme and table are in use.
:yume-whereOpen a report of where tables are searched for and found.

Every command that takes a setting answers a question when you leave the setting off: :yume-panel says where the candidates are, :yume-commit says which method is in force. The full list of commands is in Commands.

:yume-commit, :yume-chaifen and :yume-autocompletion change the engine, so they need a table to be loaded. If none is, yumete says so. The panel commands do not need a table.

Troubleshooting

Ask first: :yume-which

:yume-which (or Space z w) answers in one line:

靈明 · 碼表 ~/Library/Input Methods/Yume.app/Contents/Resources/schemes/lingming.ytab · 拆分 off · commit delayed (頂字)

That is the scheme, the table file it is reading, whether the 拆分 annotation is on, and the commit method in force. If the panel is not the box, or the code is not shown in the default place, it adds that too. For the built-in table, the table part says 出廠自帶 followed by the time that table was built, so you know how old it is.

If no table is loaded, it says so, and tells you that :yume-scheme loads one. If the binary carries no built-in table either, it tells you to install the data first.

Letters type themselves and nothing appears

Look at the status line.

  • No sign at all. The input method is off, or no table is loaded. Run :yume or press Space z z.
  • [ABC]. You are in English. Tap Shift, or press C-^.
  • [中 …] but no candidates. Check where the panel is (:yume-panel). With bottom, the candidates are on the last line of the window. With preview, the code is not shown anywhere, only the candidate.

Shift does nothing

Your terminal probably does not speak the Kitty keyboard protocol. yumete said so on the status line when it started. Use C-^, or switch to a terminal such as Ghostty, kitty or WezTerm.

A Shift tap also does nothing while the input method is off. Turn it on first.

"No 碼表 installed for …"

The scheme you asked for has no table on this machine, and yumete stayed on the one you had. Install the 宇浩 input method, or name the directory that holds your tables in [ime] data_dirs. Then run :yume-where to check that yumete finds it.

The candidates look old, or wrong

:yume-which shows which table is answering. If it is a table you did not expect, :yume-where lists every place searched, in order, and the first one holding a file wins. Each file is listed with the format stamp it carries.

To tell whether the built-in or the installed table is at fault, switch between them with :yume-builtin and :yume-installed and type the same code.

The 拆分 annotations disappeared

The data's binary format follows the engine. After an update, an older data directory can look complete while the engine refuses some files. :yume-which adds a line at the end when that happens, naming the file and what the engine expected. Update or reinstall the 宇浩 data to fix it.

Keys are swallowed in Normal mode

The system input method is active in the terminal.

  • With 宇浩 on macOS or Windows, press Esc once more in Normal mode. yumete asks it to stand aside again.
  • With another input method, or on Linux, switch the system input method to English yourself.
  • If you set [ime] system = "keep", yumete never asks; set it back to "auto".

A table of your own loads with rows missing

The reply said "N over-long rows skipped". A code or word over 255 bytes cannot be stored. A real table has none, so the path probably points at the wrong file.

Tables

A CSV file is text, and you can edit it as text. But a table with twelve columns reads badly as text: the columns do not line up, and to find the fourth field of a row you count commas. Table mode shows the same text as a grid. The file does not change; only the way you see it and move through it does.

yumete reads four kinds of table the same way:

  • A delimited file: .csv, .tsv, or any file whose every line splits into the same number of fields.
  • A Markdown pipe table: rows written between | marks, with a | --- | row under the heading.
  • A delimited stretch inside a document: a few comma- or tab-separated lines pasted into a chapter, or the entries of a code table.
  • A new table you make with :table.

All four use the same keys. This page starts with the views, then covers moving, editing, rows and columns, sorting, searching, checking, and converting.

When table mode turns on

Opening a .csv or .tsv file opens it as a table, filling the window. You do not have to ask.

Opening any other file whose lines all split the same way opens it in the basic table view. yumete tries tab, comma and semicolon, and takes the first one that appears the same number of times on every line and gives at least two columns. The status line says what it guessed:

this file looks Tab-delimited, so it opens as a table; ␣t o for the source

If the guess is wrong, press Space t o once. yumete remembers that choice for that file, so it opens as plain text next time. Press Space t b in that file later to undo the choice. The list keeps the 200 most recent files.

Starting with --table reads the file as a table even when nothing would guess it:

ye --table prices.txt

A Markdown pipe table turns on by itself when the cursor reaches one of its rows. In Markdown, only a table that parses counts: it needs its | --- | row, and it must not sit inside a code fence.

A delimited stretch inside a document turns on when you ask. Put the cursor on one of its lines and run :table-render basic. See A table inside a document.

:table 3 4 writes a new, empty pipe table on the line the cursor is on: three rows (the heading counts as one) by four columns. The line must be blank. Up to 200 rows and 32 columns. You start typing in the first heading cell, and Tab moves to the next.

| Name | Age |     |     |
| ---- | --- | --- | --- |
|      |     |     |     |
|      |     |     |     |

The three views

The same table can be drawn three ways. Choose one with a key from the Space t group or with :table-render:

ViewKeyCommandWhat you see
SourceSpace t o:table-render offThe file as you typed it.
BasicSpace t b:table-render basicThe text, with the columns lined up.
WindowSpace t t:table-render windowOnly the table, drawn as a grid, filling the window.

:table-render with no word says which view you are in. Space t q leaves the window view and returns to the view you came from. You can switch between any two directly; you do not need to go back to the source first.

Here is one small file in each view.

title,author,year
Rain Notes,Lin,1998
Glass River,Okafor,2011

Source:

1  title,author,year
2  Rain Notes,Lin,1998
3  Glass River,Okafor,2011

Basic:

             1      2    3
1  title      ,author,year
2  Rain Notes ,Lin   ,1998
3  Glass River,Okafor,2011

Window:

             1      2    3
   title       author year
1 ┆Rain Notes ┆Lin   ┆1998┆
2 ┆Glass River┆Okafor┆2011┆

Source

The file exactly as written. A comma is a comma and a | is a |. Lines wrap as usual, and every key means what it means in any other text. This is the view to use when you want to fix the punctuation of the table itself.

Basic

The basic view is still your text, with every character visible. It only adds things to the screen; it never hides or replaces a character you typed.

  • The columns line up. yumete draws extra space after short cells so that each delimiter sits under the one above it. The spaces are only on screen; the file gains nothing.

  • Widths are measured in terminal columns, not characters. A Chinese character takes two columns, so a column of Chinese mixed with Latin text still lines up:

    | 字 | reading | strokes |
    | -- | ------- | ------- |
    | 木 | mu      | 4       |
    | 森 | sen     | 12      |
    
  • A row of column numbers sits above the heading. These are the numbers that the Space t keys count with (see Sorting and Searching and jumping). :table-numbers off hides it.

  • Table rows do not wrap. One row is one line on screen, however long.

  • Tab and S-Tab move by cell. Every other key keeps its ordinary meaning: h j k l move by character, and j on the last row walks out into the paragraph below.

  • The status line shows the row, the column number and the column name: Row 7, Col 2 [author].

The basic view is the right place to edit a Markdown table inside a chapter, or the source of a CSV file, when you want to see every comma.

In a Markdown file, the basic view is on by default: every pipe table on the page is drawn lined up, whether or not the cursor is in it.

Window

The window view gives the whole window to one table. Nothing else from the file is shown.

  • The delimiters are drawn as rules between the columns. You no longer see the commas or | marks, only the grid.
  • The heading is frozen on the top line and stays there as you scroll. It is drawn, not stood on: the cursor starts on the first data row, and k on that row goes no further.
  • Row numbers are the table's own. The first data row is row 1, whatever line of the file it is on. Every number you type in this view counts the same way.
  • Keys work on cells. h j k l move from cell to cell, and the cell is the unit you copy, delete and replace. To edit characters inside a cell, press Enter. See Moving and editing in the window.
  • You stay in the table. gg, ge, :120 and search all stop at the first or last row of this table, and the status line says so. To leave, press Space t q (back to the view you came from), Space t o (source), or Space t b (basic).
  • Space t ] and Space t [ move to the next or previous pipe table in the file. The column names, numbers and widths come from that table.

A .csv opens straight into the window view. A Markdown table goes into it only when you press Space t t.

Column rules

:table-rules sets how the window view separates the columns:

CommandDrawn
:table-rules line dasha dashed line ┆ (the default)
:table-rules linea solid line │
:table-rules line doublea double line ║
:table-rules colora light background on each column, with a gap between
:table-rules offnothing; the alignment alone separates them
:table-rulessays which one is in use

To make the choice permanent, set it in the config file:

[editor]
table_rules = "off"

Wide cells

A column grows to fit its widest cell, up to 32 terminal columns. A longer cell is still kept whole in the file; only its display changes. Space t w cycles between three ways to draw it, and works only in the window view:

  • Wrapped (the default): the rest of the cell continues on the lines below, inside its own column. A row is as tall as its tallest cell, and the row number appears on its first line only.

    1 ┆1  ┆short                           ┆
    2 ┆2  ┆This cell holds a rather long   ┆
      ┆   ┆sentence that goes on well past ┆
      ┆   ┆thirty-two columns of width     ┆
    
  • Folded: the cell is cut at the column width, and a > marks the cut.

    1 ┆1  ┆short                           ┆
    2 ┆2  ┆This cell holds a rather long s>┆
    
  • Whole: every column is as wide as its widest cell, up to the width of the window. The table scrolls sideways.

Moving onto a folded cell does not unfold it, so the layout does not jump while you walk across the table. To read a folded cell, open the record panel in the sidebar (Space t I), which wraps the whole value; or press i to start typing in it, which unfolds that one cell until you press Esc. While you type in a folded cell, it scrolls sideways under the cursor, and a < at its start shows that part of it is out of view.

The source and basic views always draw every cell whole. Space t w does nothing there.

Moving and editing in the window

In the window view the keyboard works at two levels. At the cell level, the whole cell is the unit: you move between cells and copy or replace whole cells. Inside a cell, you edit characters as in any text.

KeyWhat it does
Entergo inside the current cell
Esccome back out to the cell level

The status line shows where you are: CEL at the cell level, CEL.NOR inside a cell, CEL.INS while typing.

Esc at the cell level clears a count or a selection, as everywhere else. It never leaves the table.

At the cell level

The current cell is marked by its two side rules turning gold. There is no text cursor, because there is no character to stand on.

KeyWhat it does
h lprevious / next cell in the row
j ksame column, next / previous row
0 $, Home Endfirst / last cell of the row
Tab S-Tabnext / previous cell, wrapping to the next or previous row
C-d C-uhalf a page down / up, staying in the same column
C-f C-ba full page down / up, staying in the same column
istart typing at the start of the cell
astart typing at the end of the cell
cclear the cell into the register and start typing a new value
A-cthe same, but leave the register alone
dclear the cell into the register; the delimiter stays
A-dclear the cell, but leave the register alone
ycopy the cell
Ycopy the whole row, as text
xselect the whole row; press again to add the next row
%select the whole table, heading included
v then h lextend the selection along the row, a whole cell at a time
p Pput a copied row below / above this one
Rwrite what you copied over the cells here
o Oopen a new row below / above and start typing in its first cell
tthe table menu, the same as Space t

Letters at the cell level are commands, not text. Typing x does not replace the cell with "x", as it would in a spreadsheet; c does that. Replacing a cell is the commonest edit, so learn this sequence: move to the cell, c, type the new value, Esc. One u undoes it.

A mouse click selects a cell. A double click goes inside it, with the cursor on the character you clicked.

Under the vim keymap, d, c and y wait for a second key, as in vim: cc, dd and yy act on the cell.

Inside a cell

Inside, the cell behaves like a small document. h l, w b e, the operators, and selections all mean what they mean outside a table, with the cell's edges as the walls:

  • gg and ge go to the first and last character of the cell. 0 $, Home End, and gh gl do the same.
  • w, b and e never leave the cell, and neither do operators built on them, so dw cannot eat a delimiter.
  • h at the first character and l at the last step into the next cell. l at the end of a row goes to the first cell of the next row; h at the start goes to the last character of the cell before.
  • j and k step through the lines of a cell that has several (a wrapped cell, or one with a line break in it), and leave the cell at its last line.
  • o and O open a new line inside the cell, not a new row.
  • t is till again, as outside a table. To reach the table menu from inside, use Space t.

Typing

While you type, the arrow keys move as h j k l do inside a cell, and:

  • Tab goes to the next cell and S-Tab to the previous one. You can fill a whole row without ever touching a delimiter. Tab in the last cell of the table opens a new row.
  • Backspace at the start of a cell stops, and says so. One more would join two cells.
  • Enter in a CSV or TSV cell puts a line break into the cell. yumete quotes the field for you. In the grid the break is drawn as ␊. In a pipe table a row must be one line, so Enter does nothing there.
  • Esc returns to the level you came from: to the cell level if you pressed i there, inside the cell if you pressed i inside.

Copying and putting back

A row always has the same number of cells, so the only thing you can insert without changing the table's shape is a whole row. Everything else you copy is written over cells that are already there.

  • p and P put rows. Select a row with x, copy it with y, move, and press p to put it below or P above. If the register holds a cell rather than a row, p says so and suggests R.
  • R writes over. A single cell goes over the current cell. A run of cells (copied with v, l, y) goes over the same number of cells from here to the right, delimiters kept. If the row does not have that many cells left, nothing changes and yumete says how many fit. A copied row goes over the current row.
  • A block from a spreadsheet fills right and down. Excel, Numbers, LibreOffice and browser tables all copy rows as tab-separated text. Paste it with your terminal's paste, or put it in a register and press R, and it fills from the current cell. A pipe table grows extra rows and columns as needed. A CSV's columns are fixed, so a block that does not fit is refused.

Editing in the basic view

In the basic view the table is part of the page, and most keys are the page's own. A few things help with cells:

  • Tab and S-Tab move by cell, in Normal mode and while typing.
  • mi l selects the current cell, so mi l c replaces it. See Match mode.
  • The Space t group works the same as in the window view.
  • A Markdown table keeps its lined-up look while you type, because the padding is drawn, not written.

Rows and columns

The Space t group holds the keys that change a table's structure. Press Space t and pause: the menu lists only the keys that work in the table you are in.

KeyWhat it does
Space t radd a row below
Space t Radd a row above
Space t ddelete this row
Space t jmove this row down
Space t kmove this row up
Space t cadd a column to the right
Space t Cadd a column to the left
Space t Ddelete this column
Space t hmove this column left
Space t lmove this column right
Space t ycopy this whole column
Space t p Space t Pput a copied column in to the right / left of this one
Space t vwrite a copied column over this one
Space t < Space t = Space t >align this column left, centre or right
Space t Fline the table up in the file
Space t Hswitch whether the first row is a heading
Space t eopen this table's schema

Not every table has every key:

TableMissing keysWhy
Markdown pipe tableSpace t H, Space t eThe rule row under the heading already says which row is the heading.
CSV, TSV and other table filesSpace t c C D h l, alignment, Space t FThe columns belong to the file (and to its schema), so they cannot be added, deleted or moved here.
A stretch inside a documentall that change rows or columnsSee A table inside a document.

o and O at the cell level also add a row, and drop you straight into its first cell.

The heading row cannot be deleted. If you delete every data row, the table stays, with only its heading; o, O, p and Space t r add a row again.

Headings

A table file normally has a heading row. A code table often does not: it is data from the first line. Space t H (or :table-header off) makes the first row an ordinary data row, and the columns are named by number instead. Press it again (or :table-header on) to change back.

This is also how you edit the column names of a CSV without a schema: turn the heading off, edit row 1 like any other row, and turn it back on.

Lining up a Markdown table in the file

The basic view draws a Markdown table lined up on screen, but the file keeps the spaces you typed. A table written without its outer pipes (a | b over --- | ---, which GitHub allows) is read as one too, and Space t F writes the pipes in. Space t F writes the alignment into the file, so the table also looks right on GitHub or in another editor. The widths are measured in terminal columns, so a column of Chinese lines up too. If the table has no | --- | row yet, yumete adds one.

yumete does not do this on its own. Editing one cell changes only that cell, so a loose table stays loose. A table that is already lined up in the file stays lined up after an edit to one of its cells.

Space t <, Space t = and Space t > change the column's alignment by rewriting its part of the | --- | row (:---, :---:, ---:). That row is drawn, not edited: c, i and a are blocked on it.

Sorting

A sort always names its column, and runs only when you press s or S:

KeysSorts by
Space t 1scolumn 1, ascending
Space t 1Scolumn 1, descending
Space t 0sthe column the cursor is in (0 means "this column")
Space t 1,5,9scolumns 1, 5 and 9, ascending
Space t 1a2d4scolumn 1 ascending, then 2 descending, then 4 ascending

In the last form, a and d only note a column and its direction; the final s (ascending) or S (descending) names the last column and starts the sort. The status line shows what you have typed so far, and Esc drops it. A bare Space t s does nothing: sorting a large table takes time, so it is never one key away.

Numbers sort as numbers (9, 40, 200), and everything else by Unicode code point. The heading row stays on top. u undoes a sort.

The same thing as a command:

:table-sort 1 a 2 d

Here are the books sorted with Space t 3s (by year):

   title       author year pages
1 ┆Paper Moon ┆Sato  ┆1975┆96   ┆
2 ┆Rain Notes ┆Lin   ┆1998┆212  ┆
3 ┆Salt Road  ┆Ruiz  ┆2003┆458  ┆
4 ┆Glass River┆Okafor┆2011┆340  ┆

Searching and jumping

/ and ? search the text line by line, as everywhere. A table can also be searched down its columns, which is the order you want when you ask "where else does this value appear?":

KeysWhat it does
Space t /search down the columns for what the cursor is on
Space t ?the same, showing the results in the second editor region
Space t 2/search column 2 only
Space t 2-10/search columns 2 to 10
n Nnext / previous hit

At the cell level the search looks for the whole cell; inside a cell, for the one character under the cursor. The first hit is always the first one in the first column searched, wherever you started, so the same value gives the same route every time. Each hit is selected, the end wraps to the start, and two hits in one cell count as two.

If the table's schema names its link columns (see Schemas), the search covers only those. Otherwise it covers every column, and says so.

n and N walk the last search, whichever kind it was. A new / takes them back.

:table-find gives both directions as a command:

:table-find row Lin        line by line, like /
:table-find column Lin     down the columns, like Space t ? (in the second region)
:table-find Lin            no direction: line by line

The pattern is a regular expression.

Going to a cell

KeysGoes to
Space t 20grow 20, same column
Space t 20,3grow 20, column 3

In the window view, row numbers are the table's own. In the other views they are the file's line numbers. Either way the cursor stays inside the table.

A - means a range and a , means a list or a pair: Space t 2-10/ is columns 2 to 10, Space t 1,5,9s is three columns, Space t 20,3g is a row and a column.

Going to a row by name

If a schema names a key column, :table-jump goes to the row with that name:

:table-jump Okafor

C-o comes back.

A table inside a document

Sometimes only a few lines of a file are a table: a list of prices pasted into a chapter, the entries of a code table under a heading, a LaTeX tabular. Put the cursor on one of those lines and run :table-render basic (or press Space t b).

Prices from the market:

apple,3,red
pear,5,green
plum,2,purple

More text.

yumete finds the table like this:

  • The delimiter is the one under the cursor, if it is one of tab, comma, semicolon or &. If you selected some lines first, it is the character that appears the same number of times on each. Otherwise it tries those four in that order.
  • The table runs up and down from the cursor until a line without that delimiter. A blank line ends it, and so does a heading.
  • The rows must agree. Two or three lines must all have the same number of fields. From four lines up, two thirds agreeing is enough, and the table is as wide as its widest line. If they do not agree, yumete does not draw a table.
  • The first row is data. The columns are named 1, 2, 3.

Such a table is somebody else's data in the middle of your text, so yumete reads it but does not restructure it. You can move by cell, search its columns, sort it, go to a cell, and copy a column or write one over another (Space t v stops at the table's last line). The keys that add, delete or move rows and columns are not offered. To do those, convert it into a real table first with :convert-table pipe.

When the cursor walks out of the stretch, the table view ends. Run the command again to see it again.

Checking a table

:table-check looks over the whole table and lists what is wrong:

  • rows with the wrong number of fields;
  • a quoted field that does not close on the same line;
  • a row name used twice, when a schema names a key column;
  • a name with no row of its own, when a schema names link columns. Structure marks such as ⿰ do not count.

The list opens in a new buffer, sorted by line. gf on an entry goes to that row. If nothing is wrong, yumete says so in one line and opens nothing.

name,code,note
ant,a1,small
bee,b2
ant,a3,again
3: 2 columns here, 3 expected

In a file with several tables, :table-check checks the one the cursor is in. The | --- | row is neither checked nor counted.

What the editor will not let you break

A delimited file is read by counting delimiters. One stray comma turns a three-column row into four, and every column after it shifts. So yumete keeps one rule: a row always splits into the same number of cells. Every way of changing text obeys it: typing, pasting, R, the input method, :s, and deleting.

  • A delimiter you type into a CSV cell is quoted for you. Type Smith, John into a cell and the file gets "Smith, John". Remove the comma and the quotes go. You never type or see the quotes; they are the file format's, not yours. A field can also hold a quote (written doubled in the file) or a line break.

  • A | cannot be typed into a pipe-table cell. Write \| instead, the one escape Markdown tables have: a\|b is one cell.

  • Pasting text that holds the delimiter is refused, all of it, with the row and column it would have landed in. Nothing changes, so there is nothing to undo. Pasting into a pipe table escapes | as \| instead.

  • :s checks every row it touches. A substitution that only changes what is inside cells runs as usual. One that would change the number of cells or rows is refused with the line number:

    row 2 would go from 4 cells to 5 — add the t flag to mean it (`:%s/…/…/gt`)
    

    Add the t flag when you mean it. This holds in every view, the source view included.

  • Backspace will not join two cells, and d on an empty cell will not delete the delimiter beside it.

  • A table is always horizontal. :vertical is refused while a file is in the window view, and --table overrides --vertical. A pipe table inside a vertical page is not affected.

What yumete does not do is refuse a broken table. A row with the wrong number of fields still opens; it is marked, and its missing cells are drawn empty, so you can fix it in table mode. :table-check lists them all.

Converting and exporting

Making a pipe table from delimited text

Select some comma-, semicolon- or tab-separated lines and run :convert-table pipe. With no selection, it takes the stretch around the cursor, up to the nearest blank line on each side. The first line becomes the heading, the | --- | row is added, the columns are lined up, and you are in the table.

fruit,price,colour
apple,3,red
pear,5,green

becomes

| fruit | price | colour |
| ----- | ----- | ------ |
| apple | 3     | red    |
| pear  | 5     | green  |

yumete guesses the delimiter from tab, comma and semicolon: whichever appears the same number of times on every line. It needs at least two lines to guess. If none fits, nothing changes and the status line asks you to name it:

:convert-table , pipe      the source uses commas
:convert-table tab pipe    the source uses tabs
:convert-table ; pipe      any single character works
:convert-table space pipe  spaces

tab or \t, space or \s, comma and semicolon name the characters that are hard to type on the command line. yumete never guesses a space by itself, because a space also joins the words of a sentence; you can still name one.

The guess only checks that every line has the same count. Two lines of ordinary text that each happen to contain one comma will become a two-column table. u undoes it.

Taking a table apart

The other direction turns the pipe table under the cursor into delimited text:

:convert-table csv
:convert-table tsv
:convert-table ;

The padding goes, and \| becomes | again. The same command converts between two delimited forms: :convert-table tsv on a CSV makes it tab-separated. :convert-table pipe on something that is already a pipe table is refused.

If a cell already contains the target delimiter, yumete stops and names the row and column. It does not add quotes for you; choose another delimiter or change that cell first.

Space t x is the same as a menu: Space t x p, Space t x c and Space t x t convert to pipe, CSV and TSV. The capital letters (Space t x P, C, T) paste the clipboard as a table in that format, the same as :paste-table pipe, :paste-table csv and :paste-table tsv.

Exporting a copy

:convert-table changes your file. To leave the file alone and write the table somewhere else, use :export:

:export csv
:export tsv
:export csv prices.csv

It writes the table under the cursor. Without a name, the file is named after the document, with the new extension. It works on a pipe table and on a whole table file, so reading a .csv and writing a .tsv is one command. The copy keeps the source file's line endings and byte-order mark. An existing file is not overwritten unless you add ! (:export! csv). For HTML and Typst export of the whole document, see Export.

The record panel

A wide row is hard to read across. The record panel shows the current row as a list, one field per line:

╭─Record──2──────────────╮
│                        │
│  1 title  Glass River  │
│  2 author Okafor       │
│  3 year   2011         │
│  4 pages  340          │
╰────────────────────────╯
KeyOpens it
Space t ifloating beside the cursor
Space t Iin the sidebar

Space i and Space I do the same. Press the key again to close it. The panel never opens by itself: it shows the row you asked about, and when you move to another row it empties until you ask again.

  • Every column is listed, empty ones too, with its number. Often the question you opened it for is "is this field empty?".
  • In the sidebar, a long field wraps instead of being cut, so this is the place to read a cell that is folded in the grid. The floating panel cuts it short with ….
  • A computed field from a schema has a * after its name, as a reminder that the file has no such column.
  • In a link column (see Schemas), the panel lists at the top each name the cell refers to, with the row where that name is defined. A name with no row is marked —.

The panel works in every kind of table, including code tables with no heading, where the fields are named by number.

Schemas

A schema is a small TOML file that tells yumete how to read one table file: the delimiter, which columns to show and under what names, which column names a row, and which columns refer to other rows. You do not need one; without it the file's own first line names the columns. A schema helps with a large table you work on often.

yumete looks for schemas in .yumete/tables/*.toml, starting in the folder the data file is in and going up. A file that a schema names opens as a table automatically.

[table]
file = ["parts.csv"]      # the data files this schema is for
key = "char"              # the column that names each row
delimiter = ","
header = true             # whether the first line is the heading

[[table.column]]
name = "char"
label = "Char"            # shown in the heading instead of the name

[[table.column]]
name = "parts"

[[table.column]]
name = "notes"
hidden = true             # read and written, but not drawn or stopped on

[[table.detail]]          # shown only in the record panel, never written
name = "codepoint"
compute = "codepoint(char)"

[table.link]              # the cells of these columns name other rows
from = ["parts"]
to = "char"

A [[table.detail]] field can use codepoint(column), block(column) (the Unicode block) or range_label(column, name), which looks the character up in a [ranges.name] table of your own:

[ranges.blocks]
"Extension B" = [0x20000, 0x2A6DF]

What a schema adds:

  • key makes :table-jump work and lets :table-check find repeated names.
  • [table.link] limits Space t / to the link columns, lets :table-check find names with no row, and fills the top of the record panel.
  • hidden columns stop taking space.

Space t e (or :table-schema) opens the schema of the current table file in the second editor region. If there is none, yumete drafts one, describing exactly what you see now: the delimiter it guessed, the column names from the first line, and the heading setting. The draft is an unsaved buffer bound to .yumete/tables/<name>.toml under the project root (beside the data when the file lies outside a project); nothing is on disk until you :w it, and :w makes that folder. Edit it, save, then press Space t o and Space t b (or run :table-render off and :table-render basic) to read the table the new way. An existing schema file is never overwritten.

A schema with an error is not ignored silently: yumete reports the file and what is wrong with it. Schemas are for whole table files. A pipe table or a stretch inside a document has none.

Commands

CommandWhat it does
:table-render off|basic|windowchoose the view; with no word, say which one
:table <rows> <columns>write a new pipe table on a blank line
:table-rules <style>how the window view separates columns
:table-numbers on|offthe row of column numbers
:table-header on|offwhether the first row is a heading
:table-sort 1 a 2 dsort by columns and directions
:table-find row|column <pattern>search across rows or down columns
:table-jump <name>go to the row with this name
:table-checklist problems in the table
:table-detail on|offthe record panel
:table-schemaopen or write this table's schema
:convert-table [<from>] <to>rewrite the table in another format
:paste-table [<from>] <to>paste the clipboard as a table
:export csv|tsv [<file>]write the table to another file

Every Space t key is listed on the Keymap page, and every command on the Commands page.

Checks and counts

yumete can count what you have written, keep a daily log against a target, and look over a manuscript for the slips that ordinary reading misses: two spellings of one word, a quote mark that never closes, a character the printer's font will not have. It also tells you what the character under the cursor is.

Each check opens its findings as a read-only list, one line each. Press gf on a line to go to that place in the manuscript.

How words are counted

Counts follow Word's rule: each Chinese character is one, and each Latin word is one. 我用 Rust 寫了 hello world。 counts 7. The status line, :count, the daily target and :count-progress all count this way.

The count on the status line

The status line shows how long the file is, after the cursor position: Ln 12, Col 5, Chr 5 3214 words. With a selection it shows the selection's count beside the whole: 120 / 3214 words. On a narrow window the count is the first thing to give way. The outline sidebar (Space s) shows each heading's count at its right edge, including its subsections.

[editor]
status_count = "text"
ValueWhere it shows
text (default)Markdown, plain text and Typst; not code
allevery file
offnowhere

:count

:count (or :wc) reports the whole file, or the selection if there is one:

the whole file  3214 words  2980 han  3302 characters (no spaces)  41 paragraphs

The first number is the Word-style count. The count on the status line is the same number.

A daily target: :count-target and :count-progress

:count-target 2000     set a goal of 2000 a day
:count-progress        how much today, how much in the book, how many days running
:count-target off      stop measuring against a target; the log goes on

:count-progress opens a list with one line per day, oldest first, and a bar for that day's share:

2026-09-04  -260 字  -
2026-09-05  2440 字  ████████████+
2026-09-06  1830 字  ███████████

The line under the status line sums it up: 1830/2000 字 today (91%), 62480 in the book, 3 days running.

+ marks a day over target. - marks a day you cut more than you wrote. Without a target the bars are measured against your best day.

The log counts what changed between saves, so unsaved typing does not count yet. "In the book" is the sum of every file's count at its last save. A day you have not started yet does not break the run.

The log is a plain text file, .yumete/progress.tsv in the project. Each line is a date, a file name, the count at the day's start and the count now, separated by tabs:

目標	2000
2026-09-05	chapter-1.md	0	1830
2026-09-06	chapter-1.md	1830	2440

You may edit it to fix a mistake, delete a day, or merge two machines' copies. A line it cannot read is skipped, not the whole file. Nothing is written until you run :count-target or :count-progress; after that, every :w in the project adds to it. To stop, delete the file.

Two spellings of one word: :check-usage

Many words have two accepted spellings, such as 裏 and 裡, or 為 and 爲. A long manuscript drifts between them, and no spell checker will notice, because both are correct.

:check-usage compares the manuscript with itself. It reports a group only when both spellings appear, and points at the places that use the less common one:

4: 「裡」 here (3), 「裏」 in the rest (400)

A manuscript that only ever writes one spelling gets no report. Pairs that mean different things, such as 面 and 麵, are not in the built-in table.

Your book's own names are in no table, so add them in the config, one group per string, the spelling to keep first:

[editor]
usage_groups = ["阿嬌 阿姣", "落霞鎮 落霞鄉"]

Words you lean on: :word-habit

:word-habit lists the words this file uses more often than ordinary text does, ranked by how much they stand out and how often they occur. A common word such as 的 is never listed, because every text uses it at the usual rate.

12: 「然後」 47× — 4.2 times more often than ordinary text

The line number is where the word first appears. Names the frequency table does not know, single characters, and words used fewer than three times are left out. The check needs a word-frequency table, which comes with the 宇浩 data (see The input method). yumete's built-in word list has no frequencies, so without that data the check says so and lists nothing.

Punctuation: :check-punct

:check-punct looks for three things:

  • a half-width , . ; : ! ? ( ) next to Chinese text;
  • ... where Chinese uses ……;
  • an opening 「 『 ( 《 〈 【 that never closes.
1: half-width 「,」 should be 「,」
2: 「「」 is never closed by 「」」

Only marks touching a Chinese character are reported, so 3.14, 1,000 and English sentences are left alone. Inline code and code blocks are skipped. A quote that runs over several paragraphs, opening 「 again at the head of each, is not counted as unclosed. Brackets must close in the same paragraph.

:view-punct on shows the same hint on the page as you write: the right mark is drawn beside the half-width one.

Characters a font may not have: :check-charset

:check-charset lists characters that are in none of the four standard character sets (mainland, Taiwan, Hong Kong, and the set for classical texts). Your screen may show them fine, while the typesetter's font does not have them.

There is one line per character, not per place, with the first place it appears, how many times in all, and its Unicode block:

37: 「𠮷」 is in no standard (CJK-B), occurrences in the file: 412

The check reads the character sets from the input method's data, so it needs a scheme loaded (:yume-scheme). Without one it says there is no data. 〇 and 々 are never reported.

Simplified and traditional: :convert

:convert converts the whole file between simplified and traditional forms. It runs OpenCC, which needs to be installed and on PATH.

:convert s t        simplified to traditional
:convert t s        traditional to simplified
:convert            list the codes and which pairs work
:convert-opencc     is opencc installed, where, which version
CodeForm
ssimplified
tOpenCC's plain traditional
twTaiwan
hkHong Kong
jpJapanese shinjitai (converts with t, c and g; not with s, tw or hk)
cmainland standard traditional forms
gstandard forms for classical texts

:convert! also converts vocabulary where OpenCC can (:convert! s tw turns mainland computer terms into Taiwan ones). This changes the word count and cannot be reversed by converting back, so it needs the !.

One conversion is one undo: press u to get the file back. Converting back the other way is not an undo, since the round trip is not exact.

On macOS, :convert-opencc-install runs brew install opencc. On other systems it prints the command for you to run.

To convert only the selection, use the case group: under the helix keymap ` then s (to simplified), t, w (Taiwan), k (Hong Kong), c, g, or j; under the vim keymap the group is g` .

What is the character under the cursor

The right end of the status line names the character under the cursor:

螭 U+87ED · CJK Unified Ideographs

When a rare character shows as a box, this tells you whether the character is wrong or the font is missing it. When the line is crowded, the block name goes first, then the code point. [editor] char_info = false turns it off.

Invisible characters

Some characters take no space on screen: a zero-width space, a soft hyphen, a byte-order mark in the middle of the text, a joiner outside an emoji, and the bidi controls that can make text read differently from how it is stored. The cell after one of them gets an amber background. A space that is not a plain space, such as a no-break space, has its own cell tinted. Put the cursor on the tinted cell and the status line names what is there:

U+200B · ZERO WIDTH SPACE

The ideographic space U+3000, emoji joiners and the variation selectors that pick a Chinese glyph are never marked. To turn the marks off:

[editor]
unicode_alarm = false

The settings panel has it under Marks.

The sign beside the cursor: :view-hud

A command you have started typing, such as 3, 2t or d3l, is shown at the right end of the status line and beside the cursor.

:view-hud full     a bordered panel under the cursor, over the text (default)
:view-hud basic    a small label in blank space nearby; covers nothing
:view-hud off      nothing beside the cursor
:view-hud          which one is on

The status line copy is always there. If you mind the panel covering the text you are counting over, use basic. When the input method's candidates need the same place, they win.

A note beside the sentence

When the cursor stops on a footnote mark such as [^1], the note floats in a panel in the corner, with the line it is on:

That winter it snowed[^1] on the hill.
                              ╭ [^1]───────────────────────╮
                              │ The county records say so. │
                              ╰────────────────────line 11─╯

The panel keeps to the right and moves only up and down, so the start of each line stays visible; in vertical layout it keeps to the bottom. It takes no rows from the page. Annotations written %%like this%% get the same panel, so a long one can be read without spreading the paragraph apart.

gd on the mark goes to the note, and gd there brings you back to the exact character you left. If you move somewhere else in between, the way back is forgotten.

A book of many files

A novel or a thesis is usually many files in one folder: a chapter per file, a main file that pulls them together, notes, a bibliography. This page covers how yumete treats that folder as one project: where it searches from, how it picks up where you left off, how to jump between chapters, and the book's own wiki.

The project and the working directory

Two places matter.

The working directory is where you started ye. If you give a folder on the command line, that folder is it. After that only :cd changes it (:cd alone goes home, :cd - goes back), and :pwd shows it. Space F searches it, and relative paths in :open, ! shell commands and :format start from it.

The project is worked out from the working directory. yumete looks upward for a folder with a .yumete folder or a .yumete.toml file in it, then for the nearest .git, .jj or .svn; with neither, the project is the working directory itself. The file picker Space f, the sidebar's file tree, project-wide search, the project config, the word list and the wiki all use the project. A language server is the one exception: it starts from the file's own folder (see Language servers).

~/books/winter/           the project (it has .git)
├── part-1/
│   └── chapter-1.md      started here: ye chapter-1.md
└── notes/

Started inside part-1/, Space f searches all of winter/ and Space F only part-1/. Opening a file from somewhere else does not move either one.

Both respect .gitignore, whether or not the folder is a git repository. See Search and pickers.

Picking up tomorrow: --resume

ye -r (or --resume) reopens the files that were open last time in this project, each at the line you left it on, and :resume does the same from inside the editor. See Files, saving and changes.

Marks across files

Under the helix keymap, M and a letter remember the cursor's place; ' and the same letter go back to it, opening that file if needed. Under the vim keymap it is m and a letter, as in vim. C-o returns to where you were before the jump.

Chapters in the outline

The outline sidebar (Space s) lists the headings of the current file, each with its count. In a Typst main file, every #include is read open, and its chapter titles are listed in place of the file name:

= My Book
#include "ch1.typ"
#include "ch2.typ"

So the main file works as a table of contents. Put the cursor on an #include line and press gf to open that chapter; it is read as Typst whatever its name.

Markdown links go between files: [next](chapter-2.md) with a path, or [[chapter-2]] with a title, no extension needed. [[chapter-2#snow]] goes to a heading in it, and [[chapter-2|the next day]] shows the text after |. Press gL on any of them to follow it. See The page.

Completing references

In insert mode, start a reference and a list opens under the cursor:

TypeIt lists
[^this file's footnotes, each with its first words, and a new unused number
](#this file's headings, written as anchors
[[the files in this file's folder and its subfolders
[@citation keys from the bibliography beside the file

Tab writes the next entry into the text, Shift-Tab goes back. The closing brackets are added for you. Nothing changes until you press Tab, so you can type the whole reference by hand and ignore the list. Enter and Esc keep their usual meaning.

[[ searches only this file's folder and below, not the whole book, so the list stays short. What you type matches the whole path, so rain finds part-2/rain. A file of the same kind as this one is written as its title, without the extension.

:markdown-footnote writes a new footnote: the next free number, the note opened at the foot of the file, and the cursor in the note.

Citations

[@ lists the keys from the .bib files in this file's folder and from the file the front matter names (bibliography: refs.bib, BibTeX or hayagriva), each with its title. As Knuth argued [@kn then Tab gives [@knuth84].

In Typst, @ after a space does the same, reading the file that #bibliography(...) names and the .bib files beside it. The labels the document sets come first: = Introduction <intro> offers intro. An address such as a@b.com opens nothing.

The book's wiki: :wiki

Characters, places and terms go into .yumete/wiki.md in the project. ## starts an entry; # is a group heading and not an entry itself:

# People

## Anning
The hero. Born in Luoxia.
### Childhood
…

# Places
<!-- [yumete] places.md -->

An entry runs to the next heading of its own level or higher, so ### subheadings belong to it. A comment holding [yumete] places.md pulls that file in at that point, as if pasted there. Paths are read from the file that names them, and a path outside the book is not read.

A second, global wiki lives in the data directory as wiki.md, for what a whole series shares. Both are read, this book's first.

What the wiki does:

  • Entry names become words. w steps over a name in one go. One-character entries are not words.
  • Names are marked in the text, in gold by default. :wiki-mark line uses a dotted underline, :wiki-mark off nothing. Names inside code or notes are not marked.
  • The entry floats beside the cursor when you stand on a name, and folds away when you leave. :wiki-panel on shows it in the right sidebar instead; C-w l moves into the sidebar to scroll it, and q comes back.
  • gd on a name opens the file the entry lives in, at its heading. C-o comes back.
:wiki Anning     look an entry up; misspelt or out of order still finds it
:wiki            browse the entries
:wiki-where      which files were read, and names in this chapter left unmarked
:wiki-edit       open this book's wiki.md, or start one
:wiki-global     open the global wiki
:wiki-reload     read it again

Saving any wiki file re-reads it. A name is marked only where word segmentation cuts it as one word, so a name can sit in a sentence unmarked; :wiki-where lists those with their line numbers.

Names written wrong: :check-names

:check-names uses the wiki to find a name written with a homophone in one place, the kind of slip an input method makes when the wrong candidate is taken. It needs the reading table from the input method's data; without it, it reports nothing.

Export

Vertical layout, hung punctuation and ruby exist only on screen. :export writes them into a file you can open, print or hand to a typesetter.

:export html               chapter.md → chapter.html
:export typst              chapter.md → chapter.typ
:export html out/ch1.html  a name of your own
:export! html              write over a file that is already there
:export csv                the table under the cursor, comma separated
:export tsv                the table under the cursor, tab separated

With no path, the export takes the file's name and changes the extension. An existing file is not overwritten unless you add !, and a file open in the editor is never written over. A new buffer needs a path.

HTML

:export html writes one self-contained page. Vertical text becomes writing-mode: vertical-rl, hung punctuation becomes hanging-punctuation, and ruby becomes <ruby>.

The page prints as it is. It carries print styles for the paper size and margins, starts each chapter on a new page, and keeps a heading from being left alone at the foot of a page. For vertical text the type size is worked out from the column: the paper's height divided by how many characters go in a column. Set both before you print:

[editor]
zong_length = 30   # characters per column; 0 follows the window

[export]
page = "A5"        # A4, A5, A6, B5, B6, letter, 16k, 32k, d32k, or "130x184" in mm

zong_length left at 0 means however tall your window happens to be. See Vertical layout.

There are no page numbers or running heads. Browsers do not support the CSS that would place them, so use the print dialog's header and footer.

Typst

:export typst writes Typst source, always horizontal, because Typst has no vertical layout. The file says so at the top. Ruby becomes #ruby(base, reading), with a definition at the top of the file that you can change.

What carries over

Both formats carry:

  • headings and paragraphs;
  • **bold**, *italic* and the other inline marks, as real formatting, not as asterisks;
  • lists, nested, numbered or not;
  • block quotes;
  • footnotes, numbered in the order they appear: a list at the end of the HTML page, #footnote[…] at the mark in Typst;
  • pipe tables, with their column alignment;
  • rules (---, * * *);
  • ruby.

Anything else comes out as text. Annotations to yourself, written %%like this%% or as HTML comments, are left out.

Language and line breaks

The page's language comes from the text. A file with more Latin words than Chinese characters is set as English: lang="en", a Latin serif, and paragraphs spaced apart instead of indented. Kana make it Japanese. Otherwise it is set as Chinese, with a first-line indent.

A line break inside a paragraph is kept between lines of Chinese, where it is usually meant. Between two lines of Latin text it becomes a space, as CommonMark reads it, so a hard-wrapped English paragraph comes out as one paragraph.

Tables as CSV or TSV

:export csv and :export tsv write only the table under the cursor, not the whole document, and leave the buffer as it is. See Tables.

Seeing it before you export

:view-preview shows the typeset result in a browser while you write: a live Typst preview, or an HTML export of a Markdown file. See The page.

Keymap

These are the keys of the default keymap, which is helix's. The tables are written by the editor itself (ye --docgen) from the same lists its key menus are drawn from, so a key listed here is a key the editor has.

C-w is Ctrl and w. Space f is the space bar, then f. Two keys written together, such as gg, are pressed one after the other. A key that starts a group, such as g, m or Space, opens a menu of what can follow, so you need not learn these tables by heart: press the first key and read.

The editor's own messages write the space bar as ␣, so ␣t o is Space, t, o.

The first two tables are the keys a writer uses in the first hour. They are the ones :help lists first. The rest are the groups, one table per menu.

Under the vim keymap (:keymap vim), vim's keys sit on top of these: x deletes a character, dd a line, % goes to the matching bracket. Every key not taken that way still does what it does here. See Coming from vim for the full list of differences, and Match mode for what the m group takes.

To change a key, see Key remapping. For every : command, see Commands.

moving: command + selection + action

KeyWhat it does
h j k lleft, down, up, right — down the column in vertical layout
w b enext word / back to a word start / on to a word end (in 漢字 text, b and e go by clause)
3w 5ja number in front is a count
g30g / 30Ggo to line 30
gg gestart / end of the file
gh gl gsline start / line end / first non-blank
C-d C-uhalf a page forward/back; C-n / C-p for two thirds, C-f / C-b for a whole one
gd gDgo to / show what this points at: a footnote's note, a component's row
g/ g?where else this word is: / here, ? in the other region
/ n Nsearch, next, previous
C-o C-ijump back / forward again
M a 'aremember this place / go back to it

editing

KeyWhat it does
i astart typing before/after the cursor
o Oopen a line below/above and start typing
d cdelete the selection / change it (and you are typing)
xselect the whole line — in Chinese text a line is usually a paragraph
v ;Select mode on-off / collapse to one character; Esc also leaves Select mode
L Hnext sentence / previous sentence
} {next paragraph / previous paragraph
y pyank / put
u Uundo / redo
.repeat the last insert (vim keymap: the last change)
Q qrecord keys / play them back
> <indent / unindent

Space (Space)

KeyWhat it does
Space fopen file picker (project dir)
Space Fopen file picker (working dir)
Space ebrowse files (project dir)
Space Ebrowse files (this file's dir)
Space bopen buffer picker
Space ycopy to the system clipboard
Space ppaste from the system clipboard
Space Ppaste before, from the system clipboard
Space sshow outline
Space ncharacter info (floating)
Space Ncharacter info (sidebar)
Space kdocumentation (floating)
Space Kdocumentation (sidebar)
Space dshow diagnostics (float)
Space Dshow diagnostics (sidebar)
Space xshow the change (float)
Space Xshow the change (sidebar)
Space rrename it everywhere
Space acode actions
Space ithis row's record (floating)
Space Ithis row's record (sidebar)
Space wregion
Space ttable operations
Space "registers
Space ccomment out, by the line
Space Ccomment out, in a block
Space mmerge conflict
Space oopen something recent
Space zthe input method
Space /the search panel
Space ?every command
Space Spacepage down
Space Backspacepage up

g goto (g)

KeyWhat it does
ggstart of the file
g<N>ggo to line N
geend of file
gh / glline start / end
gsfirst non-blank character
gfopen this file
gLfollow the link
gwgo to a place on this screen
gxtwo letters to locate a word
gzmore letters to locate a char
gaback to the buffer before this one
gn / gpthe next or previous buffer
gdgo to definition
gDgo to definition in the other region
grevery place it is used
g/search the selection
g?search the selection in the other region
gj / gkdown / up a logical line (with line number)
g.back to the last change
gt / gc / gbthe cursor to the top, middle or bottom of the screen

m (m)

KeyWhat it does
mmgo to the other half of the pair
miselect what is inside the pair
maselect the pair and what is inside
mswrap the selection in a pair
mdremove the pair
mrreplace the pair

next… (])

KeyWhat it does
]mmerge conflict
]ga change
]d / ]Da diagnostic (capital: the first / the last)
]pthe next paragraph
]␣a blank line below
]fa function
]ta class
]ca comment

previous… ([)

KeyWhat it does
[mmerge conflict
[ga change
[d / [Da diagnostic (capital: the first / the last)
[pthe previous paragraph
[␣a blank line above
[fa function
[ta class
[ca comment

put this line where (z)

KeyWhat it does
zzmiddle
zttop
zbbottom

region (C-w)

KeyWhat it does
C-w wnext
C-w h / C-w j / C-w k / C-w lgo that way
C-w e / C-w itoggle the left / right bar
C-w E / C-w Igo into the left / right sidebar
C-w ssplit in two
C-w qclose this one
C-w okeep only this one

t table (Space t)

KeyWhat it does
Space t osource view: the file as written
Space t bbasic view: the columns lined up
Space t twindow view: the table takes the window
Space t qleave window view
Space t ] / Space t [the next table / the one before
Space t xconvert the table's format (delimiter)
Space t wlong cells: wrap / fold / whole
Space t /search down the columns (a column range can be given)
Space t ?search down the columns in the other region
Space t ggo to a cell (␣t20,20g)
Space t r / Space t Rinsert a new row (below / above)
Space t c / Space t Cadd a column (right / left)
Space t d / Space t Ddelete this row / column
Space t j / Space t kmove this row down / up
Space t h / Space t lmove this column left / right
Space t y / Space t p / Space t Pyank this column / paste it to the right / to the left
Space t vwrite the yanked column over this one
Space t 1s / Space t 1Ssort by column, ascending / descending (␣t1s; ␣t1a2d8as for several)
Space t < / Space t = / Space t >this column left / centre / right
Space t Fline it up again
Space t ithis row's record
Space t Hwhether the table's first row is a heading row
Space t ethis table's metadata

convert the table's format (delimiter) (Space t x)

KeyWhat it does
Space t x p / Space t x Ppipe (convert / paste)
Space t x c / Space t x Ccsv (convert / paste)
Space t x t / Space t x Ttsv (convert / paste)

letter case (`)

KeyWhat it does
`lto lowercase
`uto uppercase
``switch case
`straditional → simplified
`tsimplified → traditional
`wsimplified → Taiwan 繁
`fto fullwidth
`hto halfwidth
`ksimplified → Hong Kong 繁
`csimplified → mainland 繁
`gsimplified → 古籍 繁
`jtraditional → Japanese shinjitai
vi's marks: M sets, ' returns

open something recent (Space o)

KeyWhat it does
Space o frecent files
Space o grecent files, everywhere
Space o precent projects
Space o rtake up the last session

the input method (Space z)

KeyWhat it does
Space z zinput method on / off (on is Chinese)
Space z mwhen a word commits
Space z pwhere the candidates are drawn
Space z ewhere what you are typing is written
Space z yprediction (autocompletion) on or off
Space z ipick a scheme
Space z nthe next scheme
Space z 3-9candidates on a page
Space z /the 拆分 beside a candidate
Space z tswitch to the installed 碼表
Space z wwhere the 碼表 came from
Space z Wwhere the data is

Match mode

m is the group for pairs and the text between them: go to the other half of a bracket, select inside or around something, and add, delete or replace the pair around a selection. It works as helix's match mode does, with Chinese brackets, sentences and clauses added. Each key after m opens a menu of what it takes, so you can press m and read on.

helix keymapvim keymapDoes
mm%Go to the other half of the bracket under the cursor
miviSelect inside an object or a pair
mavaSelect around it: the pair or the marks come along
msysSurround the selection with a pair
mddsDelete the pair around the cursor
mrcsReplace the pair around the cursor

Under the vim keymap m sets a mark, so the group is spelled the vim way. See Under the vim keymap below.

Go to the matching bracket

mm jumps to the other half of the bracket the cursor is on. When the cursor stands on a bracket, its partner is highlighted, so you can see where mm will land before you press it. Quotes are not matched this way: a " looks the same at both ends, so there is no telling which way to go.

With the cursor off a bracket, mm does not move. Under the vim keymap % looks along the line instead, as vim's does: it jumps to the partner of the first bracket further along the line.

In a table, a bracket is only matched inside its own cell.

Select inside and around: mi and ma

mi selects the inside of something, ma the same with whatever surrounds it. The examples use one sentence, with the cursor on 期:

他说:「今天(星期三)要下雨,记得带伞。」然后就走了。
KeysObjectSelects
mi wword星期三
mi Weverything up to a space (a WORD)the whole line
mi ssentence他说:「今天(星期三)要下雨,记得带伞。」
mi uclause, up to any punctuation mark星期三
mi pparagraphthe whole paragraph
mi mthe nearest pair, of any kind星期三
ma mthe nearest pair, with its brackets(星期三)
mi (inside parentheses (( covers () too)星期三
mi 「inside the corner brackets今天(星期三)要下雨,记得带伞。

Words are found by a Chinese segmenter, not by spaces, so mi w takes 星期三 and not the whole run of characters.

Sentences and clauses

mi s takes the sentence the cursor is in. A sentence ends at a full stop, a question mark or an exclamation mark, Chinese or ASCII, with any closing mark that follows it.

mi u stops at every punctuation mark, brackets and quotes included. With the cursor on 雨 in the sentence above it selects 要下雨. A mark inside a word does not count: on Well, it's 3.14 today; don't go. with the cursor on today, mi u selects it's 3.14 today.

Neither crosses a line break.

Paragraphs

mi p takes the paragraph and its line break. ma p adds the blank lines below it. With the cursor on a blank line, mi p takes the run of blank lines, so it is the way to close up a gap between two paragraphs.

Markdown marks

k (markup) takes the Markdown marks the cursor is in. On a **bc** d with the cursor on b:

KeysSelects
mi kbc
ma k**bc**

k also takes *italic*, ~~struck~~, ==marked==, `code`, links and [[wiki links]].

Cells and changes

mi l (cell) selects the table cell the cursor is in, so mi l c replaces it. ma l is the same: the walls are not part of the cell.

mi g selects the change the cursor's line is in, as whole lines: the lines the change bar marks against git's HEAD. ma g is the same. In a file git does not track, it says so.

Code

Seven objects read the code itself. They work in a code file, and in a Markdown file inside the fenced code block the cursor is in, read in that block's language. Each takes the innermost one around the cursor, and ma is the same as mi.

KeysObjectExample
mi ffunctionin fn add(a: i32, b: i32) -> i32 { a + b }, the whole function
mi ttype definitionthe struct, enum or class the cursor is in
mi aparameteron b in the function above: b: i32
mi ccommentthe comment the cursor is in
mi Ttestin #[test] fn adds_two() { … }, the whole test
mi eentry: a key and its valuein {"name": "x", "version": 13} on 13: "version": 13
mi xHTML element, tags and allin <p>今天<b>下雨</b></p> on 雨: <b>下雨</b>

mi T knows #[test] in Rust, def test_… in Python, func Test… in Go, it(…), test(…) and describe(…) in JavaScript, and @Test in Java. mi e knows JSON, TOML and YAML entries, Rust and Go struct fields, and Python and JavaScript dictionary entries.

Any other character

A character that is not a letter or a digit pairs with itself: mi $ on $xy$ selects xy, and ma $ selects $xy$. Two of the same character on a line are paired the way quotes are, counting from the start of the line. After mi and ma, a letter or a digit always names an object, never a pair.

Which pairs each key covers

You do not need to type a Chinese bracket to name one. Each ASCII key covers a family of pairs by shape, and either half of a pair works: mi「 and mi」 are the same. The full-width, half-width and vertical forms come with each family (the menus show them as …).

KeyCovers
(() () ︵︶
[[] [] ﹇﹈ 「」 「」 ﹁﹂ 【】 ︻︼ 〔〕 ︹︺
{{} {} ︷︸ 『』 ﹃﹄ 〖〗 ︗︘
<<> «» 〈〉 ︿﹀ 《》 ︽︾
""" "" “” 〝〞
''' '' ‘’
``` ``

When pairs of one family are nested, the innermost is taken.

m as an object means the nearest pair whose two halves differ: brackets, and quotes such as “” and 「」. A straight " or ' is left out, since in text it is as often an apostrophe; name it with " or '.

Surround: ms, md and mr

ms wraps the selection in a pair. md deletes the pair around the cursor. mr replaces it: first the pair to replace, then the new one.

md and mr take a key from the table above (its whole family), m for the nearest pair, or any other character, which pairs with itself. md also takes k for Markdown marks. ms and the second key of mr write exactly the pair you name: ms [ writes [], and ms 「 writes 「」.

In each example the cursor stands inside the pair:

TextKeysResult
foo bar, foo selectedms ((foo) bar
foo bar, foo selectedms 「「foo」 bar
foo bar, foo selectedms **foo* bar
a (bc) dmd (a bc d
a *x* bmd *a x b
这是 **一句话** 的例子md k这是 一句话 的例子
这是 [字](http://a) 的例子md k这是 字 的例子
这是 [字](http://a) 的例子md m这是 字(http://a) 的例子
f(a)mr ( [f[a]
a $x$ bmr $ (a (x) b

md m on a link takes off only its [ ], as helix does; md k takes off the whole link.

With the cursor on a quote mark, md " and mr " take the pair that mi " would select.

ms then Enter puts the selection on lines of its own: a line break before it and one after, in the file's own line ending.

mr shows the pair first

After mr and the pair to replace, the two characters it found are selected, so you can see which pair the next key will change. That key, or Esc to change nothing, gives your selection back. On a (bc) d with the cursor on b, mr ( selects the ( and the ); [ then gives a [bc] d.

Typing the pair in Chinese

With the input method on, the key after ms, md and mr goes to the input method, and the character you pick from the candidate panel is the pair. If you commit a word, its first character is used. mi and ma do not open the input method, because the key after them is usually the name of an object. See The input method.

Counts

A count goes that many pairs out. On f(a(b)c) with the cursor on b:

KeysResult
2mi( then df()
2md(fa(b)c
2mr([f[a(b)c]
3md(unchanged: there is no third pair

With m, the count counts pairs of any kind: on f[a(b)c], 2mim then d gives f[].

With quotes and other self-paired marks, a count goes out mark by mark. On the b of x "a "b" c" y, 2mi" selects a "b" c and 2md" takes off the outer two quotes.

Outside a pair

With the cursor outside any pair, mi( and ma( select nothing under the helix keymap, as in helix. Under the vim keymap they reach ahead to the next pair, as vim does: at the start of ab (cd) ef, da( gives ab ef.

md and mr never reach ahead. With no pair around the cursor they say so and change nothing.

Several selections

Each selection takes its own pair, and the whole change is one undo step. With a cursor on each line's b:

TextKeysResult
a b c / a b cms (a (b) c / a (b) c
a (b) c / a (b) cmd (a b c / a b c
a (b) c / a (b) cmr ( [a [b] c / a [b] c

md and mr change nothing at all when any cursor has no pair around it, or when two cursors are inside the same pair. Taking that pair off once per cursor would take off the pair outside it as well.

Under the vim keymap

Under the vim keymap (:keymap vim) the same work is spelled the vim and vim-surround way: di( for mi( then d, viw for mi w, ysiw( to wrap a word, ds( for md(, cs([ for mr([.

After d, c, y, vi and va, the text objects work as above: w, W, s, u, p, m, k, and every pair. b and B are vim's names for ( and {. The code objects, the cell l and the change g have no vim spelling. See Coming from vim.

Registers

A register holds text you have yanked or deleted, ready to paste. yumete keeps one unnamed register, any number of named ones, and a history of what the unnamed one held. The system clipboard is separate: it is the + and * registers, and it has its own keys.

The unnamed register

With no register named, these keys use the unnamed one:

KeyEffect
yyank the selection
d / cdelete / change the selection, keeping what was removed
A-d / A-cdelete / change the selection, leaving the register as it is
p / Ppaste after / before the selection; a count (3p) pastes that many copies as one piece
Rreplace the selection with the register; 2R replaces it with two copies (helix keymap; under vim, R in Normal mode is vim's replace mode)
C-r " (Insert)insert the register's text at the cursor

If you would rather d and c left the register alone, :keymap-yumete-flavor on swaps them with A-d and A-c. Set flavor = true under [keys] to make that the default.

Under the vim keymap a paste leaves a cursor, not the pasted text selected. The cursor goes to the last character pasted when the text is part of one line, to the first character pasted when it spans lines, and to the first non-blank of the first pasted line when whole lines were pasted.

With several selections, each yanks its own piece, and pasting into as many selections puts each piece back in its place.

Named registers

Type " and a character before a yank, delete or paste to use that register instead:

  • "ay yanks the selection into register a.
  • "ap pastes from register a.
  • "bd deletes the selection into register b.
  • C-r a in Insert mode inserts register a.

Any character can name a register, and three names are special, as in helix and vim:

  • _ keeps nothing. "_d deletes without changing what p pastes.
  • + and * are the system clipboard. "+y copies the selection out of the editor, and "+p, "+P and "+R paste or replace from it.
  • # reads as the selection's number, so "#P with three cursors writes 1, 2 and 3.

Under the vim keymap, "0 holds the last yank, as in vim: a delete fills the unnamed register, and "0p still pastes what you yanked. A delete of a line or more also goes into "1, and the older ones move down to "2 … "9.

Earlier yanks

Every yank or delete into the unnamed register replaces what was there, but the last 16 are kept. Space " or :clipboard opens a picker of everything that can be pasted:

  • the system clipboard, first
  • the unnamed register's history, newest first, numbered from 0
  • the named registers, as "a, "b and so on

Pick one and it is pasted after the selection. The picker's numbers are its own: the row 0 is the newest yank or delete, not the "0 register, which has a row of its own under the named registers.

The system clipboard

KeyCommandEffect
Space y:clipboard-yankcopy the selection to the system clipboard, and into the unnamed register
Space p:clipboard-pastepaste from the system clipboard after the selection
Space Ppaste from the system clipboard before the selection

Copying uses the terminal's OSC 52 escape, so it works over ssh and inside tmux, if the terminal allows it. Pasting runs pbpaste on macOS, or wl-paste, xclip or xsel on Linux. The terminal's own paste key also works.

Macros are not kept in registers. There is one macro, the last one recorded: Q records and q replays it under the helix keymap, the other way round under vim. A letter after q does not name a register.

Jumplist

When a motion takes you far away, yumete notes where you were. That list of places is the jumplist, and it works across files.

KeyEffect
C-oback to where the last jump came from
C-i or Tabforward again
C-sadd the current position to the list by hand

A new jump after going back drops the places ahead of you, and the list keeps the last 100. If a place is in a file you have since closed, C-o says so instead of going there.

What makes a jump

  • gg, ge, a count before G (20G), and :<line>
  • searching with /, ?, n and N
  • following a heading in the outline, or a path:line with gf
  • gd and the places gr lists
  • ]d [d, ]f [f and the other ] [ jumps to a diagnostic, function, change or conflict
  • gw, gx and gz (the cursor jumps, the view does not move)
  • going to a mark with ', and to a change with g.

Switching files with ga, gn, gp or :open does not add a jump.

Marks

A mark is a place you name, to come back to on purpose.

KeyEffect
M amark this place as a (m a under the vim keymap)
' ago back to mark a, opening its file if needed

A mark's name is one letter or digit. Marks are shared by all files, so 'a can take you to another file. In a saved file a mark remembers the line. Marks last until you quit.

Changes

KeyEffect
g.the last place this file was changed
g; / g,an older / newer change (vim keymap only)

Each file keeps its own changes, one entry per line, so g; after typing a paragraph moves by the paragraph, not by the character.

Commands

Press : to open the command line. Every command is listed below it, and the list narrows as you type. Enter runs the line, Esc closes it.

Typing a command

A space shows what comes next. After :view-wrap the line lists the words the command takes (on, off, 0, or a number); Tab cycles through them, completing the word you are typing rather than the whole line.

A prefix nobody else has is enough, for commands and for their arguments alike. :y is :yume, and :yume-scheme l is :yume-scheme lingming. When a prefix fits two names, neither is chosen: :re begins recover, redo and render, so it is refused instead of guessed. The shortest spelling of each command is shown in brackets in the list.

Declared short names win over the prefix rule: :w is write and :e is open, even though other commands begin with those letters.

A hyphen makes a family. :buffer-close is a kind of :buffer, :view-wrap a kind of :view; the list folds each family under its head (:view- +17) and opens it as you type. Only some commands have a short form, and where there is one it is usually the initials: :bc, :qa, :wa. A space where a hyphen belongs is refused with the right spelling, so :write all is not read as a file called all.

A ! forces. :q! quits without saving, :export! html overwrites an existing file. Only the commands that have something to force take it.

The line can be edited: ← →, Home End, C-w deletes a word, C-u the whole line, and ↑ ↓ walk this session's history. : and / keep separate histories.

:: — find a command by what it does

Press : on an empty command line (so the line starts ::) and type what you want to do instead of a name: ::wrap, ::sort, ::vertical. Commands and keys are on one list, ranked by how well your words fit their descriptions and a few extra search words each one carries. Chinese works too (::竖排), with the input method on for that line. Tab and Shift-Tab step through the list; Enter runs the command or presses the key.

Running a shell command

CommandWhat happens
:sh <command>runs it and opens the output in a buffer you can search and copy from
:!<command>hands the terminal to the command so you can watch it run, then asks for a key to come back
:pipe <command>sends the selection through the command and replaces it with the output
:pipe-before / :pipe-afterputs the command's output before or after the selection
:pipe-to <command>sends the selection to the command and keeps nothing back

The command goes through $SHELL -c, so pipes and globs work. On a shell line the prompt reads Shell:, Tab completes program names (the first word) and file names (the rest), and the last shell command waits in grey after the cursor for Tab to take.

Every command

This table is written from the editor's own command list (ye --docgen), so it always matches the version it was generated from.

CommandArgumentsWhat it does
:cd<path>change the working directory
:pwdshow the working directory
:open :o :e :edit<path>open a file
:new :enewstart an empty buffer
:write :w<path>save; a path writes a copy, :write-as moves to it
:write-all :wasave every file that changed
:write-as<path>save under a new name and edit that one (:w <path> only copies)
:recoverask whether to use the draft
:reloadread the file again; ! throws away what you changed here
:reload-configafter editing config.toml, without restarting
:reload-autoon|off|togglere-read by itself when the file changes outside — a warning, not a re-read, when you have changes of your own
:readonly :roon|off|toggleread-only: lock this one against editing
:goto<行號>go to a line (:42 will do)
:count :wchow much has been written; :count-progress for today, :count-target for a daily goal
:count-progresshow much was written today, and how far the target still is
:count-target<字數>|offhow many words a day; :count-target off stops counting against one
:check-usagecheck usage: where the text wrote the other spelling of something it usually spells one way
:check-configcheck the config: the line that voids the whole file, and the keys nobody reads
:check-namescheck names: a wiki name written with one homophone out of place
:diagnostics-alleverything the language servers have complained about, as a listing gf can walk
:check-charsetcheck the character set: characters no current standard carries — the ones a typesetter's font will not have
:check-mergelist the merge conflicts in this file
:check-punctcheck punctuation: half-width marks in Chinese, ... for ……, and a 「 nothing closes
:converts|t|tw|hk|jp|c|g s|t|tw|hk|jp|c|g簡繁 conversion, run by opencc — :convert lists the pairs it can do
:convert-openccwhether opencc is installed, where, and which version
:convert-opencc-installinstall opencc — run here on macOS, printed for you elsewhere
:convert-opencc-updatebring opencc up to date
:quit :qclose this file; leave only when it was the last one (quit! ignores changes)
:quit-all :qaclose every open file, then leave (short: :qa)
:write-quit :wq<path>always save, then leave (short: :wq; :x saves only if changed)
:write-quit-all :wqa :xasave every file that changed, then leave
:exit :x :xit<path>save if changed, then leave (short: :x, also :xit)
:update :upsave if changed, and stay (short: :up)
:undo :uundo the last change
:earlier :ear<步數>|<時間>undo to so many steps or so long ago (5m, 1h)
:later :lat<步數>|<時間>redo to so many steps or so long on
:redo :redredo
:word :wdwords: which list, the mark, the level
:word-markoff|color|ink|tint|line|字色|墨色word mark
:wiki<詞條名>look an entry up by name — misspelt or out of order still finds it
:wiki-editopen this project's .yumete/wiki.md, existing or not
:wiki-globalopen the global wiki.md — what a whole series shares
:keymap-yumete-flavoron|off|togglethe Yumete keymap flavour: a small set of departures over helix/vim. Bare reports it
:wiki-panelon|off|togglekeep the entry in the sidebar — while it is open it no longer floats
:wiki-markcolor|line|offhow a wiki name is marked in the text: 金, an underline, or not at all
:wiki-wherewhere the wiki looked: which files were read, how many entries each, what cannot be marked
:wiki-reloadread the wiki and every file it includes again
:word-listwhich word list is in force
:word-list-localopen this project's word list, .yumete/words.txt — names of people and places
:word-list-globalopen the global word list, segmentation.txt
:word-list-reloadreread both word lists, this project's and the global one
:word-leveloff|strict|balanced|fullword level: how readily characters join into words
:word-discovermine this file's own words: the names no dictionary has
:word-discover-workingmine the working directory's own words
:word-discover-projectmine the project's own words
:word-habithabit words: what this one says far more than ordinary text does
:verticalturn this page down
:horizontalturn this page across
:vertical-keys :vklogical|visual|<logical|visual>whether hjkl follow the text or the screen when the page runs down
:themeink|one-dark|github|monokai|dracula|tokyo-night|catppuccin|gruvbox|bw|morandi|<主題名>theme: which set of inks (light or dark is :theme-mode)
:theme-modesystem|dark|lightdark or light (system asks the terminal)
:language :langzh|zhs|enwhich language the editor speaks: zh 繁體 / zhs 简体 / en English
:version :verwhich build this is, and where the config, data and log live
:keymaphelix|vimkeymap preset: helix (default) or vim — vim's x s dd ^ $ translated
:keymap-actionslist every action a key can be bound to
:infothis file: where it is, how big, how many lines and characters, its language, which servers watch it
:instant-infodictionary|wiki|record|docs|diagnostics|changeinfo: which of record / diagnostics / dictionary / wiki / docs follows the cursor (blank: by the file)
:rules<欄>…|offtint these columns: :rules 80 100 120; bare is 80 and 100, off clears them
:theme-fillon|off|togglewhether code and quotations sit on a ground (they do not by default)
:shotscreen|png|html|txt <path>a picture: the clipboard by default, or a file with png/html/txt
:yume :yon|offthe Yume input engine's switch: on (中文) or off
:yume-wherewhere the 碼表 and other data are looked for, in order
:yume-whichwhich scheme is in use, and where its 碼表 came from
:yume-schemelingming|xingchen|qingyun|riyue|pinyinstart typing: load a scheme (with no name, the configured one)
:yume-chaifenon|off|togglethe 拆分 annotation beside the candidates
:yume-commitdefault|delayed|unique|fluency|autocommit method: default (the scheme's own), delayed (頂字), unique, fluency (whole sentence)
:yume-panelbox|bottom|inlinewhere the candidates are drawn: box by the caret, bottom on the bottom line, inline on the line under the caret
:yume-inline-preeditoff|composition|previewwhere the composition shows: off in the candidate bar, composition or preview in the text
:yume-menu-size<1–9>how many candidates a page holds (1-9, 6 by default)
:yume-autocompletionon|off|toggleautocompletion: offer candidates before the code is finished
:yume-installeduse the 碼表 the system has installed (builtin's other half)
:yume-builtinuse the built-in 靈明 table, whatever else is installed
:yume-table<path>use your own table (a Rime .dict.yaml will do)
:syntax :synmarkdown|typst|text|python|javascript|json|yaml|toml|html|css|ada|c|gas|go|java|nasm|pascal|r|rust|<語法>which markup this file is written in (with no argument, which one it is now)
:pipe<命令>send the selection through a terminal command and take back its output
:sh<命令>run a command and keep its output in a buffer
:!commandhand the terminal over and watch it run (vi's spelling)
:which-wrap<字母>which keys cross a line end, spelled as in vim: b h l < > ~ — vim keys only
:view-wrapon|off|0|<幾欄>wrap long paragraphs to the next row; :view-wrap 50 sets a measure, off stops wrapping
:view-wrap-vertical0|<幾字>how many characters go in one vertical column; 0 follows the window
:view-marginnever|dense|loose|alwaysmargin: the lane for readings, hung marks, emphasis dots and 平仄
:view-pages-horizontal<1–4>pages, horizontal: side-by-side columns, the text running on from the left one to the right
:view-pages-vertical<1–4>pages, vertical: divide the vertical page across — top right to top left, then bottom right to bottom left
:view-sentenceon|off|toggleone sentence to a column — a view of the page, not an edit
:view-hangingon|off|togglehanging punctuation: 句讀 sit in the margin
:view-numbers-fillon|off|togglewhether the line-number band has a ground of its own (no by default, in both layouts)
:view-diffon|off|togglechange bar: the cell beside the line number says which lines differ from git
:view-typewriteron|off|toggletypewriter: the row you are writing stays in the middle and the paper moves
:view-focuson|off|togglefocus: the paragraph you are writing stays lit and the rest of the page stands back
:view-meteron|off|toggle平仄: the tone class of every character in the margin, 韻腳 at the end of a 句
:view-puncton|off|togglepunctuation hints: the full-width mark a half-width one should have been
:view-codeon|off|togglecode colours: code in a fence, coloured by its own grammar
:view-long-lineoff|<幾字>stop colouring a line past this many characters
:view-hudoff|basic|fullthe little sign beside the caret: nothing, a pill, a bordered panel
:view-previewon|off|togglehand it to a real typesetter and read it in a browser
:renderoff|basic|fullhow much of the result the page shows: source, coloured, WYSIWYG
:indentoff|basic|full|<幾格,至多 8>how a paragraph opens: off / basic (indented, blank line kept) / full (the blank line folded too), or a width
:indent-tabspaces|tabwhat Tab types: spaces or a tab (Shift-Tab types the other)
:indent-width<幾格,1–16>indent width: the spaces Tab types, and how far > and < shift
:reflow<幾欄>refill the selection to a width (80 columns by default)
:auto-pairsoff|basic|fulltype an opening bracket and its closer comes too: off, basic, full; bare says which it is now
:indent-hintnone|color|symbolwhat is drawn in a paragraph's opening squares
:table<行>x<欄>an empty table: :table 3 4 is three rows by four columns, the first row the headings; only on a blank line
:table-renderoff|basic|windowhow much of a table is drawn: the source, basic, or window view; bare says which it is now
:table-checklook the whole table over: repeated row names, components with no row, rows of the wrong width
:table-rulesoff|color|line|<樣式> solid|dash|doublecolumn rules: how the columns are told apart
:table-sort<欄> a|d …sort by these columns: sort 1 a 2 d is column 1 ascending, then column 2 descending
:replace :r<path>search and replace
:replace-buffers :rbreplace across every open buffer
:replace-working :rwsearch and replace in the working directory
:replace-project :rpsearch and replace in the project
:pipe-before<命令>run a terminal command and put its output before the selection
:pipe-after<命令>run a terminal command and put its output after the selection
:read<path>put a file's contents after the selection
:pipe-to<命令>send the selection through a terminal command and keep nothing back
:yuhaoopen the Yuhao input method website
:search :s :grep<path>search: open the search panel — across files, with more options
:search-buffers :sbsearch every open buffer
:search-working :swsearch the working directory
:search-project :spsearch the project
:sidebaroff|explorer|buffers|outline|search|infothe sidebar: bare says what is open, off closes it, a name opens that panel
:table-detailon|off|togglethe record panel: on, off, or a width
:table-numberson|off|togglethe row of column numbers — what ␣t3/ and ␣t20,20g count with
:table-headeron|off|togglewhether the first row names the columns or is a row like any other
:table-schemaopen this table's schema in the other region — writing a starting one if there is none
:table-jump<列名>go to the row this table names (:table-jump 木)
:table-findrow|column|<字符串> <字符串>search: row runs across (that is /), column runs down (that is Space t / in a table)
:paste-table<格式> [<格式>]paste the clipboard's table as the named format; with two words the first is the source
:convert-table<格式> [<格式>]convert to the named format (csv, tsv, pipe); with two words the first is the source
:wheel<幾行,1–10>how far one notch of the wheel moves; :wheel 1 is the terminal's own
:clipboardthe paste menu: the clipboard and what was yanked, pick one
:clipboard-yankput the selection on the system clipboard
:clipboard-pastepaste from the system clipboard
:bufferthe open files: list, switch, close
:buffer-next :bnthe next one
:buffer-previous :bpthe previous one
:buffer-close :bcclose this one (close! ignores changes)
:format :fmtformat this file the way the config says ([language.markdown] format = …)
:run<名字>run the command this language names in the config
:markdown-footnotea footnote: the next free number, and its note opened with it
:markdown-footnote-inlinean inline note ^[…], which does not go to the foot
:settings :seta full page of settings, each with a name and a note; :w saves
:resumetake up this directory's last session — adds, closes nothing
:recent-filesfiles opened under this project, newest first
:recent-files-globalfiles opened anywhere, newest first
:recent-projectsthe directories a session was kept for
:tutora lesson: the text is copied into a file of your own, and you learn by editing it
:help :hchinese|vertical|table|commands|<題目>the keys and the commands, opened as a file you can read and search
:export :exhtml|typst|csv|tsv <path>write it out as html or typst, layout and all
:diff<path>what changed, by word rather than by line
:lsp-rename<新名>rename the name under the caret everywhere, through the language server
:git-show<提交>the committed draft, read-only, in the other region
:git-diff<提交>against what git has, word by word: HEAD unless a commit is named
:toc :outline<第幾條>list the headings; :toc 3 goes to the third
:rubyedit the reading here
:ruby-renderoff|basic|fullreading render mode: the source, coloured, laid out beside the base; with no argument it reports the mode
:ruby-autowrite the readings in, by word (the selection, or the file)
:ruby-auto-rareannotate only the characters no everyday list carries: 通用規範漢字表, its traditional forms, Taiwan's and Hong Kong's
:ruby-htmlon|off|toggleread <ruby>
:ruby-typston|off|toggleread #ruby(…)
:ruby-formathtml|typstrewrite the readings in another dialect
:s/pat/rep/substitute in the selection; %s the file, 1-40s a span, 1,5,9s a list; flags g i f c n t y j

Settings

Settings live in ~/.config/yumete/config.toml, and a project can override them in .yumete/config.toml. :settings opens all of them as a page you can edit and save with :w; :reload-config reads the files again after you edit them by hand. See Configuration for how the files work.

The sections below explain the settings that need more than a line. The list of every setting on the panel is at the end.

Auto-pairs: auto_pairs

Brackets and quotes close themselves as you type, as in helix. Type ( and you get () with the cursor between them; type ) in front of the ) and you step over it, so (x) typed in full still comes out once. Backspace between the two halves of an empty pair deletes both, and Enter between them opens the pair onto three lines, with the cursor on the middle one (indented one level in code).

[editor]
auto_pairs = "basic"   # "off" | "basic" | "full"

The same three levels are :auto-pairs off, :auto-pairs basic and :auto-pairs full; :auto-pairs alone says which one is on. The settings panel has it under Editing.

LevelWhat pairs
offnothing
basic (default)helix's six: ( ) [ ] { } ' ' " " and two backticks
fulleverything the m group knows as a pair, but < >

What full pairs, beyond the six:

KindPairs
Full-width brackets( ) [ ] { }
Chinese brackets「 」 『 』 【 】 〔 〕 〖 〗 〈 〉 《 》
Curly quotes“ ” ‘ ’ 〝 〞
Full-width quotes" " ' ' ` `
Others« » 「 」

< > is left out even under full: in code, a < b would come out as a <> b.

When a pair is not written:

  • A letter, a digit or a Chinese character follows the cursor. Typing ( before text already written is usually the start of wrapping it, so only the ( goes in.
  • A quote right after a letter or a digit. don't keeps one apostrophe.

full works through the input method too. With Yume, [ commits 「 and you get 「」; the 」 that ] commits later steps over the one already there. " commits “ the first time and ” the next, so a quotation typed straight through comes out right.

full is not the default: dialogue in a novel is typed straight through, and a closer waiting ahead of the cursor is one more thing to step past.

Status-line count: status_count

[editor]
status_count = "text"   # "text" | "all" | "off"

text (default) shows the word count on the status line for Markdown, plain text and Typst, all for every file, and off nowhere. The settings panel has it under Marks. How words are counted is in Checks and counts.

Not on the panel

A few keys are not on the settings panel. Each is explained on the page that owns it:

KeyPage
[ime] data_dirsThe input method
[editor] usage_groupsChecks and counts
[editor] screenshotConfiguration
[export] pageExport
[syntax], [editor] syntaxThe page
[language.*] and [lsp.*]Language servers
[keys.normal]Key remapping
the theme coloursThemes

Every setting on the panel

Written from the settings panel's own list (ye --docgen), grouped as the panel groups them. The default is what yumete uses when the file says nothing.

layout

SettingDefaultWhat it does
[editor] layout"horizontal"layout — whether the page runs across or down (horizontal across, vertical down)
[editor] vertical_keys"visual"hjkl when the page runs down — in vertical layout, whether hjkl follow the text or the screen (logical logical: h l by character, j k by line, visual visual: j k down/up, h l left/right)
[editor] zong_length0column length — characters to a column when set down; text reads well up to about 32 (8–64)
[editor] zong_gap0column gap — half-width cells between one column and the next (0–4)
[editor] pages_vertical1pages, vertical — how many bands a set-down page is cut into across (1–4)
[editor] pages_horizontal1pages, horizontal — how many columns the horizontal page is cut into, side by side (1–4)
[editor] indent0indent — cells at the head of a paragraph; the Chinese convention is 2 (0–8)
[editor] measure0measure — the line really breaks here; what is right of it is margin (0–400)
[editor] ruler0ruler — a line, not a break: what runs past it changes colour (0–400)
[editor] insert_final_newlinetrueadd a final line ending — on save, add the line ending this file's own lines use when the last one is missing it; an empty file stays empty
[editor] soft_wraptruesoft wrap — long lines fold when set across; off, they run past the right edge
[editor] hanging_punctuationfalsehanging punctuation — 。,、?!:;「」 hang in the margin instead of taking a cell
[editor] margin"dense"margin — when to show the lane that carries readings, emphasis and 平仄 (never never, dense when dense, loose when loose, always always)
[editor] tatechuyoko4tatechuyoko — half-width characters set across inside one cell, so a year is not one digit to a row (2–8)
[editor] paper_ticks0paper ticks — a faint tick in the margin every so many cells, the way squared paper does (0–64)
[editor] ambiguous_width"auto"ambiguous width — whether East Asian ambiguous-width characters such as — and … take one cell or two; change it if the caret sits off its character (auto ask the terminal, wide two cells, narrow one cell)

marks

SettingDefaultWhat it does
[editor] line_numbers"absolute"line numbers — the column on the left that numbers the lines; relative numbers count from the caret, which makes jumps easy to type (absolute absolute, relative relative, none none)
[editor] line_number_fillfalseline number fill — fill the line-number column with a faint ground, not just the number
[editor] diff_guttertruediff gutter — the cell beside the line number marks lines that differ from git, or from the saved file outside git
[editor] word_level"balanced"word level — how readily characters are joined into a word: strict takes only the certain ones, loose whatever the dictionary has (strict strict, balanced balanced, full full (loose))
[editor] word_mark"color"word mark — how word boundaries are shown (off none, color colour, ink brightness, tint tint, line underline)
[editor] code_highlighttruecode highlight — code in a fence is coloured by its own language; off, the whole block is one colour
[editor] show_rubyfalseshow ruby — lay readings out beside their characters. Off by default: what is in the file is what is on the page
[editor] indent_hint"none"indent hint — on a vertical page, a mark at the head of each paragraph, so the breaks in a long one are easy to find (none none, symbol a mark, color colour)
[editor] indent_symbol"↵"indent symbol — which character the paragraph mark is; used when the indent hint is set to a mark
[editor] table_rules"line dash"table rules — how the column rules of a table are drawn (line dash lines and dashes, line lines, line double lines and double lines, color colour, off none)
[editor] char_infotruechar info — the right end of the status line names the character under the caret: its code point and Unicode block
[editor] status_count"text"count on the status line — how long the file is, on the status line, and the selection too when there is one (text text files, all every file, off off)
[editor] unicode_alarmtruesuspicious characters — tint the cell after a zero-width or bidi control, and any space that is not a plain space

interface

SettingDefaultWhat it does
[editor] scrolloff3scrolloff — how many lines (columns, on a vertical page) to keep between the caret and the edge (0–20)
[editor] wheel_step3wheel step — how many lines one notch of the wheel moves; 1 is the terminal's own step (1–10)
[editor] tabs"auto"tabs — the bar along the top that lists the open files (auto two or more, always always, never never)
[editor] command_linetruecommand line — a line under the status line for commands, for what just happened, and for the keys you can press now
[panel] roundedtruerounded — whether panel corners are round or square
[editor] language"auto"language — interface language (auto the system's, zh 繁體, zhs 简体, en English)

colours

SettingDefaultWhat it does
[theme] name"ink"name — which ink to use: ten built-in themes, named by their names (墨香 and moxiang are the same) (ink moxiang: black, white-gold, gold and red ink; every other shade computed, one-dark one-dark: the scheme most VS Code users know; dark only, github github: GitHub's own scheme, as on the site and in VS Code; light and dark, monokai monokai: the classic Sublime Text scheme; dark only, dracula dracula: the purple-toned dark scheme; dark only, tokyo-night tokyo-night: soft blue-purple tones; light and dark, catppuccin catppuccin: pastels; Mocha dark, Latte light, gruvbox gruvbox: retro colours on warm brown; light and dark, bw heibai: black, white and grey only — weight says it, colour says nothing, morandi morandi: muted, greyed colours; light and dark)
[theme] mode"auto"mode — the dark or the light half of the theme; ask the terminal checks what its background is (auto ask the terminal, dark dark, light light)
[theme] ground"paint"ground — the editor paints the paper itself, or leaves the background to the terminal (use this with a transparent terminal) (paint paint the paper, terminal the terminal's own)
[theme] status_bar"sunken"status bar — whether the two bottom lines sit lower than the paper or one step towards the ink (sunken sunken, raised raised)
[theme] fillfalsefill — whether code blocks and quotes sit on a faint ground

input

SettingDefaultWhat it does
[ime] scheme"lingming"scheme — which 碼表 to type with. 靈明 and 星陳 come built in; the others need the data installed (lingming 靈明, xingchen 星陳, qingyun 卿雲, riyue 日月, pinyin 拼音)
[ime] commit"default"commit method — when a finished code goes to the page (default the scheme's own, delayed delayed (頂字), unique unique, fluency whole-sentence)
[ime] startfalseload the 碼表 at start — off by default: the word model loads as usual, and the 碼表 waits for the first Chinese you type
[ime] system"auto"move the system input method aside in Normal mode — while yumete reads the keys itself (Normal mode, the command line), ask the system's input method to step aside, so it does not eat them (auto auto, keep leave it)
[panel] display"box"where candidates go — where the candidates are drawn while you type: a box by the caret, the bottom line of the window, or the line under the caret (box box, bottom bottom line, inline in the line)
[panel] inline_preedit"off"preedit — whether what you are typing is written in the candidate bar or in the text, and if in the text, the code or the first candidate (off in the candidate bar, composition the code in the text, preview the first candidate in the text)
[panel] page_size6page size — how many candidates a page lists (1–9)
[panel] markers"123456789"markers — the characters the nine candidate numbers are drawn as
[editor] show_chaifenfalseshow 拆分 — while typing, show each candidate's 拆分 beside it

editing

SettingDefaultWhat it does
[editor] word_completion4word completion — after this many Latin letters, list words that appear in the open files; for Chinese characters, half as many (rounded up). C-n lists them at any time (0–20)
[editor] auto_pairs"basic"auto-pairs — typing an opening bracket or quote adds its closer; typing the closer then steps over it (off off, basic basic, full full)
[editor] completion_page_size6completion page size — how many items the word, code and shell completion lists show at once (1–20)
[editor] indent_width4indent width — how many columns > adds and < takes away, and how many spaces Tab types (1–16)
[editor] tab_width8tab width — how far a tab character reaches: to the next multiple of this. Code tables line up by it (1–16)
[editor] tab_inserts"spaces"tab inserts — what Tab types in Insert mode; Shift-Tab types the other (spaces spaces, tab a tab)
[editor] smart_casetruesmart case — searching: no capital in the pattern ignores case, one capital respects it
[editor] fuzzy_searchfalsefuzzy search — whether the search panel's fuzzy switch starts on

files

SettingDefaultWhat it does
[editor] reload_autotrueauto reload — reload the file when something outside changes it
[editor] autosavetrueautosave — while a file is unsaved, keep a copy beside it, so a crash loses nothing
[editor] sessionfalsesession — when started with no file: a blank page, or the files that were open last time
[editor] remembertrueremember files and places — off: nothing is written or read: the line each file was left on, recent files and projects, the last session

keys

SettingDefaultWhat it does
[editor] which_wrap"b"which wrap — which keys cross a line end, spelled as in vim: b h l < > ~ — vim keys only
[editor] language_key"C-^"language key — the key that switches 中/ABC on a terminal that cannot report a lone Shift
[keys] flavorfalseYumete keymap flavour — with this on, d and c only delete and A-d/A-c are the ones that cut, so a tidy-up between a copy and a paste cannot eat the copy. [keys.normal] still wins
[keys] preset"helix"keymap — whether the keys follow helix or vim (helix helix, vim vim)

Coming from helix

yumete's default keymap is helix's. You select first and act second, a cursor is a one-character selection, and most keys do what your hands expect. This page lists the places where they do not.

What is the same

These work as in helix. Motions: h j k l, f t F T (across lines), gg ge gh gl gs, and C-d C-u C-f C-b. Selection: x X % v ; , C s S K & _, A-;, and ( ) to rotate the primary selection. Changes: d c y p P R r ~ > < J u U C-a C-x, with A-d and A-c to leave the register alone. Macros are Q to record and q to play, "a picks a register, A-. repeats the last f or t, and C-o C-i C-s work the jump list. Search is / ? n N *. The m group is helix's, with more objects (see Match mode), and so are z, Z and ]/[ for paragraphs, changes and diagnostics.

. is helix's .: it repeats the last insert, not the last change. Under the vim keymap it is vim's . instead (see Coming from vim).

J joins lines as helix does, except across Chinese text: two lines that meet between two Chinese characters are joined with no space.

Words go through a segmenter

Chinese has no spaces between words, so w b e cannot cut at spaces. yumete finds words with a Chinese segmenter instead.

  • w moves by word: on 今天天气很好,我们去爬山 it takes 今天, then 天气.
  • e and b move by clause in Chinese, from one punctuation mark to the next: e takes 今天天气很好, up to the comma. In English they still move by word.
  • W B E still cut at spaces.

:word-level off makes a run of Chinese characters one word, the way helix sees it.

Keys that differ

Keyhelixyumete
`lower casea menu: `l lower, `u upper, `` switch, and conversions between simplified, traditional, full width and half width
A-`upper casenot bound; use `u
H Lnot boundprevious and next sentence
M 'not boundM a names this place, ' a goes back to it, across files
C-ccommentnot bound; use Space c
C-wwindow moderegions, also Space w: w next, h j k l move, s split, q close, o only, e i show or hide the left and right sidebar, E I go into it
gd gDdefinition, declarationdefinition, and definition in the second editor region
gy gi gmtype definition, implementation, last modified filenot bound
= $format, keep by shell commandnot bound
A-o A-i A-p A-n A-a A-e A-bsyntax tree selectionnot bound

gw jump labels are the same key and work the same way, with three differences. In Chinese the labels go on clause starts, the places e stops, not on every word. A label is one letter when the screen has 26 places or fewer, two otherwise. And a label covers one character, so a line of Chinese does not shift when the labels appear.

New in the g group: gL follows a link, gx jumps to a word on the screen when you type its first two letters, gz jumps to a Chinese character when you type its pinyin, g/ searches for the selection, and g? shows the results in the second editor region.

The ] and [ group adds m for a merge conflict. In a code file f, t and c go to the next function, type and comment, as in helix; a, e, T, x and G are not there.

The Space menu

These are the same as helix: f F e b y p P s k d D r a w c C / ?.

These differ:

  • Space E browses files from this file's directory. helix spells it Space ..
  • Not there: j (jump list picker), g (changed files), S (workspace symbols), h (references), ' (last picker), Y, R and G. For references use gr.
  • Added: n for what the character under the cursor is (its readings and how it is written), x for the change on this line, i for the record of the table row the cursor is in, t for tables, " for registers, m for a merge conflict, o for recent files and projects, and z for the input method.
  • A capital shows the same thing in the sidebar instead of a popup: N, K, D, X, I.
  • Space Space turns a page down and Space Backspace a page up.

Tables are on Space t

t is till, as in helix. The table group is Space t. See Tables.

Searching and replacing across a project

Space / opens the search panel in the sidebar. It starts on this file; its In row switches it to the whole project. The panel also chooses case, whole words, regex, and whether pinyin and simplified or traditional characters count as a match. s, S, K and A-K read a regex whatever that setting says, as helix's do.

  • :search-project (:sp) opens the panel already on the project.
  • :replace-project (:rp) opens it with a replace row.
  • :s/old/new/ replaces in this file, the way vim's :s does.

See Search and pickers.

The sidebar

yumete has a sidebar on each side of the text. The capital Space keys above show their panel there, and :sidebar explorer shows the project's files as a tree. Space e is a picker, as in helix, not this panel. C-w e and C-w i show or hide the left and right sidebar, and C-w E and C-w I move into it. :sidebar with a panel name opens that panel, and :sidebar off closes it. See Windows and the sidebar.

What yumete adds

Coming from vim

yumete's default keys are helix's: you select first and act second. Under them some vim habits change the wrong text: x selects a line, and dd deletes two characters. The vim keymap puts vim's way of pressing onto yumete's own commands.

Turn it on in the config:

[keys]
preset = "vim"

or type :keymap vim to use it for this session only. :keymap helix goes back, and :keymap on its own says which keymap is in force. Keys you bind in [keys.normal] win over the keymap. See Key remapping.

Operators wait for a motion

d, c, y, > and < are operators, as in vim. After one, the status line lists the motions it can take, Esc lets go, and a key that is not a motion is told so.

MotionKeys
wordsw W b B e E ge gE
in the line0 ^ $, h l
whole linesj k G gg + - Enter, and H M L
brackets%: to the other half, inclusive, looking along the line when the cursor is not on one
paragraphs, sentences{ }, ( )
find a characterf F t T, and ; , to repeat the last one
text objectsi and a with an object: diw, ci", ya(, dap
the whole linethe operator again: dd yy cc >>, or d_

d, c and y all write to a register, as in vim, so ddp swaps two lines and p after dw puts the word back.

w, b and e go through a Chinese segmenter, so in Chinese they stop at word edges rather than at spaces. See CJK motions. As in vim, w W b B stop on an empty line, and e passes it by.

Case and fill operators

gu, gU and g~ change case and wait for a motion: guw lowers a word, gUU raises the whole line. gUgU, gugu, g~g~ and gqgq are the line too, as in vim.

gq fills lines to 79 columns, as vim's gq does with its factory settings: gqip a paragraph, gqq a line, gq on a visual selection. A blank line ends a paragraph. A > quote marker, and in code a comment leader such as // or #, is written again on each filled line, and a list item's lines hang under its text. A run of Chinese has no spaces to break at, so gq leaves it whole, as vim does. Two lines joined between Chinese characters get no space. A line that opens with the two-square indent (U+3000) is a paragraph of its own: it is never joined to the line before it, and the lines filled after it carry no indent.

v or V and then Esc leaves nothing selected, with the cursor where it stood, as in vim.

Counts

A count can go before the operator, before the motion, or both: 3dw and d3w delete three words, and 2d3w deletes six. 3dd deletes three lines and 2x two characters.

Other keys take a count too. 3p pastes three copies as one piece. 3rx writes x over three characters, and changes nothing when the line has fewer left. 3~ changes the case of three characters.

A count before i, a, I, A, o or O types the text that many times: 5ix then Esc gives five x, and 3o甲 then Esc gives three lines of 甲. One u takes it all back.

Text objects

After an operator, or after v, the object names are vim's: w W s p, the brackets and quotes, and b B for ( and {. yumete adds three:

  • u is a clause, up to any punctuation mark: diu.
  • m is the nearest pair of any kind: dim.
  • k is the Markdown marks around the cursor: dik on **bold**.

A bracket key also covers the full-width and Chinese brackets: di( works inside (), and di[ inside 「」 and 【】. With the cursor outside any pair, di( and da" reach ahead to the next one, as vim does. The full list is in Match mode.

Surround

vim-surround's keys are built in: ysiw( wraps a word, ds( deletes the pair around the cursor, and cs([ changes (…) to […]. ys takes any motion or object, so ys$" quotes to the end of the line.

Visual mode

v starts a selection, V selects the whole line, and gv selects the last selection again. In visual mode o goes to the other end, d or x deletes, c or s changes, u U ~ change case, and p replaces the selection with the register.

A count before . replaces the count the change was made with: after rx, 3. writes x over three characters, and a bare . after that writes three again.

J, gJ, r, > and < in visual mode return to Normal mode afterwards, as in vim. > and < keep the cursor's column, as nvim's do. . repeats a visual J, > or < on as many lines.

C-v starts blockwise visual. The motions grow a rectangle, and the status line says SEL.

  • I and A write before or after the block on every line. I skips a line too short to reach the block, A pads one with spaces, and $A writes at the end of each line. What you type is copied to the other lines on Esc.
  • c d y r ~ u U > < J act on the block.
  • o and O move to the other corners.
  • p puts a block you took back as a block.

. is vim's

Under the vim keymap . repeats the whole last change, as vim's does: a d, a c with what you typed, an r, a paste. ciw then foo then Esc, then w., changes the next word too. Under the helix keymap . repeats only the last insert, as helix's does.

Other keys

KeyDoes
x Xcut the character under or before the cursor
s Schange one character, change the whole line
D C Ydelete, change, yank to the end of the line (Y as in Neovim)
rreplace the character; with the input method on, pick a Chinese one. A count writes over that many; r Enter breaks the line, in the file's own line ending
Roverwrite mode until Esc; Backspace gives back what was covered
u C-r Uundo, redo, undo all recent changes on the line
J gJjoin lines; gJ adds no space at all
0 ^ $start of line, first character, end of line
%the matching bracket; off a bracket, the partner of the first one further along the line, and d% deletes from the cursor to there
* #the word under the cursor, forward or back
n N, gn gNthe next or previous match
m a, ' aset mark a, go back to its line, across files; ` a does the same
N|go to column N; a Chinese character is two columns wide
H M Ltop, middle, bottom of the screen
( )previous, next sentence
g; g,older and newer places you changed
giinsert again where the last Insert stopped
C-o in insert moderun one Normal command and come back
C-u C-w in insert modedelete to the start of the line, the word before; each first stops where this Insert began
Kthe documentation for the thing under the cursor
q, Qrecord a macro (q again stops), play it
ZZ ZQsave and quit, quit without saving

Under the vim keymap the case and conversion menu moves from ` to g` , since ` jumps to a mark: g`s turns traditional characters into simplified ones.

Line wrapping for h and l

h and l stop at the ends of a line, as in vim. [editor] which_wrap takes vim's whichwrap letters (b h l < > ~); only b, for Backspace, is on by default. :which-wrap hl sets it for this session and :which-wrap off clears it.

What is not there

  • There are no named macros: qa…q and @a are not there. There is one macro, the last one recorded; q records it and Q plays it. See Registers.
  • The code objects (functions, parameters, tests, HTML elements) have no vim spelling, and neither has vim's tag object it.
  • vim-surround's visual S is not there. Use ys with a motion.
  • gh and gl do nothing; use 0 and $.
  • :sp is :search-project, not a split. Split with C-w s; there is no vertical split. With the window split, :q closes the region you are in, as vim's :q closes a window.
  • :set opens the settings panel. It does not take option=value; each option is its own command or config key. See Settings.
  • There is no :g and no :normal.

Everything else, the Space menu and the input method included, is the same under both keymaps. See Coming from helix.

FAQ

How do I change the interface language?

By default yumete follows your system's locale: a Taiwan, Hong Kong or Macau locale gets traditional Chinese, any other Chinese locale gets simplified Chinese, and everything else gets English. To choose one yourself, set it in the config:

[editor]
language = "zh"    # "auto" (default), "zh" traditional, "zhs" simplified, "en"

:language en switches for the current session without a restart, and ye --lang=en does it for one run. See Configuration.

Why do I get letters when I type, not Chinese?

The input method is off when yumete starts, so insert mode types letters. Type :yume (or just :y) to turn it on; the first time, it loads the scheme's code table. To have it on from the start:

[ime]
start = true

If it is on and you still get letters, it is in English ([ABC]). Tap Shift on its own to switch between Chinese and English. Terminals that cannot report a lone Shift (Apple's Terminal, for one) use C-^ instead; [editor] language_key sets that key. See The input method.

yumete crashed. Where is my work?

While a file has unsaved changes, yumete keeps a recovery copy beside it, as a hidden file: chapter.md gets .chapter.md.yumete.<number>. It is removed when you save or quit normally. On macOS and Linux, closing the terminal window (SIGHUP) or a SIGTERM writes every unsaved file's copy on the way out. After a crash, open the file again and yumete asks whether to recover the copy, throw it away, or leave it for later. :recover asks again. Typing in an unnamed buffer is kept too, in the data directory, and comes back the same way.

Recovering replaces the file's text in the buffer; nothing is saved until you :w, and u undoes it. See Files, saving and changes.

Where is the config?

WhatWhere
Your config~/.config/yumete/config.toml ($XDG_CONFIG_HOME/yumete/ if set; %APPDATA%\yumete\ on Windows)
A project's config.yumete/config.toml in the project; its values override yours, key by key
Data, sessions, log~/.local/share/yumete/ ($XDG_DATA_HOME/yumete/ if set; %APPDATA%\yumete\ on Windows)

:version shows the exact paths on your machine. :settings opens a panel of every setting with a note on each, and :w there saves it. After editing a file by hand, :reload-config applies it, and :check-config points out lines it cannot read. See Configuration.

Some cells have an amber background. What is that?

A hidden character is next to them: a zero-width space, a soft hyphen, a byte-order mark, or a bidi control. A tinted space is a space that is not a plain one, such as a no-break space. Put the cursor on the tinted cell and the status line names the character. To turn this off:

[editor]
unicode_alarm = false

See Checks and counts.

How do I hide the count on the status line?

Set [editor] status_count = "off", or "all" to show it in code files too. See Settings.

How do I make ye reopen yesterday's files?

ye -r reopens what was open last time in this project. Set [editor] session = true to do it every time. See Files, saving and changes.

Can I use vim keys?

Yes. Set [keys] preset = "vim". See Coming from vim.

What is not there yet?

  • Reading marks (the circles and dots used in classical texts). They can be drawn in the margin, but there is no markup for them yet.
  • Rotated Latin text in vertical layout. A terminal cannot rotate letters, so a long Latin run stacks letter by letter down the column. A few half-width characters, four by default, can sit side by side in one square (see Vertical layout), but not a sentence.
  • Editor regions side by side, or more than two. C-w s splits the editor into two, one above the other, and that is all. See Windows and the sidebar.