termic.dev
Documentation menu

Code navigation

Go to definition, find usages, a file outline and hover types across seven languages, powered by the same language servers a full IDE uses. Off until you switch it on for a project, and it tells you what it costs first.


Reading code an agent just wrote means following symbols: what is this, who calls it, where does it come from. Termic does that with the same language servers a full IDE uses, from your own toolchain, and starts nothing until you ask it to.

Turning it on

Open a file in a language Termic can serve and a compass button appears above the editor. Click it and you get two choices:

  • This task: navigation for this checkout, for now. The server stops when the checkout’s last task is closed or archived.
  • Always for this project: the same, plus every future task on this project starts with it on.

Termic's compass button open above a models.py editor: a popover headed "Python · zuban" explaining that go to definition, find usages, an outline and hover types will be answered by zuban rather than guessed from the text, that zuban holds about 85 MB on a project this size, with "Turn on for this task" and "Always for this project" buttons and the resolved binary path underneath.

The compass button above the editor. It names the server that will actually run and what it holds, and offers the two scopes: this task, or every task on this project.

Before the first server starts, Termic tells you what it will cost: a language server holds its index in memory and does not give it back while it runs, and the figures are real (about 300 MB for TypeScript, around 3 GB for rust-analyzer on a large repo). You agree once per machine, not once per repo.

Termic's consent dialog headed "Turn on code navigation?": zuban holds about 85 MB on a project this size and keeps it for as long as it runs, it runs once for this checkout however many tasks share it, and it stops when the checkout's last task is closed or archived. A "Show this every time" checkbox is ticked, with Cancel and Turn on buttons.

A real figure for the server that is about to start, not a generic warning. Untick the box and the second project is one click.

Nothing runs in the background, nothing is bundled in the app, and nothing is downloaded without a click.

What you get

Go to definitionF12, or -click the symbol
Find usages⇧F12, or -click a definition
Go to implementation⌥⇧B
Go to type definition⌥⇧T
File structure⌘F12
Back / Forward⌘[ / ⌘]
Search everywheredouble-tap

Hover a symbol for its type and documentation. Completion comes from the server, so it can offer .objects on a Django model rather than only the words already on screen.

-click is one gesture with two meanings, which is the JetBrains behaviour people miss most: click a usage and you jump to the definition; click the definition and you get its callers. A symbol nobody calls says “No usages” rather than doing nothing.

The usages popover floating under a Django model in Termic: headed "ProxyKey" with "11 usages" on the right, listing each hit as file, line number and the source line, with ProxyKey highlighted in every one.

⌘-clicking the definition of ProxyKey asks who uses it. The list floats at the symbol rather than taking over a panel, and each row jumps to that line.

Definitions outside your checkout open too, read-only: a class in site-packages or node_modules is a click away. When the definition lands in a stub file (a .pyi full of ...), Termic hops to the source it describes, because a wall of ellipses is the right answer to “what is the type” and the wrong answer to “show me this code”.

Search everywhere (double-tap ) searches file names always, and classes and functions too once a checkout has navigation on. Definitions rank above imports of them, and prefix matches above substring matches, so Store finds the class rather than the twenty files that import it.

Termic's Search everywhere dialog with the query "Proxy": a SYMBOLS section listing the Proxy class in common/models.py, then ProxyKey, ProxyAdmin, ProxyError, ProxyKeyAdmin and more with their kind and file:line, followed by a FILES section of matching paths and a "⌘P to search only files" hint.

Double-tap Shift. The Proxy class comes first, ahead of the longer names that also match and the twenty files with “proxy” in the path.

The languages

LanguageServerWhere it comes from
TypeScript, JavaScript, TSXtsgo (TypeScript 7)your repo’s node_modules, your PATH, or Termic downloads it
Pythonzuban, ty or basedpyrightyour .venv, your PATH, or Termic installs one
Rustrust-analyzeryour rustup toolchain, your PATH, or Termic downloads it
Gogoplsyour PATH (go install golang.org/x/tools/gopls@latest)
C, C++, Objective-Cclangdships with the Xcode Command Line Tools; apt install clangd on Linux
Swiftsourcekit-lspships with the Xcode Command Line Tools, or the Swift toolchain
Rubyruby-lspyour project’s binstub, or gem install ruby-lsp

A copy inside your project always wins over anything else, because a repo that pins its own toolchain has already made the decision.

Downloads are pinned to a version, verified against the checksum the upstream release publishes, and unpacked into Termic’s own folder. They are never added to your PATH, and deleting Termic deletes them.

Choosing a different server

Some languages have more than one good server, and the right one depends on your project. Python is the clearest case: on a Django codebase with no stubs installed, zuban types Model.objects correctly while the others report it missing.

Settings → EditorLanguage servers lists every server for every language, which ones this machine already has, and lets you pick:

  • Automatic uses Termic’s order (the one in the table above).
  • Pick one and that is what starts.
  • Custom command runs a binary Termic does not ship at all: pyright-langserver --stdio, solargraph stdio, a wrapper script in your repo. It is run as typed, with no shell.

Termic's Code navigation settings: an "Offer it in the editor" toggle, a "Type checking" toggle labelled EXPERIMENTAL and off, then a Language servers table grouped by language. TypeScript and Python show Automatic selected with a radio per candidate (tsgo, typescript-language-server; zuban, ty, basedpyright), each row saying whether it is on this machine, and a Custom command field per language.

Every server for every language, which ones this machine already has, and what happens if you pick one. The custom command field takes a binary Termic does not ship at all.

Every project can override the machine setting from its own settings page, so “this repo needs pyright” does not change what your other repos use.

Changing any of it stops what is currently running, so the new choice takes effect immediately rather than at the next relaunch.

Type checking (experimental)

Navigation is the product; type checking is a separate switch, off by default, in Settings → Editor.

A type checker reads your project the way its own config tells it to, and most projects have never written one. On a large framework codebase, some of what it underlines is about missing type stubs, a library’s own annotations, or a rule you would have turned off. That is why it ships off and is labelled experimental.

When it is on, every underline names the server and the rule that produced it (zuban [assignment]), so you can look one up or silence it in your project’s own checker config. Navigation works exactly the same either way.

When the answers look wrong

Termic tells you when the problem is the environment rather than the code, before you conclude the feature is broken:

  • A Django project with no django-stubs installed, where every model query looks undefined.
  • A TypeScript project whose only tsconfig.json is one a framework generated somewhere else, so path aliases and generated globals read as missing.
  • A C or C++ project with no compile_commands.json, where clangd has to guess your include paths.
  • A Ruby project with a Gemfile and no Gemfile.lock, where ruby-lsp refuses to start at all.
  • A Swift package that has never been built, so there is no index to answer from.

Each one says what is wrong and the one command that fixes it.

What it costs, and when it stops

One server per checkout per language, however many tasks share that checkout. It starts when you open a file it can answer about, and stops three minutes after the last editor on that checkout closes. The permission itself lapses when the checkout’s last task is closed or archived, so a five-minute code read cannot resurrect a multi-gigabyte server months later.

Live servers appear in the Activity monitor with their real memory use, and can be stopped from there.

Last reviewed: August 25, 2026