Skip to content

Auto-pair & smart indentation

As you type, the editor closes brackets and quotes for you, keeps your indentation on new lines, opens blocks between braces, and applies one consistent indent unit (tabs or spaces) to the Tab key, backspace-unindent and auto-indent alike. Everything here is a built-in feature of the editor; the matching menu commands and the indent-style setting come from the bundled Indentation plugin.

It is meant to be invisible — it only acts on the character you just typed, and stands down wherever it would get in the way (inside comments and strings, in front of existing words, with the wrong partner character).

Auto-pairing brackets and quotes

Typing an opening bracket or quote inserts its partner and leaves the caret between the two:

You type You get Caret
( () between
[ [] between
{ {} between
" "" between
' '' between

A few rules keep this from being annoying:

  • Type-over. Typing a closing character when the same character is already to the right of the caret steps over it instead of inserting a second one — so typing straight through () or "" leaves a single, balanced pair.
  • No pairing in front of a word. An opener is only completed when the next character is whitespace, a closer, or end of line — so adding ( just before existing text does not inject a stray ) into the middle of it.
  • Apostrophe-aware quotes. A ' typed right after a letter (as in don't) is treated as an apostrophe and is not paired. Quotes only auto-pair in a neutral context (start of a token, after whitespace or an operator).
  • Angle brackets only in markup. < … > auto-pair only in HTML/XML documents, where < opens a tag. Everywhere else < is a comparison or shift operator and is left alone.

Wrapping a selection (type-to-wrap)

With text selected, typing an opener does not replace the selection — it wraps it:

select value  then type (        →   (value)
  • Works with ( [ { " ' (not < — outside markup it's an operator).
  • The inner text stays selected afterwards, so wraps chain: type ( then " and you get ("value").
  • With multiple carets, every selection is wrapped independently — and the whole operation is one undo step.

Deleting a pair

Pressing Backspace with the caret between an empty auto-inserted pair ((|), [|], {|}, "|", '|', or <|> in markup) deletes both characters in one step, so you can undo an auto-pair with a single keystroke. Once there is content between the pair, Backspace behaves normally.

Smart indentation on Enter

Pressing Enter carries the current line's leading whitespace onto the new line, so you stay at the same depth without re-typing tabs or spaces:

  • Keep indent. The new line starts with the same indentation as the line you left.
  • Open one level. If the line you left ends with an opener ({, [, (), the new line is indented one extra level.
  • Open a block. If the caret sits between an opener and its matching closer, the block opens out: the new line is indented one level deeper and the closer drops to its own line at the opener's depth.
if (cond) {|}        Enter        if (cond) {
                     ───────►          |
                                   }

Indent style — tabs, spaces, or auto

One indent unit drives the Tab key, backspace-unindent and the automatic indentation above, so they always agree. Choose it in Preferences ▸ Plugins ▸ Indentation:

Style Behaviour
Tabs (default for new files) One level is a real tab character; Spaces per indent sets its visual width.
Spaces One level is Spaces per indent space characters.
Auto (detect from document) The editor scans the document's existing indentation and follows it — a tab-indented file keeps using tabs, a 2-space file keeps using two spaces. A file with no indentation yet falls back to the default (tabs).

New, untitled files default to Tabs, width 4.

Adjusting indentation by hand

The Indentation plugin adds two commands (also under Edit ▸ Indent) that indent or outdent every line touched by the selection — or the caret's line when nothing is selected:

Command Shortcut
Increase Indent Ctrl+]
Decrease Indent Ctrl+[

Pasting a block of code

Copy a few lines that carry their own indentation — say three lines each starting with twelve spaces — and paste them at an indented caret. Pasted verbatim, the first line would land at your indentation plus the block's twelve spaces (far off to the right) and every following line at column 0 plus twelve, leaving you to re-indent the whole thing by hand.

OctoEdit re-indents the block for you instead: the indentation the block's lines share is removed, the first line lands where the caret already is, and every following line gets the caret line's indentation plus whatever indentation it had inside the block. The block's inner structure is kept exactly; tabs stay tabs and spaces stay spaces.

Only the leading indentation is replaced: the block keeps its own indentation characters, so a space-indented block pasted into a tab-indented document stays space-indented inside. Converting the block's inner indentation to the document's style is a possible future option — the editor does not do it today.

This applies only to a paste that is unambiguously a block:

  • the clipboard holds more than one line;
  • there is one caret and no rectangular (column) selection;
  • there is nothing but whitespace between the start of the line and the caret — pasting into the middle of a line is a splice, and is left verbatim.

Anything else pastes exactly as before. The whole paste is a single Undo step, and a selection is replaced as usual.

Turn it off with Re-indent pasted lines to the caret's indentation in Preferences ▸ Plugins ▸ Indentation (on by default).

Paste as Markdown (Ctrl+Shift+V) goes through the same rule. The converted text is a block that starts at column 0 with its nested list items indented relative to it, so at an indented caret every line gains the caret's indentation and the nesting is kept exactly. See Editing for what the command does.

Per-language defaults

Some languages have one near-universal indentation convention, and the editor ships it as that language's default — a freshly opened file of the type indents correctly with no configuration at all:

Language Default
Python 4 spaces (PEP 8)
Rust 4 spaces (rustfmt)
Markdown 4 spaces (nested lists)
YAML 2 spaces (tabs are not allowed)
JSON 2 spaces
Go tabs (gofmt)
C / C++ tabs

Every other language deliberately carries no default and follows your global indent setting. Switching a document's language by hand (the Language menu) re-applies the matching default immediately.

The default lives in the language's definition file (colorizers/<id>.json, key "indent": { "style": "tabs|spaces", "width": N }). A power user can change a shipped default by placing an edited copy of that file in the user override directory — the catalog replaces definitions by id.

Planned: per-language overrides in Preferences

A settings table for overriding these defaults per language (without touching any JSON) is designed and planned as the next step of this feature; until it lands, the JSON override above and the layers below are the ways to steer indentation.

Where the indent unit comes from (precedence)

When several sources have an opinion about indentation, the editor resolves them in one place and applies the winner, highest precedence first:

  1. The project's .editorconfig — when present and "Apply .editorconfig settings" is on, a project's indent_style / indent_size / tab_width win for matching files.
  2. The language's shipped default — the table above, when the document's language declares one.
  3. Your global indent setting — the Tabs / Spaces / Auto choice above.

So a project that ships an .editorconfig overrides everything for that project's files; a Python file outside any such project indents with PEP-8 spaces even when your global setting is tabs; and everything else follows your global setting. You can always change a document's indentation by hand afterwards.

The precedence holds the moment anything changes: applying a different Tabs / Spaces / Auto choice in Preferences re-resolves the open document immediately, so a file governed by an .editorconfig or a language default keeps its indentation without you having to switch tabs.

Multi-caret

All of the above apply at every caret when you edit with multiple carets: typing ( inserts () at each, Enter keeps each line's indentation, and Backspace between empty pairs deletes both cells at each. A single Undo reverts the whole multi-caret edit in one step.

Backspace across mixed carets

Backspace only deletes pairs when every caret sits between an empty pair. If some carets are elsewhere, Backspace falls back to its normal behaviour at all of them, so it never deletes a stray closer at one caret while editing a word at another.

In comments and strings

When a syntax colorizer is active, auto-pairing and the brace-driven block-open stand down inside comments and string/character literals — there a { or " is ordinary text, not code, so the editor leaves it exactly as typed. Plain indentation (carrying the previous line's whitespace) still works. In a plain-text document with no colorizer there is nothing to suppress, so pairing behaves the same everywhere.