vi.html

A markdown editor with vim keybindings

Composability

Vim commands are built from parts: an operator (what to do) combined with a motion or text object (to what). Learn the parts and you can construct hundreds of commands from a small vocabulary.

Operator Action
d Delete (cut — goes to a register for pasting)
c Change (delete, then enter Insert mode)
y Yank (copy)
gq Reflow / hard-wrap text
gw Reflow, keep cursor
> Indent
< Dedent
Motion / Text Object Target
w From cursor to next word
iw / aw Inner word / a word (includes trailing space)
is / as Inner sentence / a sentence
ip / ap Inner paragraph / a paragraph
i" / i( / i{ Inside quotes / parens / braces
$ To end of line
} To next blank line
G To end of file

Any operator + any motion = a valid command. dap deletes a paragraph. ciw changes a word. gqap reflows a paragraph. >} indents to the next blank line. Every new motion you learn works with every operator you already know.

Semantic Line Breaks

Semantic line breaks mean putting one sentence per line in your source. The rendered output joins them into paragraphs — the line breaks are for the writer, not the reader.

Vim is a natural fit for this style. Its line-oriented commands become sentence-oriented: dd deletes a sentence, yy copies one, p moves one. { and } jump between paragraphs since blank lines mark the boundaries. And gqap reflows a paragraph while respecting its structure.

Getting Around

These motions work in Normal mode. They also work after an operator — d} deletes to the next blank line, c$ changes to end of line.

Key Action
h j k l Left, down, up, right
w / b Next word / previous word
e End of current word
0 / $ Start / end of line
^ First non-blank character
( / ) Previous / next sentence
{ / } Previous / next blank line (paragraph boundary)
gg / G Top / bottom of document
42G or :42 Jump to line 42
Ctrl-d / Ctrl-u Scroll half-screen down / up
fx / Fx Jump to next / previous x on this line
tx / Tx Jump to just before next / after previous x
; / , Repeat / reverse last f/F/t/T
% Matching bracket
* / # Next / previous occurrence of word under cursor
Since markdown separates paragraphs with blank lines, { and } are your fastest way to move between sections — much quicker than repeated j and k.

Entering Insert Mode

Each of these drops you into Insert mode at a different position. Press Esc to return to Normal.

Key Action
i / a Insert before / after cursor
I / A Insert at start / end of line
o / O Open new line below / above
R Replace mode (overwrite characters in place)
Keep your Insert mode sessions short and focused. Vim treats everything between entering and leaving Insert mode as a single edit — short sessions mean precise undo with u and precise repeat with .

Changing and Deleting Text

The c (change) operator deletes text and drops you into Insert mode. The d (delete) operator removes text and stays in Normal. Both work with any motion or text object.

Key Action
ciw Change inner word (whole word, regardless of cursor position)
cw Change from cursor to end of word
cc Change entire line
C Change from cursor to end of line
ci" Change inside quotes (also ' ` ( [ {)
dd Delete entire line
D Delete from cursor to end of line
diw Delete inner word
dap Delete a paragraph (including trailing blank line)
di( Delete inside parentheses
x Delete character under cursor
~ Toggle case of character
Ctrl-a Increment number under cursor
Ctrl-x Decrement number under cursor
Why ciw instead of cw? cw changes from the cursor to the end of the word. ciw changes the entire word no matter where your cursor is in it. The i means “inner” — the text object without surrounding whitespace. There’s also aw (“a word”) which includes the trailing space.
Delete is really cut. When you dd a line, it goes to a register — p will paste it right back. ddp swaps the current line with the one below. ddkP moves a line up.
Increment and decrement. Ctrl-a and Ctrl-x find the next number on the current line and add or subtract 1. Use a count to change by more: 5 Ctrl-a adds 5. Works with decimal, hex (0x1f), octal (07), and binary (0b101) formats.

Copying and Pasting

Vim calls copying “yanking.” The y operator works with any motion or text object, just like d and c.

Key Action
yy Yank (copy) entire line
ymotion Yank over motion (e.g. yap = yank a paragraph)
p Paste after cursor (or below, for whole lines)
P Paste before cursor (or above)

System Clipboard

Use the "+ or "* register to yank and paste with the system clipboard:

Key Action
"+yy Yank current line to system clipboard
"+ymotion Yank motion to system clipboard
"+p Paste from system clipboard after cursor
"+P Paste from system clipboard before cursor

In the Preview pane, the Copy button copies the document as rich text (minimal HTML) for pasting into Word, email, or other rich text editors. The plain-text fallback is the raw markdown.

Text Objects

Text objects select structured chunks of text. Prefix with i for “inner” (contents only) or a for “around” (includes delimiters or trailing whitespace). Use them with any operator.

Object Meaning Example
w Word diw = delete inner word
W WORD (whitespace-delimited) daW = delete around WORD
s Sentence cis = change inner sentence
p Paragraph gqap = reflow around paragraph
" ' ` Quoted string ci" = change inside quotes
( [ { Bracket pair di( = delete inside parens
With semantic line breaks, sentence text objects (is, as) and line operations (dd, yy) overlap — both target a single sentence. Use whichever feels more natural.

Working with Prose

The gq and gw operators hard-wrap text to a specified width. Set your preferred width with :set tw=72. If textwidth is 0, they wrap at 79 columns (matching vim’s default). gq moves the cursor to the end of the formatted text; gw keeps the cursor in place.

Key Action
gqq Reflow current line
gqap Reflow current paragraph
gq} Reflow from cursor to next blank line
gqj Reflow current line and next
Visual + gq Reflow selected lines
gwap Reflow paragraph, keep cursor position

With textwidth set, lines also wrap automatically as you type in Insert mode. Lines are broken at word boundaries and indentation is preserved.

gqap is the command you’ll use most. Write freely, then reflow the paragraph when you’re done. It respects blank-line paragraph boundaries — it won’t merge separate paragraphs.

List Continuation

Pressing Enter in Insert mode on a markdown list item starts the next line with the same marker and indent. Numbered lists increment automatically (1.2.), and task-list checkboxes carry forward unchecked.

Supported markers: -, *, +, 1. / 1), and GFM task lists (- [ ] / - [x]).

To exit a list, clear the empty marker with Backspace, or press Esc to return to Normal mode and use dd to remove the empty line.

Finding and Replacing

Key Action
/pattern Search forward
?pattern Search backward
n / N Next / previous match
* / # Search for word under cursor (forward / backward)
:noh Clear search highlighting
Command Action
:s/old/new/ Replace first match on current line
:s/old/new/g Replace all on current line
:%s/old/new/g Replace all in file
:%s/old/new/gc Replace all in file, confirm each
:g/pat/cmd Run cmd on every line matching pat
:v/pat/cmd Run cmd on every line not matching pat
Search /^# then press n to jump between markdown headings. Use /^## for subheadings only.
Global edits: :g/TODO/d deletes every TODO line. :v/^#/d keeps only headings. :g/^$/d strips blank lines.

The Dot Command

The most powerful key in vim. . repeats your last change — whatever you just did, do it again.

The dot command repeats the entire last edit, including what you typed in Insert mode. So ciw + new text + Esc is a complete repeatable unit.

The n.n.n. pattern: Search with /, make a change (e.g. ciw + new word + Esc), then n to jump to the next match and . to repeat the change. Step through the whole file with n.n.

Macros

Where . repeats your last change, a macro records an arbitrary sequence of keys into a named register and replays it on demand.

Key Action
qa Start recording into register a (any letter az)
q Stop recording (while recording)
@a Replay macro from register a
@@ Replay the last macro you ran
Counts work: 10@a runs macro a ten times in a row.

Undo and Redo

Key Action
u Undo
Ctrl-r Redo
Vim’s undo is per-edit, and an “edit” is everything between entering and leaving Insert mode. Keep your Insert trips short and undo stays precise.

Counts

Most commands accept a numeric prefix: 5j moves down 5 lines, 3dd deletes 3 lines, 2dw deletes 2 words.

Marks

Bookmarks within your document.

Key Action
ma Set mark a (any lowercase letter)
'a Jump to the line of mark a
`a Jump to the exact position of mark a
Set a mark before jumping away: ma, do your work elsewhere, then 'a to come back.

Visual Mode

Select text, then act on the selection.

Key Action
v Character-wise visual mode
V Line-wise visual mode

Use any motion to extend the selection, then apply an operator: d to delete, c to change, y to yank, gq to reflow, > to indent.


vi.html Commands

Type : in Normal mode to enter the command line.

Command Action
:w Save current buffer to storage
:e Reload current buffer (with name: open buffer)
:editor Switch to Editor tab
:pre[view] Switch to Preview tab
:help Show this help
:tog[gle] Toggle between Editor and Preview
:persist Enable auto-save (default)
:nopersist Disable auto-save
:clear Wipe saved content from storage
:settings Show all current settings
:exrc Edit startup commands
:wq Save and quit (in exrc: save and apply)
:q! Quit without saving (in exrc: discard changes)
:ls List all buffers
:b name Switch to buffer
:bd Delete buffer
:sav[eas] name Save-as to new buffer
:f name Show or rename current buffer

Buffers

vi.html supports multiple named buffers — like files on disk, but stored in your browser. Each buffer has its own content and cursor position. Buffers persist across sessions until you delete them.

Command Action
:e name Open or create a buffer (without name: reload current)
:b name Switch to a buffer by name
:b# Switch to the alternate (previous) buffer
Ctrl-^ Toggle alternate buffer (Normal mode)
:ls / :buffers List all buffers
:w name Name an unnamed buffer, or save-as to a new name
:sav[eas] name Copy current buffer to a new name
:f name Rename current buffer (without name: show current)
:bd [name] Delete a buffer (default: current buffer)
No data loss on switch. Unlike vim, switching buffers automatically saves the current buffer first. You’ll see a confirmation flash — no need to :w before switching.

vi.html Options

Set with :set option. Boolean options can be negated with :set nooption. Use :exrc to persist settings across sessions.

Option Short Type Default Description
textwidth tw number 0 (off) Auto-wrap lines at this column in Insert mode
number nu bool on Show line numbers
relativenumber rnu bool off Relative line numbers
tabstop ts number 4 Tab display width
shiftwidth sw number 4 Indent width for >> / <<
expandtab et bool on Insert spaces instead of tabs
wrap bool on Soft-wrap long lines
spell bool off Enable spell checking (uses browser spellcheck)
spelllang spl string en Spell checking language (e.g. en_us, fr, de)
foldgutter fdc bool on Show fold indicators in gutter
Defaults that differ from vim: tabstop and shiftwidth default to 4 (vim: 8), expandtab defaults to on (vim: off), and number defaults to on (vim: off). These are UX choices for a markdown editor. spell uses the browser’s built-in spellcheck rather than vim’s spell engine.
Appearance: vi.html follows your operating system’s light/dark mode preference automatically. Change your OS appearance and the editor recolors immediately — no reload, no setting.

vi.html Keyboard Shortcuts

Key Context Action
\p Normal mode Toggle between Editor and Preview
\ Preview / Help Return to Editor
Ctrl-^ Normal mode Toggle alternate buffer

Abbreviations

Define short text expansions that trigger automatically in Insert mode. When you type an abbreviation followed by a non-keyword character (space, punctuation, Enter), it expands to the full text. To persist abbreviations across sessions, add them to your :exrc.

Command Action
:ab[breviate] {lhs} {rhs} Define abbreviation: typing lhs expands to rhs
:ab[breviate] List all defined abbreviations
:ab[breviate] {lhs} Show the abbreviation for lhs
:una[bbreviate] {lhs} Remove the abbreviation for lhs
:abc[lear] Remove all abbreviations

Example: :ab teh the — now typing teh in Insert mode produces the . Multi-word expansions work too: :ab sig Best regards, Alice.

See :help Abbreviations in the vim docs.

exrc (Startup Commands)

vi.html supports an exrc — a list of Ex commands that run every time the editor loads. Use it to persist your :set options, abbreviations, and other configuration.

Type :exrc to edit your startup commands. The editor switches to your exrc text. Use :wq to save and apply, or :q! to discard changes.

Lines starting with " are comments. Blank lines are ignored. Example:

" My vi.html config
set ts=2
set sw=2
set tw=72
ab teh the
ab dont don't
persist

Any valid Ex command works. Changes take effect immediately after saving.

See :help exrc in the vim docs.

End-of-Buffer Lines

Lines below the end of the document are marked with ~, matching vim’s end-of-buffer indicator. These are not part of the document — they show where the file ends and empty screen space begins.

Status Bar

The bar across the bottom of the editor shows the current vim mode, the active buffer name, the cursor position (line:column), and a word count with an estimated reading time at 200 words per minute (e.g. 543w · 3m). For documents under 200 words the reading-time estimate is omitted. When you select text in Visual mode, the indicator switches to count just the selection (e.g. 47w sel).

Folding

Markdown headings can be folded to hide their content. Folding is nested: closing a heading hides everything under it, including sub-headings.

Command Description
zo Open fold at cursor
zc Close fold at cursor
za Toggle fold at cursor
zR Open all folds in buffer
zM Close all folds in buffer
Fold gutter: Clickable fold indicators appear in the gutter by default. Toggle with :set nofoldgutter / :set foldgutter.

Persistence

  • Buffers are auto-saved as you type and persist until explicitly deleted with :bd. Use :ls to see all your buffers.
  • Settings and abbreviations are ephemeral by default. To persist them, add commands to your :exrc.
  • Disable auto-save with :nopersist. Re-enable with :persist. To persist this setting, add nopersist to your :exrc.
  • Clear all buffers and reset with :clear.

Preview & SmartyPants

The Preview tab renders your markdown with full GitHub Flavored Markdown support: tables, task lists, strikethrough, and fenced code blocks.

SmartyPants typography is applied automatically in the preview:

You type Rendered as
"hello" “hello”
'hello' ‘hello’
it's it’s
--
---
...
NORMAL [No Name] 1:1