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: 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.

FromOffers
Foo.javaFooTest.java, FooTests.java, FooSpec.java, FooIT.java
FooTest.javaFoo.java
test_foo.pyfoo.py
foo.cfoo.h
foo.cppfoo.hpp, foo.hxx, foo.h
Button.tsxButton.css, Button.scss
app.jsapp.css, app.scss, app.html
x_test.go, x.test.ts, x.spec.tsx.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.