Editora 0.20.0 docs. You are reading an archived version, kept so links from that release keep working. Read the 0.21.0 docs →
Code navigation
This page covers two parts of moving around code. Reach is whether the thing can be found at all, which the server-free symbol index handles. Flow is whether you can jump without losing your place, which the peek, preview and related-file commands handle.
For pickers, find, and project-wide search, see Navigation & search.
Go to Symbol in Project
Go to Symbol in Project (index.gotoSymbol) finds a class, method or
function anywhere in the project with no language server installed. Editora
carries its own declaration scanner, so the command works on a first run and in
every language that ships a grammar but has no server.
Sixteen language ids are covered: Java, Kotlin, C#, Python, JavaScript, JSX, TypeScript, TSX, Go, Rust, C, C++, Ruby, PHP, shell and Lua. It records the kinds of symbol a pattern can identify reliably: type, interface, enum, method, function, field, variable and module. LSP has twenty-six kinds, and most of them need real type resolution to tell apart.
The same index backs the file and symbol halves of Search Everywhere and Go to Related File.
It is built lazily
The index is not built when a project opens, because walking every file is wasted work if you never ask for a symbol. The first query does the walk. It takes under a second on a 650-file project and is announced in the status bar so it doesn’t look like a hang. After that the index is incremental: saving a file rescans only that file, from the text already in memory.
There is no second filesystem watcher; the project tree
already runs one, and a file it reports as changed outside the editor is
rescanned on its own. When the tree cannot account for a change (many files at
once, for example), the index is marked stale and the next query walks the
project again. Rebuild Symbol Index (index.rebuild) forces that walk.
It honours your .gitignore the same way Find in Files and the project tree do,
skips files over 2 MB, and is bounded at 20,000 files and 400,000 symbols so
that it cannot grow without limit in the background.
It under-reports on purpose
When the scanner isn’t sure, it records nothing. A missing declaration costs you one fallback to search, while an invented one sends you somewhere that doesn’t exist.
The index is a fallback for when no language server is running. Where a
language server is running it is better at this in every respect,
so use LSP: Go to Symbol in Workspace (lsp.gotoSymbol) there.
Turn it off in Settings → Editor → Display (“Project symbol index”), or with
view.toggleSymbolIndex. Disabling it releases the project’s symbols.
Sticky scroll
Sticky scroll pins the enclosing scope headers above the viewport, so deep in a long method you can still see the class, the method and the block it belongs to. Clicking a pinned row jumps to that header.
Two rules make it stay out of the way:
- A header already on screen is not pinned. Pinning it would cover the real line with a copy of itself.
- When the chain is deeper than the cap (5 rows), the outermost entries are kept. The innermost scope is the one you can infer from the code in front of you; the file-level type has been off screen longest.
Rows are rebuilt from the highlighting the editor has already applied, so they
match what’s on screen and cost no extra tokenizing. It’s on by default, and off in
large-file mode along with the other per-viewport work.
Toggle it in Settings → Editor → Display or with view.toggleStickyScroll.
Peek Definition
LSP: Peek Definition (lsp.peekDefinition) shows a definition over the
editor and leaves your place, scroll position and tab count where they were.
Often all you need from a definition is its signature and a few lines of body, and that isn’t worth leaving your place for. The peek shows a couple of lines of context and a dozen of the definition, syntax-highlighted. Enter makes the real jump and Esc dismisses the peek.
A library target with no file to read degrades to the full go-to-definition rather than reporting a failure.
Needs a language server for the file.
Go to Definition in a Split
LSP: Go to Definition in Split (lsp.gotoDefinitionInSplit) puts the
definition in a new editor group and leaves the
origin where it was, so the call and the thing called are on screen together.
A definition in the same file jumps without splitting: there’s only one tab, so the split would move it and leave the group it came from empty.
Preview tabs
Browsing uses one tab however many files you glance at. Arrowing through a picker, or following a definition to see what something is, reuses a single slot whose title is italic to show that it is not permanent yet, the same idea as the unsaved marker.
It becomes a permanent tab when you commit to it:
- editing the file promotes it, and
- opening it explicitly (from the file finder, the project tree, anywhere that isn’t a glance) promotes it.
An already-open file is never demoted into the slot. You opened it on purpose, and in the slot it would be replaced the next time you glanced at something else. A file with unsaved changes is never reused either.
Go to Related File
Go: Related File (nav.relatedFile) pairs a file with its counterpart: a
test and the class it tests, a header and its implementation, a component and its
stylesheet. It works in both directions, and from a test the class under test is
offered first.
| From | Offers |
|---|---|
Foo.java | FooTest.java, FooTests.java, FooSpec.java, FooIT.java |
FooTest.java | Foo.java |
test_foo.py | foo.py |
foo.c | foo.h |
foo.cpp | foo.hpp, foo.hxx, foo.h |
Button.tsx | Button.css, Button.scss |
app.js | app.css, app.scss, app.html |
x_test.go, x.test.ts, x.spec.ts | x.go, x.ts |
These are conventions, so Editora proposes candidate names in preference order
and offers only the ones that exist. A convention that doesn’t hold in your
project costs nothing, which is why the list can be generous. A
counterpart rarely sits beside its partner (a test lives under src/test, a
header under include), so the sibling directory is only the first place looked;
the symbol index knows every file in the project and
finds the rest.
One match opens. Several offer a picker, since Foo.css and Foo.scss can both
exist and only you know which was meant.
Keeping your place
Three things work together here, and each is documented where it lives:
- Back and forward steps through the jump list.
- Recent Locations lists the session’s trail with the line you were on, and previews as you arrow through it.
- Bookmarks mark places you want to return to deliberately, and a mnemonic turns one into a single chord.
Bookmarks have default chords. Back, Forward and Recent Locations do not, and neither do Go to Symbol in Project, Peek Definition, Go to Definition in Split and Related File. Bind the ones you use from Settings → Keymaps.