Code editor

Opening, highlighting and formatting

Text/code files open in the editor (Scintilla) with syntax highlighting. There is comment/uncomment (Ctrl+/), move and duplicate line, bookmarks (Ctrl+F2), a symbol/function list, word-based autocompletion, indentation guides, word wrap, zoom and detection of file changes on disk. With versioning enabled, the margin marks the lines added/changed since the last version.

There are also macros (record/replay a sequence of edits with Ctrl+Shift+R / F4, in Tools → Run → Macros), format/beautify (Shift+Alt+F), an ASCII table on the tab's toolbar (to insert characters/box-drawing) and a spell checker (Hunspell). pt-BR and en-US ship built in; other languages you download on demand from the table in Settings → Notes & writing → Spelling (one row per language, with a Download/Remove button). To follow a log file being written, use the Monitor (tail -f) button on the tab's own toolbar. Very large files (above ~1 MB) open in a virtualized lightweight editor that loads only the visible chunks; you can force it through Open → Open in Lightweight Editor... If there is not enough memory to index every line, the editor's bar warns you and only the first lines are available.

Format and lint: Shift+Alt+F formats the document. JSON, XML and SQL LightNote formats on its own, with nothing to install — and it only touches whitespace: key order, numbers and text stay exactly as you wrote them. What counts is the type shown in the status bar, not just the extension: in a draft tab, paste the JSON, choose JSON as the type and format (text that starts with {, [ or < is recognized even without choosing). When there is an error, the text does not change: the spot is underlined in red, the cursor jumps to it and the status bar gives the line, column and reason (missing comma, unquoted key…). The arrow on the Format button has Compact (the JSON or XML on a single line), and with a selection both act only on it — handy for JSON inside a log line. Text with one complete JSON document per line (JSON Lines, also called NDJSON — common in logs and exports) is recognized and formatted line by line, without being merged into a single document.

F7 checks: JSON and XML are validated in the text itself, without saving; for other languages it runs the linter (ruff, eslint, shellcheck, luacheck…), which marks the problems in the editor and in the Problems panel (bottom pane, navigable). Other languages use an external formatter (black, prettier, clang-format, gofmt, rustfmt…); if the type has none yet, Format, F7 and F5 open an assistant that lists only the tools for that type, shows which are already installed and installs the chosen one in one click (or takes another program's command). The full lists are in Settings → Code tools → Formatters by language and → Linters by language, with the format on save / lint on save options. Run the file in the output panel with F6 (captures exit code and duration, with stop/restart) or just a snippet with F8.

Convert from one format to another: the Convert submenu is under Edit, on the Format button’s arrow, in the type menu of the status bar and in the editor’s right-click menu — including in a note. It acts on the selection, or on the whole text when nothing is selected. The top says what it recognized (“Selection: JSON”, “Whole text: CSV”) and lists first the conversions that suit that content; the rest are in groups, grayed out with the reason when they don’t apply. There is JSON ↔ YAML, JSON ↔ XML (attributes become @name), JSON ↔ CSV and JSON → Markdown table (a nested object becomes the a.b column), CSV ↔ Markdown table, CSV → SQL INSERT, JSON Lines (NDJSON) ↔ JSON array, HTML ↔ Markdown, URL parameters ↔ JSON, Base64, URL encoding, HTML entities, JSON string and Unix timestamp ↔ ISO 8601 date. Key order and numbers stay as you wrote them, and CSV recognizes Excel’s ;. Converting the whole text also changes the tab’s type (a CSV turned into a table becomes Markdown), but the file keeps its extension — the status bar says so. In a JSON, the Field names submenu renames only the keys to camelCase, PascalCase, snake_case or kebab-case: values, numbers and indentation stay as they are, and nothing changes if two fields of the same object would end up with the same name. The naming styles (camelCase, PascalCase, snake_case, kebab-case and CONSTANT_CASE) are in Edit → Case, below UPPERCASE, lowercase and Title Case. They act on the selection or, with no selection, on the name under the cursor — never on the whole file — and change only the names: quotes, colons and braces stay where they were. A line that is just a phrase (“customer name”) becomes a single name (customerName).

A .md opened as code becomes a note again in one click. The editor toolbar shows Open as note when the open file is a note — the inverse of Open as → Code in the tree menu, returning the tab to the note view, with formatted editing, reading, outline and properties. The button only appears when the switch is possible: a note that is too large stays in the code editor.

Multi-cursor, history and definition

More editing features: per-occurrence multi-cursor (Ctrl+D, all with Alt+F3), duplicate line (Ctrl+Shift+D), join lines (Ctrl+Shift+J), expand/shrink selection by scope (Ctrl+Shift+Space / Ctrl+Alt+Space), rectangular selection (Alt+drag), fold/unfold all, snippets (Ctrl+J, editable in Settings → Editor), Go to symbol in file (# in the palette) and in the project (Ctrl+T or ##), navigation history (Alt+←/→), a breadcrumb trail at the top of the editor, column rulers, bracket-pair colorization, auto-close/wrap pairs and cleanup on save (trim spaces, final newline) — all in Settings → Editor.

Snippets with Tab. Type the trigger and press Tab: in an SQL file, sf becomes SELECT * FROM TABLE; with TABLE already selected — type the table name and Tab moves to the next field (Shift+Tab goes back, Esc leaves). Ready-made: sf, sc, sw, sj, sg, ins, upd and del (the last two already with WHERE). Each snippet applies to one language or to all, and in Settings → Editor you create your own in any language, with fields ${1:NAME}, repeats $1 and the final cursor $0; right-clicking a selection offers Create snippet from selection. Snippets also show up in the completion list. While the list is open, Tab belongs to it; to stop Tab from expanding snippets, turn off Expand snippets with Tab — Ctrl+J still works.

Local history and clipboard: on every save, LightNote keeps a version of the file (File → Local history…, restorable), independent of Git. Ctrl+Alt+V opens the clipboard history to paste a previously copied item. Ctrl+Shift+T reopens the last closed tab; tabs can be pinned (context menu), which protects them from being released when idle.

Go to definition: F12 or Ctrl+click on an identifier jumps to where it is defined in the workspace; Shift+F12 finds its uses.

Minimap and text operations

Minimap. A thumbnail of the whole document sits on the right edge of the editor: it shows the “shape” of the code (the syntax highlight colours, scaled down) and marks the visible region. Click or drag it to jump anywhere in the file. Toggle it in View → Minimap or in Settings → Editor. The Minimap button on the code tab's own toolbar (next to ASCII) toggles it without leaving the editor. A mark strip runs along the thumbnail's left edge: blue for bookmarked lines (the same dot as in the margin) and amber for quick-search matches — so you can see at a glance how they are spread across the whole file, including the parts that are off screen.

Text operations. The Edit menu — and the editor's context menu — hold the Lines (move, duplicate, delete, sort, join, reverse, number) and Cleanup (trim whitespace, remove empty/duplicate lines, tabs ↔ spaces) submenus. With text selected, the context menu also offers Save selection as a new file…: it writes the snippet to a file next to the current one and opens it in a tab (the original is left untouched).

Indentation, comments and brackets

File indentation. On the status bar, next to the line ending, the Spaces: 4 (or Tab: 4) button tells how the tab indents. Clicking it opens the menu: Indent with spaces or Indent with tabs, the Indentation width (2, 3, 4 or 8 columns), Convert indentation to spaces and to tabs (rewrites the indentation of the existing lines), Detect from content and Use as default, which saves the choice for new files. What you choose there applies to that tab.

Where the indentation comes from. When a file opens, LightNote looks for an .editorconfig in its folder and in the ones above (up to one with root = true) and applies indent_style, indent_size and tab_width; trim_trailing_whitespace and insert_final_newline apply on save, and end_of_line and charset only to a new file. Without an .editorconfig, it looks at the file's own lines and adopts the indentation it finds, if it is clear; otherwise the default stays. That is the order: .editorconfig, then the file, then the default from Settings → Editor, where Default indentation, Detect each file's indentation when opening and Follow the workspace's .editorconfig live.

Smart indentation. Enter after {, ( or [ — and after : at the end of the line, in Python only — opens the next line one level deeper. Between {}, Enter opens the block on three lines, with the cursor on the middle one, and a single Ctrl+Z undoes it all. A }, ) or ] typed on a blank line goes back to the indentation of the line that opened it. For Enter to just copy the indentation of the line above, turn off Smart indentation in Settings → Editor.

Comments, brackets and completion. Ctrl+/ comments in the syntax of the language the tab is using — also in a draft with no file and when you change the highlighting on the status bar — and, where the language only has block comments (HTML, CSS), it uses the block one. Ctrl+Shift+\ takes the cursor to the match of the parenthesis, bracket or brace it is on, and Ctrl+Shift+Alt+\ selects up to it; both are also on the right-click menu. After the words of the document, the completion list brings the language keywords (def, return…), even those not in the file yet. And View → Line numbers hides or shows the numbered margin.

Recent tabs (Ctrl+Tab)

Ctrl+Tab goes back to the tab you used before, in the same pane — not to the neighbouring tab. Holding Ctrl, each Tab moves down the list of recent tabs, which appears in the middle of the pane; releasing Ctrl opens the chosen one, and Esc cancels. Ctrl+Shift+Tab walks in reverse order. Each pane has its own list, a closed tab leaves it and, with a single tab, nothing appears.

Side-by-side panes and the grid

The grid. The editing area splits into panes, each with its own tabs, arranged in rows and columns: a row holds one or more panes side by side, and the rows stack up. The top-left pane is the main one and never goes away, and the last row is the terminals' row. A pane is only born when you ask for it, and it disappears on its own when its last tab leaves.

Splitting. Window → Split Right opens a second view of the active tab in a new column, right beside the pane you are in; Window → Layout → Split Down does the same in a new row, just below the active pane's row and above the terminals' row. Both views edit the same text. When there is no room for another pane, the view goes to the neighbouring pane. Window → Remove Split closes the pane to the right of the main one. The Split button on the toolbar has the same items.

Dragging a tab. While you drag a tab, each pane shows a strip along its right edge and another along its bottom edge: dropping on the right one creates a column just to the right of that pane, and on the bottom one, a row just below it. The strips at the foot of the area take the tab to the terminals' row. A strip that could not create a pane, for lack of room, does not appear. In the tab's menu (right-click), Move to a new pane on the right does the same without dragging, and Move to the next pane and Move to the previous pane take it to a neighbour.

Ready-made layouts. Window → Layout (also on the Split button) arranges the grid in one go: 1 pane, 2 columns, 3 columns or 2×2 grid. Applying a layout never closes a tab: the tabs of a pane that is left over join the neighbour that stays, and the terminals do not change. Equalize sizes gives the columns of each row the same width and the rows the same height. A layout that does not fit this screen is shown disabled, and its tooltip says why.

Empty pane. A pane that a layout creates empty shows Drag a tab here, or click here and open a file: clicking it selects it, and the next file you open goes into it. It does not disappear on its own; the Close pane button removes it.

How many panes fit. The limit comes from the size of the screen, not from a number: a new column is only born if every pane in the row keeps at least about 420 pixels of width, and a new row only if every row keeps about 240 of height — but two columns and two rows always fit. On a laptop the grid stays at two columns; on an ultrawide monitor four or five fit.

From the keyboard. Ctrl+Alt+PgDn and Ctrl+Alt+PgUp move the focus to the next and the previous pane, row by row, wrapping around; the terminals count too. Window → Layout also has Move tab to next pane and Move tab to previous pane.

Reopening the folder. The grid comes back the way you left it: the rows, the columns, the sizes and the tabs of each pane (which only load their file when you pick them). Opened on a smaller screen, the columns that do not fit are merged into the last one that fit — no tab is lost.

Project tasks

Build, tests and lint of the project run without going to the terminal, in Tools → Project tasks. LightNote finds the open folder's tasks by itself: the scripts of package.json (with npm, pnpm or yarn, according to the lock file present), the Makefile targets, the buildPresets of CMakePresets.json, cargo build/test/run/check in a Rust project, pytest in a pyproject.toml that configures it and the recipes of the justfile.

Your own tasks. Hand-written tasks go in a tasks.json in the VS Code format (version 2.0.0), read from .vscode/tasks.json and from .lightnote/tasks.json: if you already have the VS Code one, there is nothing to write. Supported: label, type (shell or process), command, args, options.cwd and options.env, group with isDefault, the windows/linux/osx blocks, comments and the variables ${workspaceFolder}, ${file}, ${fileDirname} and ${fileBasenameNoExtension}. A file with an error shows in the menu with the reason and is ignored; up to 200 tasks are read.

Running. Pick the task in the menu, or press Ctrl+Shift+B to run the default task: the one chosen in Tools → Project tasks → Default task (kept per folder); with no choice, the one the tasks.json marks with isDefault; with none, LightNote asks which to run. The output goes to the output panel, with the exit code and the stop button. Edit project tasks opens .vscode/tasks.json, if it exists, or creates a .lightnote/tasks.json with an example. If a task's program is not installed, the status bar says which one and offers External programs.

Language servers (LSP)

Out of the box, go to definition, find usages, rename and completion search by name: it is fast, but it does not understand scope. A language server (LSP) is a separate program, the same one VS Code uses, that truly understands the language. It comes off: turn it on in Settings → Code tools, in the Language servers (LSP) group, or in Tools → Language server → Use language servers (LSP). Each file type has its own: pyright (Python), typescript-language-server (JavaScript and TypeScript), clangd (C and C++), gopls (Go), rust-analyzer (Rust), lua-language-server (Lua) and bash-language-server (shell).

What changes. With the server active, F12 goes to the definition it points to, Shift+F12 lists the real usages in the search panel, and rename changes only the right symbol: LightNote asks “Rename in N files?”, changes the open tabs (one Ctrl+Z undoes it in each) and writes the closed files keeping their encoding. Completion brings what the server suggests, errors are underlined and listed in the Problems panel, the tooltip over a name shows its type and documentation, and Shift+Alt+F formats through the server when no formatter is configured for the type. If the server does not answer, takes too long (completion waits 1.5 s) or has no answer, the search by name applies, as before; a configured linter still applies on Ctrl+S.

The indicator and installing. On the status bar, an indicator shows the tab's server and its state (pyright · ready, starting…, error, not installed); clicking it brings Restart the server, Stop the server, Show log… and the configuration. To install, use Tools → Language server → Configure for .py…: the same assistant as the formatters shows the command and installs with one click — opening a tab never offers to install anything. The server starts with the first tab of its type and stops 5 minutes after the last one closes; at most 4 run at the same time, a file above 1 MB is not sent to it and each file shows up to 500 errors.

Index

Editing

Tools

Automation (AI / scripts)

Reference