Skip to content

Editing

Platform Note

Some keybindings may not work or may differ on your system due to differences in keyboard layouts, terminal emulators, and operating systems. Terminals capture and report key events differently, and some key combinations may be intercepted by your OS or terminal before reaching Fresh. If a keybinding doesn't work, check the command palette (Ctrl+P) for alternative bindings, use the keyboard configuration to customize bindings, or browse all available shortcuts in the Keybinding Editor.

Smart Editing

  • Smart Home — Home toggles between first non-whitespace character and column 0.
  • Smart Backspace — Backspace in leading whitespace removes one indent level instead of a single character.
  • Auto-indent — Enter preserves the current indentation level. After {, (, or :, an extra indent level is added.
  • Auto-close — Typing an opening bracket or quote inserts the closing pair. Controlled by auto_close (default: on), independent of auto_indent. Per-language overrides via languages.<lang>.auto_close. Plain text files never auto-close quotes, only brackets.
  • Surround selection — With text selected, typing an opening delimiter wraps the selection (e.g. select hello, type ((hello)). Controlled by auto_surround (default: on) with per-language overrides. It is independent of auto_close: either can be on with the other off.
  • Markdown types delimiters literally — Markdown ships with both languages.markdown.auto_close and languages.markdown.auto_surround set to false, because prose types backticks, quotes and brackets as literal text far more often than as pairs. Set either to true to opt back in.
  • Smart quote suppression — Quotes typed inside an existing string don't auto-close.
  • Bracket matching — Matching brackets are highlighted. Use "Go to Matching Bracket" from the command palette to jump. Enabled by default; toggle via highlight_matching_brackets in settings.

Vertical Rulers

Add column rulers at any position via "Add Ruler" from the command palette. Useful for enforcing line length limits. Remove with "Remove Ruler". Rulers are per-buffer. The rulers config setting can also set default rulers (e.g. [80, 120]).

Ruler columns are 1-based display columns — screen cells, not characters: a ruler at 80 highlights the 80th display column, the last one the text may occupy. A tab advances to the next tab stop and a full-width character (CJK, most emoji) takes two cells, so on lines containing either, the ruler column is not the character count the status bar reports; for plain ASCII text the two numbers agree. Values below 1 are not valid columns and are ignored.

When a ruler column falls inside a full-width character — for example a ruler at an even column on a line of CJK text — the guide marks that character's first cell, so it stays visible and still points at the character occupying the column. On such a row the bar can therefore sit one cell to the left of where it runs on the rows above and below.

Indentation Guides

Enable vertical indentation guides in the Settings UI. The default is off; choose to draw every indentation level, or only the innermost guide for the cursor's current block. The glyph (default ) is also configurable there.

Guides are visual-only: they replace rendered leading whitespace cells without changing buffer text, cursor positions, byte mappings, or mouse behavior. Themes can set the guide color with the indentation_guide_fg key; when omitted, it inherits the whitespace-indicator color.

Turn on editor.rainbow_indentation to color guide levels independently. Themes configure the six-color cycle with indent_rainbow_1 through indent_rainbow_6; these colors are separate from bracket-rainbow and accent colors.

Per-buffer overrideToggle Indentation Guides (Current Buffer) in the command palette flips guides for the active buffer only, leaving the global setting and other buffers untouched. Turning them on in a buffer where the global mode is none draws every level; where it is active, that mode is kept. The choice persists across restarts.

Guides are a source-code aid, so plain-text buffers (language text — undetected files, .txt, and buffers manually set to Plain Text) never draw them, even when guides are enabled globally. Any language can opt out (or plain text back in) with the per-language indentation_guide setting:

jsonc
{
  "languages": {
    "text": { "indentation_guide": true },  // bring guides back for plain text
    "yaml": { "indentation_guide": false }  // suppress guides for a language
  }
}

Current-Line Highlight

The row the cursor is on is highlighted for quick visual tracking. Enabled by default; toggle via the command palette ("Toggle Current Line Highlight") or in the Settings UI. A matching Toggle Current Column Highlight highlights the cursor's column too — useful for visually aligning code with rulers. The Settings UI also has an option to drop the line highlight while text is selected.

Occurrence Highlighting

Every occurrence of the word under the cursor is highlighted in the viewport. While text is selected the selection takes over: the word highlight is dropped and every other instance of the selected text in the viewport is highlighted instead. Only single-line selections of at least min_word_length characters are matched - a one-character selection would mark most of the screen. The highlight uses the ui.semantic_highlight_bg theme key, which every builtin keeps clearly separated from both editor.bg and editor.selection_bg - in truecolor and after 256-colour quantisation. Because a background that visible necessarily overlaps the range where syntax colours live, a foreground that would become unreadable on it is nudged to the nearest legible shade for as long as the highlight is painted. Enabled by default; toggle with Toggle Occurrence Highlight from the command palette or in the Settings UI.

Post-EOF Background

Rows past the end of the buffer keep the theme's editor background, so the empty space below a short file reads as the same surface as the text above it; the end of the buffer is marked by the ~ tildes (show_tilde, on by default). A theme that wants that space called out with a shade of its own sets the editor.after_eof_bg key - in a custom theme JSON, or in the graphical theme editor under Editor -> After End-of-File Background - and those rows use it instead.

Auto-Save

Enable auto_save_enabled in settings to automatically save modified buffers to disk at a configurable interval (default 30 seconds). This is separate from the crash-recovery auto-save, which runs independently every 2 seconds to a recovery directory.

Code Folding

Fold and unfold code blocks via gutter indicators or "Toggle Fold" from the command palette. Up/Down navigation skips folded regions. Each split view maintains its own fold state. Folding works in two modes:

  • LSP folding — uses foldingRange from the language server when available.
  • Indent-based folding — fallback for files without LSP support and large file mode. Fold from any line within an indented block.

Per-buffer overrideToggle Folding Indicators (Current Buffer) in the command palette hides the ▾/▸ gutter arrows for the active buffer only. Existing folds are untouched — collapsed regions stay collapsed and keep their placeholder, and "Toggle Fold" still works — but a gutter click no longer creates a fold, since there is no arrow to aim at. The choice persists across restarts.

Read-Only Mode

Files without write permission and known library paths (rustup toolchains, /usr/include, /nix/store, Homebrew Cellar, .nuget, Xcode SDKs) open as read-only automatically. The status bar shows [RO]. Use "Toggle Read Only" from the command palette to override for a single buffer, or set auto_read_only to false in config to disable automatic read-only entirely (binary files still open read-only).

Whitespace Indicators

Control visibility of space (·) and tab () characters. Configure independently for leading, inner, and trailing positions via the Settings UI or whitespace_indicators in config. A master toggle and per-language overrides are supported. Theme color: whitespace_indicator_fg.

Toggle Whitespace Indicators (Current Buffer) in the command palette flips the master toggle for the active buffer. Switching it on marks every space, whatever the configured per-position settings say — otherwise the command looks inert in a space-indented file with the default settings (spaces off, tabs on). Tab and line-ending indicators follow the configuration, so a language that hides tab arrows keeps hiding them. "Reset Buffer Settings" drops the per-buffer choice.

Line endings can be shown too: whitespace_newlines renders at the end of every line, and whitespace_carriage_returns renders for the CR half of CRLF (and Classic-Mac CR) line endings — so a CRLF file shows ␍↵ where an LF file shows . Both are off by default and follow the same master toggle.

Inside a selection the indicators appear regardless of the settings above, so selected runs of spaces and tabs stay legible without turning indicators on for the whole buffer. Set whitespace_in_selection to false to switch that off; it is independent of the master toggle and of the per-buffer whitespace overrides. Selected indicators are drawn in whitespace_indicator_selected_fg, a subdued color that a theme may set explicitly and that is otherwise derived from selection_bg — without it they would take the selected text's own foreground and read as content rather than as marks.

Inline Diagnostics

Diagnostic messages can be displayed at the end of each line, right-aligned, with version-aware staleness dimming. Disabled by default — enable "diagnostics inline text" in the Settings UI or set diagnostics_inline_text in config.

Line Wrap

When line wrap is enabled (line_wrap in settings), wrapped continuation lines preserve the indentation of their parent line (hanging indent).

Per-buffer overridesToggle Line Wrap (Current Buffer) and Toggle Line Numbers (Current Buffer) flip these for the active buffer only, leaving the global default and other buffers untouched. The override persists across restarts; the editor-wide Toggle Line Wrap / Toggle Line Numbers commands still change the default for everything else and save it to your config. See Per-Buffer Overrides for the naming convention every settings toggle follows.

Multiple Cursors

Edit multiple locations simultaneously:

ShortcutAction
Ctrl+DAdd cursor at next occurrence of selection
Ctrl+Alt+↑Add cursor above
Ctrl+Alt+↓Add cursor below
EscRemove secondary cursors

Selection

ShortcutAction
Ctrl+WSelect word under cursor
Double-click + dragExtend selection word-by-word (after double-clicking a word)
Ctrl+LSelect current line
Ctrl+ASelect all
Shift+ArrowExtend selection in direction
Ctrl+Shift+←/→Select word left/right
Shift+Home/EndSelect to line start/end
Ctrl+Shift+Home/EndSelect to document start/end
Shift+PgUp/PgDnSelect page up/down

Selected line breaks are drawn too: every line break inside the selection highlights the single column it occupies — column 0 on an empty line, just past the text otherwise — so a selection that spans blank lines is visible instead of leaving them looking untouched. Whitespace inside the selection also gets its · / indicators (see Whitespace Indicators).

Block Selection

ShortcutAction
Alt+Shift+↑/↓Block select up/down
Alt+Shift+←/→Block select left/right

Mark Mode

Drop a selection anchor and extend the selection as you move, without holding Shift:

  • Set Mark — set the anchor at the cursor; subsequent movement (including bracket jumps and Home) extends the selection.
  • Cancel Mark — drop the anchor but keep the cursor (soft exit).
  • Clear Mark — clear the anchor and selection (hard exit).

Basic Editing

ShortcutAction
Ctrl+CCopy
Ctrl+XCut
Ctrl+VPaste
Ctrl+ZUndo
Ctrl+YRedo
TabIndent
Shift+TabDedent
Ctrl+/Toggle comment
Ctrl+TTranspose characters

Deletion

ShortcutAction
BackspaceDelete backward
DelDelete forward
Ctrl+BackspaceDelete word backward
Ctrl+DelDelete word forward
Ctrl+KDelete to end of line

Sort and Transform

Available from the command palette:

  • Sort Lines — sort selected lines alphabetically
  • Trim Trailing Whitespace — remove trailing whitespace from all lines

Configure trim_trailing_whitespace_on_save and ensure_final_newline_on_save in settings to run these automatically on save.

Case Conversion

ShortcutAction
Alt+UConvert to uppercase
Alt+LConvert to lowercase

Search and Replace

ShortcutAction
Ctrl+FSearch in buffer
Ctrl+RReplace in buffer
Ctrl+Alt+RInteractive replace (y/n/!/q for each match)
F3Find next match (works with the search bar open, which stays open)
Shift+F3Find previous match (likewise)
Alt+N / Ctrl+F3Find next occurrence of selection
Alt+P / Ctrl+Shift+F3Find previous occurrence of selection

See Search and Replace for more details.

Macros

Record and replay sequences of keystrokes:

ShortcutAction
F5Stop macro recording
F4Play last recorded macro

Use the command palette (Ctrl+P) to access Record Macro, Play Macro, Play Last Macro, and List Macros commands. Macros are recorded into registers 09.

Saving and Promoting Macros

A recorded macro lives only in its register until you persist it:

  • Macro: Save to init.ts writes the macro to your init.ts as an editable editor.defineMacro("0", [...]) block. Saved macros are seeded back into their registers at startup.
  • Macro: Promote to command turns the macro into a registerCommand handler in init.ts, seeded with the recorded steps — a starting point you can extend with loops, conditionals, or any plugin API.

Both write a sentinel-delimited block and reload init.ts immediately.

To bind a custom key to play a macro, follow the example below to add a keybindings section to your local config.json.

This example binds alt+shift+! to play macro 1 and alt+shift+@ to play macro 2.

json
{
  "theme": "dracula",
  "keybindings": [
    {
      "key": "!",
      "modifiers": ["alt"],
      "action": "play_macro",
      "args": {"char": "1"},
      "when": "normal"
    },
    {
      "key": "@",
      "modifiers": ["alt"],
      "action": "play_macro",
      "args": {"char": "2"},
      "when": "normal"
    }
  ]
}

Bookmarks

Jump quickly between locations in your code:

ShortcutAction
Ctrl+Shift+0-9Set bookmark 0-9
Alt+0-9Jump to bookmark 0-9

Markdown Editing

Smart editing for Markdown files (provided by the built-in markdown_source plugin, enabled by default):

  • Enter continues list items (bullets, ordered lists, checkboxes)
  • Enter on an empty list marker removes it
  • Tab indents list items and cycles the bullet style
  • Single-quote auto-close is disabled so apostrophes don't interfere

Compose Mode

"Markdown: Toggle Compose" from the command palette enables a distraction-free mode that conceals markup (**, *, []()), applies soft line breaks at a configurable width, and renders tables. Use "Markdown: Set Compose Width" to adjust the width. Open the same file in a vertical split to see source and composed views side by side.

Compose mode reads the document the way markdown defines it, so a paragraph, list item or quote written across several source lines is re-flowed into one block at the page width — a hard-wrapped file reads as prose rather than as its source layout. The things markdown treats as their own block still are: a blank line, a heading, a thematic break, a table row, a fenced code block, a new list marker, and a hard break (two trailing spaces or a trailing backslash).

Contents Panel

The markdown_toc plugin adds a Contents section to the sidebar, under the file explorer, listing the headings of the active Markdown file as a tree — one row per heading, indented by level, with the # markers stripped. Headings inside fenced code blocks are not listed. The section opens when a Markdown file becomes active and closes when the active file is not Markdown; "Markdown: Toggle Table of Contents" opens or closes it by hand.

  • The highlighted row is the heading whose section contains the cursor. When the pane showing the file is not focused (you are in the sidebar, or reading the file in another split), the row follows the top of the viewport instead. "Markdown: Contents — Follow Cursor/Scroll" pins one or the other for the session.
  • Click a row to put the cursor on that heading and centre it in the pane; focus stays in the sidebar. Press Enter on a row to jump and move focus to the pane. Up/Down browse the rows, Left/Right fold and unfold them.
  • The disclosure glyphs collapse and expand the outline. With foldBuffer on, collapsing a heading also folds its section in the buffer.

The panel works the same way in source and compose mode: compose conceals the markers and re-flows the text but does not move it, so the rows, the highlight and the jump target are identical in both.

Settings live under Plugin Settings → markdown_toc (plugins.markdown_toc.settings in config.json):

SettingDefaultMeaning
follow"cursor""cursor" highlights the heading containing the cursor; "scroll" the one at the top of the viewport
foldBufferfalseCollapsing a heading in the panel also folds its section in the buffer
autoOpentrueOpen the panel for Markdown files and close it for other files automatically
rows10Rows the section asks for; dragging the section divider overrides it

Files above the editor.large_file_threshold_bytes limit keep the headings last scanned and the section title reads "Contents (stale)".

Shell Integration

Run shell commands on your buffer or selection:

ShortcutAction
Alt+|Run shell command on buffer/selection (output shown)
Alt+Shift+|Run shell command and replace selection with output
ShortcutAction
Ctrl+HomeMove to document start
Ctrl+EndMove to document end
Ctrl+GGo to line number
F8Jump to next error/diagnostic
Shift+F8Jump to previous error/diagnostic
Alt+←Navigate back in history
Alt+→Navigate forward in history

See Navigation for more details.

Basic Completions

Fresh offers buffer-word completions without needing a language server — candidates are pulled from the words already present in your open buffers. These appear in the completion popup below any LSP results, so you still get both when an LSP is running.

  • Open the popup explicitly with Trigger Completion from the command palette (check the Keybinding Editor for the current key — by default Ctrl+Space).
  • A setting controls whether the popup also appears automatically as you type (default: explicit only).
  • Tab accepts the highlighted completion; Enter dismisses the popup and inserts a newline.

See LSP Integration for richer completions when a language server is available.

Vim Mode

A Vim emulation plugin is available, providing modal editing with normal, insert, and visual modes. To enable it, open the command palette (Ctrl+P) and search for "vi mode".

:set commands work inside vi mode for common options, e.g. :set number / :set nonumber and :set wrap. The plugin also has settings (in the vi_mode plugin config):

  • autoStart (default false) — enable vi mode on startup.
  • arrowKeys (default true) — allow arrow keys for navigation.
  • searchWordUnderCursor (default true) — * / # search for the word under the cursor.

Released under the Apache 2.0 License